Commit graph

778 commits

Author SHA1 Message Date
joshua-berri
3549143bcd
Merge pull request #34919 from BerriAI/litellm_fix_mcp_peek_utf8_boundary
fix(mcp): handle split UTF-8 routing previews
2026-09-21 19:56:50 +00:00
Joshua Valluru
a835e75620 fix(mcp): handle split UTF-8 routing previews in place 2026-09-21 12:24:19 -07:00
Joshua Valluru
bf5dff8986 chore: sync MCP UTF-8 fix with main 2026-09-21 12:15:57 -07:00
Joshua Valluru
1499d84f5a fix(mcp): paginate optional discovery lists 2026-09-21 11:37:55 -07:00
yassin
f9244749e0 fix(proxy): return 422 instead of 429 for BudgetExceededError
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-20 06:00:06 +00:00
mateo-berri
a3e9ed34fe fix(mcp): gate the pre-call listing per tool, not per server
A tools/call on a cold worker listed the target server once and then never
again, so a later caller whose credentials expose a wider upstream catalog
got 404 for tools the first caller never had. Gate the pre-call listing on
whether this worker already exposes the requested tool, so callers with
different catalogs no longer mask each other. Removing the per-server guard
also drops the empty-listing case that re-listed on every call.
2026-09-19 19:29:33 -07:00
mateo-berri
92ff54f134 fix(mcp): list a never-listed server before its first tools/call
The startup tool-name fill skips servers whose upstream wants the caller's
own token (true_passthrough, OAuth discovery), and mcp 2 no longer runs the
list handler before an uncached tools/call, so every uvicorn worker that had
not served tools/list answered 404 "Tool not found" for prefixed tools/call
and the REST server_id route on those servers.

