From 3a48f243029518d2a7f9d0da6b577802d978d4d7 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 16:50:29 -0800 Subject: [PATCH 01/12] feat: add curated MCP server registry for discovery UI Curated list of 31 well-known MCP servers with names, icons, categories, transport config, and registry URLs. Includes HTTP endpoints for GitHub, Atlassian, Sentry, Snowflake, and Cloudflare. --- mcp_registry.json | 448 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 448 insertions(+) create mode 100644 mcp_registry.json diff --git a/mcp_registry.json b/mcp_registry.json new file mode 100644 index 000000000000..1a89d0c20ac6 --- /dev/null +++ b/mcp_registry.json @@ -0,0 +1,448 @@ +{ + "servers": [ + { + "name": "github", + "title": "GitHub", + "description": "Repository management, file operations, and GitHub API integration", + "icon_url": "https://cdn.simpleicons.org/github", + "category": "Developer Tools", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-github", + "transport": "http", + "url": "https://mcp.github.com/sse", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"], + "env_vars": [ + {"name": "GITHUB_PERSONAL_ACCESS_TOKEN", "description": "GitHub Personal Access Token", "secret": true} + ] + }, + { + "name": "gitlab", + "title": "GitLab", + "description": "GitLab API integration for project and repository management", + "icon_url": "https://cdn.simpleicons.org/gitlab", + "category": "Developer Tools", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-gitlab", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-gitlab"], + "env_vars": [ + {"name": "GITLAB_PERSONAL_ACCESS_TOKEN", "description": "GitLab Personal Access Token", "secret": true}, + {"name": "GITLAB_API_URL", "description": "GitLab API URL (defaults to https://gitlab.com/api/v4)", "secret": false} + ] + }, + { + "name": "atlassian", + "title": "Atlassian (Jira & Confluence)", + "description": "Jira issues, Confluence pages, and Atlassian product integration", + "icon_url": "https://cdn.simpleicons.org/atlassian", + "category": "Developer Tools", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-atlassian", + "transport": "http", + "url": "https://mcp.atlassian.com/v1/sse", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-atlassian"], + "env_vars": [] + }, + { + "name": "linear", + "title": "Linear", + "description": "Issue tracking, project management, and team workflow automation", + "icon_url": "https://cdn.simpleicons.org/linear", + "category": "Developer Tools", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-linear", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-linear"], + "env_vars": [ + {"name": "LINEAR_API_KEY", "description": "Linear API Key", "secret": true} + ] + }, + { + "name": "sentry", + "title": "Sentry", + "description": "Error tracking, performance monitoring, and issue management", + "icon_url": "https://cdn.simpleicons.org/sentry", + "category": "Developer Tools", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-sentry", + "transport": "http", + "url": "https://mcp.sentry.dev/sse", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-sentry"], + "env_vars": [ + {"name": "SENTRY_AUTH_TOKEN", "description": "Sentry Auth Token", "secret": true}, + {"name": "SENTRY_ORGANIZATION", "description": "Sentry Organization Slug", "secret": false} + ] + }, + { + "name": "slack", + "title": "Slack", + "description": "Channel management, messaging, and Slack workspace integration", + "icon_url": "https://cdn.simpleicons.org/slack", + "category": "Communication", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-slack", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-slack"], + "env_vars": [ + {"name": "SLACK_BOT_TOKEN", "description": "Slack Bot User OAuth Token", "secret": true}, + {"name": "SLACK_TEAM_ID", "description": "Slack Team/Workspace ID", "secret": false} + ] + }, + { + "name": "discord", + "title": "Discord", + "description": "Discord server management, messaging, and bot integration", + "icon_url": "https://cdn.simpleicons.org/discord", + "category": "Communication", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-discord", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-discord"], + "env_vars": [ + {"name": "DISCORD_BOT_TOKEN", "description": "Discord Bot Token", "secret": true} + ] + }, + { + "name": "postgresql", + "title": "PostgreSQL", + "description": "Query and manage PostgreSQL databases with read-only access", + "icon_url": "https://cdn.simpleicons.org/postgresql", + "category": "Databases", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-postgres", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-postgres"], + "env_vars": [ + {"name": "POSTGRES_CONNECTION_STRING", "description": "PostgreSQL connection string (e.g., postgresql://user:pass@host:5432/db)", "secret": true} + ] + }, + { + "name": "sqlite", + "title": "SQLite", + "description": "Query and manage SQLite databases", + "icon_url": "https://cdn.simpleicons.org/sqlite", + "category": "Databases", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-sqlite", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-sqlite"], + "env_vars": [ + {"name": "SQLITE_DB_PATH", "description": "Path to SQLite database file", "secret": false} + ] + }, + { + "name": "mysql", + "title": "MySQL", + "description": "Query and manage MySQL databases", + "icon_url": "https://cdn.simpleicons.org/mysql", + "category": "Databases", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-mysql", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-mysql"], + "env_vars": [ + {"name": "MYSQL_HOST", "description": "MySQL host", "secret": false}, + {"name": "MYSQL_USER", "description": "MySQL username", "secret": false}, + {"name": "MYSQL_PASSWORD", "description": "MySQL password", "secret": true}, + {"name": "MYSQL_DATABASE", "description": "MySQL database name", "secret": false} + ] + }, + { + "name": "mongodb", + "title": "MongoDB", + "description": "Query and manage MongoDB databases and collections", + "icon_url": "https://cdn.simpleicons.org/mongodb", + "category": "Databases", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-mongodb", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-mongodb"], + "env_vars": [ + {"name": "MONGODB_CONNECTION_STRING", "description": "MongoDB connection string", "secret": true} + ] + }, + { + "name": "redis", + "title": "Redis", + "description": "Interact with Redis key-value stores", + "icon_url": "https://cdn.simpleicons.org/redis", + "category": "Databases", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-redis", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-redis"], + "env_vars": [ + {"name": "REDIS_URL", "description": "Redis connection URL (e.g., redis://localhost:6379)", "secret": true} + ] + }, + { + "name": "snowflake", + "title": "Snowflake", + "description": "Query and manage Snowflake data warehouses via Snowflake-managed MCP", + "icon_url": "https://cdn.simpleicons.org/snowflake", + "category": "Databases", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-snowflake", + "transport": "http", + "url": "https://.snowflakecomputing.com/api/v2/databases//schemas//mcp-servers/", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-snowflake"], + "env_vars": [ + {"name": "SNOWFLAKE_ACCOUNT", "description": "Snowflake account identifier (e.g., xy12345.us-east-1)", "secret": false} + ] + }, + { + "name": "notion", + "title": "Notion", + "description": "Search, read, and manage Notion pages and databases", + "icon_url": "https://cdn.simpleicons.org/notion", + "category": "Productivity", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-notion", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-notion"], + "env_vars": [ + {"name": "NOTION_API_KEY", "description": "Notion Integration Token", "secret": true} + ] + }, + { + "name": "google_drive", + "title": "Google Drive", + "description": "Search and access files in Google Drive", + "icon_url": "https://cdn.simpleicons.org/googledrive", + "category": "Productivity", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-gdrive", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-gdrive"], + "env_vars": [ + {"name": "GOOGLE_CLIENT_ID", "description": "Google OAuth Client ID", "secret": false}, + {"name": "GOOGLE_CLIENT_SECRET", "description": "Google OAuth Client Secret", "secret": true} + ] + }, + { + "name": "google_calendar", + "title": "Google Calendar", + "description": "Manage events and calendars in Google Calendar", + "icon_url": "https://cdn.simpleicons.org/googlecalendar", + "category": "Productivity", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-google-calendar", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-google-calendar"], + "env_vars": [ + {"name": "GOOGLE_CLIENT_ID", "description": "Google OAuth Client ID", "secret": false}, + {"name": "GOOGLE_CLIENT_SECRET", "description": "Google OAuth Client Secret", "secret": true} + ] + }, + { + "name": "obsidian", + "title": "Obsidian", + "description": "Read, search, and manage Obsidian vault notes and files", + "icon_url": "https://cdn.simpleicons.org/obsidian", + "category": "Productivity", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-obsidian", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-obsidian"], + "env_vars": [ + {"name": "OBSIDIAN_VAULT_PATH", "description": "Path to Obsidian vault directory", "secret": false} + ] + }, + { + "name": "brave_search", + "title": "Brave Search", + "description": "Web and local search using the Brave Search API", + "icon_url": "https://cdn.simpleicons.org/brave", + "category": "Search", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-brave-search", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-brave-search"], + "env_vars": [ + {"name": "BRAVE_API_KEY", "description": "Brave Search API Key", "secret": true} + ] + }, + { + "name": "exa", + "title": "Exa", + "description": "AI-powered web search and content retrieval", + "icon_url": "https://cdn.simpleicons.org/exa", + "category": "Search", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-exa", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-exa"], + "env_vars": [ + {"name": "EXA_API_KEY", "description": "Exa API Key", "secret": true} + ] + }, + { + "name": "tavily", + "title": "Tavily", + "description": "AI-optimized search engine for research and retrieval", + "icon_url": "https://cdn.simpleicons.org/tavily", + "category": "Search", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-tavily", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-tavily"], + "env_vars": [ + {"name": "TAVILY_API_KEY", "description": "Tavily API Key", "secret": true} + ] + }, + { + "name": "puppeteer", + "title": "Puppeteer", + "description": "Browser automation, web scraping, and screenshot capture", + "icon_url": "https://cdn.simpleicons.org/puppeteer", + "category": "Web & Browser", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-puppeteer", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-puppeteer"], + "env_vars": [] + }, + { + "name": "playwright", + "title": "Playwright", + "description": "Browser automation and testing with Playwright", + "icon_url": "https://cdn.simpleicons.org/playwright", + "category": "Web & Browser", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-playwright", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-playwright"], + "env_vars": [] + }, + { + "name": "browserbase", + "title": "Browserbase", + "description": "Cloud browser automation and session management", + "icon_url": "https://cdn.simpleicons.org/browserbase", + "category": "Web & Browser", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-browserbase", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-browserbase"], + "env_vars": [ + {"name": "BROWSERBASE_API_KEY", "description": "Browserbase API Key", "secret": true}, + {"name": "BROWSERBASE_PROJECT_ID", "description": "Browserbase Project ID", "secret": false} + ] + }, + { + "name": "aws", + "title": "AWS", + "description": "Interact with Amazon Web Services resources and APIs", + "icon_url": "https://cdn.simpleicons.org/amazonaws", + "category": "Cloud", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-aws", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-aws"], + "env_vars": [ + {"name": "AWS_ACCESS_KEY_ID", "description": "AWS Access Key ID", "secret": true}, + {"name": "AWS_SECRET_ACCESS_KEY", "description": "AWS Secret Access Key", "secret": true}, + {"name": "AWS_REGION", "description": "AWS Region (e.g., us-east-1)", "secret": false} + ] + }, + { + "name": "cloudflare", + "title": "Cloudflare", + "description": "Manage Cloudflare Workers, KV, R2, and D1 resources", + "icon_url": "https://cdn.simpleicons.org/cloudflare", + "category": "Cloud", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-cloudflare", + "transport": "http", + "url": "https://mcp.cloudflare.com/sse", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-cloudflare"], + "env_vars": [ + {"name": "CLOUDFLARE_API_TOKEN", "description": "Cloudflare API Token", "secret": true}, + {"name": "CLOUDFLARE_ACCOUNT_ID", "description": "Cloudflare Account ID", "secret": false} + ] + }, + { + "name": "filesystem", + "title": "Filesystem", + "description": "Read, write, and manage files and directories on disk", + "icon_url": "https://cdn.simpleicons.org/files", + "category": "System", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-filesystem", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem"], + "env_vars": [] + }, + { + "name": "docker", + "title": "Docker", + "description": "Manage Docker containers, images, and networks", + "icon_url": "https://cdn.simpleicons.org/docker", + "category": "System", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-docker", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-docker"], + "env_vars": [] + }, + { + "name": "stripe", + "title": "Stripe", + "description": "Manage payments, customers, and subscriptions via the Stripe API", + "icon_url": "https://cdn.simpleicons.org/stripe", + "category": "Finance", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-stripe", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-stripe"], + "env_vars": [ + {"name": "STRIPE_API_KEY", "description": "Stripe Secret API Key", "secret": true} + ] + }, + { + "name": "shopify", + "title": "Shopify", + "description": "Manage Shopify stores, products, orders, and customers", + "icon_url": "https://cdn.simpleicons.org/shopify", + "category": "E-Commerce", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-shopify", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-shopify"], + "env_vars": [ + {"name": "SHOPIFY_ACCESS_TOKEN", "description": "Shopify Admin API Access Token", "secret": true}, + {"name": "SHOPIFY_STORE_URL", "description": "Shopify Store URL (e.g., mystore.myshopify.com)", "secret": false} + ] + }, + { + "name": "twilio", + "title": "Twilio", + "description": "Send SMS, make calls, and manage communication via Twilio", + "icon_url": "https://cdn.simpleicons.org/twilio", + "category": "Communication", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-twilio", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-twilio"], + "env_vars": [ + {"name": "TWILIO_ACCOUNT_SID", "description": "Twilio Account SID", "secret": false}, + {"name": "TWILIO_AUTH_TOKEN", "description": "Twilio Auth Token", "secret": true} + ] + }, + { + "name": "supabase", + "title": "Supabase", + "description": "Manage Supabase projects, databases, and storage", + "icon_url": "https://cdn.simpleicons.org/supabase", + "category": "Databases", + "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-supabase", + "transport": "stdio", + "command": "npx", + "args": ["-y", "@anthropic/mcp-server-supabase"], + "env_vars": [ + {"name": "SUPABASE_URL", "description": "Supabase Project URL", "secret": false}, + {"name": "SUPABASE_SERVICE_ROLE_KEY", "description": "Supabase Service Role Key", "secret": true} + ] + } + ] +} From bcc1ad1a4860ab43f014e51eafe306a695c0f5f6 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 16:50:35 -0800 Subject: [PATCH 02/12] feat: add GET /v1/mcp/discover endpoint for MCP discovery Admin-only endpoint that serves the curated MCP registry with optional query and category filters. Used by the UI discovery modal. --- .../mcp_management_endpoints.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index 597521ae7734..472a79a9e963 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -10,6 +10,7 @@ - DELETE `/v1/mcp/server/{server_id}` - Deletes the mcp server given `server_id`. - GET `/v1/mcp/tools - lists all the tools available for a key - GET `/v1/mcp/access_groups` - lists all available MCP access groups +- GET `/v1/mcp/discover` - Returns curated list of well-known MCP servers for discovery UI """ @@ -1176,3 +1177,90 @@ async def make_mcp_servers_public( except Exception as e: verbose_proxy_logger.exception(f"Error making agent public: {e}") raise HTTPException(status_code=500, detail=str(e)) + + # --- MCP Discovery --- + import json + import os + + _MCP_REGISTRY_PATH = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))), + "mcp_registry.json", + ) + + _mcp_registry_cache: Optional[Dict[str, Any]] = None + + def _load_mcp_registry() -> Dict[str, Any]: + """Load the curated MCP registry from disk. Cached after first read.""" + global _mcp_registry_cache + if _mcp_registry_cache is not None: + return _mcp_registry_cache + try: + with open(_MCP_REGISTRY_PATH, "r") as f: + data: Dict[str, Any] = json.load(f) + except Exception as e: + verbose_proxy_logger.warning( + f"Failed to load MCP registry from {_MCP_REGISTRY_PATH}: {e}" + ) + data = {"servers": []} + _mcp_registry_cache = data + return data + + @router.get( + "/discover", + description="Returns a curated list of well-known MCP servers for discovery UI", + dependencies=[Depends(user_api_key_auth)], + ) + async def discover_mcp_servers( + query: Optional[str] = Query( + None, description="Search filter for server names and descriptions" + ), + category: Optional[str] = Query( + None, description="Filter by category" + ), + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), + ): + """ + Returns a curated list of well-known MCP servers that can be added to the proxy. + + Used by the UI to show a discovery grid when adding new MCP servers. + """ + if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN: + raise HTTPException( + status_code=403, + detail={ + "error": "Only proxy admins can access MCP discovery. Your role={}".format( + user_api_key_dict.user_role + ) + }, + ) + + registry = _load_mcp_registry() + servers = registry.get("servers", []) + + # Apply query filter + if query: + query_lower = query.lower() + servers = [ + s + for s in servers + if query_lower in s.get("name", "").lower() + or query_lower in s.get("title", "").lower() + or query_lower in s.get("description", "").lower() + ] + + # Apply category filter + if category: + servers = [ + s for s in servers if s.get("category", "") == category + ] + + # Extract unique categories from the full list (before filtering) + all_servers = registry.get("servers", []) + categories = sorted( + set(s.get("category", "Other") for s in all_servers) + ) + + return { + "servers": servers, + "categories": categories, + } From b76510390e775723d43bebbfc1c1f2634859a90e Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 16:50:43 -0800 Subject: [PATCH 03/12] feat: add DiscoverableMCPServer types for MCP discovery --- .../src/components/mcp_tools/types.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx index ecc4171a8ac9..b5357e2b5b06 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx @@ -175,3 +175,23 @@ export interface MCPServerProps { userRole: string | null; userID: string | null; } + +// Discoverable MCP server from the curated registry +export interface DiscoverableMCPServer { + name: string; + title: string; + description: string; + icon_url?: string | null; + category: string; + registry_url?: string | null; + transport: string; + url?: string | null; + command?: string | null; + args?: string[] | null; + env_vars?: Array<{ name: string; description?: string; secret?: boolean }> | null; +} + +export interface DiscoverMCPServersResponse { + servers: DiscoverableMCPServer[]; + categories: string[]; +} From 2141a2b617d9bc529a1106a2e834f03199ae476c Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 16:50:47 -0800 Subject: [PATCH 04/12] feat: add fetchDiscoverableMCPServers network function --- .../src/components/networking.tsx | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 162fdb9bc690..945ef02dd88f 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -6097,6 +6097,34 @@ export const updateInternalUserSettings = async (accessToken: string, settings: } }; +export const fetchDiscoverableMCPServers = async (accessToken: string) => { + try { + const url = proxyBaseUrl + ? `${proxyBaseUrl}/v1/mcp/discover` + : `/v1/mcp/discover`; + + const response = await fetch(url, { + method: HTTP_REQUEST.GET, + headers: { + [globalLitellmHeaderName]: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + const errorData = await response.json(); + const errorMessage = deriveErrorMessage(errorData); + handleError(errorMessage); + throw new Error(errorMessage); + } + + return await response.json(); + } catch (error) { + console.error("Failed to fetch discoverable MCP servers:", error); + throw error; + } +}; + export const fetchMCPServers = async (accessToken: string) => { try { // Construct base URL From dbb35db198323f282b2a3b749784229d7e9e94ec Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 16:50:52 -0800 Subject: [PATCH 05/12] feat: add MCP discovery modal component Compact list-row layout with category filters, search, and grouped server list. Follows dev-tool aesthetic. --- .../components/mcp_tools/mcp_discovery.tsx | 309 ++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx new file mode 100644 index 000000000000..559b72c5d92e --- /dev/null +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx @@ -0,0 +1,309 @@ +import React, { useState, useMemo, useEffect } from "react"; +import { Modal, Input } from "antd"; +import { Text } from "@tremor/react"; +import { fetchDiscoverableMCPServers } from "../networking"; +import { DiscoverableMCPServer, DiscoverMCPServersResponse } from "./types"; + +const { Search } = Input; + +interface MCPDiscoveryProps { + isVisible: boolean; + onClose: () => void; + onSelectServer: (server: DiscoverableMCPServer) => void; + onCustomServer: () => void; + accessToken: string | null; +} + +const INITIAL_COLORS = [ + "#3B82F6", + "#10B981", + "#F59E0B", + "#EF4444", + "#8B5CF6", + "#EC4899", + "#06B6D4", + "#84CC16", +]; + +function getInitialAvatar(name: string) { + const initial = name.charAt(0).toUpperCase(); + const colorIndex = + name.split("").reduce((acc, ch) => acc + ch.charCodeAt(0), 0) % + INITIAL_COLORS.length; + return { initial, backgroundColor: INITIAL_COLORS[colorIndex] }; +} + +const MCPDiscovery: React.FC = ({ + isVisible, + onClose, + onSelectServer, + onCustomServer, + accessToken, +}) => { + const [servers, setServers] = useState([]); + const [categories, setCategories] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [searchQuery, setSearchQuery] = useState(""); + const [selectedCategory, setSelectedCategory] = useState("All"); + + useEffect(() => { + if (isVisible && accessToken) { + setLoading(true); + setError(null); + fetchDiscoverableMCPServers(accessToken) + .then((data: DiscoverMCPServersResponse) => { + setServers(data.servers || []); + setCategories(data.categories || []); + }) + .catch((err: Error) => { + setError(err.message || "Failed to load MCP servers"); + }) + .finally(() => { + setLoading(false); + }); + } + }, [isVisible, accessToken]); + + useEffect(() => { + if (isVisible) { + setSearchQuery(""); + setSelectedCategory("All"); + } + }, [isVisible]); + + const filteredServers = useMemo(() => { + let result = servers; + if (selectedCategory !== "All") { + result = result.filter((s) => s.category === selectedCategory); + } + if (searchQuery.trim()) { + const q = searchQuery.toLowerCase(); + result = result.filter( + (s) => + s.name.toLowerCase().includes(q) || + s.title.toLowerCase().includes(q) || + s.description.toLowerCase().includes(q), + ); + } + return result; + }, [servers, selectedCategory, searchQuery]); + + const groupedServers = useMemo(() => { + const groups: Record = {}; + for (const server of filteredServers) { + const cat = server.category || "Other"; + if (!groups[cat]) groups[cat] = []; + groups[cat].push(server); + } + return groups; + }, [filteredServers]); + + return ( + + + Add MCP Server + + + + } + open={isVisible} + onCancel={onClose} + footer={null} + width={640} + styles={{ + body: { maxHeight: "70vh", overflowY: "auto", padding: "12px 24px 24px" }, + header: { padding: "16px 24px 0", border: "none" }, + }} + > + {/* Filter pills */} +
+ {["All", ...categories].map((cat) => { + const isSelected = selectedCategory === cat; + return ( + + ); + })} +
+ + {/* Search */} + setSearchQuery(e.target.value)} + style={{ marginBottom: 12 }} + size="small" + allowClear + /> + + {/* Loading skeleton */} + {loading && ( +
+ {Array.from({ length: 8 }).map((_, i) => ( +
+ ))} +
+ )} + + {error && ( +
+ Failed to load servers: {error} +
+ )} + + {!loading && !error && filteredServers.length === 0 && ( +
+ + No servers found.{" "} + + Add a custom server + + +
+ )} + + {/* Server list grouped by category */} + {!loading && + !error && + Object.entries(groupedServers).map(([category, categoryServers]) => ( +
+
+ {category} +
+ {categoryServers.map((server) => { + const avatar = getInitialAvatar(server.title || server.name); + return ( +
onSelectServer(server)} + style={{ + display: "flex", + alignItems: "center", + padding: "6px 8px", + borderRadius: 4, + cursor: "pointer", + transition: "background 0.1s ease", + }} + onMouseEnter={(e) => { + e.currentTarget.style.background = "#f9fafb"; + }} + onMouseLeave={(e) => { + e.currentTarget.style.background = "transparent"; + }} + > + {server.icon_url ? ( + {server.title} { + const target = e.currentTarget; + target.style.display = "none"; + const next = target.nextElementSibling as HTMLElement; + if (next) next.style.display = "flex"; + }} + /> + ) : null} +
+ {avatar.initial} +
+ + {server.title || server.name} + + + › + +
+ ); + })} +
+ ))} + + ); +}; + +export default MCPDiscovery; From 9ee6187b2a1a4f9e429c19858243f24c96f73060 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 16:50:57 -0800 Subject: [PATCH 06/12] feat: wire MCP discovery modal into server management page Add MCP Server button now opens discovery modal. Card click pre-fills the create form. Custom Server opens blank form. --- .../src/components/mcp_tools/mcp_servers.tsx | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_servers.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_servers.tsx index 9ebf900cb922..1c851c43996b 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_servers.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_servers.tsx @@ -12,9 +12,10 @@ import CreateMCPServer from "./create_mcp_server"; import MCPConnect from "./mcp_connect"; import { mcpServerColumns } from "./mcp_server_columns"; import { MCPServerView } from "./mcp_server_view"; -import { MCPServer, MCPServerProps, Team } from "./types"; +import { DiscoverableMCPServer, MCPServer, MCPServerProps, Team } from "./types"; import MCPSemanticFilterSettings from "../Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterSettings"; import MCPNetworkSettings from "./MCPNetworkSettings"; +import MCPDiscovery from "./mcp_discovery"; const { Text: AntdText, Title: AntdTitle } = Typography; const EDIT_OAUTH_UI_STATE_KEY = "litellm-mcp-oauth-edit-state"; @@ -66,6 +67,8 @@ const MCPServers: React.FC = ({ accessToken, userRole, userID }) const [selectedMcpAccessGroup, setSelectedMcpAccessGroup] = useState("all"); const [filteredServers, setFilteredServers] = useState([]); const [isModalVisible, setModalVisible] = useState(false); + const [isDiscoveryVisible, setDiscoveryVisible] = useState(false); + const [prefillData, setPrefillData] = useState(null); const [isDeletingServer, setIsDeletingServer] = useState(false); const isInternalUser = userRole === "Internal User"; @@ -291,14 +294,35 @@ const MCPServers: React.FC = ({ accessToken, userRole, userID }) isModalVisible={isModalVisible} setModalVisible={setModalVisible} availableAccessGroups={uniqueMcpAccessGroups} + prefillData={prefillData} + onBackToDiscovery={() => { + setModalVisible(false); + setPrefillData(null); + setDiscoveryVisible(true); + }} /> MCP Servers Configure and manage your MCP servers {isAdminRole(userRole) && ( - )} + setDiscoveryVisible(false)} + onSelectServer={(server: DiscoverableMCPServer) => { + setPrefillData(server); + setDiscoveryVisible(false); + setModalVisible(true); + }} + onCustomServer={() => { + setPrefillData(null); + setDiscoveryVisible(false); + setModalVisible(true); + }} + accessToken={accessToken} + />
From 26006c15acf72726f434b076138e95f95097fc7f Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 16:51:02 -0800 Subject: [PATCH 07/12] feat: add prefill from discovery and back-to-registry link Create form accepts prefillData from discovery selection and shows a Browse MCP Registry link to return to discovery modal. --- .../mcp_tools/create_mcp_server.tsx | 75 +++++++++++++++---- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index bf7f1a674311..e38e7b8cd9b2 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -3,7 +3,7 @@ import { Modal, Tooltip, Form, Select, Input } from "antd"; import { InfoCircleOutlined } from "@ant-design/icons"; import { Button, TextInput } from "@tremor/react"; import { createMCPServer } from "../networking"; -import { AUTH_TYPE, OAUTH_FLOW, MCPServer, MCPServerCostInfo } from "./types"; +import { AUTH_TYPE, DiscoverableMCPServer, OAUTH_FLOW, MCPServer, MCPServerCostInfo } from "./types"; import OAuthFormFields from "./OAuthFormFields"; import MCPServerCostConfig from "./mcp_server_cost_config"; import MCPConnectionStatus from "./mcp_connection_status"; @@ -25,6 +25,8 @@ interface CreateMCPServerProps { isModalVisible: boolean; setModalVisible: (visible: boolean) => void; availableAccessGroups: string[]; + prefillData?: DiscoverableMCPServer | null; + onBackToDiscovery?: () => void; } const AUTH_TYPES_REQUIRING_AUTH_VALUE = [AUTH_TYPE.API_KEY, AUTH_TYPE.BEARER_TOKEN, AUTH_TYPE.BASIC]; @@ -38,6 +40,8 @@ const CreateMCPServer: React.FC = ({ isModalVisible, setModalVisible, availableAccessGroups, + prefillData, + onBackToDiscovery, }) => { const [form] = Form.useForm(); const [isLoading, setIsLoading] = useState(false); @@ -183,6 +187,39 @@ const CreateMCPServer: React.FC = ({ setPendingRestoredValues(null); }, [pendingRestoredValues, form, transportType]); + // Pre-fill form from discovery selection + React.useEffect(() => { + if (!isModalVisible || !prefillData) { + return; + } + // Sanitize server name: strip vendor prefix, replace hyphens with underscores + const sanitizedName = (prefillData.name || "") + .replace(/[^a-zA-Z0-9_]/g, "_") + .replace(/_+/g, "_") + .replace(/^_|_$/g, ""); + + const transport = prefillData.transport || ""; + setTransportType(transport); + + const prefillValues: Record = { + server_name: sanitizedName, + alias: sanitizedName, + description: prefillData.description || "", + transport: transport, + }; + + if (transport === "stdio") { + prefillValues.command = prefillData.command || ""; + prefillValues.args = prefillData.args || []; + } else if (prefillData.url) { + prefillValues.url = prefillData.url; + } + + form.setFieldsValue(prefillValues); + setFormValues(prefillValues); + setAliasManuallyEdited(false); + }, [isModalVisible, prefillData, form]); + const handleCreate = async (values: Record) => { setIsLoading(true); try { @@ -391,19 +428,29 @@ const CreateMCPServer: React.FC = ({ return ( - MCP Logo -

Add New MCP Server

+
+
+ MCP Logo +

Add New MCP Server

+
+ {onBackToDiscovery && ( + + )}
} open={isModalVisible} From 78c08b170727afb64596254d973ac2f291ccee82 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 16:51:06 -0800 Subject: [PATCH 08/12] test: add unit tests for MCP discovery endpoint and registry Tests for registry JSON structure validation and endpoint query/category filtering logic. 15 tests total. --- .../mcp_server/test_mcp_discovery.py | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_discovery.py 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 new file mode 100644 index 000000000000..9dacec1904bf --- /dev/null +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_discovery.py @@ -0,0 +1,180 @@ +import json +import os +import sys + +import pytest + +sys.path.insert( + 0, os.path.abspath("../../../..") +) # Adds the parent directory to the system path + + +class TestMCPRegistryFile: + """Tests for the curated MCP registry JSON file.""" + + @pytest.fixture + def registry_path(self): + return os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "..", + "..", + "..", + "..", + "mcp_registry.json", + ) + + def test_registry_file_exists(self, registry_path): + assert os.path.exists(registry_path), f"Registry file not found at {registry_path}" + + def test_registry_file_is_valid_json(self, registry_path): + with open(registry_path, "r") as f: + data = json.load(f) + assert isinstance(data, dict) + assert "servers" in data + + def test_registry_servers_have_required_fields(self, registry_path): + with open(registry_path, "r") as f: + data = json.load(f) + servers = data["servers"] + assert len(servers) > 0, "Registry should have at least one server" + + required_fields = ["name", "title", "description", "category", "transport"] + for server in servers: + for field in required_fields: + assert field in server, f"Server {server.get('name', '?')} missing field '{field}'" + + def test_registry_server_names_are_unique(self, registry_path): + with open(registry_path, "r") as f: + data = json.load(f) + names = [s["name"] for s in data["servers"]] + assert len(names) == len(set(names)), f"Duplicate server names found: {[n for n in names if names.count(n) > 1]}" + + def test_registry_transport_values_are_valid(self, registry_path): + with open(registry_path, "r") as f: + data = json.load(f) + valid_transports = {"stdio", "http", "sse"} + for server in data["servers"]: + assert server["transport"] in valid_transports, ( + f"Server {server['name']} has invalid transport '{server['transport']}'" + ) + + def test_stdio_servers_have_command(self, registry_path): + with open(registry_path, "r") as f: + data = json.load(f) + for server in data["servers"]: + if server["transport"] == "stdio": + assert "command" in server and server["command"], ( + f"stdio server {server['name']} missing 'command'" + ) + + def test_http_servers_have_url(self, registry_path): + with open(registry_path, "r") as f: + data = json.load(f) + for server in data["servers"]: + if server["transport"] in ("http", "sse"): + assert "url" in server and server["url"], ( + f"HTTP/SSE server {server['name']} missing 'url'" + ) + + 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: + data = json.load(f) + names = {s["name"] for s in data["servers"]} + expected = {"github", "slack", "postgresql", "snowflake", "atlassian"} + missing = expected - names + assert not missing, f"Missing well-known servers: {missing}" + + def test_env_vars_structure(self, registry_path): + with open(registry_path, "r") as f: + data = json.load(f) + for server in data["servers"]: + if "env_vars" in server: + assert isinstance(server["env_vars"], list) + for var in server["env_vars"]: + assert "name" in var, f"env_var in {server['name']} missing 'name'" + + +class TestDiscoverEndpointFiltering: + """Tests for the discover endpoint filtering logic (unit-level).""" + + @pytest.fixture + def sample_servers(self): + return [ + { + "name": "github", + "title": "GitHub", + "description": "Repository management", + "category": "Developer Tools", + "transport": "http", + "url": "https://mcp.github.com/sse", + }, + { + "name": "slack", + "title": "Slack", + "description": "Channel management and messaging", + "category": "Communication", + "transport": "stdio", + "command": "npx", + }, + { + "name": "postgresql", + "title": "PostgreSQL", + "description": "Query and manage databases", + "category": "Databases", + "transport": "stdio", + "command": "npx", + }, + ] + + def test_query_filter_by_name(self, sample_servers): + query = "github" + q = query.lower() + result = [ + s + for s in sample_servers + if q in s.get("name", "").lower() + or q in s.get("title", "").lower() + or q in s.get("description", "").lower() + ] + assert len(result) == 1 + assert result[0]["name"] == "github" + + def test_query_filter_by_description(self, sample_servers): + query = "messaging" + q = query.lower() + result = [ + s + for s in sample_servers + if q in s.get("name", "").lower() + or q in s.get("title", "").lower() + or q in s.get("description", "").lower() + ] + assert len(result) == 1 + assert result[0]["name"] == "slack" + + def test_category_filter(self, sample_servers): + category = "Databases" + result = [s for s in sample_servers if s.get("category") == category] + assert len(result) == 1 + assert result[0]["name"] == "postgresql" + + def test_no_filter_returns_all(self, sample_servers): + assert len(sample_servers) == 3 + + def test_query_filter_no_match(self, sample_servers): + query = "nonexistent" + q = query.lower() + result = [ + s + for s in sample_servers + if q in s.get("name", "").lower() + or q in s.get("title", "").lower() + or q in s.get("description", "").lower() + ] + assert len(result) == 0 + + def test_categories_extraction(self, sample_servers): + categories = sorted(set(s.get("category", "Other") for s in sample_servers)) + assert categories == ["Communication", "Databases", "Developer Tools"] From 8375866c829c08614d0abaeab1e985d4204c8b32 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 17:16:27 -0800 Subject: [PATCH 09/12] fix: sync registry with official MCP API and fix stdio prefill - Updated transport types and URLs from registry.modelcontextprotocol.io API - GitHub: streamable-http at api.githubcopilot.com/mcp/ - GitLab: streamable-http at gitlab.com/api/v4/mcp (remote only) - Atlassian: SSE at mcp.atlassian.com/v1/sse (remote only) - Linear: SSE at mcp.linear.app/sse (remote only) - Notion: SSE at mcp.notion.com/sse (remote only) - Stripe: streamable-http at mcp.stripe.com (remote only) - Exa: streamable-http at mcp.exa.ai/mcp (remote only) - Cloudflare: SSE at bindings.mcp.cloudflare.com/sse (remote only) - Sentry: stdio via @sentry/mcp-server (npm, correct package) - Snowflake: stdio via snowflake-labs-mcp (pypi/uvx, not npm) - Brave Search: stdio via @brave/brave-search-mcp-server (correct package) - Fixed stdio prefill to generate stdio_config JSON instead of separate fields - Discovery modal matches create modal width and header style - Back arrow positioned on left of create modal header --- mcp_registry.json | 160 ++++++------- .../mcp_tools/create_mcp_server.tsx | 45 ++-- .../components/mcp_tools/mcp_discovery.tsx | 217 +++++++++--------- 3 files changed, 210 insertions(+), 212 deletions(-) diff --git a/mcp_registry.json b/mcp_registry.json index 1a89d0c20ac6..2e1e8f64eaef 100644 --- a/mcp_registry.json +++ b/mcp_registry.json @@ -3,14 +3,12 @@ { "name": "github", "title": "GitHub", - "description": "Repository management, file operations, and GitHub API integration", + "description": "Manage repos, issues, PRs, and workflows through natural language", "icon_url": "https://cdn.simpleicons.org/github", "category": "Developer Tools", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-github", + "registry_url": "https://registry.modelcontextprotocol.io/servers/io.github.github%2Fgithub-mcp-server", "transport": "http", - "url": "https://mcp.github.com/sse", - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-github"], + "url": "https://api.githubcopilot.com/mcp/", "env_vars": [ {"name": "GITHUB_PERSONAL_ACCESS_TOKEN", "description": "GitHub Personal Access Token", "secret": true} ] @@ -18,16 +16,14 @@ { "name": "gitlab", "title": "GitLab", - "description": "GitLab API integration for project and repository management", + "description": "Official GitLab MCP Server for project and repository management", "icon_url": "https://cdn.simpleicons.org/gitlab", "category": "Developer Tools", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-gitlab", - "transport": "stdio", - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-gitlab"], + "registry_url": "https://registry.modelcontextprotocol.io/servers/com.gitlab%2Fmcp", + "transport": "http", + "url": "https://gitlab.com/api/v4/mcp", "env_vars": [ - {"name": "GITLAB_PERSONAL_ACCESS_TOKEN", "description": "GitLab Personal Access Token", "secret": true}, - {"name": "GITLAB_API_URL", "description": "GitLab API URL (defaults to https://gitlab.com/api/v4)", "secret": false} + {"name": "GITLAB_PERSONAL_ACCESS_TOKEN", "description": "GitLab Personal Access Token", "secret": true} ] }, { @@ -36,11 +32,9 @@ "description": "Jira issues, Confluence pages, and Atlassian product integration", "icon_url": "https://cdn.simpleicons.org/atlassian", "category": "Developer Tools", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-atlassian", - "transport": "http", + "registry_url": "https://registry.modelcontextprotocol.io/servers/com.atlassian%2Fatlassian-mcp-server", + "transport": "sse", "url": "https://mcp.atlassian.com/v1/sse", - "command": "npx", - "args": ["-y", "@anthropic/mcp-server-atlassian"], "env_vars": [] }, { @@ -49,28 +43,23 @@ "description": "Issue tracking, project management, and team workflow automation", "icon_url": "https://cdn.simpleicons.org/linear", "category": "Developer Tools", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-linear", - "transport": "stdio", - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-linear"], - "env_vars": [ - {"name": "LINEAR_API_KEY", "description": "Linear API Key", "secret": true} - ] + "registry_url": "https://registry.modelcontextprotocol.io/servers/app.linear%2Flinear", + "transport": "sse", + "url": "https://mcp.linear.app/sse", + "env_vars": [] }, { "name": "sentry", "title": "Sentry", - "description": "Error tracking, performance monitoring, and issue management", + "description": "Error monitoring, issue tracking, and debugging for AI assistants", "icon_url": "https://cdn.simpleicons.org/sentry", "category": "Developer Tools", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-sentry", - "transport": "http", - "url": "https://mcp.sentry.dev/sse", + "registry_url": "https://registry.modelcontextprotocol.io/servers/io.github.getsentry%2Fsentry-mcp", + "transport": "stdio", "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-sentry"], + "args": ["-y", "@sentry/mcp-server"], "env_vars": [ - {"name": "SENTRY_AUTH_TOKEN", "description": "Sentry Auth Token", "secret": true}, - {"name": "SENTRY_ORGANIZATION", "description": "Sentry Organization Slug", "secret": false} + {"name": "SENTRY_ACCESS_TOKEN", "description": "Sentry Access Token", "secret": true} ] }, { @@ -79,7 +68,7 @@ "description": "Channel management, messaging, and Slack workspace integration", "icon_url": "https://cdn.simpleicons.org/slack", "category": "Communication", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-slack", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-slack"], @@ -94,7 +83,7 @@ "description": "Discord server management, messaging, and bot integration", "icon_url": "https://cdn.simpleicons.org/discord", "category": "Communication", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-discord", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-discord"], @@ -108,7 +97,7 @@ "description": "Query and manage PostgreSQL databases with read-only access", "icon_url": "https://cdn.simpleicons.org/postgresql", "category": "Databases", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-postgres", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-postgres"], @@ -122,7 +111,7 @@ "description": "Query and manage SQLite databases", "icon_url": "https://cdn.simpleicons.org/sqlite", "category": "Databases", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-sqlite", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-sqlite"], @@ -136,7 +125,7 @@ "description": "Query and manage MySQL databases", "icon_url": "https://cdn.simpleicons.org/mysql", "category": "Databases", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-mysql", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-mysql"], @@ -153,7 +142,7 @@ "description": "Query and manage MongoDB databases and collections", "icon_url": "https://cdn.simpleicons.org/mongodb", "category": "Databases", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-mongodb", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-mongodb"], @@ -167,7 +156,7 @@ "description": "Interact with Redis key-value stores", "icon_url": "https://cdn.simpleicons.org/redis", "category": "Databases", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-redis", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-redis"], @@ -178,31 +167,29 @@ { "name": "snowflake", "title": "Snowflake", - "description": "Query and manage Snowflake data warehouses via Snowflake-managed MCP", + "description": "MCP Server for Snowflake from Snowflake Labs", "icon_url": "https://cdn.simpleicons.org/snowflake", "category": "Databases", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-snowflake", - "transport": "http", - "url": "https://.snowflakecomputing.com/api/v2/databases//schemas//mcp-servers/", - "command": "npx", - "args": ["-y", "@anthropic/mcp-server-snowflake"], + "registry_url": "https://registry.modelcontextprotocol.io/servers/io.github.Snowflake-Labs%2Fmcp", + "transport": "stdio", + "command": "uvx", + "args": ["snowflake-labs-mcp"], "env_vars": [ - {"name": "SNOWFLAKE_ACCOUNT", "description": "Snowflake account identifier (e.g., xy12345.us-east-1)", "secret": false} + {"name": "SNOWFLAKE_ACCOUNT", "description": "Snowflake account identifier (e.g., xy12345.us-east-1)", "secret": false}, + {"name": "SNOWFLAKE_USER", "description": "Snowflake username", "secret": false}, + {"name": "SNOWFLAKE_PASSWORD", "description": "Snowflake password", "secret": true} ] }, { "name": "notion", "title": "Notion", - "description": "Search, read, and manage Notion pages and databases", + "description": "Official Notion MCP server for pages and databases", "icon_url": "https://cdn.simpleicons.org/notion", "category": "Productivity", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-notion", - "transport": "stdio", - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-notion"], - "env_vars": [ - {"name": "NOTION_API_KEY", "description": "Notion Integration Token", "secret": true} - ] + "registry_url": "https://registry.modelcontextprotocol.io/servers/com.notion%2Fmcp", + "transport": "sse", + "url": "https://mcp.notion.com/sse", + "env_vars": [] }, { "name": "google_drive", @@ -210,7 +197,7 @@ "description": "Search and access files in Google Drive", "icon_url": "https://cdn.simpleicons.org/googledrive", "category": "Productivity", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-gdrive", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-gdrive"], @@ -225,7 +212,7 @@ "description": "Manage events and calendars in Google Calendar", "icon_url": "https://cdn.simpleicons.org/googlecalendar", "category": "Productivity", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-google-calendar", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-google-calendar"], @@ -240,7 +227,7 @@ "description": "Read, search, and manage Obsidian vault notes and files", "icon_url": "https://cdn.simpleicons.org/obsidian", "category": "Productivity", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-obsidian", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-obsidian"], @@ -251,13 +238,13 @@ { "name": "brave_search", "title": "Brave Search", - "description": "Web and local search using the Brave Search API", + "description": "Web results, images, videos, and AI summaries via Brave Search API", "icon_url": "https://cdn.simpleicons.org/brave", "category": "Search", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-brave-search", + "registry_url": "https://registry.modelcontextprotocol.io/servers/io.github.brave%2Fbrave-search-mcp-server", "transport": "stdio", "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-brave-search"], + "args": ["-y", "@brave/brave-search-mcp-server"], "env_vars": [ {"name": "BRAVE_API_KEY", "description": "Brave Search API Key", "secret": true} ] @@ -265,13 +252,12 @@ { "name": "exa", "title": "Exa", - "description": "AI-powered web search and content retrieval", + "description": "Fast, intelligent web search and web crawling", "icon_url": "https://cdn.simpleicons.org/exa", "category": "Search", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-exa", - "transport": "stdio", - "command": "npx", - "args": ["-y", "@anthropic/mcp-server-exa"], + "registry_url": "https://registry.modelcontextprotocol.io/servers/ai.exa%2Fexa", + "transport": "http", + "url": "https://mcp.exa.ai/mcp", "env_vars": [ {"name": "EXA_API_KEY", "description": "Exa API Key", "secret": true} ] @@ -282,7 +268,7 @@ "description": "AI-optimized search engine for research and retrieval", "icon_url": "https://cdn.simpleicons.org/tavily", "category": "Search", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-tavily", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-tavily"], @@ -296,7 +282,7 @@ "description": "Browser automation, web scraping, and screenshot capture", "icon_url": "https://cdn.simpleicons.org/puppeteer", "category": "Web & Browser", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-puppeteer", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-puppeteer"], @@ -308,7 +294,7 @@ "description": "Browser automation and testing with Playwright", "icon_url": "https://cdn.simpleicons.org/playwright", "category": "Web & Browser", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-playwright", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-playwright"], @@ -320,7 +306,7 @@ "description": "Cloud browser automation and session management", "icon_url": "https://cdn.simpleicons.org/browserbase", "category": "Web & Browser", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-browserbase", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-browserbase"], @@ -335,7 +321,7 @@ "description": "Interact with Amazon Web Services resources and APIs", "icon_url": "https://cdn.simpleicons.org/amazonaws", "category": "Cloud", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-aws", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-aws"], @@ -348,18 +334,13 @@ { "name": "cloudflare", "title": "Cloudflare", - "description": "Manage Cloudflare Workers, KV, R2, and D1 resources", + "description": "Manage Cloudflare Workers, KV, R2, D1, and more", "icon_url": "https://cdn.simpleicons.org/cloudflare", "category": "Cloud", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-cloudflare", - "transport": "http", - "url": "https://mcp.cloudflare.com/sse", - "command": "npx", - "args": ["-y", "@anthropic/mcp-server-cloudflare"], - "env_vars": [ - {"name": "CLOUDFLARE_API_TOKEN", "description": "Cloudflare API Token", "secret": true}, - {"name": "CLOUDFLARE_ACCOUNT_ID", "description": "Cloudflare Account ID", "secret": false} - ] + "registry_url": "https://registry.modelcontextprotocol.io/servers/com.cloudflare.mcp%2Fmcp", + "transport": "sse", + "url": "https://bindings.mcp.cloudflare.com/sse", + "env_vars": [] }, { "name": "filesystem", @@ -367,7 +348,7 @@ "description": "Read, write, and manage files and directories on disk", "icon_url": "https://cdn.simpleicons.org/files", "category": "System", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@modelcontextprotocol/server-filesystem", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem"], @@ -379,7 +360,7 @@ "description": "Manage Docker containers, images, and networks", "icon_url": "https://cdn.simpleicons.org/docker", "category": "System", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-docker", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-docker"], @@ -388,16 +369,13 @@ { "name": "stripe", "title": "Stripe", - "description": "Manage payments, customers, and subscriptions via the Stripe API", + "description": "Manage payments, customers, and subscriptions via Stripe", "icon_url": "https://cdn.simpleicons.org/stripe", "category": "Finance", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-stripe", - "transport": "stdio", - "command": "npx", - "args": ["-y", "@anthropic/mcp-server-stripe"], - "env_vars": [ - {"name": "STRIPE_API_KEY", "description": "Stripe Secret API Key", "secret": true} - ] + "registry_url": "https://registry.modelcontextprotocol.io/servers/com.stripe%2Fmcp", + "transport": "http", + "url": "https://mcp.stripe.com", + "env_vars": [] }, { "name": "shopify", @@ -405,7 +383,7 @@ "description": "Manage Shopify stores, products, orders, and customers", "icon_url": "https://cdn.simpleicons.org/shopify", "category": "E-Commerce", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-shopify", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-shopify"], @@ -420,7 +398,7 @@ "description": "Send SMS, make calls, and manage communication via Twilio", "icon_url": "https://cdn.simpleicons.org/twilio", "category": "Communication", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-twilio", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-twilio"], @@ -435,7 +413,7 @@ "description": "Manage Supabase projects, databases, and storage", "icon_url": "https://cdn.simpleicons.org/supabase", "category": "Databases", - "registry_url": "https://registry.modelcontextprotocol.io/servers/@anthropic/mcp-server-supabase", + "registry_url": null, "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic/mcp-server-supabase"], diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index e38e7b8cd9b2..18d7503acfbf 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -209,8 +209,19 @@ const CreateMCPServer: React.FC = ({ }; if (transport === "stdio") { - prefillValues.command = prefillData.command || ""; - prefillValues.args = prefillData.args || []; + const stdioObj: Record = {}; + if (prefillData.command) stdioObj.command = prefillData.command; + if (prefillData.args && prefillData.args.length > 0) stdioObj.args = prefillData.args; + if (prefillData.env_vars && prefillData.env_vars.length > 0) { + const envObj: Record = {}; + for (const v of prefillData.env_vars) { + envObj[v.name] = v.description ? `<${v.description}>` : ""; + } + stdioObj.env = envObj; + } + if (Object.keys(stdioObj).length > 0) { + prefillValues.stdio_config = JSON.stringify(stdioObj, null, 2); + } } else if (prefillData.url) { prefillValues.url = prefillData.url; } @@ -428,29 +439,27 @@ const CreateMCPServer: React.FC = ({ return ( -
- MCP Logo -

