Skip to content

feat(agents): attach access groups to agents and enforce them for models, MCP servers and agent calls - #41634

Merged
yassin-berriai merged 16 commits into
mainfrom
litellm_agent_access_groups
Sep 22, 2026
Merged

yassin-berriai merged 16 commits into
mainfrom
litellm_agent_access_groups

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Access groups can be attached to keys and teams but not to agents
  • An agent key could still reach any model, MCP server, or agent the key allowed
  • Admins had no single place to cap what an agent is allowed to touch
  • A user with narrow access could reach the agent's wider access by invoking it

How it solves it:

  • New access_group_ids field on agents, persisted through create, update, and PATCH
  • Attached groups form a ceiling: their model, MCP server, and agent grants are unioned
  • That ceiling is intersected with the key, team, and object permission checks that already run
  • Missing or unreadable groups fail closed instead of widening access
  • /v1/models for an agent key only lists what the ceiling allows. The attached group ids are read from the in-memory agent registry (the same read-through that already serves agent lookups), so the request path adds no DB query and an agent write applies on the next request; sibling workers pick it up through the existing agents table config sync
  • Admin UI agent form gets an Access Groups selector, and the agent Overview shows what is attached
  • Deleting an access group drops its id from every agent that referenced it
  • An agent key that echoes x-litellm-user-id / x-litellm-team-id (the headers /a2a already forwards) is further capped at that user's and team's models, MCP servers and agents
  • The echoed ids only ever narrow, so a forged header cannot widen the key; an agent that calls another agent forwards the original human, so a chain stays capped at the first caller

User Flow

Before: an admin attaches an access group to an agent, the request is silently accepted, and the agent's key still reaches everything

  1. The admin opens http://localhost:3000/agents/, clicks the agent and then Edit, and finds no Access Groups field on the form
  2. They send PATCH http://localhost:4000/v1/agents/{agent_id} with {"access_group_ids": ["<group id>"]} and get 200 back, but the response has no access_group_ids field
  3. With the agent's key they send POST http://localhost:4000/chat/completions for a model that is not in the group and get 200 with a completion
  4. GET http://localhost:4000/mcp-rest/tools/list lists tools from every MCP server the key can see, including servers not in the group
  5. POST http://localhost:4000/a2a/{agent_name}/message/send to an agent outside the group returns 200 with a reply
  6. Another team's model, MCP server, or agent stays reachable through this agent even though the admin thought they had restricted it
  7. A user whose team may only call gpt-4o-mini sends POST http://localhost:4000/a2a/{agent_name}/message/send, and the agent answers using claude-sonnet, a model that user could never call directly

After: the same admin attaches the group in the UI or API, and the agent's key is denied everything the group does not grant

  1. The admin opens http://localhost:3000/agents/, clicks the agent and then Edit, picks a group in the new Access Groups selector, and clicks Save Changes
  2. The Overview now shows an Access Groups row with the group name and id, and the same is possible through PATCH http://localhost:4000/v1/agents/{agent_id} with {"access_group_ids": ["<group id>"]}, whose 200 response echoes access_group_ids
  3. With the agent's key, POST http://localhost:4000/chat/completions for a model in the group returns 200, and for a model outside the group returns 403 with "type": "agent_model_access_denied"
  4. GET http://localhost:4000/mcp-rest/tools/list only lists tools from MCP servers in the group, and calling a tool on a server outside the group returns 403 access_denied
  5. POST http://localhost:4000/a2a/{agent_name}/message/send returns 200 for an agent in the group and 403 "not allowed for your key/team" for one outside it
  6. Another team's model, MCP server, or agent can no longer be reached through this agent unless one of its access groups grants it
  7. The same user sends POST http://localhost:4000/a2a/{agent_name}/message/send; the agent's own POST http://localhost:4000/chat/completions for claude-sonnet, carrying the forwarded x-litellm-user-id / x-litellm-team-id, returns 403 team_model_access_denied, while gpt-4o-mini still returns 200. Its MCP tool list and agent calls are cut down the same way

Relevant issues

Affected release

Linear ticket

Resolves LIT-8014

Pre-Submission checklist

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

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays 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

Revalidated at d338d3f (merged with main on 2026-09-20)

