diff --git a/docs/my-website/docs/mcp.md b/docs/my-website/docs/mcp.md index 9a1e25a516c..0913196e749 100644 --- a/docs/my-website/docs/mcp.md +++ b/docs/my-website/docs/mcp.md @@ -569,7 +569,7 @@ mcp_servers: ### How It Works 1. **Tool-specific filtering**: Each tool can have its own list of allowed parameters -2. **Flexible naming**: Tool names can be specified with or without the server prefix (e.g., both `"getpetbyid"` and `"my_api_mcp-getpetbyid"` work) +2. **Flexible naming**: Tool names can be specified with or without the server prefix (e.g., both `"getpetbyid"` and `"my_api_mcp--getpetbyid"` work) 3. **Whitelist approach**: Only parameters in the allowed list are permitted 4. **Unlisted tools**: If `allowed_params` is not set, all parameters are allowed 5. **Error handling**: Requests with disallowed parameters receive a 403 error with details about which parameters are allowed diff --git a/litellm/proxy/_experimental/mcp_server/utils.py b/litellm/proxy/_experimental/mcp_server/utils.py index d801b312aac..f590eb88ab5 100644 --- a/litellm/proxy/_experimental/mcp_server/utils.py +++ b/litellm/proxy/_experimental/mcp_server/utils.py @@ -1,16 +1,15 @@ """ MCP Server Utilities """ -from typing import Tuple, Any - -import os import importlib +import os +from typing import Any, Tuple # Constants LITELLM_MCP_SERVER_NAME = "litellm-mcp-server" LITELLM_MCP_SERVER_VERSION = "1.0.0" LITELLM_MCP_SERVER_DESCRIPTION = "MCP Server for LiteLLM" -MCP_TOOL_PREFIX_SEPARATOR = os.environ.get("MCP_TOOL_PREFIX_SEPARATOR", "-") +MCP_TOOL_PREFIX_SEPARATOR = os.environ.get("MCP_TOOL_PREFIX_SEPARATOR", "--") MCP_TOOL_PREFIX_FORMAT = "{server_name}{separator}{tool_name}" @@ -113,9 +112,7 @@ def is_tool_name_prefixed(tool_name: str) -> bool: return MCP_TOOL_PREFIX_SEPARATOR in tool_name -def validate_mcp_server_name( - server_name: str, raise_http_exception: bool = False -) -> None: +def validate_mcp_server_name(server_name: str, raise_http_exception: bool = False) -> None: """ Validate that MCP server name does not contain 'MCP_TOOL_PREFIX_SEPARATOR'. @@ -132,8 +129,6 @@ def validate_mcp_server_name( from fastapi import HTTPException from starlette import status - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail={"error": error_message} - ) + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail={"error": error_message}) else: raise Exception(error_message) diff --git a/tests/mcp_tests/test_mcp_server.py b/tests/mcp_tests/test_mcp_server.py index a99ef772cf1..8562e3132dc 100644 --- a/tests/mcp_tests/test_mcp_server.py +++ b/tests/mcp_tests/test_mcp_server.py @@ -1207,7 +1207,7 @@ async def test_mcp_server_manager_alias_tool_prefixing(): # Verify tool is prefixed with alias assert len(tools) == 1 - assert tools[0].name == "my_alias-send_email" + assert tools[0].name == "my_alias--send_email" # Verify mapping is updated correctly assert ( @@ -1215,7 +1215,7 @@ async def test_mcp_server_manager_alias_tool_prefixing(): == "my_alias" ) assert ( - test_manager.tool_name_to_mcp_server_name_mapping["my_alias-send_email"] + test_manager.tool_name_to_mcp_server_name_mapping["my_alias--send_email"] == "my_alias" ) @@ -1267,7 +1267,7 @@ async def test_mcp_server_manager_server_name_tool_prefixing(): # Verify tool is prefixed with server_name (normalized) assert len(tools) == 1 - assert tools[0].name == "Test_Server-send_email" + assert tools[0].name == "Test_Server--send_email" # Verify mapping is updated correctly assert ( @@ -1275,7 +1275,7 @@ async def test_mcp_server_manager_server_name_tool_prefixing(): == "Test Server" ) assert ( - test_manager.tool_name_to_mcp_server_name_mapping["Test_Server-send_email"] + test_manager.tool_name_to_mcp_server_name_mapping["Test_Server--send_email"] == "Test Server" ) @@ -1327,7 +1327,7 @@ async def test_mcp_server_manager_server_id_tool_prefixing(): # Verify tool is prefixed with server_id assert len(tools) == 1 - assert tools[0].name == "test-server-123-send_email" + assert tools[0].name == "test-server-123--send_email" # Verify mapping is updated correctly assert ( @@ -1336,7 +1336,7 @@ async def test_mcp_server_manager_server_id_tool_prefixing(): ) assert ( test_manager.tool_name_to_mcp_server_name_mapping[ - "test-server-123-send_email" + "test-server-123--send_email" ] == "test-server-123" ) @@ -1487,19 +1487,19 @@ def test_add_server_prefix_to_name(): # Test basic prefixing result = add_server_prefix_to_name("send_email", "My Server") - assert result == "My_Server-send_email" + assert result == "My_Server--send_email" # Test with server name that already has underscores result = add_server_prefix_to_name("create_event", "my_server") - assert result == "my_server-create_event" + assert result == "my_server--create_event" # Test with empty name result = add_server_prefix_to_name("", "My Server") - assert result == "My_Server-" + assert result == "My_Server--" # Test with empty server name result = add_server_prefix_to_name("send_email", "") - assert result == "-send_email" + assert result == "--send_email" def test_get_server_auth_header_with_alias(): 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 4fc94000d61..4b01e9c43f7 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 @@ -1088,7 +1088,7 @@ async def test_call_mcp_tool_user_unauthorized_access(): # Try to call a tool from "restricted_server" - should raise HTTPException with 403 status with pytest.raises(HTTPException) as exc_info: await call_mcp_tool( - name="restricted_server-send_email", + name="restricted_server--send_email", arguments={ "to": "test@example.com", "subject": "Test", diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py index ef783981ee5..d80376cab1f 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -9,6 +9,8 @@ from fastapi import HTTPException sys.path.insert(0, "../../../../../") import httpx +from mcp import ReadResourceResult, Resource +from mcp.types import GetPromptResult, Prompt, ResourceTemplate, TextResourceContents from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( MCPServerManager, @@ -17,8 +19,6 @@ from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( from litellm.proxy._types import LiteLLM_MCPServerTable, MCPTransport from litellm.types.mcp import MCPAuth from litellm.types.mcp_server.mcp_server_manager import MCPOAuthMetadata, MCPServer -from mcp import ReadResourceResult, Resource -from mcp.types import GetPromptResult, Prompt, ResourceTemplate, TextResourceContents class TestMCPServerManager: @@ -1005,7 +1005,7 @@ class TestMCPServerManager: # Case 1: add_prefix=True (default for multi-server) -> expect prefixed tools_prefixed = await manager._get_tools_from_server(server, add_prefix=True) assert len(tools_prefixed) == 1 - assert tools_prefixed[0].name == "zapier-send_email" + assert tools_prefixed[0].name == "zapier--send_email" # Case 2: add_prefix=False (single-server) -> expect unprefixed tools_unprefixed = await manager._get_tools_from_server(