mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
* feat(mcp): add v1 bridge + none/api_key resolver arms (unwired) PR4a of the MCP v2 outbound-credential migration, stacked on the resolver skeleton. Builds the bridge for the first live modes without wiring it onto the request path: - resolver.py: the none arm (NoOpAuth) and the api_key shared-key arm (StaticHeaderAuth from the config); the BYOK source and the other five arms stay not_implemented. - adapter.py: the v1 <-> v2 edge (to_subject, to_server_spec, raise_public, should_defer). to_server_spec maps only none + the static-header family and returns None to defer every other mode to v1. Imports v1, kept out of the package __init__ so the resolver core stays v1-free. - MCPClient gains an optional resolved_auth that feeds the factory's auth= slot, taking precedence over the SigV4 aws_auth; default None keeps current behavior. Nothing calls these from _create_mcp_client yet, so production behavior is unchanged; the graft lands in PR4b. Unit tests cover the two arms, the full mapping table, and the auth plumbing. * feat(mcp): graft v2 resolver onto _create_mcp_client for migrated modes Wire the none + api_key static-family resolver arms from PR4a onto v1's live request path. In _create_mcp_client's HTTP/SSE branch, to_server_spec decides per mode: a migrated mode resolves through the injected UpstreamCredentialProvider and feeds the resulting httpx.Auth into the new resolved_auth slot; every other mode returns None and falls through to the unchanged v1 construction. resolve_mcp_auth now runs only when the mode defers, so a migrated server skips the v1 token-exchange / M2M I/O. stdio is untouched: auth_type/auth_value never reach the upstream on the stdio path (_get_auth_headers is HTTP/SSE only), so there is nothing to graft there. No v1 code is deleted yet; resolve_mcp_auth's static return still backs stdio and the not-yet-migrated modes until later PRs retire it. * test(mcp): cover the v2-resolver graft in _create_mcp_client Regression tests for the PR4 graft. Migrated HTTP modes resolve through the provider into resolved_auth: none -> NoOpAuth, and the static api_key family emits the right header per scheme (X-API-Key, Bearer, token, raw authorization, base64 basic). Deferred modes (oauth2) and a missing static token fall back to v1's auth_value. A stdio server with a migrated auth_type still defers to v1, since httpx.Auth never reaches the subprocess. A resolver Error is mapped to the public HTTP contract (401) via an injected provider, exercising the DI seam. * fix(mcp): defer to v1 when an inbound credential would be overridden The graft attaches the resolved static credential as an httpx.Auth, whose auth flow writes its header after extra_headers. That silently overrode an inbound Authorization: a per-request mcp_auth_header override, or a header supplied via a guardrail hook / static_headers / forwarded caller header. v1 lets those win, so the graft had inverted the credential precedence for the migrated static modes. Mirror the v2 egress credential-isolation invariant: defer the request to v1 when mcp_auth_header is set, or when the header the resolved credential would write is already present in extra_headers. none writes no header, so it never defers. * test(mcp): cover the credential-isolation defer guard Regression tests for the precedence fix. A per-request mcp_auth_header override and an Authorization already present in extra_headers (guardrail hook like the JWT signer, static_headers, or a forwarded caller header) both defer a migrated static server to v1 so the inbound credential wins; none stays on v2 and does not clobber an inbound Authorization since NoOpAuth writes nothing. The deferred cases assert resolved_auth is None, which fails if the guard is removed. * refactor(mcp): resolve inbound-header conflict on v2 instead of deferring For an Authorization already supplied via extra_headers (a guardrail hook such as the JWT signer, static_headers, or a forwarded caller header), keep the request on the v2 path and skip resolved_auth rather than deferring to v1. The inbound header still wins since nothing overwrites it, but hooks no longer pin a v1 fallback, which is what lets resolve_mcp_auth be retired once the remaining modes migrate. The mcp_auth_header per-request override still defers to v1, since that value becomes the upstream credential rather than sitting in extra_headers; that defer falls away once the per-user modes stop writing mcp_auth_header. * fix(mcp): clear UP037 lint gate and fix allowed-servers test under the graft adapter.py uses `from __future__ import annotations`, so the quoted "UserAPIKeyAuth" / "MCPServer" annotations in to_subject/to_server_spec/_shared_key_spec were unnecessary and pushed UP037 over the strict-rule budget; drop the quotes. test_list_tools_only_returns_allowed_servers passed a MagicMock as user_api_key_auth. The graft now builds a Subject from the principal, and the MagicMock's non-string org_id/user_id fail Subject validation, so the listing came back empty. Use a real UserAPIKeyAuth instead (MagicMock for an injected dependency was the anti-pattern here). * test(mcp): assert config token via resolved_auth, not the headers dict test_mcp_server_config_auth_value_header_used inspected _get_auth_headers(), but the graft now carries the static credential on the client's httpx.Auth (resolved_auth) and writes the header at send time, so that dict is empty. Assert the header the StaticHeaderAuth emits onto the request instead. Both config keys (authentication_token, auth_value) stay covered. * chore(typecheck): set reportMatchNotExhaustive slack to 0 The previous slack of 3 put the ceiling at baseline + slack = 4, so a newly non-exhaustive match (for instance dropping an Error arm off a Result match) could land without tripping the gate. Setting slack to 0 pins the ceiling at the current baseline of 1, so any added non-exhaustive match now fails CI while the one pre-existing violation in router.py stays within budget
194 lines
3.4 KiB
JSON
194 lines
3.4 KiB
JSON
{
|
|
"reportAny": {
|
|
"baseline": 24989,
|
|
"slack": 2500
|
|
},
|
|
"reportArgumentType": {
|
|
"baseline": 1934,
|
|
"slack": 180
|
|
},
|
|
"reportAssignmentType": {
|
|
"baseline": 220,
|
|
"slack": 22
|
|
},
|
|
"reportAttributeAccessIssue": {
|
|
"baseline": 346,
|
|
"slack": 35
|
|
},
|
|
"reportCallIssue": {
|
|
"baseline": 87,
|
|
"slack": 10
|
|
},
|
|
"reportConstantRedefinition": {
|
|
"baseline": 39,
|
|
"slack": 4
|
|
},
|
|
"reportDeprecated": {
|
|
"baseline": 217,
|
|
"slack": 22
|
|
},
|
|
"reportDuplicateImport": {
|
|
"baseline": 28,
|
|
"slack": 3
|
|
},
|
|
"reportExplicitAny": {
|
|
"baseline": 6931,
|
|
"slack": 700
|
|
},
|
|
"reportFunctionMemberAccess": {
|
|
"baseline": 7,
|
|
"slack": 3
|
|
},
|
|
"reportGeneralTypeIssues": {
|
|
"baseline": 151,
|
|
"slack": 15
|
|
},
|
|
"reportIncompatibleMethodOverride": {
|
|
"baseline": 52,
|
|
"slack": 5
|
|
},
|
|
"reportIncompatibleVariableOverride": {
|
|
"baseline": 8,
|
|
"slack": 3
|
|
},
|
|
"reportInconsistentOverload": {
|
|
"baseline": 12,
|
|
"slack": 3
|
|
},
|
|
"reportIndexIssue": {
|
|
"baseline": 26,
|
|
"slack": 3
|
|
},
|
|
"reportInvalidTypeForm": {
|
|
"baseline": 23,
|
|
"slack": 3
|
|
},
|
|
"reportInvalidTypeVarUse": {
|
|
"baseline": 2,
|
|
"slack": 3
|
|
},
|
|
"reportMatchNotExhaustive": {
|
|
"baseline": 1,
|
|
"slack": 0
|
|
},
|
|
"reportMissingParameterType": {
|
|
"baseline": 3933,
|
|
"slack": 390
|
|
},
|
|
"reportMissingTypeArgument": {
|
|
"baseline": 10612,
|
|
"slack": 1000
|
|
},
|
|
"reportMissingTypeStubs": {
|
|
"baseline": 27,
|
|
"slack": 10
|
|
},
|
|
"reportOperatorIssue": {
|
|
"baseline": 6,
|
|
"slack": 3
|
|
},
|
|
"reportOptionalCall": {
|
|
"baseline": 4,
|
|
"slack": 3
|
|
},
|
|
"reportOptionalIterable": {
|
|
"baseline": 3,
|
|
"slack": 3
|
|
},
|
|
"reportOptionalMemberAccess": {
|
|
"baseline": 724,
|
|
"slack": 72
|
|
},
|
|
"reportOptionalOperand": {
|
|
"baseline": 3,
|
|
"slack": 3
|
|
},
|
|
"reportOptionalSubscript": {
|
|
"baseline": 11,
|
|
"slack": 3
|
|
},
|
|
"reportPossiblyUnboundVariable": {
|
|
"baseline": 52,
|
|
"slack": 10
|
|
},
|
|
"reportPrivateUsage": {
|
|
"baseline": 1625,
|
|
"slack": 160
|
|
},
|
|
"reportRedeclaration": {
|
|
"baseline": 8,
|
|
"slack": 3
|
|
},
|
|
"reportReturnType": {
|
|
"baseline": 126,
|
|
"slack": 100
|
|
},
|
|
"reportTypedDictNotRequiredAccess": {
|
|
"baseline": 20,
|
|
"slack": 3
|
|
},
|
|
"reportUndefinedVariable": {
|
|
"baseline": 2,
|
|
"slack": 3
|
|
},
|
|
"reportUnknownArgumentType": {
|
|
"baseline": 30603,
|
|
"slack": 3000
|
|
},
|
|
"reportUnknownLambdaType": {
|
|
"baseline": 75,
|
|
"slack": 10
|
|
},
|
|
"reportUnknownMemberType": {
|
|
"baseline": 27037,
|
|
"slack": 2500
|
|
},
|
|
"reportUnknownParameterType": {
|
|
"baseline": 13612,
|
|
"slack": 1000
|
|
},
|
|
"reportUnknownVariableType": {
|
|
"baseline": 21445,
|
|
"slack": 2000
|
|
},
|
|
"reportUnnecessaryCast": {
|
|
"baseline": 118,
|
|
"slack": 10
|
|
},
|
|
"reportUnnecessaryComparison": {
|
|
"baseline": 683,
|
|
"slack": 100
|
|
},
|
|
"reportUnnecessaryContains": {
|
|
"baseline": 4,
|
|
"slack": 3
|
|
},
|
|
"reportUnnecessaryIsInstance": {
|
|
"baseline": 808,
|
|
"slack": 80
|
|
},
|
|
"reportUntypedBaseClass": {
|
|
"baseline": 110,
|
|
"slack": 11
|
|
},
|
|
"reportUntypedFunctionDecorator": {
|
|
"baseline": 22,
|
|
"slack": 3
|
|
},
|
|
"reportUnusedClass": {
|
|
"baseline": 22,
|
|
"slack": 3
|
|
},
|
|
"reportUnusedFunction": {
|
|
"baseline": 137,
|
|
"slack": 10
|
|
},
|
|
"reportUnusedImport": {
|
|
"baseline": 670,
|
|
"slack": 50
|
|
},
|
|
"reportUnusedVariable": {
|
|
"baseline": 865,
|
|
"slack": 50
|
|
}
|
|
}
|