Control MCP Tool Access
Narrow an Access Binding from whole servers down to individual tools, add conditions on tool arguments and responses, and switch a server to a default-deny posture. For the model behind these controls, see Team Access.
Grant specific tools
- Go to Setup → Settings → Access Bindings and edit the binding (or create one — see Create a Team Access Binding).
Under MCP tools, each connected server has three choices:
- All tools grants the whole server.
- Selected tools grants the tools you check. The list comes from the server's tool inventory. Until you check a tool, you can't save.
- None grants nothing on the server.
These choices don't change the binding's Servers for conditions and scopes list under Advanced. That list sets where the binding's conditions and scopes apply. While the binding has no tool grant, each server in it also grants every tool. If a change would remove the binding's last tool grant while that list has servers, the editor refuses it: remove the servers from the list first.
A Used recently tag marks the tools the binding's team subjects called recently. It's a hint only: the picker never selects a tool for you.
To grant a pattern, or the same tools on several servers, use Add a tool pattern. Enter the servers, or
*for every server, and the tools, then select Add pattern:To express Servers Tools Every read-style tool on every server *read_*One tool on two servers github-mcp, gitlab-mcpget_issueTool names are the per-server names shown in the tool inventory. Simple globs use
*as the only wildcard. A pattern appears under Other tool grants as a tag you can remove.Save. The right-hand Compiled policies pane shows the exact CEL policies the binding now enforces — the same names appear in Security Events on every decision they make.
Set the binding's Decision mode, under Advanced, to Audit first and watch Security Events; flip it to its intended mode once the matches look right.
Deny specific tools
Create a binding, set Decision mode to Deny under Advanced, and grant the tools to block under MCP tools, for example a tool pattern of delete_repository, transfer_*. A deny always wins — even if another binding grants the same tool to the same caller.
Grant LLM models
In the binding editor, under Models, choose models from the list. It shows every model your enabled upstreams advertise. When the same model ID is on more than one upstream, choose (any upstream) or one upstream, for example
claude-sonnet-4 (bedrock). To grant a pattern, type it, such asclaude-*, or*for every model.- Select Add model grant.
- Save. The Compiled policies pane shows
accessbinding:<binding>:llm-allow-markerfor an allow-mode binding, oraccessbinding:<binding>:llmfor another mode.
A model grant never widens a team or PAT model scope or a virtual key's allowed models. For the full matching rules, see Access policy.
Let workloads use a binding
Each Workload Blueprint selects exactly one Access Binding, and a workload's override can reference more by ID in its bindingRefs. The workloads then get the binding's access. To create a binding only workloads use, leave Subjects empty; the editor warns you that the binding grants nothing until a Blueprint selects it or a workload references it.
- Create or edit the binding with the servers, tools, and models the workloads need.
- Select the binding in a Workload Blueprint's Access section, or reference its ID in a registration's override.
- Reopen the binding. The Used by panel lists the Workload Blueprints and registrations that use it.
Saved edits to a binding apply to its workloads on their next call, including workloads in Enforce, after an impact preview and a confirmation. Every edit is recorded in the binding's history, and you can restore an earlier revision. You can't rename a binding after you create it, and you can't delete a binding that a Blueprint selects (409 binding_in_use). See Review and restore a binding change. See Binding subjects.
Add input and output conditions
In the binding editor, under Advanced:
-
Input conditions run on the request, before the tool executes — typically over
object.tool.arguments:string(object.tool.arguments.path).startsWith("/workspace/") -
Output conditions run on the tool's response, before the agent sees it — over
object.mcp.response.*:!object.mcp.response.hasPII
A failed condition always denies, whatever the binding's decision mode. See Policy Context for the available fields in each phase.
Set a default-deny posture per server
With the posture set to deny, a caller can only reach tools that an enabled binding grants to them specifically — one binding's grant never opens that tool to another binding's subjects, even if both bindings cover the same server.
In the UI: Inventory → MCP Servers → Details → Access, and switch Unlisted tools on this server to Deny. The same drawer's Tools tab shows the server's full declared inventory with drift states and per-tool grants, and its Governance tab holds the drift response mode and the scheduled re-scan interval.
Via the API:
curl -X PATCH "$YOUR_WORKSPACE_URL/api/v1/mcp-servers/github-mcp/tool-default" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"action": "deny"}'
Set "action": "allow" to return to the default posture. The change takes effect on the next policy refresh — no restart.
Before enabling deny posture on a busy server, review the tool inventory (below) for tools in active use that no binding grants — those calls start failing the moment the posture applies.
See one identity's access across every server
The views above are server-centric — pick a server, see who can reach it. Setup → Settings → Access Bindings → Access Matrix is the identity-centric complement: pick a team, user, agent, or agent label and see its effective allow/deny across every (server, tool) pair fleet-wide, with the binding (or the server's default posture) that decided each one. Use it to answer "what can this identity actually do" without checking every server one at a time.
Review the tool inventory
The tool inventory lists every tool each connected MCP server declares — including tools that have never been called, which usage views can't show. In the UI, it's on the Tools tab of a server's details drawer — reachable from Inventory → MCP Servers → Registration → Details (as above) or, for any role, from Inventory → MCP Servers → Observed → (click a server). Via the API:
curl "$YOUR_WORKSPACE_URL/api/v1/tools?server=github-mcp" \
-H "Authorization: Bearer $ADMIN_TOKEN"
Each entry shows the tool's drift state, call count, last-called time, which binding subjects are granted it, its access type, whether it's Elevated Privileged, and its datasource's data classification. Filters: ?status=never_called, ?status=drifted, ?status=new, ?accessType=read|write|delete|execute|unknown, ?elevated=true, and ?q=<substring>. The card header also counts tools and servers with no data classification assigned yet, so an unclassified gap doesn't require scanning the whole table to notice.
Filtering by access type is a quick way to build a binding's tool selector without writing CEL — for example, filter to every non-read tool on a server before creating a deny-mode binding that blocks everything but lookups.
Fix or override a tool's access type
Access type is classified automatically (see Tool access types) — you shouldn't normally need to touch it. When a tool lands on Unknown, or a rule picks the wrong category for a tool with an unusual name, override it directly:
- Go to Inventory → MCP Servers → a server → Tools (the Tool Schema Pinning table).
- On the tool's row, open the ⋮ menu and choose Set access type override.
- Pick the correct access type, or Inherit (classify automatically) to remove an existing override, and save.
The override applies immediately — no re-scan needed — and is labeled (override) wherever the tool's access type appears, including in policy decisions and the audit log. It also feeds Elevated Privileged status, since that's derived from access type plus data classification.
Tune the elevation matrix
Elevated Privileged status comes from a matrix of access type × data classification, shipped with a sensible default. Adjust it for your risk tolerance:
- Go to Setup → Settings → Risk Scoring → Tools.
- For each access type, tick the classifications that should make it elevated, or check Always to elevate that access type regardless of classification — including against a datasource with no classification set.
- Save. The change applies immediately to every server's tools — no re-scan needed.
Use Reset to defaults to return to the shipped matrix. A per-tool override (set from the Tool Schema Pinning table, or via the tool_access_overrides settings API for the Elevated flag itself) always wins over the matrix for that one tool.
The editor writes the whole matrix, and a saved matrix is used exactly as stored. A later change to the shipped default — such as Unknown becoming elevated at Confidential and Restricted — does not reach a workspace that has already saved one. Reset to defaults followed by Save is what adopts the current shipped matrix.
Cover unclassifiable tools
A tool whose access type nothing could derive is tagged Unknown. The shipped matrix elevates Unknown at Confidential and Restricted, but if your workspace saved an elevation matrix before that — or deliberately left the Unknown row empty — those tools are not Elevated Privileged, so no policy matching object.mcp.elevated applies to them, including the built-in mcp-require-approval-elevated-tool.
The Tool Schema Pinning panel (Inventory → MCP Servers → a server → Tool Schema Pinning) warns when a server has tools in that state and counts them. Pick whichever remedy fits:
| Remedy | Use it when | Where |
|---|---|---|
| Elevate the Unknown row in the matrix | You want every unclassifiable tool covered, on every server, from now on | Setup → Settings → Risk Scoring → Tools — tick Confidential and Restricted on the Unknown row (or Always), then Save |
| Override the tool's access type | The tool really is a read, write, delete, or execute and the classifier just missed it | The tool's row ⋮ menu → Set access type override (see above) |
| Match Unknown directly in a policy | You want a distinct rule for unclassifiable tools, separate from elevation | A CEL policy matching object.mcp.accessType == "unknown" |
The third option keeps the two concerns apart — for example, audit every unclassifiable call while you work through the overrides:
matchConditions:
- name: is-mcp-call
expression: 'object.mcp.tool != ""'
- name: is-unclassified
expression: 'object.mcp.accessType == "unknown"'
See Policy Context for the field's full definition, and Write a policy for the surrounding policy structure.
Verify
- As a granted caller, invoke a selected tool — it succeeds; the Security Events entry records the binding.
- Invoke a tool outside the grant on a deny-posture server — it fails with "tool not granted by any access binding", policy
mcptooldefault:<server>. - Violate an input condition (e.g. a path outside the allowed root) — the call is denied with the condition's message, policy
mcpaccessbinding:<binding>:input.
See also
- Team Access — the model, compiled-policy names, and precedence
- Binding subjects — subject types and derived workload subjects
- Access policy — workload deny lists and model selectors
- Rate-limit MCP tools
- MCP Governance — tool pinning and drift response