On a resolution miss, execute_mcp_tool now lists the prefix-matched (or
server_id-requested) server once, with the caller's credentials, through the
existing tools/list path, then resolves as before. Listing failures fall
through to the existing 404, a worker that already listed the server never
re-lists it, and a server outside the caller's allowed set is never listed.
2026-09-19 18:49:26 -07:00
mateo-berri
854f5d959e Merge remote-tracking branch 'origin/main' into litellm_org_alias_from_team 2026-09-19 12:55:15 -07:00
ryan
84e56a60d2 Merge remote-tracking branch 'origin/main' into litellm_org_alias_from_team 2026-09-19 08:48:37 +00:00
Joshua Valluru
f5ab563499 fix(mcp): preserve session expiry signals and scope dependency CI 2026-09-18 22:52:10 -07:00
Joshua Valluru
aea13ee03b fix(mcp): preserve legacy behavior on SDK2 and streamline verification 2026-09-18 22:28:31 -07:00
Mateo Wang
078a60478f
Merge pull request #41485 from BerriAI/litellm_jwt_token_exchange_grant
feat(proxy): add RFC 8693 token exchange for IdP JWTs on the gateway token endpoint
2026-09-18 21:27:20 -07:00
jesus
3301fdafaf Merge remote-tracking branch 'origin/main' into litellm_org_alias_from_team 2026-09-19 00:48:54 +00:00
mateo-berri
50629ff5ca Merge branch 'main' of https://github.com/BerriAI/litellm into litellm_jwt_token_exchange_grant
# Conflicts:
#	tests/test_litellm/proxy/auth/test_auth_checks.py
2026-09-18 17:30:38 -07:00
joshua
7410d00d2e merge(main): resolve mcp server.py import conflict and relock
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-19 00:08:33 +00:00
jesus
82e3f3980d refactor(auth): resolve org identity through an auth_checks helper
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
LiteLLM Rust / rust-wheel (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 23:59:33 +00:00
joshua
8d8efe7203 style(mcp): satisfy lint and type budgets for the SDK 2 port
Format the ported files, annotate mutable wire payloads, give the e2e
OAuth client the SDK 2 httpx2/AuthorizationCodeResult API, tighten the
transport-streams alias to the two-stream SDK 2 shape, and add a
test-quality reason for the MockTransport factory injection.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 23:49:23 +00:00
joshua
0d2963fe89 test(mcp): update MCP suites for SDK2 handler signatures and ctx var
Call handlers with ServerRequestContext and params models, seed the
litellm contextvar instead of the removed SDK request_ctx, forward
headers/auth through the httpx2 MockTransport factory, and add
regressions for handler registration, context propagation, and modern
protocol-version rejection.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 23:34:30 +00:00
yassin
2231a3ca43 feat(mcp): give each allowed MCP client an alias and a value
mcp_allowed_clients entries become {alias, value} objects: the value is what the JWT claim or header must equal, the alias is the name the dashboard and logs show. The Network Settings section is renamed Allowed Clients with one alias/value row per client

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 22:29:17 +00:00
joshua
545bbeb001 test(mcp): update MCP suites for SDK 2 APIs
Rename McpError/isError/inputSchema-style references to the SDK 2
spellings, parse the JSONRPCMessage union with a TypeAdapter, and drive
the SDK transports off httpx2 MockTransport injection where respx can no
longer intercept. Adjust for SDK 2 behavior: the initialize handshake
negotiates handshake-era protocol versions only, an empty SSE stream
surfaces CONNECTION_CLOSED, non-2xx tool responses surface INTERNAL_ERROR
MCPError instead of HTTPStatusError, and the SDK read timeout carries the
JSON-RPC REQUEST_TIMEOUT code.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 22:13:18 +00:00
yassin
cca7ab8b1b test(mcp): type the REST allowlist test stubs
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 21:50:16 +00:00
yassin
0536fb3062 fix(mcp): fail closed on empty JWT claims and gate the REST tool routes on mcp_allowed_clients
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 21:29:59 +00:00
jesus
96dc32ddcf Merge remote-tracking branch 'origin/main' into litellm_org_alias_from_team 2026-09-18 20:58:38 +00:00
yassin
9917375d4b Merge remote-tracking branch 'origin/main' into litellm_mcp_client_allowlist
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/proxy/_experimental/mcp_server/server.py
2026-09-18 20:46:50 +00:00
yassin
fe8cf02823 feat(mcp): resolve the allowlisted client identity from the JWT claim or an opt-in header instead of clientInfo.name
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:27:43 +00:00
yassin
c5919a3c0e Merge remote-tracking branch 'origin/main' into litellm_mcp_client_allowlist
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/proxy/_experimental/mcp_server/server.py
#	litellm/proxy/proxy_server.py
2026-09-18 19:01:08 +00:00
yassin
ce48a3fbcc test(mcp): give the new cache and tombstone patches TQ008 reasons and match the keyword eviction call
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 02:49:41 +00:00
yassin
fd834f6f8b fix(mcp): broadcast BYOK and OAuth credential eviction to peer workers and expire admin session tombstones
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 02:10:50 +00:00
yassin
ea37596b88 feat(mcp): let proxy admins force-close live MCP sessions and revoke stored user credentials
Adds an admin-only DELETE /v1/mcp/sessions that terminates stateful MCP gateway sessions on the current worker by session id prefix and/or by the LiteLLM user that opened them, tombstones the terminated ids so a client reusing one gets 404 instead of a silently recreated stateless session, and lets PROXY_ADMIN name a user_id on the BYOK and OAuth credential delete routes. Full and view-only admins can list every user's stored credential metadata for a server (never the secret). The dashboard gains Disconnect controls on the Live Connections tab and a User Credentials tab with Revoke controls, both hidden from read-only admins.

Resolves LIT-8001

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 01:01:26 +00:00
mateo-berri
788e6eb737 merge: origin/main into litellm_jwt_token_exchange_grant 2026-09-17 17:59:23 -07:00
mateo-berri
82d252210e fix(proxy): word the token exchange's 503 by whether the database fault can clear
A permanent database fault (a missing or version-skewed query engine)
in the subject_token check was answered with the same "retry" wording
as a transient outage. The status stays 503 temporarily_unavailable,
the only OAuth error a client reads as the server's fault and what the
mint path already answers to the same fault, but the description now
says retrying will not help until the deployment is repaired, using
PrismaDBExceptionHandler.is_permanent_database_fault the way the mint
path does.
2026-09-17 17:20:17 -07:00
Joshua Valluru
3519d01549 test(mcp): add isolated SDK2 dependency compatibility gate 2026-09-17 17:16:47 -07:00
mateo-berri
8e3742a5f3 fix(proxy): answer 503 temporarily_unavailable when the token exchange cannot verify the subject token
Some checks are pending
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
A subject token JWT auth could not check, because the IdP's JWKS was unreachable with no cached copy or the auth database was down, came back as 400 invalid_request with the same fixed message a bad token gets, so clients re-logged in instead of retrying the way they already do for a mint-time 503. Those checks now answer 503 temporarily_unavailable and log the reason, while real rejections stay 400 invalid_request.
2026-09-17 16:39:02 -07:00
yassin
33531649c3 perf(mcp): count gateway session groups with Counter and pin the oversized initialize peek invariant
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 23:20:47 +00:00
yassin
c230393731 fix(mcp): refuse sessionless and stale-session POSTs that skip initialize while mcp_allowed_clients is set
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 23:18:54 +00:00
jesus
1b69a5b0a4 test(proxy): model missing organizations in MCP auth fixtures
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 22:34:02 +00:00
yassin
5f6702ee47 fix(mcp): admit an allowlisted initialize that fills the peek cap exactly and type the allowlist test helpers
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 22:22:19 +00:00
yassin
a9ab7392ae feat(mcp): show live gateway sessions by AI client and user
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 22:15:55 +00:00
yassin
c0f335d8ca fix(mcp): cap the body the client allowlist inspects at 64 KiB
With mcp_allowed_clients set the gateway used to read the whole POST body to
find clientInfo.name, so an authenticated client could make the proxy buffer an
arbitrarily large payload. Inspection is now capped at MCP_ALLOWLIST_PEEK_MAX_BYTES
and a sessionless POST that exceeds the cap is rejected with 403 before routing,
while posts on an admitted session stream through unchanged

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 21:46:15 +00:00
yassin
d240a5b6bb test(mcp): import json at module level in the MCP allowlist tests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 21:04:56 +00:00
yassin
14639bbb5a fix(mcp): read the whole initialize body under allowlist enforcement and surface a stored empty allowlist in the UI
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 20:55:09 +00:00
yassin
f29be6e1ee fix(ui): save the MCP private ranges and client allowlist sequentially
The proxy stores both fields with a whole-row read-modify-write of
general_settings, so two concurrent writes from one save can drop one
of them. Also drops docstrings and suppressions the diff did not need

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 20:05:35 +00:00
yassin
99eeb813c4 Merge remote-tracking branch 'origin/main' into litellm_mcp_client_allowlist 2026-09-17 19:34:42 +00:00
yassin
a705e0396e refactor(mcp): type the allowlist 403 body and replay consumed messages immutably
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 18:55:47 +00:00
yassin
47be6c8aeb feat(mcp): allowlist client applications for MCP gateway access
Adds the mcp_allowed_clients general setting, enforced against the
clientInfo.name each MCP client sends in its initialize request. A client
not on the list, or one that does not identify itself, is rejected with
403 before any stateful session is created. The setting is configurable
from config.yaml and from the Admin UI MCP network settings page

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 18:34:36 +00:00
Joshua Valluru
5b91195406 fix(mcp): retain selected guardrails for virtual REST calls 2026-09-17 10:40:33 -07:00
Joshua Valluru
743684bdbe fix(mcp): preserve request-selected guardrails during tool execution 2026-09-17 09:57:08 -07:00
mateo-berri
d8c3a38a51 Merge remote-tracking branch 'origin/main' into litellm_jwt_token_exchange_grant
# Conflicts:
#	tests/test_litellm/proxy/auth/test_auth_checks.py
2026-09-16 18:17:57 -07:00
mateo-berri
769b47457e fix(proxy): keep the token exchange off gateways that map JWTs to virtual keys 2026-09-16 17:22:27 -07:00
Mateo Wang
9e1eb546e4
Merge pull request #41514 from BerriAI/litellm_mcp_api_key_static_header_slot
fix(mcp): count admin static headers as api_key credential slots
2026-09-16 17:22:08 -07:00