The branch was merged with the current main (two merges, one test-file conflict in test_auth_checks.py resolved by keeping both appended sections) and the whole matrix was re-run against a proxy started from this head with PYTHONPATH pointing at the checkout, same fixtures as below: one access group agent-ceiling (9ac89b21-46ce-4297-b153-42808b3f9703) granting gpt-4o-mini, one MCP server and one target agent, attached to caller-agent through the Admin UI

Admin UI (Chrome, http://localhost:3000/agents/): click caller-agent, Settings, Edit, pick agent-ceiling in Access Groups, Save Changes. The toast says Agent updated successfully and the Overview shows the group with its id

Agent overview at d338d3f2d2 showing Access Groups agent-ceiling with its id

Add New Agent, Configure, Next: the Entitlements step offers the same Access Groups selector with agent-ceiling selectable (the wizard was not submitted)

Add New Agent Entitlements step at d338d3f2d2 with agent-ceiling selected in Access Groups

Live results with the caller agent's key right after the UI save, each line is one curl against http://localhost:4000

POST /chat/completions model=gpt-4o-mini          200 (completion returned)
POST /chat/completions model=anthropic-haiku-4-5  403 agent_model_access_denied
GET  /v1/models                                   200 ['gpt-4o-mini']
GET  /mcp-rest/tools/list                         200 servers: ['mcp_allowed']
POST /a2a/target-allowed/message/send             200 (reply returned)
POST /a2a/target-denied/message/send              403 not allowed for your key/team

Deleted group edge case: DELETE /v1/access_group/9ac89b21-... returned 204, GET /v1/agents/{caller} then returned "access_group_ids": [], and the same key got 200 on anthropic-haiku-4-5, both models from /v1/models and both MCP servers from tools/list, so the detached agent is back to key and team permissions only

The handler branch each leg entered: chat goes through common_checks -> _check_agent_access_group_model_access (the Responses and Anthropic Messages routes share common_checks, not exercised live), /v1/models through _agent_access_group_visible_models, MCP REST list and call through MCPRequestHandler._agent_capped_servers (the streamable MCP transport shares that handler, not exercised live), A2A through _intersect_agent_access with the ceiling applied as the object permission

Taxonomy audit (A-BB) on the d338d3f diff

Confirmed and fixed: H2 (a banner comment in test_auth_checks.py, removed), H4 (Optional["Router"] in utils.py -> "Router | None", bare dict parameter in test_agent_registry.py -> PatchAgentRequest, one 121 character docstring reworded)

Judged and accepted with reason: B1, _detach_access_group_from_agents reads and rewrites the agent's access_group_ids list inside the delete transaction; two admins deleting two groups attached to the same agent at the same instant could leave one stale id behind. A stale id points at a group that no longer loads and so contributes nothing to the union, which narrows access rather than widening it, and the next agent update clears it. A2, the ceiling reads group ids from the in-memory agent registry and the group object through get_access_object, so a sibling worker sees an agent write on the existing agents-table config sync and a group write on the existing access-group cache TTL, the same propagation keys and teams have today; no new cache was introduced

Not applicable or clean: F3/O4 (enforcement sits in common_checks and MCPRequestHandler, shared by the sibling surfaces, /model/info is metadata only), X1/E5/Z1 (access_group_ids uses is None versus empty deliberately: None or missing means no ceiling, an attached group naming nothing denies that resource kind), W1/W4 (all helpers build new tuples and frozensets, no caller-owned mutation, no mutable defaults), V1 (no api_base or key forwarding in the diff), D4/Z2/O3 (a group that cannot be loaded or a missing DB client contributes nothing to the union, so the failure narrows access), C1-C7 (new optional field with [] default, no status code or default flips, no removed symbols), T1-T5/T17 (the mapped suites exercise each ceiling branch through injected loaders rather than class patches and leave no global state behind), A1 (agent and access-group writes refresh the handling worker's registry and cache), H1/H3/H5-H10 (no docs in source, no em dashes, Prisma column present in all three schemas plus the migration, UI bundle is rebuilt by the Docker and UI workflows, CI path filters cover every changed path)

Screenshots (25729c5, the PR tip)

Admin UI: open http://localhost:3000/agents/, click caller-agent, click Edit, pick agent-ceiling in the new Access Groups selector, click Save Changes. The Overview then shows the attached group with its id

Agent edit form with the agent-ceiling access group selected

Agent overview showing Access Groups agent-ceiling with its id

Models: with the group attached, the agent's key gets 200 for gpt-4o-mini (in the group) and 403 agent_model_access_denied for anthropic-haiku-4-5 (not in the group)

Terminal: chat completion allowed for the model in the group and denied with 403 for the model outside it

MCP: mcp-rest/tools/list only shows mcp_allowed, listing mcp_denied by name is 403, a tool call on mcp_allowed returns 200 with the DeepWiki result, and the same call on mcp_denied is 403

Terminal: MCP discovery and tool calls allowed on mcp_allowed and denied with 403 on mcp_denied

Agent to agent: a2a/target-allowed/message/send returns 200, a2a/target-denied/message/send returns 403

Terminal: A2A message send allowed for target-allowed and denied with 403 for target-denied

More Admin UI: the Overview goes back to None after clearing the selector, and the Create Agent form (Add Agent on http://localhost:3000/agents/) offers the same selector

Agent overview with no access group attached showing None

Create Agent form with the Access Groups selector

Recording of the UI flow (select, save, overview, clear, reselect, create form)

Recording of the agent access group UI flow

The three terminal captures are the After leg below, run from the same shell against the proxy at 25729c5; the full commands and outputs follow in text

Setup

Shared setup, same Postgres database and fixtures for both legs. Proxy started with python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --use_v2_migration_resolver (the Before leg from a worktree at the merge base on port 4001, the After leg from the PR tip on port 4000), Admin UI with npm run dev on port 3000. Real provider calls to gpt-4o-mini (OpenAI) and anthropic-haiku-4-5 (Anthropic)

Fixtures created with the master key: two MCP servers mcp_allowed ($MCP_ALLOWED) and mcp_denied ($MCP_DENIED), both pointing at the public DeepWiki MCP endpoint; two target agents target-allowed and target-denied backed by the proxy itself; one caller agent caller-agent ($CALLER) whose key is $AGENT_KEY; and one access group agent-ceiling ($AG) with access_model_names: ["gpt-4o-mini"], access_mcp_server_ids: ["$MCP_ALLOWED"], access_agent_ids: ["<target-allowed id>"]. The key, team, and object permissions of the caller agent are left wide open so that every denial below comes from the attached group alone. Every command is run with $AGENT_KEY unless it says sk-1234

Before (dc81cf5)

Attach the group to the agent

  1. curl -X PATCH http://localhost:4001/v1/agents/$CALLER -H 'Authorization: Bearer sk-1234' -d '{"access_group_ids":["$AG"]}'
  2. HTTP 200, response has no access_group_ids field: {"agent_name": "caller-agent", "access_group_ids": "<field absent>"}
  3. Admin UI at http://localhost:3000/agents/, agent Edit form: no Access Groups section exists at this commit (the Rate Limits section is followed directly by MCP Servers), and the Overview has no Access Groups row

Model in group

  1. curl http://localhost:4001/chat/completions -H 'Authorization: Bearer $AGENT_KEY' -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Reply with the single word ok"}],"max_tokens":5}'
  2. "Ok." HTTP 200

Model not in group

  1. curl http://localhost:4001/chat/completions -H 'Authorization: Bearer $AGENT_KEY' -d '{"model":"anthropic-haiku-4-5",...}'
  2. "ok" HTTP 200 (should have been denied)

MCP servers visible

  1. curl 'http://localhost:4001/mcp-rest/tools/list' -H 'Authorization: Bearer $AGENT_KEY'
  2. servers: ['mcp_allowed', 'mcp_denied'] tools: 6 HTTP 200 (both servers visible)

MCP list scoped to the denied server

  1. curl 'http://localhost:4001/mcp-rest/tools/list?mcp_server_name=mcp_denied' -H 'Authorization: Bearer $AGENT_KEY'
  2. servers: ['mcp_denied'] tools: 3 HTTP 200

MCP tool call on the allowed server

  1. curl http://localhost:4001/mcp-rest/tools/call -H 'Authorization: Bearer $AGENT_KEY' -d '{"server_id":"$MCP_ALLOWED","name":"mcp_allowed-read_wiki_structure","arguments":{"repoName":"BerriAI/litellm"}}'
  2. "Available pages for BerriAI/litellm:\n\n- 1 Overview..." HTTP 200

MCP tool call on the denied server

  1. curl http://localhost:4001/mcp-rest/tools/call -H 'Authorization: Bearer $AGENT_KEY' -d '{"server_id":"$MCP_DENIED","name":"mcp_denied-read_wiki_structure","arguments":{"repoName":"BerriAI/litellm"}}'
  2. "Available pages for BerriAI/litellm:\n\n- 1 Overview..." HTTP 200 (should have been denied)

A2A agent in group

  1. curl http://localhost:4001/a2a/target-allowed/message/send -H 'Authorization: Bearer $AGENT_KEY' -d '{"jsonrpc":"2.0","id":"1","method":"message/send","params":{"message":{"role":"user","parts":[{"kind":"text","text":"Reply with the single word ok"}],"messageId":"m1"}}}'
  2. ["Ok"] HTTP 200

A2A agent not in group

  1. curl http://localhost:4001/a2a/target-denied/message/send -H 'Authorization: Bearer $AGENT_KEY' -d '{"jsonrpc":"2.0","method":"message/send",...}'
  2. ["Ok"] HTTP 200 (should have been denied)

After (25729c5, the PR tip)

The curl matrix below was re-run against a proxy started from 25729c5, the PR tip. The UI screenshots and recording were captured at 433c804; the only dashboard change after that commit is prettier formatting of add_agent_form.test.tsx (git diff 433c804a6c..25729c521f --stat -- ui/)

Attach the group to the agent

  1. Open http://localhost:3000/agents/, click caller-agent, click Edit. The form has a new Access Groups section between Rate Limits and MCP Servers. Pick agent-ceiling and click Save Changes (first screenshot above)
  2. The Overview now shows the attached group with its id (second screenshot above)
  3. Clearing the selector and saving takes the Overview back to None, reselecting brings it back, and the Create Agent form at http://localhost:3000/agents/ (Add Agent) offers the same selector (screenshots and recording above)
  4. The same through the API: curl http://localhost:4000/v1/agents/$CALLER -H 'Authorization: Bearer sk-1234'
  5. HTTP 200, {"agent_name": "caller-agent", "access_group_ids": ["51cebbbb-b237-49fb-9e46-4cd64adf33b7"]}

Model listing follows the ceiling

  1. curl http://localhost:4000/v1/models -H 'Authorization: Bearer $AGENT_KEY'
  2. HTTP 200, ['gpt-4o-mini'] (the same key with no group attached lists all 40 deployments in the config, see the PATCH round trip below)

PATCH round trip, the change applies on the next request

  1. curl -X PATCH http://localhost:4000/v1/agents/$CALLER -H 'Authorization: Bearer sk-1234' -d '{"access_group_ids": []}' returns {"access_group_ids": []}
  2. curl http://localhost:4000/v1/models -H 'Authorization: Bearer $AGENT_KEY' now lists ['anthropic-haiku-4-5', 'anthropic-opus-4-5', ..., 'gpt-4o-mini', 'gpt-5.5', ..., 'voyage-4-large'], 40 models
  3. curl -X PATCH http://localhost:4000/v1/agents/$CALLER -H 'Authorization: Bearer sk-1234' -d '{"access_group_ids": ["$AG"]}' returns {"access_group_ids": ["51cebbbb-b237-49fb-9e46-4cd64adf33b7"]}
  4. curl http://localhost:4000/v1/models -H 'Authorization: Bearer $AGENT_KEY' is back to ['gpt-4o-mini']

Model in group

  1. curl http://localhost:4000/chat/completions -H 'Authorization: Bearer $AGENT_KEY' -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Reply with the single word ok"}],"max_tokens":5}'
  2. "Ok." HTTP 200

Model not in group

  1. curl http://localhost:4000/chat/completions -H 'Authorization: Bearer $AGENT_KEY' -d '{"model":"anthropic-haiku-4-5",...}'
  2. HTTP 403 {"message": "The requested model 'anthropic-haiku-4-5' is not available for this API key, or the model name is invalid. Check the models available to you and try again.", "type": "agent_model_access_denied", "param": "model", "code": "403"}

MCP servers visible

  1. curl 'http://localhost:4000/mcp-rest/tools/list' -H 'Authorization: Bearer $AGENT_KEY'
  2. servers: ['mcp_allowed'] tools: 3 HTTP 200 (only the granted server)

MCP list scoped to the denied server

  1. curl 'http://localhost:4000/mcp-rest/tools/list?mcp_server_name=mcp_denied' -H 'Authorization: Bearer $AGENT_KEY'
  2. HTTP 403 {"error": "access_denied", "message": "The key is not allowed to access server mcp_denied"}

MCP tool call on the allowed server

  1. curl http://localhost:4000/mcp-rest/tools/call -H 'Authorization: Bearer $AGENT_KEY' -d '{"server_id":"$MCP_ALLOWED","name":"mcp_allowed-read_wiki_structure","arguments":{"repoName":"BerriAI/litellm"}}'
  2. "Available pages for BerriAI/litellm:\n\n- 1 Overview..." HTTP 200

MCP tool call on the denied server

  1. curl http://localhost:4000/mcp-rest/tools/call -H 'Authorization: Bearer $AGENT_KEY' -d '{"server_id":"$MCP_DENIED","name":"mcp_denied-read_wiki_structure","arguments":{"repoName":"BerriAI/litellm"}}'
  2. HTTP 403 {"detail": {"error": "access_denied", "message": "The key is not allowed to access server bb713243-b7c0-408b-a981-96a6138f3689"}}

A2A agent in group

  1. curl http://localhost:4000/a2a/target-allowed/message/send -H 'Authorization: Bearer $AGENT_KEY' -d '{"jsonrpc":"2.0","id":"1","method":"message/send","params":{"message":{"role":"user","parts":[{"kind":"text","text":"Reply with the single word ok"}],"messageId":"m1"}}}'
  2. ["Ok."] HTTP 200

A2A agent not in group

  1. curl http://localhost:4000/a2a/target-denied/message/send -H 'Authorization: Bearer $AGENT_KEY' -d '{"jsonrpc":"2.0","method":"message/send",...}'
  2. HTTP 403 "Agent 'target-denied' is not allowed for your key/team. Contact proxy admin for access."

Type

🆕 New Feature

Caveats (if any)

Severe

  • Attaching a group to an agent restricts that agent's key immediately; a group that grants nothing blocks everything

Medium

  • MCP grants are per server, so tool level filtering follows the server; there is no per tool grant in access groups today
  • The agent ceiling only applies to requests authenticated with the agent's own key (agent_id on the key)
  • The invoking user cap depends on the agent backend echoing x-litellm-user-id / x-litellm-team-id back on its own calls; an agent that drops them runs at its key and access group ceiling only
  • The cap uses the invoking user's and team's grants, not the specific key they invoked with, since that key never reaches the agent
  • An echoed user id with no user row places no user level cap, matching a direct call by a user with no row; the team id, when echoed, is still enforced and fails closed if it cannot be loaded
  • Group deletion clears the id from agents in one pass; an agent whose groups are all deleted becomes unrestricted again, matching no attached groups

Low

  • The Before leg UI screenshot is described rather than captured; the merge base build has no Access Groups field on the form or overview
  • The taxonomy audit accepted B1 and A2 with the reasons above; both narrow rather than widen access
  • Multiple group union and the fail closed path for an unreadable group are covered by unit tests, not by the live run above
  • The invoking user cap (models, MCP servers, agents, nested A2A forwarding, forged agent_caller rejected on the auth model) is covered by unit tests with a mutation check (dropping each caller check turns 7 of them red), not yet by the live run above
  • The agent edit form shows a localhost discovery warning in the dev setup; it is unrelated to this change
  • osv-scan (not a required check) is red on soupsieve 2.8.4 (GHSA-gjv8-xp57-g29c, GHSA-j934-xhv5-fg8f). Both advisories were published 2026-09-17T20:32Z; this PR does not touch uv.lock, its own scan passed at 20:40Z on the same lockfile, and every PR scan in the repo has failed since 20:45Z. The bump to 2.9.0 is dependabot PR build(deps): bump soupsieve from 2.8.4 to 2.9.2 #41679, whose scan is the only green one

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

Note

High Risk
Changes authorization for agent-bound API keys across models, MCP, and agent routing; misconfiguration or empty group ceilings can deny all access immediately.

Overview
Agents can now be linked to unified access groups via a new access_group_ids field (DB migration, Prisma, agent CRUD/API types). Attached groups define a ceiling (union of each group’s models, MCP servers, and target agents); that ceiling is intersected with existing key/team/object-permission checks for agent keys.

Enforcement spans chat completions (agent_model_access_denied), MCP server allowlists (alongside agent object_permission), agent-to-agent access, and /v1/models listing so agent keys only see models the ceiling allows. Deleting an access group strips its id from agents in the DB and updates the in-memory agent registry.

The dashboard Create/Edit Agent flows add an Access Groups selector; overview shows attached groups. Tests cover ceiling resolution, intersections, and delete cleanup.

Reviewed by Cursor Bugbot for commit 7229fc1. Bugbot is set up for automated code reviews on this repo. Configure here.

Link to Devin session: https://app.devin.ai/sessions/47c201b13a3e4c85b3bc8a0d199eb483
Open in Devin Desktop: https://app.devin.ai/desktop/session/47c201b13a3e4c85b3bc8a0d199eb483?variant=devin
Requested by: @yassin-berriai

yassin-berriai and others added 3 commits September 17, 2026 19:24
…els, MCP servers and agent calls

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…e gate

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@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.

@codspeed

codspeed Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_agent_access_groups (82eef2f) with main (3d26a29)

Open in CodSpeed

@codecov

codecov Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.89313% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/proxy/agent_endpoints/auth/agent_caller.py 60.52% 15 Missing ⚠️
litellm/proxy/auth/auth_checks.py 96.96% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@greptile-apps

greptile-apps Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge; no new actionable issue remains, and all previous findings are resolved.

Summary

This PR adds access-group ceilings to agents and enforces them across model invocation and discovery, MCP server access, and agent-to-agent calls.

  • Persists access_group_ids through agent creation, updates, schemas, and migration.
  • Intersects attached-group grants with existing key, team, and object permissions.
  • Adds agent access-group selection and display to the Admin UI.
  • Detaches deleted access groups from affected agents.
  • Adds coverage for empty, missing, multiple-group, CRUD, model, MCP, and A2A behavior.

Reviews (4) · Last reviewed commit: "style(agents): tidy typing and docstring..."

Comment thread litellm/proxy/agent_endpoints/auth/agent_access_groups.py
Comment thread litellm/proxy/agent_endpoints/auth/agent_access_groups.py
Comment thread litellm/proxy/auth/auth_checks.py Outdated
Comment thread tests/test_litellm/proxy/agent_endpoints/auth/test_agent_permission_handler.py Outdated
Comment thread litellm/proxy/agent_endpoints/auth/agent_access_groups.py Outdated
Comment thread litellm/proxy/agent_endpoints/auth/agent_permission_handler.py Fixed
yassin-berriai and others added 5 commits September 17, 2026 20:04
…ching it

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ne budget

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…e and cap the model listing

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ments

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Comment thread litellm/proxy/agent_endpoints/auth/agent_access_groups.py Fixed
yassin-berriai and others added 2 commits September 17, 2026 20:51
…try instead of the DB on the request path

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…deQL sees no fall-through

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai please re-review at 7229fc1: the registry read-through refactor, the model listing cap, and the CodeQL fall-through fix landed since your last pass

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment •

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.

Stale Bugbot comment from a previous run.

…rough match

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai please re-review at 25729c5: both earlier findings are fixed and resolved, and the agent access intersection is now a typed helper

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment

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.

✅ 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 25729c5. Configure here.

yassin-berriai and others added 3 commits September 20, 2026 08:52
…groups

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	tests/test_litellm/proxy/auth/test_auth_checks.py
…groups

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai please re-review at d338d3f: main merged twice, re-verified live, Section H typing and comment fixes landed

An agent key that echoes the x-litellm-user-id / x-litellm-team-id headers
forwarded by /a2a is capped at that user's and team's models, MCP servers
and agents, on top of its own grants and access group ceiling. The echoed
ids only narrow, and nested A2A hops forward the original human caller

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
proxy_logging_obj=proxy_logging_obj,
)
if user_object is None:
verbose_proxy_logger.debug("agent caller user %r not found; no user ceiling applied", caller.user_id)
@yassin-berriai
yassin-berriai merged commit 9cc5b78 into main Sep 22, 2026
102 of 103 checks passed
@yassin-berriai
yassin-berriai deleted the litellm_agent_access_groups branch September 22, 2026 00:13

This branch is waiting to be deployed

1 waiting deployment
e2e-changed — 82eef2fc Waiting Sep 21, 2026 by devin-ai-integration[bot] via oauth #166
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants