feat(mcp): support oauth2_token_exchange auth type via REST API and dashboard - #31772
Conversation
Greptile SummaryThis PR adds
Confidence Score: 4/5Mostly safe to merge; the create wizard missing transport guard can block form submission for stdio servers with the new auth type, but has no effect on correctly configured servers. The missing !isStdioTransport guard in create_mcp_server.tsx means selecting stdio transport + oauth2_token_exchange makes client_id/client_secret required fields, blocking form submission for that combination. The edit form already applies the guard correctly. All other logic — DB migration, blob-to-column promotion, sanitization, and runtime egress — is well-tested and appears correct. create_mcp_server.tsx (missing transport guard around TokenExchangeFormFields) and db.py (empty-blob edge case in the explicit-TE-write migration path).
|
| Filename | Overview |
|---|---|
| litellm/proxy/_experimental/mcp_server/db.py | Adds blob-to-column migration logic for token-exchange fields; create, update, and explicit-column-write paths all handle the migration correctly and are covered by dedicated tests. |
| litellm/proxy/_experimental/mcp_server/mcp_server_manager.py | Reads token-exchange columns with credentials-blob fallback; _obo_needs_endpoint_discovery and _build_mcp_server_table updated correctly; DEFAULT_SUBJECT_TOKEN_TYPE constant replaces inline string literals. |
| litellm/proxy/management_endpoints/mcp_management_endpoints.py | Sanitization functions for non-admin and virtual-key views now correctly clear the three new TE columns, consistent with existing authorization_url/token_url handling. |
| ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx | Adds isTokenExchangeAuthType flag and renders TokenExchangeFormFields when selected; missing !isStdioTransport guard unlike the edit form. |
| ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx | Edit form correctly guards TokenExchangeFormFields with !isStdioTransport; auth-type-switch clearing sends explicit nulls for the TE columns. |
| ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx | Correctly renames isObo to isAuthorizationCode throughout; token_exchange mode handled as passthrough with no auth gate. |
| ui/litellm-dashboard/src/components/mcp_tools/TokenExchangeFormFields.tsx | New component for token-exchange form fields; top-level form names correctly match the new column-based API shape. |
| litellm-proxy-extras/litellm_proxy_extras/migrations/20260630120000_add_token_exchange_to_mcp_servers/migration.sql | Idempotent IF NOT EXISTS migration adds the three nullable columns; timestamp ordering caveat documented in the file header. |
| tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_partial_update.py | Comprehensive blob-to-column migration tests covering create, credential merge, explicit-column-write, and auth-type-switch paths; all mock-based, no network calls. |
| tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py | New TestMCPServerTokenExchangeColumns covers column reads, blob fallback, default token type, and round-trip fidelity; mock-based. |
Reviews (13): Last reviewed commit: "fix(mcp): scrub subject_token_type in th..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This comment was marked as outdated.
This comment was marked as outdated.
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 1 · PR risk: 0/10 |
bf91140 to
19bb79e
Compare
19bb79e to
a70b291
Compare
8b56f66 to
2176477
Compare
a70b291 to
3aba74a
Compare
3aba74a to
c112117
Compare
|
@greptileai rereview |
|
bugbot run |
|
@coderabbitai review |
✅ Action performedReview finished.
|
📝 WalkthroughWalkthroughThis PR adds RFC 8693 Token Exchange (OBO) support to MCP servers: database migration and schema fields for ChangesBackend schema, models, and server manager logic
Estimated code review effort: 3 (Moderate) | ~30 minutes UI OAuth mode rename and token exchange form support
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant MCPServerManager
participant LiteLLM_MCPServerTable
Caller->>MCPServerManager: build_mcp_server_from_table(mcp_server)
MCPServerManager->>LiteLLM_MCPServerTable: read token_exchange_endpoint, audience, subject_token_type
alt columns present
MCPServerManager->>MCPServerManager: use dedicated columns
else columns absent
MCPServerManager->>MCPServerManager: fallback to credentials_dict
end
MCPServerManager-->>Caller: MCPServer instance
sequenceDiagram
participant User
participant MCPToolsUI as mcp_tools.tsx
participant CredentialQuery
participant OAuthFlow as useUserMcpOAuthFlow
User->>MCPToolsUI: open tools tab
MCPToolsUI->>CredentialQuery: check authorization_code credential status
CredentialQuery-->>MCPToolsUI: authorizationCodeNeedsAuth
alt needs auth
MCPToolsUI-->>User: show Authentication required panel
User->>MCPToolsUI: click authorize
MCPToolsUI->>OAuthFlow: startAuthorizationCodeAuthorize
OAuthFlow-->>MCPToolsUI: onAuthorizationCodeAuthSuccess
MCPToolsUI->>CredentialQuery: refetch status and tools
else has credential
MCPToolsUI-->>User: list tools
end
Related Issues: None referenced. Related PRs: None referenced. Suggested labels: enhancement, mcp, ui Suggested reviewers: litellm maintainers familiar with MCP server auth and dashboard UI 🐰 Hop, hop, through columns new, 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx (1)
820-834: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winClear auth-specific fields on auth type change
ThisSelectstill leavesauthorization_url/token_url/registration_url/credentialsin form state, andhandleSavespreadsrestValuesstraight into the payload. Add anonChangereset here so switching auth types doesn’t persist stale config.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx` around lines 820 - 834, The Authentication Select in mcp_server_edit.tsx leaves auth-specific fields in form state when auth_type changes, and handleSave will still include them via restValues. Update the auth_type Select to clear dependent fields like authorization_url, token_url, registration_url, and credentials on change so switching between auth types does not persist stale config. Use the existing form field names in mcp_server_edit and keep the reset logic localized to the auth_type control.ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx (1)
911-932: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winClear auth-specific fields when
auth_typechanges. The backend stores the top-level OAuth/token-exchange fields exactly as submitted, andupdate_mcp_serveronly clearscredentialsin a narrow case. Switching between OAuth2 and OAuth Token Exchange can leave staleauthorization_url/token_url/registration_urlortoken_exchange_endpoint/audience/subject_token_typevalues on the row.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx` around lines 911 - 932, Clear auth-specific form values whenever auth_type changes in create_mcp_server so stale OAuth/OBO fields do not persist. Update the auth selection handling around the Authentication Form.Item/Select to reset or conditionally clear unrelated fields when switching between oauth2, oauth2_token_exchange, token, basic, api_key, bearer_token, none, and aws_sigv4, and make sure the submit/update path in update_mcp_server only sends the fields relevant to the current auth_type.
🧹 Nitpick comments (1)
ui/litellm-dashboard/src/components/mcp_tools/TokenExchangeFormFields.tsx (1)
9-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the shared field-label helper
FieldLabelandfieldClassNameare duplicated inui/litellm-dashboard/src/components/mcp_tools/TokenExchangeFormFields.tsxandOAuthFormFields.tsx; move them into a shared helper/module to keep the two form variants in sync.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/litellm-dashboard/src/components/mcp_tools/TokenExchangeFormFields.tsx` around lines 9 - 18, The shared field-label helper is duplicated between TokenExchangeFormFields and OAuthFormFields, so move fieldClassName and FieldLabel into a common reusable module and import them from both form components. Update the TokenExchangeFormFields and OAuthFormFields implementations to use the shared helper by its existing symbol names so the styling and tooltip label behavior stay in sync without duplicated definitions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx`:
- Around line 380-385: The block comment above the auth gate overstates its
scope by claiming it applies to both authorization_code and token_exchange, but
this gate is only reachable through isAuthorizationCode via
authorizationCodeNeedsAuth and authorizationCodeTokenRejected. Update the
comment in mcp_tools.tsx to describe only the authorization_code flow and the
401/credential-missing cases, keeping it consistent with the existing
token_exchange note near the earlier gate logic.
---
Outside diff comments:
In `@ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx`:
- Around line 911-932: Clear auth-specific form values whenever auth_type
changes in create_mcp_server so stale OAuth/OBO fields do not persist. Update
the auth selection handling around the Authentication Form.Item/Select to reset
or conditionally clear unrelated fields when switching between oauth2,
oauth2_token_exchange, token, basic, api_key, bearer_token, none, and aws_sigv4,
and make sure the submit/update path in update_mcp_server only sends the fields
relevant to the current auth_type.
In `@ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx`:
- Around line 820-834: The Authentication Select in mcp_server_edit.tsx leaves
auth-specific fields in form state when auth_type changes, and handleSave will
still include them via restValues. Update the auth_type Select to clear
dependent fields like authorization_url, token_url, registration_url, and
credentials on change so switching between auth types does not persist stale
config. Use the existing form field names in mcp_server_edit and keep the reset
logic localized to the auth_type control.
---
Nitpick comments:
In `@ui/litellm-dashboard/src/components/mcp_tools/TokenExchangeFormFields.tsx`:
- Around line 9-18: The shared field-label helper is duplicated between
TokenExchangeFormFields and OAuthFormFields, so move fieldClassName and
FieldLabel into a common reusable module and import them from both form
components. Update the TokenExchangeFormFields and OAuthFormFields
implementations to use the shared helper by its existing symbol names so the
styling and tooltip label behavior stay in sync without duplicated definitions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 9e7b3bdd-ab60-4e53-b078-aec92ecab828
📒 Files selected for processing (22)
litellm-proxy-extras/litellm_proxy_extras/migrations/20260630120000_add_token_exchange_to_mcp_servers/migration.sqllitellm-proxy-extras/litellm_proxy_extras/schema.prismalitellm/models/mcp_server.pylitellm/proxy/_experimental/mcp_server/mcp_server_manager.pylitellm/proxy/_types.pylitellm/proxy/management_endpoints/mcp_management_endpoints.pylitellm/proxy/schema.prismaschema.prismatests/mcp_tests/test_mcp_server.pytests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.pytests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.pytests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.pytests/test_litellm/proxy/_experimental/mcp_server/test_mcp_sigv4_auth.pytests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.pyui/litellm-dashboard/src/components/mcp_tools/TokenExchangeFormFields.tsxui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsxui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsxui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsxui/litellm-dashboard/src/components/mcp_tools/mcp_servers.tsxui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsxui/litellm-dashboard/src/components/mcp_tools/types.test.tsxui/litellm-dashboard/src/components/mcp_tools/types.tsx
|
(again, @tin-berri , I'm just testing coderabbit. What it or bugbot mentions <- don't consider them blocking) |
…ashboard OAuth 2.0 Token Exchange (RFC 8693, a.k.a. OBO) for MCP servers could previously only be configured through config.yaml; the create/update REST API and the dashboard had no way to express it. This wires token_exchange_endpoint, audience, and subject_token_type end to end. These three are persisted as dedicated columns on LiteLLM_MCPServerTable, mirroring how token_url and oauth2_flow are stored, so the edit form prefills them and they are unaffected by the credentials-blob clearing on auth_type change. build_mcp_server_from_table reads the columns first and falls back to the credentials blob so servers persisted before the columns existed still load. client_id and client_secret continue to ride the existing encrypted credentials path. On the dashboard, "OAuth Token Exchange (OBO)" is a distinct auth-type option with its own field section. The McpOAuthMode classifier gains a token_exchange arm keyed off auth_type; the previous catch-all oauth2 mode was renamed from "obo" to "authorization_code" so the two on-behalf-of mechanisms are no longer conflated. The token-exchange IdP endpoint and audience are scrubbed from non-admin and virtual-key responses, matching how token_url is treated.
|
@greptileai rereview |
|
bugbot run |
…trict budget) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…itten without credentials The migrate-on-write in the credentials merge lifts blob values into null columns, which is correct for legacy rows but could repopulate a column an admin had cleared in an earlier no-credentials update (that path never touched the blob, so the stale copy survived to be lifted later). An explicit token-exchange column write (set or clear) now migrates the row even when the update carries no credentials: untouched null columns are lifted, every blob copy is stripped, and unrelated blob keys stay as-is. A cleared column can then never be resurrected, because no write path leaves a blob copy behind. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Medium: migrate-on-write repopulates cleared columns — confirmed and fixed in 35a6ddc. The gap was the no-credentials clear: an update that explicitly nulled a column but didn't carry One bounded caveat: rows whose column was cleared before this code ships (blob still dirty from the old behavior) can see the lift once more on their next update; any explicit set/clear of the field now fully purges the row. That residual is not distinguishable from a genuine legacy never-set row without a marker column, which didn't seem worth it. Also in this push: eda61c6 fixes the I001 strict-budget failure (import order in |
|
@greptileai rereview |
…ntial keys The three token-exchange keys on MCPCredentials are the pre-column REST shape (the only REST shape from 2026-05 until this PR). Document on both the blob type and the request models that the dedicated columns are authoritative and that writes lift blob values into them and strip the stored copy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@greptileai rereview |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 2baa43b. Configure here.
|
@greptileai wheres the review, can you just reply the review here |
mateo-berri
left a comment
There was a problem hiding this comment.
the only open item is a minor inconsistency in the non-admin response scrubbing where subject_token_type is left exposed while the other two token-exchange fields are cleared
Is this a legit concern?
Also, can you please attach e2e proof that before=fail/after=works?
…anitizers The other two token-exchange fields were cleared while subject_token_type was left visible. It is a public RFC 8693 URN with no disclosure value, but the sanitizers' rule is that these views receive no token-exchange config at all — cleared for uniformity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
subject_token_type left exposed in the non-admin scrub — fixed in f3ee8ac for uniformity. For the record, the field itself has no disclosure value (it is a URN from RFC 8693's fixed public vocabulary — no hostnames, identifiers, or secrets, unlike the endpoint/audience), but the sanitizers' rule is that non-admin and virtual-key views receive no token-exchange config at all, so it is now cleared alongside the other two. Both sanitizer tests assert it. |
|
@greptileai rereview |
1 similar comment
|
@greptileai rereview |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit f3ee8ac. Configure here.
mateo-berri
left a comment
There was a problem hiding this comment.
The edit form guards TokenExchangeFormFields with !isStdioTransport, but the create form has no such guard. Selecting stdio transport and oauth2_token_exchange auth type in the create wizard will render the TE fields even though stdio servers don't use OAuth — client_id/client_secret would then be required and the form cannot be submitted without them.
Is this a valid concern?
Relevant issues
Stacked on #31983, the tip of the token_exchange (OBO) backend chain (#31526 -> #31622 -> #31762 -> #31983). Base is that branch so this diff is just the UI and REST-config work; it retargets to
litellm_internal_stagingas the backend stack merges. This is the frontend/API half that gives users parity with that backend work: the backend can resolve and run RFC 8693 token exchange, and this lets an admin actually configure such a server through the API and the dashboard instead of only config.yamlLinear ticket
N/A
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewDelays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Reproduction on a live proxy backed by Postgres (master key
sk-1234)The read-back shows the three fields populated and
subject_token_typedefaulting tourn:ietf:params:oauth:token-type:access_token. Inspecting the row directly confirms the three fields are dedicated columns whileclient_id/client_secretare encrypted in the credentials blob (neither appears in plaintext)Dashboard before/after, rendered from the real create and edit form components on the merge-base commit (before) versus this branch (after)
Create form auth-type dropdown. Before, the Authentication options stop at OAuth then AWS SigV4, so there is no way to pick token exchange
After, "OAuth Token Exchange (OBO)" is a selectable option
Selecting it reveals the token-exchange field section: Token Exchange Endpoint, Client ID, Client Secret, Audience, Subject Token Type, and Scopes
Edit form for an existing
oauth2_token_exchangeserver. Before, the auth type falls back to the raw "oauth2_token_exchange" string and the form jumps straight to Variables, so none of the token-exchange config is visible or editableAfter, the auth type resolves to "OAuth Token Exchange (OBO)" and the saved Token Exchange Endpoint, Audience, and Subject Token Type prefill while Client Secret shows "leave blank to keep existing"
Type
🆕 New Feature
Changes
OAuth 2.0 Token Exchange (RFC 8693, on-behalf-of) for MCP servers could previously only be configured through config.yaml; the create/update REST API and the dashboard had no way to express it even though the backend chain this stacks on can resolve and run the exchange. This wires
token_exchange_endpoint,audience, andsubject_token_typeend to end so anauth_type=oauth2_token_exchangeserver can be created and edited through the API and the UIThese three are stored as dedicated columns on
LiteLLM_MCPServerTable, mirroring howtoken_urlandoauth2_floware stored, rather than in the encrypted credentials blob. That keeps them returned to the dashboard so the edit form prefills them, and keeps them independent of the credentials-blob clearing that happens whenauth_typechanges.build_mcp_server_from_tablereads the columns first and falls back to the credentials blob so a server persisted before the columns existed still loads with its token-exchange config intact.client_idandclient_secretcontinue to ride the existing encrypted credentials path. The v2 resolver's_token_exchange_specalready reads these three off the runtimeMCPServer, so the exchange arm consumes this config unchangedBecause the base chain adds RFC 9728 to RFC 8414 endpoint discovery (#31762), two things line up with it here. The DB-build discovery gate now reads the
token_exchange_endpointcolumn first before deciding whether to discover, so a server whose endpoint is configured through the UI keeps the "explicit config wins, skip discovery" contract instead of being ignored because the value lives in a column rather than the blob. And the Token Exchange Endpoint field in the form is optional, since an admin can leave it blank and let discovery find it from the upstream's protected-resource metadataOn the dashboard, "OAuth Token Exchange (OBO)" is a distinct auth-type dropdown option with its own field section in both the create and edit forms. The
McpOAuthModeclassifier gains atoken_exchangearm keyed offauth_type; the previous catch-all oauth2 mode was renamed fromobotoauthorization_codeso the two on-behalf-of mechanisms (RFC 8693 token exchange versus the stored-per-user authorization_code token) are no longer conflated under one confusing label. The token-exchange IdP endpoint and audience are scrubbed from non-admin and virtual-key responses, matching howtoken_urlis already treatedTests cover the column read path and the credentials-blob fallback, the
subject_token_typedefault, the table round-trip, the REST create and partial-update write paths reaching the prepared column data, the non-admin and virtual-key sanitizers dropping the new fields, thegetMcpOAuthModetoken_exchange classification, and the create form routing the token-exchange payload to the backendCursor Bugbot flagged that switching an existing server's auth_type left the previous flow's endpoint config on the row: an oauth2 server switched to oauth2_token_exchange kept its stale token_url, which the OBO resolver reads as the configured exchange endpoint (token_exchange_endpoint or token_url), so the stale value both suppressed the endpoint discovery described above and pointed the exchange grant at the previous flow's token endpoint. update_mcp_server now extends its existing stale-credentials rule to the flow-scoped columns (authorization_url, token_url, registration_url, oauth2_flow, token_exchange_endpoint, audience, subject_token_type): when auth_type changes, each one is cleared unless the same request explicitly provides it, so a deliberate override in the switch request still wins. The edit form sends explicit nulls for the previous flow's fields on a switch, since antd preserves unmounted field values and would otherwise re-send the stale token_url as an explicit override. Updates that keep the auth_type never touch these columns, so legacy OBO rows that use token_url as their exchange endpoint keep working
Storage contract: token-exchange settings (blob → columns)
token_exchange_endpoint,audience, andsubject_token_typelive in dedicated columns, which are authoritative. The same keys insidecredentialsare the legacy pre-column REST shape (the only REST shape from May 2026 until this PR): they are still accepted, but every write lifts them into the columns and strips them from the stored blob — an explicit top-level value (including an explicit null) always wins. Thecolumn or blobread fallback therefore only ever serves untouched pre-column rows; the first write that touches auth_type, credentials, or a token-exchange field migrates the row permanently. Columns hold settings; the blob holds actual credentials (client_id/client_secret, encrypted at rest).Field semantics, since two similarly named settings are easy to conflate:
subject_token_typeis the RFC 8693 parameter declaring what kind of token the gateway presents as thesubject_tokenbeing exchanged — defaulturn:ietf:params:oauth:token-type:access_token, set to e.g.urn:ietf:params:oauth:token-type:jwtfor IdPs that validate the subject as a signed JWT. It is unrelated totoken_endpoint_auth_method(client_secret_basic/client_secret_post), which controls how the gateway authenticates itself to the token endpoint. The field renders only for theoauth2_token_exchangeauth type (pinned by the stacked test PR #32385), and theentra_oboprofile (#32144) ignores it entirely, since Entra's OBO is the RFC 7523jwt-bearergrant.Note
Medium Risk
Touches MCP OAuth persistence, auth-type switching, and encrypted credential merge paths; mistakes could mis-route token exchange or leak IdP endpoints to restricted callers, though scrubbing and extensive tests mitigate this.
Overview
Adds OAuth Token Exchange (OBO / RFC 8693) as a first-class MCP auth type through the REST API and dashboard, not only
config.yaml.token_exchange_endpoint,audience, andsubject_token_typeare stored in newLiteLLM_MCPServerTablecolumns (with a Prisma migration); reads prefer columns and fall back to legacy keys insidecredentials. Writes lift those keys into columns and strip them from the encrypted blob so a cleared column cannot be resurrected from stale JSON.update_mcp_serverclears flow-scoped OAuth fields whenauth_typechanges (unless explicitly set in the same request) and migrates legacy blob token-exchange settings on create/update. Non-admin / virtual-key responses hide the new IdP-oriented fields liketoken_url.The UI adds OAuth Token Exchange (OBO) with
TokenExchangeFormFields, andgetMcpOAuthModegainstoken_exchangewhile renaming the interactive OAuth2 mode fromobotoauthorization_code.DEFAULT_SUBJECT_TOKEN_TYPEis centralized inlitellm.types.mcpfor runtime and egress types.Reviewed by Cursor Bugbot for commit 2baa43b. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Bug Fixes
Link to Devin session: https://app.devin.ai/sessions/f03da2725ec94d28b3facf766871b102