diff --git a/litellm/proxy/mcp_registry.json b/litellm/proxy/mcp_registry.json index f37fc39813e..ae3bfb26295 100644 --- a/litellm/proxy/mcp_registry.json +++ b/litellm/proxy/mcp_registry.json @@ -258,6 +258,20 @@ {"name": "EXA_API_KEY", "description": "Exa API Key", "secret": true} ] }, + { + "name": "nimble", + "title": "Nimble", + "description": "Web search, SERP, and site extraction data for AI agents", + "icon_url": "https://avatars.githubusercontent.com/u/94446617?v=4", + "category": "Search", + "registry_url": null, + "transport": "http", + "url": "https://mcp.nimbleway.com/mcp", + "auth_type": "bearer_token", + "env_vars": [ + {"name": "NIMBLE_API_KEY", "description": "Nimble API Key", "secret": true} + ] + }, { "name": "tavily", "title": "Tavily", diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_discovery.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_discovery.py index 43cf35c152d..38b26411451 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_discovery.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_discovery.py @@ -90,6 +90,31 @@ class TestMCPRegistryFile: assert linear["url"] == "https://mcp.linear.app/mcp" assert "/sse" not in linear["url"] + def test_nimble_uses_streamable_http_with_api_key(self, registry_path): + """Nimble should expose its authenticated streamable HTTP endpoint.""" + with open(registry_path, "r") as f: + data = json.load(f) + + nimble = next(s for s in data["servers"] if s["name"] == "nimble") + assert nimble == { + "name": "nimble", + "title": "Nimble", + "description": "Web search, SERP, and site extraction data for AI agents", + "icon_url": "https://avatars.githubusercontent.com/u/94446617?v=4", + "category": "Search", + "registry_url": None, + "transport": "http", + "url": "https://mcp.nimbleway.com/mcp", + "auth_type": "bearer_token", + "env_vars": [ + { + "name": "NIMBLE_API_KEY", + "description": "Nimble API Key", + "secret": True, + } + ], + } + def test_well_known_servers_present(self, registry_path): """Ensure key well-known MCPs are in the registry.""" with open(registry_path, "r") as f: diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.integration.test.tsx index 6ab11d1c9ea..9f28a4bb400 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.integration.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.integration.test.tsx @@ -1926,7 +1926,7 @@ describe("CreateMCPServer", () => { }); describe("when prefillData is provided", () => { - it("should populate form fields from discovery data", async () => { + it("should sanitize the server name from discovery data", async () => { const prefillData = { name: "github-mcp", title: "GitHub MCP", @@ -1939,11 +1939,30 @@ describe("CreateMCPServer", () => { render(); await waitFor(() => { - // Server name should be sanitized (hyphens replaced with underscores) - const nameInput = getServerNameInput(); - expect(nameInput).toHaveValue("github_mcp"); + expect(getServerNameInput()).toHaveValue("github_mcp"); }); }); + + it("should populate URL and auth type from discovery data", async () => { + const prefillData = { + name: "nimble", + title: "Nimble", + description: "Nimble search server", + category: "Search", + transport: "http", + url: "https://mcp.nimbleway.com/mcp", + auth_type: "bearer_token", + }; + + render(); + + const urlInput = await screen.findByPlaceholderText("https://your-mcp-server.com"); + + expect(getServerNameInput()).toHaveValue("nimble"); + expect(urlInput).toHaveValue("https://mcp.nimbleway.com/mcp"); + expect(screen.getByText("Bearer Token")).toBeInTheDocument(); + expect(screen.getByText("Authentication Value")).toBeInTheDocument(); + }); }); describe("with back to discovery button", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.tsx index ebca780c766..4b6814dd3cb 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.tsx @@ -383,6 +383,7 @@ const CreateMCPServer: React.FC = ({ alias: sanitizedName, description: prefillData.description || "", transport: transport, + auth_type: prefillData.auth_type || undefined, }; if (transport === "stdio") { diff --git a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx index 67e51ab23aa..6aad69b79e8 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx @@ -542,6 +542,7 @@ export interface DiscoverableMCPServer { category: string; registry_url?: string | null; transport: string; + auth_type?: string | null; url?: string | null; command?: string | null; args?: string[] | null;