mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
fix(mcp): satisfy type discipline gate and the merged input-schema key
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
c2f85ca2e7
commit
072898ab19
5 changed files with 18 additions and 18 deletions
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue