From dd4249db7815e3ad83a5a7d0e5d5ae609c41c98d Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 8 Oct 2025 16:27:01 -0700 Subject: [PATCH] feat(mcp_server_manager.py): initial commit adding allowed params support allow admin to specify which parameters to allow/disallow by MCP tool --- .../mcp_server/mcp_server_manager.py | 75 ++++++++++++++++++- litellm/proxy/_new_secret_config.yaml | 39 +++++++++- .../types/mcp_server/mcp_server_manager.py | 3 + 3 files changed, 110 insertions(+), 7 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index 10e40b76efd..129a756e4f5 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -215,6 +215,7 @@ class MCPServerManager: extra_headers=server_config.get("extra_headers", None), allowed_tools=server_config.get("allowed_tools", None), disallowed_tools=server_config.get("disallowed_tools", None), + allowed_params=server_config.get("allowed_params", None), access_groups=server_config.get("access_groups", None), ) self.config_mcp_servers[server_id] = new_server @@ -602,6 +603,60 @@ class MCPServerManager: return tool_name not in server.disallowed_tools return True + def filter_allowed_params( + self, tool_name: str, arguments: Dict[str, Any], server: MCPServer + ) -> Dict[str, Any]: + """ + Filter arguments to only include allowed parameters for the given tool. + + Args: + tool_name: Name of the tool (with or without prefix) + arguments: Dictionary of arguments to filter + server: MCPServer configuration + + Returns: + Filtered dictionary containing only allowed parameters + + Raises: + HTTPException: If allowed_params is configured for this tool but arguments contain disallowed params + """ + from litellm.proxy._experimental.mcp_server.utils import ( + get_server_name_prefix_tool_mcp, + ) + + # If no allowed_params configured, return all arguments + if not server.allowed_params: + return arguments + + # Get the unprefixed tool name to match against config + unprefixed_tool_name, _ = get_server_name_prefix_tool_mcp(tool_name) + + # Check both prefixed and unprefixed tool names + allowed_params_list = server.allowed_params.get( + tool_name + ) or server.allowed_params.get(unprefixed_tool_name) + + # If this tool doesn't have allowed_params specified, allow all params + if allowed_params_list is None: + return arguments + + # Filter arguments to only include allowed parameters + disallowed_params = [ + param for param in arguments.keys() if param not in allowed_params_list + ] + + if disallowed_params: + raise HTTPException( + status_code=403, + detail={ + "error": f"Parameters {disallowed_params} are not allowed for tool {tool_name}. " + f"Allowed parameters: {allowed_params_list}. " + f"Contact proxy admin to allow these parameters." + }, + ) + + return {k: v for k, v in arguments.items() if k in allowed_params_list} + async def check_tool_permission_for_key_team( self, tool_name: str, @@ -621,18 +676,20 @@ class MCPServerManager: Raises: HTTPException: If tool is not allowed for this key/team """ - from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import MCPRequestHandler - + from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( + MCPRequestHandler, + ) + if not user_api_key_auth: return - + # Check if tool is allowed is_allowed = await MCPRequestHandler.is_tool_allowed_for_server( tool_name=tool_name, server_id=server.server_id, user_api_key_auth=user_api_key_auth, ) - + if not is_allowed: raise HTTPException( status_code=403, @@ -667,6 +724,16 @@ class MCPServerManager: user_api_key_auth=user_api_key_auth, ) + ## filter parameters based on allowed_params configuration + filtered_arguments = self.filter_allowed_params( + tool_name=name, + arguments=arguments, + server=server, + ) + # Update arguments with filtered version + arguments.clear() + arguments.update(filtered_arguments) + pre_hook_kwargs = { "name": name, "arguments": arguments, diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index 52a6fc16ff1..b2f4635ac99 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -1,6 +1,39 @@ model_list: - model_name: gpt-5-mini litellm_params: - model: azure/gpt-5-mini-2 - api_key: os.environ/AZURE_API_KEY_ALT - api_base: os.environ/AZURE_API_BASE_ALT + model: openai/gpt-4o-mini + api_base: "https://webhook.site/2f385e05-00aa-402b-86d1-efc9261471a5" + api_key: dummy + - model_name: "byok-wildcard/*" + litellm_params: + model: openai/* + - model_name: xai-grok-3 + litellm_params: + model: xai/grok-3 + - model_name: hosted_vllm/whisper-v3 + litellm_params: + model: hosted_vllm/whisper-v3 + api_base: "https://webhook.site/2f385e05-00aa-402b-86d1-efc9261471a5" + api_key: dummy + +mcp_servers: + my_api_mcp: + url: "http://0.0.0.0:8090" + spec_path: "/Users/krrishdholakia/Documents/temp_py_folder/example_openapi.json" + auth_type: none + allowed_tools: ["getpetbyid", "my_api_mcp-findpetsbystatus"] + # Configure allowed parameters per tool + # Key: tool name (with or without prefix) + # Value: list of allowed parameter names + allowed_params: + # Using unprefixed tool name + "getpetbyid": ["petId"] + # Using prefixed tool name (both formats work) + "my_api_mcp-findpetsbystatus": ["status", "limit"] + # Example: allow only specific params for another tool + # "another_tool": ["param1", "param2"] + + +litellm_settings: + callbacks: ["prometheus"] + custom_prometheus_metadata_labels: ["metadata.initiative", "metadata.business-unit"] diff --git a/litellm/types/mcp_server/mcp_server_manager.py b/litellm/types/mcp_server/mcp_server_manager.py index 3e0c2b20e39..81c116bdab8 100644 --- a/litellm/types/mcp_server/mcp_server_manager.py +++ b/litellm/types/mcp_server/mcp_server_manager.py @@ -25,6 +25,9 @@ class MCPServer(BaseModel): ) allowed_tools: Optional[List[str]] = None disallowed_tools: Optional[List[str]] = None + allowed_params: Optional[Dict[str, List[str]]] = ( + None # map of tool names to allowed parameter lists + ) # OAuth-specific fields client_id: Optional[str] = None client_secret: Optional[str] = None