From a60016c16a94ee9ef79f51539742d4cb0785ca64 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 1 Jul 2026 10:34:05 +0530 Subject: [PATCH] fix(mcp): let BYOM submitters see their approved servers Approved user-submitted MCP servers defaulted to no access groups and allow_all_keys=false, so submitters could not see them after admin approval. Grant creator visibility for active submissions in get_allowed_mcp_servers. Co-authored-by: Cursor --- litellm/proxy/_experimental/mcp_server/db.py | 17 +++++++ .../mcp_server/mcp_server_manager.py | 20 ++++++++ .../mcp_server/test_mcp_server.py | 47 +++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index a2ce3307061..9a4e2d207e3 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -1228,6 +1228,23 @@ def _remaining_token_seconds(expires_at: str | None) -> int | None: return remaining if remaining > 0 else None +async def get_active_submitted_mcp_server_ids_for_user( + prisma_client: PrismaClient, + user_id: str, +) -> List[str]: + """Return active BYOM servers submitted by this user (creator visibility).""" + if not user_id: + return [] + + rows = await MCPServerRepository(prisma_client).table.find_many( + where={ + "submitted_by": user_id, + "approval_status": MCPApprovalStatus.active, + }, + ) + return [row.server_id for row in rows] + + async def approve_mcp_server( prisma_client: PrismaClient, server_id: str, diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index b6760e58852..740cc13f736 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -1325,6 +1325,26 @@ class MCPServerManager: ] combined_servers.update(delegate_server_ids) + # BYOM: approved submissions stay visible to the submitter even when + # allow_all_keys=false and no access groups were configured at approval. + submitter_user_id = getattr(user_api_key_auth, "user_id", None) if user_api_key_auth else None + if submitter_user_id: + from litellm.proxy._experimental.mcp_server.db import ( # noqa: PLC0415 + get_active_submitted_mcp_server_ids_for_user, + ) + from litellm.proxy.proxy_server import prisma_client + + if prisma_client is not None: + submitted_server_ids = await get_active_submitted_mcp_server_ids_for_user( + prisma_client, + submitter_user_id, + ) + combined_servers.update( + server_id + for server_id in submitted_server_ids + if self.get_mcp_server_by_id(server_id) is not None + ) + if len(combined_servers) == 0: verbose_logger.debug("No allowed MCP Servers found for user api key auth.") return list(combined_servers) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py index abefb2fd984..bc808bf09f9 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py @@ -6498,3 +6498,50 @@ class TestMCPMetaTraceCarrier: assert _mcp_meta_trace_carrier(SimpleNamespace(meta=None)) is None only_progress = RequestParams.Meta.model_validate({"progressToken": "p1"}) assert _mcp_meta_trace_carrier(SimpleNamespace(meta=only_progress)) is None + + +@pytest.mark.asyncio +async def test_get_allowed_mcp_servers_includes_active_servers_submitted_by_user(): + """BYOM submitters can see approved servers they submitted without allow_all_keys.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth + + submitted_server = _make_mcp_server_for_scope_filter("submitted-1", "user_mcp") + submitter = UserAPIKeyAuth( + user_id="submitter-user", + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-submitter", + ) + other_user = UserAPIKeyAuth( + user_id="other-user", + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-other", + ) + + async def _submitted_ids(prisma_client, user_id): + return ["submitted-1"] if user_id == "submitter-user" else [] + + with ( + patch.object( + global_mcp_server_manager, + "get_registry", + return_value={"submitted-1": submitted_server}, + ), + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp." + "MCPRequestHandler.get_allowed_mcp_servers", + AsyncMock(return_value=[]), + ), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_active_submitted_mcp_server_ids_for_user", + side_effect=_submitted_ids, + ), + ): + submitter_allowed = await global_mcp_server_manager.get_allowed_mcp_servers(submitter) + other_allowed = await global_mcp_server_manager.get_allowed_mcp_servers(other_user) + + assert "submitted-1" in submitter_allowed + assert "submitted-1" not in other_allowed