From 7292864c059197422fe562eac04b4de55c249bb9 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 1 Jul 2026 10:55:54 +0530 Subject: [PATCH] fix(security): restrict MCP semantic filter settings to proxy admins Add an explicit PROXY_ADMIN check on PATCH /update/mcp_semantic_filter_settings and hide Semantic Filter and Network Settings tabs from non-admin users in the MCP Servers UI. Co-authored-by: Cursor --- .../proxy_setting_endpoints.py | 6 +++++ .../test_proxy_setting_endpoints.py | 26 +++++++++++++++++++ .../src/components/mcp_tools/mcp_servers.tsx | 20 ++++++++------ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py index 0fc303737b4..3611a4a1401 100644 --- a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py +++ b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py @@ -1160,6 +1160,12 @@ async def update_mcp_semantic_filter_settings( Update MCP semantic filter settings in database. Settings will be picked up by all pods within approximately 10 seconds via background polling. """ + if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN: + raise HTTPException( + status_code=403, + detail="Only proxy admins can update MCP semantic filter settings.", + ) + result = await _update_litellm_setting( settings=settings, settings_key="mcp_semantic_tool_filter", diff --git a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py index cb77c42fe9b..69845ec59c2 100644 --- a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py +++ b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py @@ -2367,3 +2367,29 @@ def test_update_ui_settings_writes_audit_log(monkeypatch): assert after["disable_custom_api_keys"] is True finally: app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_update_mcp_semantic_filter_settings_requires_proxy_admin(monkeypatch): + """Non-admin callers must not mutate global MCP semantic filter settings.""" + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + + async def _internal_user_auth(): + return UserAPIKeyAuth( + user_id="internal-user-1", + api_key="hashed-internal-key", + user_role=LitellmUserRoles.INTERNAL_USER, + ) + + app.dependency_overrides[user_api_key_auth] = _internal_user_auth + try: + resp = client.patch( + "/update/mcp_semantic_filter_settings", + json={"enabled": True, "top_k": 99, "similarity_threshold": 0.01}, + ) + assert resp.status_code == 403 + assert "proxy admin" in resp.json()["detail"].lower() + finally: + app.dependency_overrides.pop(user_api_key_auth, None) 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 0a445265e2d..f23e7a9179b 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_servers.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_servers.tsx @@ -497,8 +497,8 @@ const MCPServers: React.FC = ({ accessToken, userRole, userID }) All Servers Toolsets Connect - Semantic Filter - Network Settings + {isAdminRole(userRole) && Semantic Filter} + {isAdminRole(userRole) && Network Settings} {isAdminRole(userRole) && ( @@ -652,12 +652,16 @@ const MCPServers: React.FC = ({ accessToken, userRole, userID }) - - - - - - + {isAdminRole(userRole) && ( + + + + )} + {isAdminRole(userRole) && ( + + + + )} {isAdminRole(userRole) && (