Add New MCP Server

-
+
{onBackToDiscovery && ( )} + MCP Logo +

Add New MCP Server

} open={isModalVisible} diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx index 559b72c5d92e..f77453699e88 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx @@ -3,6 +3,7 @@ import { Modal, Input } from "antd"; import { Text } from "@tremor/react"; import { fetchDiscoverableMCPServers } from "../networking"; import { DiscoverableMCPServer, DiscoverMCPServersResponse } from "./types"; +import { mcpLogoImg } from "./create_mcp_server"; const { Search } = Input; @@ -102,22 +103,24 @@ const MCPDiscovery: React.FC = ({ return ( - - Add MCP Server - +
+
+ MCP Logo +

Add MCP Server

+
@@ -126,14 +129,15 @@ const MCPDiscovery: React.FC = ({ open={isVisible} onCancel={onClose} footer={null} - width={640} + width={1000} + className="top-8" styles={{ - body: { maxHeight: "70vh", overflowY: "auto", padding: "12px 24px 24px" }, - header: { padding: "16px 24px 0", border: "none" }, + body: { padding: "24px", maxHeight: "70vh", overflowY: "auto" }, + header: { padding: "24px 24px 0 24px", border: "none" }, }} > {/* Filter pills */} -
+
{["All", ...categories].map((cat) => { const isSelected = selectedCategory === cat; return ( @@ -141,13 +145,13 @@ const MCPDiscovery: React.FC = ({ key={cat} onClick={() => setSelectedCategory(cat)} style={{ - padding: "2px 8px", + padding: "4px 12px", borderRadius: 4, border: isSelected ? "1px solid #111827" : "1px solid #e5e7eb", background: isSelected ? "#111827" : "#fff", color: isSelected ? "#fff" : "#4b5563", cursor: "pointer", - fontSize: 11, + fontSize: 12, fontWeight: isSelected ? 500 : 400, lineHeight: "20px", }} @@ -163,20 +167,19 @@ const MCPDiscovery: React.FC = ({ placeholder="Search servers..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} - style={{ marginBottom: 12 }} - size="small" + style={{ marginBottom: 16 }} allowClear /> {/* Loading skeleton */} {loading && ( -
+
{Array.from({ length: 8 }).map((_, i) => (
@@ -185,18 +188,18 @@ const MCPDiscovery: React.FC = ({ )} {error && ( -
+
Failed to load servers: {error}
)} {!loading && !error && filteredServers.length === 0 && ( -
+
No servers found.{" "} Add a custom server @@ -204,11 +207,11 @@ const MCPDiscovery: React.FC = ({
)} - {/* Server list grouped by category */} + {/* Server list grouped by category — 2 columns */} {!loading && !error && Object.entries(groupedServers).map(([category, categoryServers]) => ( -
+
= ({ color: "#9ca3af", textTransform: "uppercase", letterSpacing: "0.05em", - padding: "4px 0", + padding: "6px 0", borderBottom: "1px solid #f3f4f6", - marginBottom: 2, + marginBottom: 4, }} > {category}
- {categoryServers.map((server) => { - const avatar = getInitialAvatar(server.title || server.name); - return ( -
onSelectServer(server)} - style={{ - display: "flex", - alignItems: "center", - padding: "6px 8px", - borderRadius: 4, - cursor: "pointer", - transition: "background 0.1s ease", - }} - onMouseEnter={(e) => { - e.currentTarget.style.background = "#f9fafb"; - }} - onMouseLeave={(e) => { - e.currentTarget.style.background = "transparent"; - }} - > - {server.icon_url ? ( - {server.title} { - const target = e.currentTarget; - target.style.display = "none"; - const next = target.nextElementSibling as HTMLElement; - if (next) next.style.display = "flex"; - }} - /> - ) : null} +
+ {categoryServers.map((server) => { + const avatar = getInitialAvatar(server.title || server.name); + return (
onSelectServer(server)} style={{ - width: 18, - height: 18, - borderRadius: 3, - backgroundColor: avatar.backgroundColor, - color: "#fff", - display: server.icon_url ? "none" : "flex", + display: "flex", alignItems: "center", - justifyContent: "center", - fontWeight: 600, - fontSize: 10, - flexShrink: 0, - marginRight: 10, + padding: "8px 10px", + borderRadius: 6, + cursor: "pointer", + transition: "background 0.1s ease", }} - > - {avatar.initial} -
- { + e.currentTarget.style.background = "#f9fafb"; + }} + onMouseLeave={(e) => { + e.currentTarget.style.background = "transparent"; }} > - {server.title || server.name} - - - › - -
- ); - })} + {server.icon_url ? ( + {server.title} { + const target = e.currentTarget; + target.style.display = "none"; + const next = target.nextElementSibling as HTMLElement; + if (next) next.style.display = "flex"; + }} + /> + ) : null} +
+ {avatar.initial} +
+ + {server.title || server.name} + + + › + +
+ ); + })} +
))} From 5cf73346647d57175fdb178cfffb3e05eb8aad1c Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 12 Feb 2026 17:39:03 -0800 Subject: [PATCH 10/12] Update ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx index f77453699e88..ed8ffdf5d3e2 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx @@ -1,6 +1,6 @@ import React, { useState, useMemo, useEffect } from "react"; import { Modal, Input } from "antd"; -import { Text } from "@tremor/react"; +import { Typography } from "antd"; import { fetchDiscoverableMCPServers } from "../networking"; import { DiscoverableMCPServer, DiscoverMCPServersResponse } from "./types"; import { mcpLogoImg } from "./create_mcp_server"; From f32e900ae2142e0094b5cdc15067cf734e14b710 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 12 Feb 2026 17:39:18 -0800 Subject: [PATCH 11/12] Update litellm/proxy/management_endpoints/mcp_management_endpoints.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- litellm/proxy/management_endpoints/mcp_management_endpoints.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index 472a79a9e963..949c9bed1cf2 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -1179,8 +1179,6 @@ async def make_mcp_servers_public( raise HTTPException(status_code=500, detail=str(e)) # --- MCP Discovery --- - import json - import os _MCP_REGISTRY_PATH = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))), From a7d092f3ec2e9a438a6bc0db911443379418734c Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 17:50:41 -0800 Subject: [PATCH 12/12] fix: address Greptile review feedback - Move `import json` and `import os` to module top level - Move mcp_registry.json into litellm/proxy/ for pip distribution - Fix `Text` component: destructure from antd Typography instead of deprecated Tremor - Update test fixture path to match new registry location --- .../proxy/management_endpoints/mcp_management_endpoints.py | 4 +++- mcp_registry.json => litellm/proxy/mcp_registry.json | 0 .../proxy/_experimental/mcp_server/test_mcp_discovery.py | 2 ++ .../src/components/mcp_tools/mcp_discovery.tsx | 4 ++-- 4 files changed, 7 insertions(+), 3 deletions(-) rename mcp_registry.json => litellm/proxy/mcp_registry.json (100%) diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index 949c9bed1cf2..8c4d4e7937e8 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -15,6 +15,8 @@ """ import importlib +import json +import os from dataclasses import dataclass from datetime import datetime, timedelta from typing import Any, Dict, Iterable, List, Literal, Optional @@ -1181,7 +1183,7 @@ async def make_mcp_servers_public( # --- MCP Discovery --- _MCP_REGISTRY_PATH = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))), + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "mcp_registry.json", ) diff --git a/mcp_registry.json b/litellm/proxy/mcp_registry.json similarity index 100% rename from mcp_registry.json rename to litellm/proxy/mcp_registry.json 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 9dacec1904bf..dde73016271a 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 @@ -21,6 +21,8 @@ def registry_path(self): "..", "..", "..", + "litellm", + "proxy", "mcp_registry.json", ) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx index ed8ffdf5d3e2..558814f4dc8c 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx @@ -1,11 +1,11 @@ import React, { useState, useMemo, useEffect } from "react"; -import { Modal, Input } from "antd"; -import { Typography } from "antd"; +import { Modal, Input, Typography } from "antd"; import { fetchDiscoverableMCPServers } from "../networking"; import { DiscoverableMCPServer, DiscoverMCPServersResponse } from "./types"; import { mcpLogoImg } from "./create_mcp_server"; const { Search } = Input; +const { Text } = Typography; interface MCPDiscoveryProps { isVisible: boolean;