diff --git a/litellm/proxy/mcp_server/mcp_server_manager.py b/litellm/proxy/mcp_server/mcp_server_manager.py index b890e5fde1c..ca806cdf51e 100644 --- a/litellm/proxy/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/mcp_server/mcp_server_manager.py @@ -2643,6 +2643,12 @@ class MCPServerManager: warn_if_mcp_stdio_blocked(server_name, server_config.get("transport")) auth_type = server_config.get("auth_type", None) + if auth_type == MCPAuth.secret_headers: + raise ValueError( + f"Invalid config for MCP server '{server_name}': auth_type secret_headers is not supported in " + "config.yaml, because the headers are stored encrypted with the server. Create the server in " + "the dashboard or through the API instead." + ) manual_issuer = _blank_to_none(server_config.get("issuer")) manual_authorization_url = _blank_to_none(server_config.get("authorization_url")) manual_token_url = _blank_to_none(server_config.get("token_url")) diff --git a/tests/unit/proxy/mcp_server/test_mcp_secret_headers.py b/tests/unit/proxy/mcp_server/test_mcp_secret_headers.py index 3eec457b1b9..d6a48b89952 100644 --- a/tests/unit/proxy/mcp_server/test_mcp_secret_headers.py +++ b/tests/unit/proxy/mcp_server/test_mcp_secret_headers.py @@ -263,3 +263,35 @@ def test_preview_ignores_credential_headers_for_any_other_auth_type(auth_type: M ) assert rest_endpoints._preview_static_headers(request) == _STATIC_HEADERS assert rest_endpoints._preview_credential_header_names(request) == () + + +@pytest.mark.asyncio +async def test_config_file_server_with_secret_headers_is_rejected_when_loaded() -> None: + manager: Final = MCPServerManager() + config: Final = { + "datadog": { + "url": _URL, + "transport": MCPTransport.http, + "auth_type": MCPAuth.secret_headers, + "credentials": {"headers": dict(_CREDENTIAL_HEADERS)}, + } + } + with pytest.raises(ValueError, match="'datadog'.*secret_headers"): + await manager.load_servers_from_config(config) + assert not manager.config_mcp_servers + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.none, MCPAuth.api_key]) +async def test_config_file_server_with_another_auth_type_still_loads(auth_type: MCPAuth) -> None: + manager: Final = MCPServerManager() + config: Final = { + "datadog": { + "url": _URL, + "transport": MCPTransport.http, + "auth_type": auth_type, + "static_headers": _STATIC_HEADERS, + } + } + await manager.load_servers_from_config(config) + assert [server.auth_type for server in manager.config_mcp_servers.values()] == [auth_type]