Skip to guide
ver·tias/ PassControlAll guides

Tooling guide

MCP Server Security: Controlling Agent Tool Access

MCP made agent tooling portable, and in doing so made every MCP server an API surface whose caller is a language model. This guide covers what that changes and which controls belong where.

Written for: Agent builders, MCP server authors and platform engineers

An MCP server is an API whose caller is a model

The Model Context Protocol standardised how an agent discovers and invokes tools, so a single server can serve many clients and a single client can reach many servers. That is the value, and it is also the security change: in ordinary software a function is called from a fixed call site written by a person, while an MCP tool is called because a model decided, at runtime, that the tool matched the situation.

Nothing about that decision is authenticated by the protocol. The server sees a well-formed request for a tool it advertises. Whether the request reflects the operator's intent, a user's instruction, or a sentence a web page smuggled into the agent's context is invisible from inside the server. So the server has to enforce its own limits rather than trusting the caller's judgement, exactly as a public HTTP API does.

Design the server as though the caller can be argued into anything, because the caller can be argued into anything.

Tool descriptions are untrusted input, in both directions

An MCP client places tool names, descriptions and parameter documentation directly into the model's context so the model can choose between them. That text is therefore prompt content supplied by whoever runs the server. A hostile or compromised server can write a description that instructs the model to call it first, to pass along data from other tools, or to ignore an earlier instruction — an injection that arrives through the tool catalogue rather than through a document.

The same applies to results. A tool that returns text fetched from elsewhere is returning attacker-influenced content into the reasoning loop. Neither risk is solved by authenticating the connection, because both travel on a properly authenticated channel.

  • Pin the servers an agent may connect to; do not resolve them from model output.
  • Review tool catalogues on change, and treat a description edit as a code change.
  • Keep tool results out of the privileged instruction position in the prompt.
  • Do not let one tool's output select the next tool without a deterministic check.

Authenticate the agent, not only the human who set it up

Most MCP deployments start with a shared secret in a configuration file, which authenticates the installation and nothing finer. If two agents on one machine read the same config, the server cannot tell them apart, and neither can an audit log. That gap matters the moment one of those agents is doing something a reviewer later wants to attribute.

The useful shape is a per-agent credential the server can resolve to an owner and a policy. Proof-of-possession — the agent signs a challenge with a private key it holds — is stronger than a bearer token, because a bearer token is equally useful to whoever copies it. Where a bearer credential is unavoidable, make it per-agent, revocable and short-lived, and label it as the lower-assurance option rather than describing it as identity.

Scope tools the way you would scope an API key

A server that exposes read_file, write_file and run_command as one permission set has collapsed three very different risks into one grant. Scope belongs per tool, and for tools that take a target, per target: a filesystem server should bound the roots it will serve, and a database server should bound the schemas and the statement types.

Write the scope where the enforcement is, which is the server. A client-side allowlist is a usability feature; it is not a control, because a second client can connect with different settings.

  • Default to read-only, and make write and execute separate, explicit grants.
  • Bound every path, host, schema or resource identifier the tool accepts.
  • Require an out-of-band approval for irreversible actions rather than a confirmation the model can answer itself.

Keep upstream credentials out of the agent and the server process

An MCP server that reaches a paid API usually ends up holding that API's key, which puts a long-lived provider credential inside a process a model can influence. Moving the key behind a gateway that injects it at forward time keeps it out of both the agent and the tool process, so a compromise of either yields a scoped, revocable, observable credential rather than the real one.

This is also what makes revocation meaningful. A key that only exists inside a gateway can be rotated once; a key that has been copied into several MCP configurations has to be found first.

Log the tool call, not just the conversation

Model transcripts are a poor audit record: they are long, they contain content the operator may not be entitled to retain, and they describe intent rather than effect. The durable record is the call — which agent, which tool, which arguments in a bounded form, which decision the server made, and when.

If that record is signed, a third party can check it without being given access to the system that produced it. That is the difference between an operator saying an agent stayed inside its limits and an operator being able to show it.

Frequently asked questions

Short answers to the questions that matter.

Is MCP itself insecure?

No. MCP is a protocol for describing and invoking tools; it does not decide who may connect or what a tool may reach. The risk lives in what a given server exposes, who can reach it, and whether tool metadata is treated as trusted text. Those are deployment decisions, and they are the ones this guide covers.

Does adding OAuth to an MCP server solve agent identity?

It solves part of it. OAuth establishes that a legitimate client obtained a token on behalf of an account, which is useful and worth doing. It does not by itself distinguish two agents running under the same account, and a bearer token is equally usable by anything that copies it. Per-agent credentials, ideally proof-of-possession, are what make attribution and revocation precise.

What is the minimum for a local stdio MCP server?

Bound what the tools can reach — roots, hosts, statement types — keep any upstream API key outside the server process, and record each tool call somewhere the operator can read later. A local server skips network authentication, but it does not skip scope, credential isolation or evidence.

How do I stop a tool description from steering the model?

You cannot make the model ignore text it is shown, so control which text it is shown. Pin the set of servers, review catalogues on change, keep tool output out of the instruction position, and put the decisions that matter — authorization, spend, irreversible actions — in deterministic code outside the model loop.

Primary sources

Further reading from standards and security bodies.