mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
* fix(mcp): stop one unauthenticated server from emptying the aggregate tools/list On the aggregate MCP route (/mcp), the gateway fans out to every server the caller can access and flattens their tools. _fetch_and_filter_server_tools re-raises MCPUpstreamAuthError unconditionally (added with the OAuth passthrough feature in #28356) so it surfaces a 401 on single-server routes, but on the aggregate route that exception propagates through the asyncio.gather fan-out and the outer handler turns it into an empty list. The result: a single delegate/passthrough OAuth server the user has not authenticated (e.g. a delegate-auth server) zeroes the tools of every other server, including the ones that resolve fine, so the client connects and sees no tools. Surface the upstream auth error only when a single server was explicitly targeted (so that route still drives the upstream OAuth flow); across the aggregate, absorb it to [] for that one server so the rest still list their tools. This restores the graceful per-server degradation that predated #28356. Adds regression tests: the aggregate keeps a healthy server's tools when a sibling raises MCPUpstreamAuthError, and a single-server listing still surfaces it. * fix(mcp): decide aggregate vs single-server listing by route scope, not server count Addresses review: keying the surface-vs-absorb decision off the server count (len(allowed_mcp_servers), and even len(mcp_servers)) misclassifies an aggregate /mcp request from a key that can access exactly one server as a targeted single-server listing, so that one server's MCPUpstreamAuthError re-raises and empties the aggregate again for one-server permission sets. Use the path-derived single-server scope instead: _mcp_gateway_server_name, set by _gateway_initialize_instructions_request_scope only when the request path names exactly one upstream server (/<server>/mcp) and never from client headers, is None on the aggregate route (/mcp) regardless of how many servers the key can access. Single-server routes still surface the upstream-auth challenge; the aggregate absorbs it per server. Adds a regression test that an aggregate request with a single accessible server still absorbs, plus renames the single-server test to drive the route scope explicitly. The new test fails on the count-based logic. * fixing aggregation error * style(mcp): collapse single-line debug log to satisfy ruff format |
||
|---|---|---|
| .. | ||
| auth | ||
| guardrail_translation | ||
| outbound_credentials | ||
| test_byok_oauth_endpoints.py | ||
| test_callback_oauth_error_responses.py | ||
| test_db_credentials.py | ||
| test_discoverable_endpoints.py | ||
| test_is_tool_name_prefixed.py | ||
| test_jwt_mcp_enforcement.py | ||
| test_jwt_mcp_simple.py | ||
| test_mcp_cost_calculator.py | ||
| test_mcp_custom_fields.py | ||
| test_mcp_debug.py | ||
| test_mcp_discovery.py | ||
| test_mcp_elicitation_handler.py | ||
| test_mcp_env_vars.py | ||
| test_mcp_header_alias_utils.py | ||
| test_mcp_hook_extra_headers.py | ||
| test_mcp_metadata_preservation.py | ||
| test_mcp_oauth_passthrough.py | ||
| test_mcp_oauth_passthrough_cold_start.py | ||
| test_mcp_oauth_passthrough_tools.py | ||
| test_mcp_partial_update.py | ||
| test_mcp_sampling_completion_flow.py | ||
| test_mcp_sampling_model_access.py | ||
| test_mcp_sampling_model_resolution.py | ||
| test_mcp_sampling_priority_selection.py | ||
| test_mcp_sampling_request_builder.py | ||
| test_mcp_sampling_response_conversion.py | ||
| test_mcp_sampling_tool_conversion.py | ||
| test_mcp_server.py | ||
| test_mcp_server_identity_env.py | ||
| test_mcp_server_manager.py | ||
| test_mcp_session_logging.py | ||
| test_mcp_sigv4_auth.py | ||
| test_mcp_stale_session.py | ||
| test_mcp_toolset_scope.py | ||
| test_oauth2_token_cache.py | ||
| test_openapi_to_mcp_generator.py | ||
| test_openapi_tool_auth.py | ||
| test_rest_endpoints.py | ||
| test_semantic_tool_filter.py | ||
| test_short_mcp_tool_prefix.py | ||
| test_ui_session_utils.py | ||