BlogSecurity
Giving an MCP server only the permissions its tools need
To connect an assistant to internal systems without broad access, decide permissions at three points: the scopes on the token the client presents to the MCP server, a check inside each tool before it acts, and the credential the server uses for the business system behind it. Broad scopes alone are too coarse. One shared service account also puts the combined privileges of every tool into a single credential, so a bypassed check or a leaked key exposes everything that account can reach.
How a server ends up with broad access
A common path to an over-permissioned server looks like this: a team wraps an internal API for a demo, hands the server the key it already had, and the assistant can now do everything that key allows. Typical signs in a review:
- One service account or API key shared by every tool, sometimes with write or admin rights
- A single omnibus scope requested once and never narrowed
- Tool annotations such as readOnlyHint treated as if they enforced something
Annotations are hints. The MCP tools specification says clients must treat them as untrusted unless they come from trusted servers, so enforcement belongs in the server and the systems behind it.
Three places where access is decided
Authorization is optional in MCP. For a remote server that you protect with the HTTP authorization flow, the current specification treats the server as an OAuth resource server that must reject tokens issued for any other audience. A local server started over stdio is outside that flow: the specification says it should take credentials from its environment, so the scoping work moves to whatever credential you put there.
- 1
Token scopes at the MCP server
Scopes say which groups of tools a client may attempt. The current specification recommends a minimal starting set and, when a tool needs more, a 403 response with an insufficient_scope challenge naming the required scopes. Check that the client and server revisions you deploy support this step-up, since clients built for older revisions may not.
- 2
A check inside each tool
Before acting, the tool confirms that this user may perform this action on this record. The MCP security guidance calls treating token scopes as sufficient, without server-side authorisation logic, a common mistake.
- 3
The downstream credential
The specification forbids passing the client's token through. The server calls the business system with its own credential from that system's authorisation server, acting for the user or as a narrow service identity.
These layers do not chain automatically. A valid MCP token gives the server no rights in your CRM; acting there for the user needs a separate grant from the CRM's own authorisation server, with its own scopes.
A permission matrix for one hypothetical server
Suppose a support team wants its assistant to work with the helpdesk and the billing system. Before writing code, we would agree a matrix like this with both system owners. Tool names and scopes are invented.
- search_tickets: scope support.read; helpdesk called with a token delegated by the requesting support employee, so results stay within queues that person already sees
- get_invoice_status: scope billing.read; read-only service identity for the billing API, plus a check that the invoice belongs to a customer on one of the caller's tickets
- draft_ticket_reply: scope support.write; posts nothing and returns a draft for the support employee to edit
- post_ticket_reply: scope support.write; delegated helpdesk token; the support employee approves the exact reply, and the server posts it only if a stored approval matches that reply, so a direct call or an edited text is refused
- issue_credit_note: not exposed; it stays in the billing application with its existing four-eyes check
The last row is a decision too. Some actions cost less to keep outside the assistant than to secure inside it, and the matrix shows that to the people who own the risk.
What each tool verifies before it acts
A broad scope such as support.read does not settle whether this caller may open this particular record. Each tool should take the caller's identity from the verified token, never from an argument the model supplied, and check the action against the record:
- Object-level checks on every identifier, including IDs taken from an earlier tool result
- Server-side limits on amounts, date ranges and result sizes, independent of the input schema
- State handles bound to the authenticated user, since possession of a handle must not count as authentication
- Rate limits per user and per tool
- A log entry with user, client, tool, decision and target record, without tokens or secrets
In code, that is usually one authorisation function per tool, called before any downstream request and tested against each row of the matrix.
What the business system should see
Audit trails break when the downstream system cannot tell who acted, which is one reason the MCP security guidance forbids forwarding a client's token. With a delegated token, the helpdesk sees the support employee and applies its own permission model. With a service identity, it sees only the MCP server, so the server's log must record the service identity and, for interactive calls, the person behind them and the identity's rights must be no wider than the tools that use it. Separate identities per server or tool family also mean that revoking one does not stop the rest.
Costs and limits of fine-grained permissions
Finer scopes add consent prompts and identity provider configuration, and some internal APIs offer nothing narrower than one key. A small proxy with its own rules can then sit in front of the API, or the tool stays read-only until narrower credentials exist.
Narrow permissions limit the damage of a mistake or a successful prompt injection; they prevent neither. Approvals and output checks reduce the remaining risk of a permitted action going wrong, but they do not remove it.
Coding agent security for engineering teamsMCP server development
A practical first step: write the matrix for the one or two tools your team needs first, and review it with the owner of each system they touch.
Questions
Can our identity provider handle all of this with scopes?
It handles the coarse part: which clients reach which server and which groups of tools. Some identity providers can issue finer scopes or claims, but whether a support employee may see one particular customer's invoice usually depends on data in the business system, so the tool or that system still checks each record.
Is a local stdio server safer because it never leaves the laptop?
stdio itself opens no HTTP listener, but it is a transport, not a sandbox. The server process can still call APIs over the network and runs with the credentials in its environment and the privileges of the user who started it. A personal admin token there makes the local server as powerful as its owner.
How do we know the permissions work as designed?
Write negative tests: no token, a token for another audience, a valid token without the needed scope, a valid user asking for someone else's record, and a write call with no matching approval or with a changed reply. Each should fail in the expected way before the server reaches users.
Related services
MCP server development
MCP servers that connect approved internal APIs to AI assistants, with narrow permissions, approval for changes and tests.
AI engineering and architecture
Assistants, voice interfaces and agents in the products you sell, with the serving and retrieval underneath.
AI agents and process automation
Agents that take on the routine steps of complex cases and pass decisions to people, with a cost per handled case.
More from the blog
MCP and enterprise identity
Whose identity an MCP tool should use when it calls a business system: user delegation, token exchange or a service identity, with the token boundaries to check.
MCP tool design
Why an MCP server should usually offer a few tools shaped around business actions instead of mirroring every API endpoint, with a worked example and a tool contract.
MCP server test plan
A test plan for an MCP server before users connect: contract tests, negative authorisation tests, failure tests and state-change tests, with checks that run in CI.
Coding agent security
Practical security and governance for coding agents: prompt injection, secrets, permissions, sandboxing, unattended runs and ownership.
Talk to an engineer
Bring one piece of work you would like to improve. The call takes 30 minutes and needs no access to your systems or your data.
Booking runs on Google Calendar.
Prefer to write first? Leave your email and a few lines, and we reply with times.
Look for the draft in your email program
Send it from there, and an engineer replies in person with times for a 30-minute call. If no draft has opened, write to us at miki@cloudsail.com.