Skip to content

fix: resolve MCP server creation failures (schema drift, oauth2_flow mapping, session permissions) - #24941

Closed
RoyVivat wants to merge 3 commits into
BerriAI:mainfrom
RoyVivat:fix/mcp_creation
Closed

fix: resolve MCP server creation failures (schema drift, oauth2_flow mapping, session permissions)#24941
RoyVivat wants to merge 3 commits into
BerriAI:mainfrom
RoyVivat:fix/mcp_creation

Conversation

@RoyVivat

@RoyVivat RoyVivat commented Apr 1, 2026

Copy link
Copy Markdown
  • Restore approval_status and submission fields to root schema.prisma and
    litellm-proxy-extras schema (previously dropped by auto-generated sync migration)
  • Add migration to re-add the dropped columns with correct nullability and defaults
  • Map UI oauth_flow_type (m2m/interactive) → oauth2_flow (client_credentials/authorization_code)
    in create and edit MCP server forms before sending to backend
  • Allow non-admin users to create ephemeral session MCP servers (no DB write, no security risk)
  • Fix stale test expecting session endpoint to reject non-admins; add comprehensive new tests

Relevant issues

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test

Changes

…mapping, session permissions)

- Restore approval_status and submission fields to root schema.prisma and
  litellm-proxy-extras schema (previously dropped by auto-generated sync migration)
- Add migration to re-add the dropped columns with correct nullability and defaults
- Map UI oauth_flow_type (m2m/interactive) → oauth2_flow (client_credentials/authorization_code)
  in create and edit MCP server forms before sending to backend
- Allow non-admin users to create ephemeral session MCP servers (no DB write, no security risk)
- Fix stale test expecting session endpoint to reject non-admins; add comprehensive new tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Apr 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 1, 2026 8:22pm

Request Review

@RoyVivat

RoyVivat commented Apr 1, 2026

Copy link
Copy Markdown
Author

@greptileai

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes three distinct MCP server creation failures: (1) restores approval_status, source_url, and submission lifecycle columns that were accidentally dropped by an auto-generated schema sync migration, (2) maps the UI-only oauth_flow_type field (m2m/interactive) to the backend field oauth2_flow (client_credentials/authorization_code) in both the create and edit forms before sending to the backend, and (3) allows non-admin users to create ephemeral session MCP servers so they can run the OAuth auth-test before submitting a server for review.

Key changes:

  • Migration (20260401000000_restore_mcp_approval_columns): Uses ADD COLUMN IF NOT EXISTS guards and an explicit UPDATE backfill to normalise existing NULL and 'approved' values to 'active'; well-documented with the reason for the restoration.
  • Schema drift fix: Both schema.prisma (root) and litellm-proxy-extras/schema.prisma are brought back in sync with the restored columns and a new approval_status index.
  • Security guard preserved: While the admin-only gate on session server creation is removed (safe — no DB write, ephemeral TTL), _inherit_credentials_from_existing_server is now explicitly skipped for non-admin callers, preventing any key holder from silently acquiring stored OAuth/AWS secrets from a permanent server by supplying its server_id.
  • Test hygiene: The stale test_add_session_mcp_server_rejects_non_admins test is replaced with test_add_session_mcp_server_allows_non_admins, and a new comprehensive test file covers approval_status propagation, oauth2_flow field acceptance, admin status override, non-admin session creation, and credential-inheritance prevention. UI tests for OAuth flow mapping are added to both create and edit test suites.

Confidence Score: 5/5

  • Safe to merge — all three bug fixes are well-scoped, the credential-inheritance security concern from the prior review is correctly addressed, and the changes are backed by comprehensive mock-based tests.
  • No P0 or P1 issues found. The migration is idempotent (IF NOT EXISTS + conditional UPDATE), the schema changes match the SQL, the credential-inheritance guard is explicit and tested, and the UI mapping logic matches the backend field names. All new tests are mock-based with no real network calls, satisfying the CI rules. The existing test that was rewritten reflects a deliberate, documented behavior change rather than masking a regression.
  • No files require special attention.

Important Files Changed

Filename Overview
litellm-proxy-extras/litellm_proxy_extras/migrations/20260401000000_restore_mcp_approval_columns/migration.sql New migration that restores accidentally dropped columns using IF NOT EXISTS guards and a targeted backfill; well-documented with clear rationale.
litellm-proxy-extras/litellm_proxy_extras/schema.prisma Restores source_url, approval_status (now nullable String? @default("active")), submission lifecycle fields, and adds an index; matches migration SQL.
schema.prisma Root schema brought into sync with the proxy-extras schema by adding the same approval/submission lifecycle fields and index to LiteLLM_MCPServerTable.
litellm/proxy/management_endpoints/mcp_management_endpoints.py Removes admin-only gate on session server creation (ephemeral, no DB write) and guards credential inheritance behind an admin-role check, directly addressing the security concern from prior review.
tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_creation_fixes.py New comprehensive mock-based test file covering approval_status inclusion, oauth2_flow field acceptance, admin override, non-admin session creation, and non-admin credential-inheritance prevention.
tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py Existing test renamed from rejects_non_admins → allows_non_admins to match the intentional behavior change; updated to verify the session server is created successfully rather than raising HTTPException.
ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx Adds oauth_flow_type → oauth2_flow mapping block before payload construction; removes the UI-only field from the submitted payload.
ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx Identical oauth_flow_type → oauth2_flow mapping block added to the edit form's submit handler; initializes oauth_flow_type from token_url presence (line 191) enabling correct round-trip mapping.
ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx New test suite covers both M2M and interactive OAuth flow mapping for admin create and non-admin register paths; all mock-based, no real network calls.
ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx Adds tests for oauth_flow_type mapping on the edit path, verifying client_credentials (token_url present) and authorization_code (no token_url) cases.

Sequence Diagram

sequenceDiagram
    participant UI as UI Form
    participant BE as add_session_mcp_server
    participant IC as _inherit_credentials
    participant Cache as _temporary_mcp_servers

    Note over UI: oauth_flow_type = "m2m" | "interactive"
    UI->>UI: Map oauth_flow_type → oauth2_flow<br/>("client_credentials" | "authorization_code")<br/>delete oauth_flow_type
    UI->>BE: POST /mcp/session {oauth2_flow, ...}

    alt PROXY_ADMIN caller
        BE->>IC: _inherit_credentials_from_existing_server(payload)
        IC-->>BE: payload + stored secrets
    else Non-admin caller
        Note over BE: Skip credential inheritance
        BE-->>BE: payload_with_credentials = payload (no secrets)
    end

    BE->>Cache: _cache_temporary_mcp_server(server, ttl=300s)
    BE-->>UI: MCPServer (ephemeral, no DB write)
Loading

Reviews (3): Last reviewed commit: "fix: guard credential inheritance from n..." | Re-trigger Greptile

Comment on lines +527 to +533
if (restValues.oauth_flow_type) {
restValues.oauth2_flow =
restValues.oauth_flow_type === OAUTH_FLOW.M2M
? "client_credentials"
: "authorization_code";
delete restValues.oauth_flow_type;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing test coverage for edit form oauth mapping

The identical oauth_flow_type → oauth2_flow mapping block added to create_mcp_server.tsx received comprehensive tests in create_mcp_server.test.tsx, but mcp_server_edit.tsx has the same logic with no corresponding tests added to mcp_server_edit.test.tsx. If the mapping silently breaks during a refactor, there's nothing to catch it on the edit path.

Consider adding at least one test to mcp_server_edit.test.tsx verifying that when the edit form is submitted with oauth_flow_type: "m2m" the payload contains oauth2_flow: "client_credentials" (and vice-versa for "interactive").

Comment on lines 1291 to 1295
# Session servers are ephemeral (in-memory, ~5 min TTL, no DB write) so
# any authenticated user may create one. This lets non-admin users run
# the OAuth auth-test before submitting a server for review.
created_by = user_api_key_dict.user_id or LITELLM_PROXY_ADMIN_NAME
payload_with_credentials = _inherit_credentials_from_existing_server(payload)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No resource guard after removing admin-only restriction

Removing the PROXY_ADMIN check means any authenticated user can now call this endpoint repeatedly. Because the session cache likely has no per-user quota, a malicious (but valid) key holder could:

  1. Resource exhaustion — flood the in-memory session cache with thousands of short-lived entries until the proxy OOMs.
  2. SSRF probe — point the session server URL at an internal host (e.g. http://169.254.169.254/) and inspect error messages that leak reachability, since session servers attempt to connect to the given URL when the OAuth flow is exercised.

Neither risk existed before, because only admins (who already have full access) could reach this path. Consider adding a lightweight per-user rate limit or at least an explicit URL allow-list / block-list before broadening this to all authenticated callers.

Rule Used: What: Fail any PR which may contains a security in... (source)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pre-existing concern at the API level, not specific to session servers. Any authenticated key can already flood numerous other endpoints. Neither issue is introduced by this diff in isolation, and reverting the restriction breaks the intended OAuth-test-before-submit workflow. The SSRF concern is real but belongs in validate_and_normalize_mcp_server_payload as a URL allowlist/blocklist, not as a role gate.

Addresses reviewer feedback: the create form tests covered the mapping
but the edit form had no equivalent.  Two tests added to
mcp_server_edit.test.tsx verify that saving an OAuth2 server with
token_url set produces oauth2_flow: "client_credentials", and saving
one without token_url produces oauth2_flow: "authorization_code", in
both cases without oauth_flow_type in the payload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
# any authenticated user may create one. This lets non-admin users run
# the OAuth auth-test before submitting a server for review.
created_by = user_api_key_dict.user_id or LITELLM_PROXY_ADMIN_NAME
payload_with_credentials = _inherit_credentials_from_existing_server(payload)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Credential inheritance by non-admin callers

_inherit_credentials_from_existing_server runs unconditionally here and will copy sensitive credentials (OAuth client_secret, auth_value, AWS access/secret key, etc.) from any permanently-registered server whose server_id matches the caller-supplied value.

Before this PR only PROXY_ADMIN could reach this code path, so credential access was already gated. Now any authenticated user can pass an arbitrary server_id they know (or guess) and cause the endpoint to pick up that server's stored secrets. The credentials are redacted from the response (_redact_mcp_credentials(temp_record)) but the session server is cached in _temporary_mcp_servers with the full credentials intact, and subsequent tool-call / OAuth-flow requests that use the temp server_id will execute with those inherited secrets.

A minimal fix would be to skip inheritance if the caller is not an admin:

if LitellmUserRoles.PROXY_ADMIN == user_api_key_dict.user_role:
    payload = _inherit_credentials_from_existing_server(payload)
# else: non-admin must supply explicit credentials; no inheritance

Alternatively, restrict _inherit_credentials_from_existing_server to only run when the caller supplies no server_id (forcing non-admins to always specify a fresh ID), which prevents piggybacking on an existing server's stored secrets.

Rule Used: What: Fail any PR which may contains a security in... (source)

…rs; fix migration backfill

Security: _inherit_credentials_from_existing_server now only runs for
PROXY_ADMIN callers.  Non-admins opening a session server must supply
their own credentials; they cannot inherit OAuth/AWS secrets from a
permanently-registered server by supplying its server_id.

Migration: backfill UPDATE also normalises rows with approval_status =
'approved' (written by the prior schema version that used
@default("approved")) to 'active', so they are no longer excluded by
the server manager's approval_status = 'active' filter.

Tests: add test_add_session_mcp_server_non_admin_does_not_inherit_credentials
to verify the credential isolation invariant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@github-actions github-actions Bot added the stale label Jul 1, 2026
@github-actions github-actions Bot closed this Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants