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) && (