litellm/litellm/types
Ishaan Jaff 1bb713bc7b
feat(mcp): BYOK MCP servers with OAuth 2.1 PKCE authorization flow (#22850)
* feat(mcp): BYOK (Bring Your Own Key) for OpenAPI MCP servers with OAuth 2.1 flow

Adds per-user credential storage for BYOK MCP servers so external clients
can authenticate via standard OAuth 2.1 PKCE without needing a full identity
provider.

Backend:
- New DB table LiteLLM_MCPUserCredentials (user_id, server_id, credential_b64)
- is_byok, byok_description, byok_api_key_help_url fields on MCPServerTable
- OAuth 2.1 authorization server endpoints (/.well-known/oauth-authorization-server,
  /.well-known/oauth-protected-resource, /v1/mcp/oauth/authorize, /v1/mcp/oauth/token)
- 401 challenge with WWW-Authenticate header when BYOK server has no credential
- CRUD endpoints: POST/DELETE /v1/mcp/server/{id}/user-credential
- has_user_credential annotated on GET /v1/mcp/server response

UI:
- ByokCredentialModal: 2-step Connect flow (access description + API key entry)
- BYOK toggle + description fields on admin MCP server create form
- Connect/Connected state in MCP server table
- BYOK Demo page (/tools/byok-demo) showing full OAuth 2.1 PKCE flow

* feat(mcp/byok): redesign OAuth authorize page to match 2-step Connect mockup

- Step 1: L→S logos, requested access checklist, How it works box, Continue button
- Step 2: API key input, Save toggle, Duration pills (1h/24h/7d/30d/until_revoked), security note
- Matches screenshots: white modal on dark bg, progress dots, dark CTA buttons
- Authorize handler now fetches byok_description and byok_api_key_help_url from server registry
- CLAUDE.md: replace SQL snippet with proper DB migration troubleshooting guidance

* fix: address greptile review feedback (greploop iteration 1)

- XSS: escape all user-supplied values in _build_authorize_html() with html.escape()
- Open redirect: validate redirect_uri scheme and URL-encode code/state in redirect
- N+1 query: batch BYOK credential lookup into single find_many() call
- Critical path DB: add 60s TTL in-memory cache to _check_byok_credential()
- Encrypt BYOK credentials at rest using encrypt_value_helper/decrypt_value_helper

* fix(byok): update OAuth popup with LiteLLM logo, MCP title suffix, remove emojis

* fix(byok-demo): fix token endpoint URL (/v1/mcp/oauth/token not /v1/mcp/token)

* feat(byok): inject stored BYOK credential as mcp_auth_header on tool execution

* feat(byok): use contextvars to inject per-user credential into OpenAPI tool closures; remove byok-demo from LiteLLM UI

OpenAPI tools have auth headers baked into their closures at registration time. BYOK servers have
no static auth token, so per-user credentials were never reaching the HTTP calls.

Fix: add _request_auth_header ContextVar in openapi_to_mcp_generator.py. create_tool_function now
reads this var at call time and overrides the Authorization header if set. execute_mcp_tool resolves
the MCP server and performs BYOK checks before the local-tool dispatch branch, then sets the
ContextVar around _handle_local_mcp_tool so the credential flows into the HTTP request.

Also remove the /tools/byok-demo page from the LiteLLM UI dashboard — the demo lives at
~/Downloads/litellm-byok-demo/index.html (served separately on port 8080).

* fix: address greptile review feedback (greploop iteration 2)

- Cache invalidation: add _invalidate_byok_cred_cache() and call it after
  store_user_credential() in both token endpoint and management endpoint
- Unbounded cache: add _BYOK_CRED_CACHE_MAX_SIZE=4096 with clear-on-overflow
- Unbounded auth codes: add _AUTH_CODES_MAX_SIZE=1000 with 503 on overflow
- Double DB query: merge _check_byok_credential + _get_byok_credential into
  single _get_byok_credential call; raise 401 inline if None returned
- Sidebar: remove byok-demo entry (page was deleted in prior commit)
- JWT comment: document why byok_session HS256 token can't be used as proxy auth

* fix: address greptile review feedback (greploop iteration 3)

- auth_type: pre-format Authorization header (Bearer/ApiKey/Basic) in server.py
  before setting ContextVar so openapi_to_mcp_generator respects server auth_type
- cache invalidation on delete: call _invalidate_byok_cred_cache after
  delete_user_credential so stale True entries don't persist for 60s
- ContextVar guard: only set _request_auth_header when mcp_auth_header is set,
  avoiding unnecessary ContextVar overhead on non-BYOK tool calls

* fix: address greptile review feedback (greploop iteration 4)

- Unified credential cache: store actual credential value (Optional[str])
  instead of just bool so _get_byok_credential also benefits from caching —
  eliminates the DB hit on every BYOK tool call within the 60s TTL window
- Extracted _write_byok_cred_cache() helper for consistent cache writes
- Replaced has_user_credential with get_user_credential in _check_byok_credential
  so one DB call satisfies both existence check and value retrieval
- Remove false 'encrypted at rest' claim from OAuth HTML and ByokCredentialModal

* Update tests/test_litellm/proxy/_experimental/mcp_server/test_byok_oauth_endpoints.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update tests/test_litellm/proxy/_experimental/mcp_server/test_byok_oauth_endpoints.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-04 21:19:25 -08:00
..
containers
google_genai
images
integrations Merge pull request #22103 from Harshit28j/litellm_feat_datadog_metrics 2026-02-28 17:25:23 +05:30
interactions fix: add INCOMPLETE status to Interactions API enum and test 2026-02-23 15:07:41 -03:00
litellm_core_utils
llms feat(batches): support output_expires_after passthrough 2026-03-03 10:46:45 -08:00
management_endpoints Add fallback endpoints support 2026-01-16 10:51:33 +05:30
mcp_server feat(mcp): BYOK MCP servers with OAuth 2.1 PKCE authorization flow (#22850) 2026-03-04 21:19:25 -08:00
passthrough_endpoints
prompts
proxy Add CrowdStrike AIDR guardrail hook (#17876) 2026-03-02 17:26:54 -08:00
responses remove comment 2026-02-25 17:40:46 +05:30
secret_managers
videos Fix: Passing of image and parameters in videos api 2026-02-26 14:49:46 +05:30
access_group.py allow editing of access group names 2026-02-13 17:03:41 -08:00
adapter.py
agents.py Agents - assign tools (#22064) 2026-02-25 11:44:30 -08:00
caching.py perf(proxy): pipeline Redis RPUSH/LPOP in spend update cycle 2026-02-24 12:46:52 -08:00
completion.py
embedding.py
files.py [Feat] Manus FILES API - Add File upload, get, delete, list (#18904) 2026-01-10 13:27:54 -08:00
fine_tuning.py
guardrails.py feat: guardrail-mode-default-list 2026-03-03 22:45:32 +05:30
mcp.py
policy_engine.py [Feat] New LiteLLM Policy engine - create policies to manage guardrails, conditions - permissions per Key, Team (#19612) 2026-01-22 19:49:53 -08:00
rag.py [Feat] RAG API - Add support for using s3 Vectors as Vector Store Provider for /rag/ingest (#19888) 2026-01-27 14:45:26 -08:00
realtime.py
rerank.py
router.py fix: complexity_router crashes on list-format message content (OpenAI multi-part messages) (#22761) 2026-03-04 16:18:49 -08:00
scheduler.py
search.py search tools list includes config defined search tools 2026-02-03 15:57:13 -08:00
services.py
tag_management.py
tool_management.py [Feat] Add Tool Policies for AI Gateway (#22732) 2026-03-03 20:22:20 -08:00
utils.py Merge pull request #22752 from BerriAI/litellm_search_api_add 2026-03-04 18:29:10 +05:30
vector_store_files.py
vector_stores.py [Feat] LiteLLM Vector Stores - Add permission management for users, teams (#19972) 2026-01-28 18:55:40 -08:00