= ({
)}
+ {requiresDiscoveryToken && (
+
+ Enter your API token to test this connection. Copy only the token, without Bearer or quotes.{" "}
+ {prefillData?.credential_url && (
+
+ Get your API token
+
+ )}
+
+ )}
+
{isOAuthAuthType && (
{
expect(onSelectServer).toHaveBeenCalledWith(githubServer);
});
+ it.each([
+ ["suno", "Suno by Ace Data Cloud"],
+ ["seedance", "Seedance by Ace Data Cloud"],
+ ["happy horse", "HappyHorse by Ace Data Cloud"],
+ ["gpt image", "GPT Image by Ace Data Cloud"],
+ ["nanobanana", "Nano Banana by Ace Data Cloud"],
+ ])("finds %s from the bundled catalog and selects its authenticated connection", async (query, title) => {
+ const testDirectory = dirname(fileURLToPath(import.meta.url));
+ const registry = JSON.parse(
+ readFileSync(resolve(testDirectory, "../../../../../../../litellm/proxy/mcp_registry.json"), "utf8"),
+ ) as { servers: DiscoverableMCPServer[] };
+ vi.mocked(fetchDiscoverableMCPServers).mockResolvedValue({
+ servers: registry.servers,
+ categories: [...new Set(registry.servers.map((server) => server.category))],
+ });
+ const onSelectServer = vi.fn();
+ renderWithProviders();
+ await screen.findByText(title);
+
+ fireEvent.change(screen.getByPlaceholderText("Search servers..."), { target: { value: query } });
+ await userEvent.click(await screen.findByText(title));
+
+ const server = registry.servers.find((entry) => entry.title === title)!;
+ expect(onSelectServer).toHaveBeenCalledWith(server);
+ expect(server.auth_type).toBe("bearer_token");
+ expect(server.credential_url).toContain("https://platform.acedata.cloud/console/applications?");
+ expect(screen.queryByText("GitHub")).not.toBeInTheDocument();
+ });
+
it("offers a custom-server escape hatch", async () => {
const onCustomServer = vi.fn();
render();
diff --git a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx
index 22fd64f10d8..2974b106c0c 100644
--- a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx
+++ b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx
@@ -562,6 +562,8 @@ export interface DiscoverableMCPServer {
category: string;
registry_url?: string | null;
transport: string;
+ auth_type?: "bearer_token" | null;
+ credential_url?: string | null;
url?: string | null;
command?: string | null;
args?: string[] | null;
diff --git a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx
index d27e1ca8bf9..8ecbb63dcd1 100644
--- a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx
+++ b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx
@@ -27,6 +27,7 @@ interface UseTestMCPConnectionProps {
oauthAccessToken?: string | null;
formValues: Record;
enabled?: boolean; // Optional flag to enable/disable auto-fetching
+ requiresAuthValue?: boolean;
}
interface UseTestMCPConnectionReturn {
@@ -46,6 +47,7 @@ export const useTestMCPConnection = ({
oauthAccessToken,
formValues,
enabled = true,
+ requiresAuthValue = false,
}: UseTestMCPConnectionProps): UseTestMCPConnectionReturn => {
const [tools, setTools] = useState([]);
const [isLoadingTools, setIsLoadingTools] = useState(false);
@@ -60,6 +62,8 @@ export const useTestMCPConnection = ({
const requiresOAuthToken = (formValues.auth_type === AUTH_TYPE.OAUTH2 && !isM2MOAuth) || isBrowserHeldTokenMode;
const isOpenAPITransport = formValues.transport === TRANSPORT.OPENAPI;
const hasEndpoint = isOpenAPITransport ? !!formValues.spec_path : !!formValues.url;
+ const authValue = formValues.credentials?.auth_value;
+ const hasRequiredAuthValue = !requiresAuthValue || (typeof authValue === "string" && authValue.trim().length > 0);
// For OpenAPI: tools are derived from the spec itself — no auth needed to load them.
// For other transports: auth_type is required, and OAuth interactive flows need a token.
@@ -67,6 +71,7 @@ export const useTestMCPConnection = ({
? !!(hasEndpoint && accessToken)
: !!(
hasEndpoint &&
+ hasRequiredAuthValue &&
formValues.transport &&
formValues.auth_type &&
accessToken &&
@@ -77,6 +82,9 @@ export const useTestMCPConnection = ({
const credentialsKey = JSON.stringify(formValues.credentials ?? {});
const fetchTools = async () => {
+ if (!hasRequiredAuthValue) {
+ return;
+ }
if (!accessToken || (!formValues.url && !formValues.spec_path)) {
return;
}