Grant MCP Server Access to Keys and Teams
This guide walks through granting an MCP server to a virtual key and to a team, first in the Admin UI and then through the management API, and shows how the resulting access resolves when the key and the team both carry a grant. The rules themselves (six-level intersection, no-mcp-servers, require_key_mcp_access_defined, access groups, per-entity tool permissions) live in MCP Permission Management; this page is the procedure that applies them.
Before you start​
Register the MCP server first, either in config.yaml under mcp_servers or from MCP Servers in the Admin UI (see MCP Gateway). Every grant below refers to the server by its server_id (a UUID for servers added in the UI) or by its server_name alias (the config key, for example deepwiki). Both forms are accepted by the API and resolve to the same server.
If several servers should always be granted together, put them in an access group and grant the group instead. If a key or team should see a hand-picked subset of tools across servers, create a toolset and grant that.
The grant fields​
MCP access is stored in the object_permission block of the key or team. The same four fields work on both, and they map one to one onto the controls in the Admin UI MCP Settings section.
| Field | Type | What it grants |
|---|---|---|
mcp_servers | list[str] | Server IDs or aliases the entity may reach. The sentinel no-mcp-servers blocks all MCP access, see Opting a key out |
mcp_access_groups | list[str] | Access group names. Every server in the group is granted |
mcp_toolsets | list[str] | Toolset IDs. Grants the servers the toolset draws from, limited to the tools it names |
mcp_tool_permissions | dict[str, list[str]] | Per-server tool allowlist, keyed by server ID or alias. Omit a server to allow all of its tools. A server named here counts as granted even if it is missing from mcp_servers |
A key or team with none of these fields set has no MCP restriction of its own; what that means at runtime depends on the other levels, see How key and team grants resolve.
Grant an MCP server to a virtual key​
Admin UI​
Open Virtual Keys in the left sidebar (http://localhost:4000/ui/api-keys) and click + Create New Key. Fill in the owner, key name and models as usual, then scroll down and expand the MCP Settings accordion. The Allowed MCP Servers selector lists every registered server, access group and toolset, plus a No MCP Servers entry that blocks all MCP access for the key.
Pick one or more entries. Each selected server (including servers resolved from an access group) expands into its tool list underneath, with every tool on by default. Untick a tool to remove it from the key; the header shows how many tools remain allowed. Click Create Key and copy the key from the confirmation dialog.
The selection is saved as object_permission.mcp_servers (or mcp_access_groups / mcp_toolsets, depending on what you picked) and the tool toggles as object_permission.mcp_tool_permissions. Read it back with GET /key/info?key=<key>.
To change an existing key, click the key in the Virtual Keys table, open its Settings tab, click Edit Settings, change MCP Servers / Access Groups and the tool toggles, then click Save Changes.
API​
POST /key/generate takes the grant inline. The example below grants one server and restricts the key to two of its tools:
curl -X POST "http://localhost:4000/key/generate" \
-H "Authorization: Bearer sk-master-key" \
-H "Content-Type: application/json" \
-d '{
"key_alias": "wiki-reader",
"team_id": "<team-id>",
"object_permission": {
"mcp_servers": ["deepwiki"],
"mcp_tool_permissions": {
"deepwiki": ["read_wiki_structure", "read_wiki_contents"]
}
}
}'
POST /key/update takes the same block plus the key to change. Fields you send replace the stored value for that field and fields you omit are kept, so adding a toolset to the key above leaves mcp_servers and mcp_tool_permissions in place:
curl -X POST "http://localhost:4000/key/update" \
-H "Authorization: Bearer sk-master-key" \
-H "Content-Type: application/json" \
-d '{
"key": "sk-...",
"object_permission": {
"mcp_toolsets": ["<toolset-id>"]
}
}'
To grant every server in an access group, send "mcp_access_groups": ["research"] instead of mcp_servers. To block all MCP access, send "mcp_servers": ["no-mcp-servers"].
Confirm what the key sees by listing tools with the key itself:
curl -s "http://localhost:4000/mcp-rest/tools/list" \
-H "Authorization: Bearer sk-..." | jq '[.tools[] | .name]'
["read_wiki_contents", "read_wiki_structure"]
Grant an MCP server to a team​
Admin UI​
Open Teams in the left sidebar (http://localhost:4000/ui/teams) and click Create Team. Fill in the team name and models, then expand the MCP Settings accordion. The Allowed MCP Servers selector offers the same servers, access groups and toolsets as the key form. Selecting an access group shows each server it resolves to, tagged with the group name, so you can still toggle tools per server. Click Create Team.
To change an existing team, click the team in the Teams table, open its Settings tab, click Edit Settings, change MCP Servers / Access Groups and the tool toggles, then click Save Changes. Keys that belong to the team inherit the new grant; nothing on the keys needs to be edited.
API​
POST /team/new and POST /team/update take the same object_permission block as the key endpoints, with team_id identifying the team on update:
- /team/new
- /team/update
curl -X POST "http://localhost:4000/team/new" \
-H "Authorization: Bearer sk-master-key" \
-H "Content-Type: application/json" \
-d '{
"team_alias": "research-team",
"object_permission": {
"mcp_servers": ["deepwiki"],
"mcp_access_groups": ["research"],
"mcp_toolsets": ["<toolset-id>"],
"mcp_tool_permissions": {
"deepwiki": ["read_wiki_structure", "read_wiki_contents"]
}
}
}'
curl -X POST "http://localhost:4000/team/update" \
-H "Authorization: Bearer sk-master-key" \
-H "Content-Type: application/json" \
-d '{
"team_id": "<team-id>",
"object_permission": {
"mcp_servers": ["deepwiki"]
}
}'
/team/update merges the same way /key/update does: only the fields you send are replaced. Read the stored grant back with GET /team/info?team_id=<team-id>; it is returned under team_info.object_permission.
How key and team grants resolve​
The full rule set is in Permission Hierarchy and Per-entity Tool-Level Permissions. The cases below are the ones you hit when only a key and its team carry grants, in the order LiteLLM applies them.
A key with no MCP grant of its own inherits the team's grant. Every server and every tool the team allows is available to the key, and nothing else. With require_key_mcp_access_defined: true in general_settings the same key gets no MCP servers at all until it is granted some explicitly, see Require keys to define their own MCP access.
When both the key and the team list servers, the key reaches the intersection. Tool permissions intersect too, per server: a team that allows two tools on deepwiki and a key that allows one of them yields that one tool. If only one side sets mcp_tool_permissions for a server, that side's list applies unchanged.
Within a single level, a toolset and a direct mcp_tool_permissions entry are unioned before the levels are intersected. A key granted the toolset wiki_readonly (two read tools) plus mcp_tool_permissions: {"deepwiki": ["read_wiki_structure"]} sees both read tools, not just the one named directly.
no-mcp-servers on the key wins over any team grant. tools/list returns an empty list and tools/call is refused, even though the team allows the server.
A key inside a team can only be granted servers the team already allows (or servers marked allow_all_keys). /key/generate and /key/update enforce this at write time (the Admin UI saves through the same endpoints) and answer 403:
Key requests MCP servers not allowed by team '<team-id>': ['<server-id>']. Team allows: ['<server-id>']. Global (allow_all_keys) servers: [].
A key that is not in a team can be granted any server by a proxy admin; a non-admin caller can only grant allow_all_keys servers to such a key. Servers the key already holds are grandfathered on /key/update, so shrinking the team's list does not break existing keys until you try to add a new server.
Organization, internal user, end user and agent grants sit above the key and team and only ever narrow the result further. Narrowing happens at tools/list time and again at tools/call time, so a tool outside the effective set is neither advertised nor callable.
Worked example​
Team research-team allows deepwiki with mcp_tool_permissions: {"deepwiki": ["read_wiki_structure", "read_wiki_contents"]} and the toolset wiki_readonly (the same two tools).
| Key grant | Effective tools on deepwiki |
|---|---|
| none | read_wiki_structure, read_wiki_contents (inherited from the team) |
mcp_servers: ["deepwiki"], mcp_tool_permissions: {"deepwiki": ["read_wiki_structure"]} | read_wiki_structure (intersection) |
the row above plus mcp_toolsets: ["wiki_readonly"] | read_wiki_structure, read_wiki_contents (key-level union, then intersected with the team) |
mcp_servers: ["no-mcp-servers"] | none, tools/list is empty |
mcp_servers: ["deepwiki_backup"] (not allowed by the team) | write rejected with 403 |
Related​
MCP Permission Management for the rules and the remaining levels (organization, internal user, end user, agent), MCP Toolsets for creating toolsets, Agent Permission Management for agent grants, and MCP Gateway for registering servers.