litellm - 💡(How to fix) Fix [Bug]: Virtual key MCP access ignores team access groups (discovery AND enforcement)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

=> {"error": "access_denied", "message": "The key is not allowed to access server ..."}

Root Cause

Root cause (confirmed in source)

Fix Action

Fix / Workaround

Status: Open — not fixed in latest stable (v1.83.14-stable.patch.3, 2026-05-07) Severity: High — blocks all virtual-key tool calls, not just UI display Affected endpoints: GET /v1/mcp/server, GET /v1/mcp/toolset, POST /mcp-rest/tools/call Confirmed versions: 1.83.14 (running locally), v1.83.14-stable.patch.3 (latest stable as of 2026-05-07)

Workaround

v1.83.14-stable.patch.3

RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

Status: Open — not fixed in latest stable (v1.83.14-stable.patch.3, 2026-05-07) Severity: High — blocks all virtual-key tool calls, not just UI display Affected endpoints: GET /v1/mcp/server, GET /v1/mcp/toolset, POST /mcp-rest/tools/call Confirmed versions: 1.83.14 (running locally), v1.83.14-stable.patch.3 (latest stable as of 2026-05-07)

Description

LiteLLM's MCP access group enforcement checks the requesting key's own access_group_ids field. Virtual keys always have access_group_ids: [] even when their team has access groups correctly assigned. LiteLLM does not traverse the key → team → access_group_ids relationship, so:

Discovery (GET /v1/mcp/server, /v1/mcp/toolset) returns an empty list for all virtual keys.

Enforcement (POST /mcp-rest/tools/call) returns 403 access_denied for all virtual keys, even when their team's access group grants the server.

The master key bypasses this filter entirely and sees/accesses all servers correctly.

Expected behavior

All MCP access checks (/v1/mcp/server, /v1/mcp/toolset, /mcp-rest/tools/call) should resolve the key's team and join through team.access_group_ids to determine allowed servers:

key.team_id → team.access_group_ids → access_group.access_mcp_server_ids

Actual behavior

Only key.access_group_ids is checked. This field is never populated for virtual keys — only the master key bypasses the check.

Impact

LiteLLM UI Playground: "Select MCP server" dropdown shows "No Data" for all virtual keys.

All virtual key tool calls via LiteLLM proxy: blanket 403 access_denied regardless of team membership.

Master key unaffected: full access, no filtering.

Direct gateway calls (bypassing LiteLLM proxy, hitting mcp-jedai-gateway:8000 directly): work correctly — gateway's own auth is not affected.

Workaround

Use the master key (PROXY_MASTER_KEY) for all UAT tool calls and UI testing. The master key bypasses access group filtering and sees/accesses all servers.

Root cause (confirmed in source)

The MCP runtime resolves allowed servers via MCPRequestHandler._get_team_object_permission(), which loads the team's object_permission row from LiteLLM_ObjectPermissionTable. The bug is that this table row is never created or updated when an access group is assigned to a team.

When POST /v1/access_group assigns a team (assigned_team_ids), LiteLLM writes access_group_mcp_server_ids to the team table row — but never creates the corresponding LiteLLM_ObjectPermissionTable record. The MCP runtime then finds team.object_permission_id = None, returns None, and the access check falls through to deny.

What exists after make access-groups:

team.access_group_ids = ["37eaf962-..."] ✅ written team.access_group_mcp_server_ids = ["b9a670c0-..."] ✅ written team.object_permission_id = None ❌ never created

What the MCP runtime actually reads:

team.object_permission = None (object_permission_id is null) → _get_team_object_permission() returns None → allowed_mcp_servers = [] → 403 access_denied

This PR looks like it was working on the fix but was closed. PR #25378 (fix(access_group): propagate MCP/agents and preserve direct models on access group sync) targeted exactly this — adding _upsert_mcp_agents_in_object_permission to write the LiteLLM_ObjectPermissionTable row when access groups are synced. However, this PR was closed without merging and is not in any released version.

Fix (upstream)

When POST /v1/access_group assigns assigned_team_ids, LiteLLM must upsert a LiteLLM_ObjectPermissionTable row for each team, writing the access group's access_mcp_server_ids into object_permission.mcp_servers. The same must happen on access group update/delete to keep the table in sync.

Steps to Reproduce

Reproduction

LITELLM=http://localhost:4000 MASTER=sk-XXX VIRTUAL_KEY="sk-XXY" # users-local team GENERAL_SID="SERVER_ID"

Key's own access_group_ids is always empty curl -s "$LITELLM/key/info" -H "Authorization: Bearer $MASTER"
-G --data-urlencode "key=$VIRTUAL_KEY" | jq '.info.access_group_ids' => []

But the key's team DOES have the access group correctly assigned TEAM_ID=$(curl -s "$LITELLM/key/info" -H "Authorization: Bearer $MASTER"
-G --data-urlencode "key=$VIRTUAL_KEY" | jq -r '.info.team_id') curl -s "$LITELLM/team/info?team_id=$TEAM_ID"
-H "Authorization: Bearer $MASTER" | jq '.team_info.access_group_ids' => ["37eaf962-e253-4dc7-afa7-4b9f9a5f86c3"]

Discovery returns empty for virtual key curl -s "$LITELLM/v1/mcp/server" -H "Authorization: Bearer $VIRTUAL_KEY" | jq 'length' => 0 (expected: 1)

Tool call returns 403 for virtual key — even for an authorized server curl -s -X POST "$LITELLM/mcp-rest/tools/call"
-H "Authorization: Bearer $VIRTUAL_KEY" -H "Content-Type: application/json"
-d "{"server_id":"$GENERAL_SID","name":"health","arguments":{}}"
| jq '.detail' => {"error": "access_denied", "message": "The key is not allowed to access server ..."}

Master key works fine on both curl -s -X POST "$LITELLM/mcp-rest/tools/call"
-H "Authorization: Bearer $MASTER" -H "Content-Type: application/json"
-d "{"server_id":"$GENERAL_SID","name":"health","arguments":{}}"
| jq '.isError' => false

Relevant log output

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on ?

v1.83.14-stable.patch.3

Twitter / LinkedIn details

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

litellm - 💡(How to fix) Fix [Bug]: Virtual key MCP access ignores team access groups (discovery AND enforcement)