litellm/tests/test_litellm/proxy/proxy_server
Tin Chi Lo a78130461f feat(mcp): gateway DCR session admission at the aggregate /mcp endpoint (LIT-3637)
Admits a keyless SSO user (no virtual key) at the aggregate /mcp endpoint from a gateway DCR
session bearer, resolving team/org/SCIM/budget authorization fresh on every call.

- Aggregate DCR front door: stateless /register (sealed llm_dcrc_ client ids), SSO-backed
  /authorize + /authorize/complete, and /token minting identity-only session tokens with PKCE,
  single-use codes/flows, and rotating refresh tokens.
- Admission: a session-shaped Authorization at the aggregate scope opens via _admit_gateway_session,
  reloads the live user, and runs the centralized policy gate; failures return the RFC 9728
  invalid_token challenge. Gated on the un-forgeable, server-only mcp_admitted_user_subject marker,
  so virtual-key and JWT auth are unchanged.
- Authorization model: an admitted subject is resolved as one plain UserAPIKeyAuth per grant source
  (its own grants, plus each team it is a live roster member of), each answered by the SAME resolver
  virtual keys use, then unioned. That branch is the FIRST statement of BOTH public resolvers, so no
  single-credential prelude runs for it and a fault in a lookup it never uses cannot deny its grants. A source team counts only while it is a live grantor: roster membership, not
  blocked, and neither the team nor its owning org over budget (enforced through the SAME
  _team_max_budget_check / _organization_max_budget_check owners common_checks uses for keys).
  Each team source carries that team's own org, so the existing org
  ceiling caps it; for a keyless source the org list only ever intersects (a ceiling must not become
  a grant) and an unresolvable ceiling denies rather than silently uncapping, on both the server and
  tool axes. _roster_team_object is the single owner of "which teams count": a team whose roster no
  longer lists the user neither grants servers nor throttles, in one place.
- Rate limits: the subject is bounded by its user rpm/tpm AND by the per-server mcp_rpm_limit of
  the team a call is ATTRIBUTED to — the same single source billing charges, from the same owner. A key charges its one pinned team's bucket; a keyless
  subject has no team_id, so admission stamps each granting team's limit map onto the auth
  (server-only field, stripped from validated input like the marker) and the limiter emits that
  team's mcp_per_team descriptor. Charging every granting team instead would let one cross-team user
  drain several teams' SHARED buckets on a single call and block their other members; and a server
  the user's OWN grant reaches charges no team bucket at all, because no team provided it. Per-KEY
  MCP limits do not apply because there is no key.
- Wrapper channels: the manager-level union treats the admitted subject by the same grant model.
  The admin-role short-circuit and the absolute no_mcp_servers early-return are key-credential
  rules and never apply to it (a session bearer is a third-party client credential, not the
  dashboard, and the subject's opt-out silences only its own source). Operator-open channels
  (allow_all_keys, the user's own BYOM submissions) are owned by one operator_open_server_ids
  helper that BOTH the server union and the admitted tool resolution consult (suppress-BYOM-when-
  explicitly-scoped is a key-credential rule and never applies to the subject, whose user row
  carries the DB-default empty mcp_servers), so an open-channel
  server is default-open for tools instead of listable but uninvokable.
- Redirect URIs: one owner, validate_redirect_uri_shape, decides redirect-URI hygiene (bad scheme,
  fragment, missing host, userinfo, backslash host) and resolves allowlisted native callbacks, shared
  by DCR registration and the OAuth endpoints. Registration keeps a deliberately wider trust policy
  than validate_trusted_redirect_uri: public dynamic registration accepts any https client, and its
  controls are mandatory S256 PKCE plus the consent screen.
- Egress leak-defense: a gateway admission credential (session bearer / bridge envelope) is scrubbed
  from EVERY egress header context, anchored to the credential shape, so it can never be forwarded
  upstream and replayed.
- Single-use guard: auth-code, refresh and connect-flow claims resolve the proxy's cross-worker redis
  cache themselves rather than trusting the cache passed in, and fail CLOSED on a Redis fault instead
  of falling back to a per-worker count that a captured id could replay through another worker.
- Sign-in return_to: one shared, never-raising helper persists a safe return_to for every sign-in
  branch (SSO/Okta/generic and username/password), and every branch RESUMES through the same
  _sso_return_to_redirect the SSO callback uses, so however a deployment signs in the stored value
  is honored identically (same-origin path directly; control_plane_url via the one-time login-code
  handoff). A stale cookie is ignored rather than failing a completed sign-in.

- Budgets, both halves: ENFORCEMENT (an already over-budget team or its owning org stops being a
  grantor, in the source gate) and ACCOUNTING (a team-derived tool call is billed to the granting
  team and ITS org, so that budget accumulates and the right organization is charged). A server the
  user's own grant reaches bills the user; when several teams grant one server the pick is the
  lowest team_id, stable and auditable. Billing rides a COPY, so authorization still sees the full
  union, and it is inert when the target server cannot be resolved from the tool name.

Deferred (tracked): client-selected server scoping of the session token (LIT-4680).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 00:24:28 -07:00
..
.coverage_baseline test(proxy): add harness for proxy_server.py behavior-pinning (#28827) 2026-05-25 20:26:44 -07:00
__init__.py test(proxy): add harness for proxy_server.py behavior-pinning (#28827) 2026-05-25 20:26:44 -07:00
_coverage_check.py test(proxy): add harness for proxy_server.py behavior-pinning (#28827) 2026-05-25 20:26:44 -07:00
_pin_check.py test(proxy): add harness for proxy_server.py behavior-pinning (#28827) 2026-05-25 20:26:44 -07:00
conftest.py test(proxy): add harness for proxy_server.py behavior-pinning (#28827) 2026-05-25 20:26:44 -07:00
test_background_health.py fix(router): tag-aware pre-routing strategy selection for shared model_name (#33691) 2026-07-17 09:26:07 -07:00
test_exception_handlers.py fix(otel): restore proxy-level error.* attributes on v2 failure spans (LIT-4179) (#33664) 2026-07-18 10:52:27 -07:00
test_harness_smoke.py test(proxy): add harness for proxy_server.py behavior-pinning (#28827) 2026-05-25 20:26:44 -07:00
test_lifecycle.py fix(proxy): redact secrets on the db-config and litellm_settings log paths too 2026-07-03 13:03:45 -07:00
test_openapi_customization.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_proxy_config.py feat(proxy): make DB config-reload interval configurable via config.yaml and UI (#34130) 2026-07-21 22:03:22 +00:00
test_routes_anthropic_beta.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_assistants.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_audio.py fix(proxy): extend response headers hook to streaming, TTS, image gen, and pass-through (#24232) 2026-06-09 22:10:23 +02:00
test_routes_chat_completions.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_completions.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_config.py feat(proxy): make DB config-reload interval configurable via config.yaml and UI (#34130) 2026-07-21 22:03:22 +00:00
test_routes_embeddings.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_invitation.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_login_sso.py feat(mcp): gateway DCR session admission at the aggregate /mcp endpoint (LIT-3637) 2026-07-23 00:24:28 -07:00
test_routes_misc.py fix(router): tag-aware pre-routing strategy selection for shared model_name (#33691) 2026-07-17 09:26:07 -07:00
test_routes_model_cost_map.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_model_info.py fix(proxy): restore wildcard expansion in /v1/model/info (#31444) 2026-06-26 08:50:58 -07:00
test_routes_model_metrics.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_models.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_moderations.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_onboarding.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_queue.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_threads.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_routes_utils.py tests(proxy_server): surface current behavior in tests (#29309) 2026-05-29 23:17:24 -07:00
test_spend_counters.py perf(spend): gather independent per-scope spend-counter increments (#31578) 2026-06-30 12:07:47 -07:00
test_streaming_helpers.py feat(complexity-router): add return_raw_model_name toggle for response model field (#33875) 2026-07-18 19:24:56 -07:00
test_team_model_name_translation.py fix(vertex_ai): exclude Gemini Google Search grounding tokens from input token billing (#33742) 2026-07-17 21:17:49 -07:00