diff --git a/litellm/proxy/_experimental/mcp_server/caller_sign_in.py b/litellm/proxy/_experimental/mcp_server/caller_sign_in.py index b6b5b728924..e11c313f94a 100644 --- a/litellm/proxy/_experimental/mcp_server/caller_sign_in.py +++ b/litellm/proxy/_experimental/mcp_server/caller_sign_in.py @@ -136,7 +136,7 @@ def caller_sign_in_for(server: MCPServer, user_api_key_auth: UserAPIKeyAuth | No """The merged sign-in requirement for ``server``: the OBO server's own issuer/scopes plus every registered provider's contribution. ``None`` when nothing requires sign-in, which is also the gate the connect-time challenge branches on.""" - contributions: Final = [ + contributions: Final = tuple( contribution for contribution in ( *( @@ -147,7 +147,7 @@ def caller_sign_in_for(server: MCPServer, user_api_key_auth: UserAPIKeyAuth | No *(provider.caller_sign_in(server, user_api_key_auth) for provider in _providers()), ) if contribution is not None - ] + ) if not contributions: return None issuers: Final = tuple(dict.fromkeys(itertools.chain.from_iterable(c.issuers for c in contributions))) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 849c1feb524..5c4894c3bd6 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -2625,9 +2625,9 @@ def _caller_sign_in_protected_resource_response( if sign_in is None or not sign_in.issuers: return None return { - "authorization_servers": list(sign_in.issuers), + "authorization_servers": sign_in.issuers, "resource": resource_url, - "scopes_supported": list(sign_in.scopes), + "scopes_supported": sign_in.scopes, } diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index a42c2b54cec..9763c20cd4d 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -1589,7 +1589,7 @@ if MCP_AVAILABLE: ) -> bool: """Sign-in challenges are issued only on a single-server connect the key's grant admits, so a key without access gets the grant's 403 instead of a sign-in it could not use.""" - if len(mcp_servers or []) != 1: + if len(mcp_servers or ()) != 1: return False allowed: Final = await operations._get_allowed_mcp_servers( user_api_key_auth=user_api_key_auth, mcp_servers=mcp_servers, client_ip=client_ip @@ -1746,8 +1746,8 @@ if MCP_AVAILABLE: await operations._get_allowed_mcp_servers( user_api_key_auth=user_api_key_auth, mcp_servers=mcp_servers, client_ip=client_ip ) - if server and len(mcp_servers or []) == 1 - else [] + if server and len(mcp_servers or ()) == 1 + else () ) if ( server diff --git a/litellm/proxy/guardrails/guardrail_hooks/agent_365/agent_365.py b/litellm/proxy/guardrails/guardrail_hooks/agent_365/agent_365.py index 8bbb4c54b03..7208041f4da 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/agent_365/agent_365.py +++ b/litellm/proxy/guardrails/guardrail_hooks/agent_365/agent_365.py @@ -447,8 +447,10 @@ class Agent365Guardrail(CustomGuardrail): if not (self.default_on and server.keeps_caller_authorization): return None if user_api_key_auth is not None: - probe: Final[dict[str, Mapping[str, object]]] = { # pyright: ignore[reportUnknownVariableType] # UserAPIKeyAuth metadata dicts are untyped - "metadata": { + probe: Final[ + dict[str, Mapping[str, object]] + ] = { # mutable-ok: should_run_guardrail takes a mutable data dict # pyright: ignore[reportUnknownVariableType] # UserAPIKeyAuth metadata dicts are untyped + "metadata": { # mutable-ok: should_run_guardrail takes a mutable data dict "user_api_key_metadata": user_api_key_auth.metadata, # pyright: ignore[reportUnknownMemberType] # UserAPIKeyAuth.metadata is a raw dict "user_api_key_team_metadata": user_api_key_auth.team_metadata, # pyright: ignore[reportUnknownMemberType] # UserAPIKeyAuth.team_metadata is a raw dict } diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_agent_365.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_agent_365.py index d64ec31574a..3d4fab45322 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_agent_365.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_agent_365.py @@ -193,9 +193,7 @@ def _make_guardrail( ) -def _default_fallback_guardrail( - handler: FakeHandler, exchanger: StubTokenExchanger | None = None -) -> Agent365Guardrail: +def _default_fallback_guardrail(handler: FakeHandler, exchanger: StubTokenExchanger | None = None) -> Agent365Guardrail: return _make_guardrail(handler, exchanger=exchanger) @@ -416,7 +414,7 @@ class TestAllowFlow: handler: Final = FakeHandler([_allow_response()]) guardrail: Final = _make_guardrail(handler) schema: Final = {"type": "object", "properties": {"to": {"type": "string"}}, "required": ["to"]} - await _run(guardrail, _mcp_data(mcp_tool_description="Send an email", mcp_tool_input_schema=schema)) + await _run(guardrail, _mcp_data(mcp_tool_description="Send an email", mcp_input_schema=schema)) assert handler.calls[0].json["tool"] == { "name": "send_email", "description": "Send an email", @@ -431,7 +429,7 @@ class TestAllowFlow: async def test_evaluate_payload_omits_missing_or_malformed_tool_metadata(self, description, schema): handler: Final = FakeHandler([_allow_response()]) guardrail: Final = _make_guardrail(handler) - await _run(guardrail, _mcp_data(mcp_tool_description=description, mcp_tool_input_schema=schema)) + await _run(guardrail, _mcp_data(mcp_tool_description=description, mcp_input_schema=schema)) assert handler.calls[0].json["tool"] == {"name": "send_email"} @pytest.mark.asyncio @@ -617,9 +615,7 @@ class TestFailOpenOptIn: @pytest.mark.asyncio @pytest.mark.parametrize(("responses", "exchange_results"), AVAILABILITY_FAILURES) async def test_constructor_default_blocks_each_availability_failure_with_503(self, responses, exchange_results): - guardrail: Final = _default_fallback_guardrail( - FakeHandler(responses), StubTokenExchanger(exchange_results) - ) + guardrail: Final = _default_fallback_guardrail(FakeHandler(responses), StubTokenExchanger(exchange_results)) assert guardrail.unreachable_fallback == "fail_closed" with pytest.raises(HTTPException) as exc_info: await _run(guardrail, _mcp_data()) @@ -846,7 +842,9 @@ class TestUnreachableFallback: @pytest.mark.asyncio async def test_exchange_upstream_unavailable_follows_fail_open(self): - exchanger: Final = StubTokenExchanger([Error(CredError.of_upstream_unavailable("Entra throttled the exchange"))]) + exchanger: Final = StubTokenExchanger( + [Error(CredError.of_upstream_unavailable("Entra throttled the exchange"))] + ) handler: Final = FakeHandler([]) guardrail: Final = _make_guardrail(handler, exchanger=exchanger, unreachable_fallback="fail_open") data: Final = _mcp_data()