TUTORIALS 12 min read

MCP Tool Permissions: Build Boundaries Your AI Agents Cannot Ignore

An MCP server can turn a helpful model into a production operator. This guide designs scopes, approvals, credentials, and hard execution boundaries.

By EgoistAI ·
MCP Tool Permissions: Build Boundaries Your AI Agents Cannot Ignore

Connecting an agent to tools is easy. Preventing it from doing one catastrophic thing at 3 a.m. is the actual engineering job.

MCP tool permissions should determine which resources an agent can see, which actions it can request, which actions require a human, and which actions are impossible even if the model is manipulated. A system prompt is not an authorization layer. It is text the model tries to follow. Real boundaries live in credentials, policy engines, network routes, and the server that executes the action.

This tutorial builds those boundaries from the outside in.

Start With Capabilities, Not Tool Names

A tool called manage_project tells a reviewer almost nothing. Split broad tools into explicit capabilities:

  • project.read
  • issue.create
  • issue.update
  • comment.create
  • deployment.preview
  • deployment.production
  • secret.read
  • billing.modify

Tool schemas should expose intent. A destructive action should not hide behind an optional flag on a harmless-looking method. If delete=true changes the risk class, make deletion a separate tool with a separate scope.

Build a capability matrix for each agent role. A support summarizer might read tickets but never write. A coding agent might create a preview deployment but never promote it. A finance assistant might prepare a payment but never release funds.

Step 1: Enforce Least-Privilege Credentials

Never give an MCP server a universal API key because “the model needs flexibility.” Issue credentials per environment, tenant, user, and agent role where the upstream system allows it.

The authorization server should mint a short-lived token with explicit scopes and an audience restricted to the MCP resource server. The server validates issuer, audience, expiration, and scopes on every call.

{
  "sub": "agent:release-assistant",
  "aud": "mcp://deployments.example.com",
  "scope": "deployment.read deployment.preview",
  "tenant_id": "tenant_42",
  "exp": 1785564000
}

The agent never receives a token that can deploy to production. No prompt injection can invent a missing scope.

If an upstream service offers only a powerful legacy key, put a broker in front of it. The broker accepts narrow requests, validates policy, and performs the smallest upstream action. Do not expose the legacy key to the model process.

Step 2: Separate Read, Prepare, and Commit

High-risk workflows should use a three-stage protocol:

  1. Read: collect facts and current state.
  2. Prepare: generate an immutable proposed action with a preview and idempotency key.
  3. Commit: execute that proposal only after policy or human approval.

For example, a payment tool should not accept recipient and amount and immediately transfer money. A prepare call can return a proposal containing normalized details, fees, account, risk checks, and expiry. The commit call accepts only the proposal ID, not a new amount.

This prevents the model from changing parameters after approval. It also creates a clean audit trail.

Step 3: Bind Human Approval to Exact Arguments

“Approve deployment?” is not enough. The approval artifact should bind to a canonical hash of the tool, arguments, tenant, environment, and expiration time.

{
  "proposal_id": "prop_01J...",
  "tool": "deployment.production",
  "argument_hash": "sha256:8b2...",
  "target": "checkout-api",
  "revision": "9d8f24a",
  "environment": "production",
  "expires_at": "2026-08-01T06:00:00Z"
}

The executor recalculates the hash. Any changed field invalidates approval. Display the human-readable diff, not a blob of JSON nobody will inspect.

Set thresholds. A preview deployment may be automatic. A production deployment may require one approver. A database migration or payment above a limit may require two.

Step 4: Add Resource and Tenant Boundaries

Scopes describe action classes, but resource boundaries describe where those actions apply. Enforce both.

An agent authorized to update issues for repository A must not update repository B by changing an argument. Validate resource membership server-side using the authenticated identity. Never trust a model-supplied tenant_id.

Use allowlists for domains, repository IDs, storage prefixes, and database schemas. For file access, resolve paths and reject traversal after canonicalization. For SQL, prefer predefined operations or read-only connections over arbitrary query strings.

Network egress deserves the same treatment. If a tool can fetch any URL, it can become a data-exfiltration channel. Restrict protocols, destinations, redirects, response sizes, and private network ranges.

Step 5: Design Safe Tool Responses

Tool output can contain prompt injection too. A webpage, issue, or document may tell the agent to reveal secrets or call another tool. Mark content as untrusted data and keep server metadata separate from user-controlled text.

Return bounded fields. Avoid dumping complete environment variables, access tokens, private headers, or enormous logs. A tool can return a summary, cursor, and explicit method for requesting the next safe chunk.

For errors, do not echo secrets or raw upstream responses. Provide a stable error code, safe message, and correlation ID.

Step 6: Make Side Effects Idempotent

Agents retry. Networks retry. Users retry. Every mutation needs an idempotency key and a deterministic outcome for duplicates.

Record the authenticated actor, tool, canonical arguments, policy decision, upstream result, and timestamp. If the same key returns with different arguments, reject it. If the previous call succeeded, return the original result rather than performing the action again.

This is crucial for payments, invitations, messages, deployments, and record creation.

Test the Boundary Like an Attacker

Build tests that attempt to:

  • call a tool without the required scope
  • switch tenant or resource IDs
  • reuse expired approval
  • change arguments after approval
  • traverse a file path
  • follow redirects to a private address
  • inject instructions through tool output
  • replay a mutation with the same idempotency key
  • exceed rate or spend limits

Your success criterion is not that the model refuses. It is that the server refuses even when the model eagerly complies.

Common Permission Mistakes

The ugliest mistake is using one service account for every agent and every user. Close behind it: checking permissions in the client, trusting tool descriptions, logging secrets, and bundling destructive behavior into generic tools.

Another trap is “read-only” access that can still expose sensitive data. Reading customer records, source code, calendars, or cloud logs can be high risk. Least privilege applies to data visibility as much as mutation.

The Takeaway

MCP makes tools interoperable; it does not make them safe by default. Split capabilities, issue narrow credentials, bind approvals to exact arguments, enforce tenant and network boundaries, and make mutations idempotent. The model can suggest. The server decides. That is the permission boundary your agent cannot talk its way around.

Share this article

> Want more like this?

Get the best AI insights delivered weekly.

> Related Articles

Tags

MCPAI AgentsAuthorizationLeast PrivilegeOAuthSecurity

> Stay in the loop

Weekly AI tools & insights.