MCP integrations: tool permissions and authorization boundaries
By the SynapseTel Cloud principal engineers · AI Agents & MCP Integration · Published August 12, 2026 · 7 min read
Plan MCP tool schemas, token audiences, per-client tool allowlists, downstream credentials, approvals and recovery boundaries before exposing an internal system to an AI host.
Scope and applicability
This note applies to teams exposing an internal system, such as a network manager, a ticketing system or a Kubernetes cluster, to an AI application through a Model Context Protocol (MCP) server, and to teams running the hosts and clients that connect to it. It follows the MCP 2025-11-25 specification for tools, transports and authorization. It does not argue that every workflow needs MCP. A fixed API client with a schema is often the right integration, and this note helps decide which controls are needed when MCP is chosen.
The seam runs through all three layers. The system being exposed lives in T1 or T2 and keeps its own authorization model. The MCP server runs as a T2 workload under identities the platform team issues. The host, the client and the reviewer interface are T3 applications. The integration team owns the server and its tool contract; the system owner decides which operations may ever be exposed; the platform team owns the identities and the network path between them.
This is an engineering reference, not a report of a customer deployment and not a security certification. The policy below is an example contract; the client identifiers, tool names and token claims are set per environment and reviewed as security-sensitive changes. Lock and test the actual MCP SDK, authorization server and policy engine versions before adoption.
Where the handoff seam breaks
Token passthrough is the first and most common failure. A server that forwards the client’s access token to the downstream system turns itself into a confused deputy: the downstream API sees a token minted for a different audience and cannot tell which client, which tool or which approval stands behind the call. The MCP security guidance is explicit that the server must not accept tokens that were not issued for it, and the server’s downstream credentials must be its own, scoped per tool.
Tool descriptions are the second seam. A host reads the description to decide when to call a tool, and the description is text under the server owner’s control, not the host’s. A description that changes after the host was approved, or that carries instructions aimed at the model, is a supply-chain problem. Pin the tool list and descriptions the host was approved against, and treat a change as a new approval.
Confirmation dialogs are the third seam. A user-interface prompt that asks whether the model may annotate a ticket is a usability feature; it is not server-side authorization. If the server executes a write tool because the client said the user agreed, the server has delegated authorization to text. The approval has to be a record the server can verify independently: who approved, which proposal, until when.
Transports and sessions are the fourth seam. A stdio server started on an engineer’s workstation has no network endpoint and no authorization server; an HTTP server does, and a session identifier is not a credential. Exposing a stdio server through an HTTP bridge without adding authorization is how a local development convenience becomes an unauthenticated write path to a production system.
Pre-flight and validation matrix
Run the matrix against the server in a non-production environment with production-shaped tokens, then again before any new client or tool is added. A row without an evidence reference is a failed row. The commands are read-only; the roles name who signs the evidence.
| Check | Command or role | Pass criteria | Stop condition |
|---|---|---|---|
| Protected resource metadata and resource indicators | curl -s https://mcp.example.invalid/.well-known/oauth-protected-resource, read-only; security reviewer | Metadata names the authorization server and the resource identifier clients must request tokens for | Metadata missing or a resource identifier shared with another API |
| Token audience and issuer validation | Negative test with a valid token minted for a different resource; security reviewer | The server rejects tokens for any other audience and any other issuer | A token for another audience is accepted |
| Per-client tool allowlist | Policy unit tests for every client and tool pair; integration engineer | Each client can call only the tools in its allowlist and nothing else | A client can call a tool outside its allowlist |
| Input schema validation and size limits | Oversized, malformed and unknown-field requests against each tool; integration engineer | Every bad request is rejected with a recorded reason before any downstream call | A malformed request reaches the downstream system |
| Downstream credentials scoped per tool | Credential inventory and kubectl auth can-i --list for the server identity; platform engineer | The server holds its own credentials, scoped to the operations each tool performs, and never forwards a client token | A client token is forwarded or one credential covers every tool |
| Write tools gated on a verifiable approval | Call a write tool with no approval, an expired approval and a self-approval; named reviewer | All three are refused and the refusals are logged | Any of the three succeeds |
| Audit and egress | One approved and one refused call traced end to end; network egress rules reviewed; security reviewer | Client, tool, arguments digest, decision and outcome recorded separately, and egress limited to the downstream systems the tools need | Any record missing or unrestricted egress from the server |
Inspectable artifact: deny-by-default tool policy
The policy below is evaluated by the server for every tool call, outside the model and before any downstream request. It allows a call only when the token was issued by the expected authorization server for this server’s resource identifier, the client identifier in the request matches the token, the tool is on that client’s allowlist, and, for tools that change state, an approval record exists that names the same proposal, was made by someone other than the calling client and has not expired.
Open Policy Agent is one engine; the same rules can live in the server’s own code or in a gateway. What matters is that the decision is deterministic, versioned in Git with tests for every denial path, and that a tool description, a prompt or a model output has no input into it.
# tool_authorization.rego -- evaluated by the MCP server before any tool runs.
# Deny by default. Nothing in a tool description, a prompt or a model output changes the decision.
package mcp.authz
import rego.v1
default allow := false
# Clients the server knows, keyed by the OAuth client identifier the access token carries.
known_clients := {
"noc-assistant": {"tools": {"alarm_list", "alarm_detail", "ticket_search"}},
"release-preflight": {"tools": {"preflight_validate"}},
}
# Tools that change state in a downstream system. They need a recorded approval.
write_tools := {"ticket_annotate", "alarm_acknowledge"}
allow if {
token_valid
input.tool in known_clients[input.client_id].tools
not input.tool in write_tools
}
allow if {
token_valid
input.tool in write_tools
input.tool in known_clients[input.client_id].tools
approval_recorded
}
token_valid if {
input.token.iss == "https://auth.example.invalid"
# RFC 8707 resource indicator: the token was issued for this server, not for a downstream API.
input.token.aud == "https://mcp.example.invalid"
input.token.exp > time.now_ns() / 1000000000
input.client_id == input.token.client_id
}
approval_recorded if {
input.approval.proposal_id == input.arguments.proposal_id
input.approval.approver != input.client_id
input.approval.expires_at > time.now_ns() / 1000000000
}Rollback and recovery boundaries
The server and its policy are control-plane state. Rolling back means redeploying the previous policy revision from Git, revoking the client registrations or tokens that should no longer work, rotating the server’s downstream credentials if they may have been exposed, and removing the route. Rehearse it; the measured time to revoke a client is the number the incident plan needs.
State written downstream is the other boundary. A ticket annotated, an alarm acknowledged or a resource patched through a write tool persists in the downstream system after the server is gone and is governed by that system’s own rollback. The server’s audit log, keyed by proposal identifier, is the only way to find those records later, so preserve it before any cleanup and name who reviews the writes when a client is withdrawn.
Read-only tools still leave a boundary: cached results, retrieved documents and logs may contain data from the downstream system. Set retention for them and treat them with the classification of the source, not of the server.
Write the recovery authority down: who can revoke a client, on which signals, who rotates credentials, and who owns the downstream reconciliation for each write tool.
How we approach it
Bring the system you intend to expose, the operations you would allow, the clients that would call them and your authorization server to the discussion. The Assessment Sprint produces the tool contract, the policy with its denial tests and the recovery boundaries in writing. The server implementation, the host integration and ongoing operation are scoped separately.