Skip to main content

Control MCP Tool Access

Tasks

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

  1. Go to Setup → Settings → Access Bindings and edit the binding (or create one — see Create a Team Access Binding).
  2. Under Tools, add one or more rows. Each row names servers and tools:

    To expressServersTools
    Two tools on one servergithub-mcpget_issue, list_pull_requests
    Every read-style tool on every server*read_*
    A whole server (same as no tool rows)github-mcp*

    Tool names are the per-server names shown in the tool inventory. Simple globs use * as the only wildcard.

  3. Optionally add subjects beyond teams under Additional subjects — an individual user, an agent ID (a trailing wildcard like ci-runner-* is allowed), or an agent label like env=prod.
  4. 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.

Stage before enforcing

Set the binding's decision mode to Audit first and watch Security Events; flip it to its intended mode once the matches look right.

Deny specific tools

Create a binding with decision mode Deny and tool rows naming what to block (e.g. delete_repository, transfer_*). A deny always wins — even if another binding grants the same tool to the same caller.

Add input and output conditions

In the binding editor:

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

warning

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, and which binding subjects are granted it. Filters: ?status=never_called, ?status=drifted, ?status=new, and ?q=<substring>.

Verify

  1. As a granted caller, invoke a selected tool — it succeeds; the Security Events entry records the binding.
  2. Invoke a tool outside the grant on a deny-posture server — it fails with "tool not granted by any access binding", policy mcptooldefault:<server>.
  3. 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