How to security-review MCP tools before production
Reviewing MCP tool surfaces before agents can call them. Wildcard exposure, scope creep, missing approval policies, schema strictness — what good and bad look like.
The reason teams adopt the Model Context Protocol is the same reason MCP needs a release review: connecting a new tool surface to an agent takes about five minutes. The five minutes you save on integration are the five minutes you’d otherwise have spent on review.
Most teams skip that review entirely. This post walks through how to do it deliberately, with the MCP filesystem server as the worked example. The same five questions apply to any MCP server you’re about to connect.
What MCP gives you and what it doesn’t
MCP standardizes how tool inventories are exposed, how tool calls flow between agent and server, and how errors come back. That standardization is genuinely useful — it’s why the protocol has adoption.
MCP does not validate that the tools are safe for your agent to call. It doesn’t limit which operations are appropriate at runtime. It doesn’t tell you whether the schemas are too loose, whether the inventory is too broad for the agent’s declared purpose, or whether destructive tools need approval gates.
That gap is what a release review fills. The protocol is necessary infrastructure; the review is the static check you run before any agent gets access to a server’s tools.
The five questions to ask about any MCP server
For any MCP server you’re about to connect to a tool-using AI agent:
- Inventory. What tools does this server expose, by name? Do you need all of them?
- Schema. What does each tool’s input schema actually accept? Strict, bounded, named fields — or freeform JSON the model can pack with anything?
- Auth scopes. What scopes does each tool need? Are the scopes declared in your manifest narrower than the server’s actual permissions?
- Approval and confirmation. Which tools have side effects? Which require human approval before firing? Which require customer confirmation?
- Idempotency. Can a write be retried safely? If a transient network blip causes a retry, does the same operation fire twice?
These five map onto the seven dimensions of tool-use readiness. For MCP specifically, the first four show up in almost every release review; idempotency is the one teams most often miss until production.
Worked example: reviewing the MCP filesystem server
Imagine you’re connecting @modelcontextprotocol/server-filesystem to
a customer-support agent. The agent needs to read support tickets
uploaded under /tickets/ and summarize them for a human reviewer.
It does not need to write, edit, move, or delete files.
Run through the five questions on this concrete surface.
1. Inventory
The MCP filesystem server (@modelcontextprotocol/server-filesystem,
late-2026 release) exposes 13 tools:
read_text_file,read_media_file,read_multiple_fileswrite_file,edit_filecreate_directory,move_filelist_directory,list_directory_with_sizes,directory_treesearch_files,get_file_infolist_allowed_directories
(Earlier versions split read_text_file and read_media_file out of a
single read_file tool. If your snapshot was taken from an older
server, the inventory will look different — pin the version in your
MCP export so future reviewers know which surface they are reading.)
The release-readiness question isn’t “is this server safe” — it’s
“is this surface what the agent needs?” For a read-only ticket
lookup, the agent needs read_text_file, list_directory, and
search_files. Everything else is over-grant.
Inventory finding: ten tools the agent doesn’t need are part of its declared surface.
2. Schema
Each filesystem tool takes a path parameter. The schema accepts
any string. The MCP server enforces a configured set of allowed
paths at startup — but that boundary lives in the server config, not
in the manifest the release reviewer reads.
For the review, what you want is:
pathfield present and required on every tool that takes one.additionalProperties: falseso the model can’t pack unexpected fields into the call.- The allowed-paths configuration is referenced in the manifest so a reviewer can see what slice of the filesystem the agent can touch.
Schema finding: schemas are strict by default in the MCP server, but the path scope lives outside the manifest. Add it explicitly.
3. Auth scopes
For MCP filesystem, “auth scope” doesn’t mean an OAuth scope — it means which paths the server is configured to expose, and which of those paths the agent’s manifest declares it needs.
Two scope failures show up in practice:
- The server is configured with broader paths than the agent’s declared purpose covers. The agent says “tickets only,” the server is configured for the entire repo.
- The manifest doesn’t pin which subdirectory the agent reads from. A future change to the server config silently expands the agent’s access.
Scope finding: pin paths in both the server config and the manifest; the release reviewer should be able to read both and confirm they match.
4. Approval and confirmation
For the read-only customer-support use case, the agent doesn’t need any of the write/edit/move tools. If you keep them in the surface — even with the intent that “the model just won’t use them” — each one needs an explicit approval policy.
The cleaner path is to configure the MCP server itself to expose only
the tools the agent needs (most MCP servers, including filesystem,
support a tool allowlist or read-only flag at startup). The exported
snapshot then contains only the safe tools. Declare the broader
intent in the agent’s prohibited_actions so a future reviewer can
confirm the boundary without re-deriving it from the server config:
agent:
name: customer-support-agent
prohibited_actions:
- write or modify any files on disk
- create, move, or list directories outside /tickets/
Approval finding: write tools that survive into the MCP snapshot
without an approval policy are critical findings under
SHIP-POLICY-APPROVAL-MISSING.
5. Idempotency
write_file overwrites by default. A retry overwrites a file you
may not have wanted to overwrite again. If you keep the tool on the
surface, either mark it as not safe to retry or require an
idempotency key as part of the schema.
For the read-only support agent, this is moot — we’re removing the write tools entirely. But it’s the kind of finding that survives unnoticed when a team keeps “just in case” tools on the surface.
What good looks like
The shipgate.yaml manifest after a clean review:
version: "0.1"
project:
name: customer-support-agent
agent:
name: customer-support-agent
declared_purpose:
- "Read customer support tickets from /tickets/ only"
- "Summarize ticket history for a given customer"
prohibited_actions:
- write or modify any files on disk
- create, move, or list directories outside /tickets/
environment:
target: production_like
tool_sources:
- id: filesystem
type: mcp
path: mcp-exports/filesystem-server.json
risk_overrides:
tools:
filesystem.read_text_file:
tags: [read_only, pii_access]
owner: customer-support-platform
reason: reads support tickets which may contain customer PII; path scope /tickets/ enforced by MCP server config
filesystem.list_directory:
tags: [read_only]
owner: customer-support-platform
reason: lists /tickets/ contents only; path scope enforced by MCP server config
filesystem.search_files:
tags: [read_only]
owner: customer-support-platform
reason: searches within /tickets/ only; path scope enforced by MCP server config
ci:
mode: advisory
pr_comment: true
output:
directory: agents-shipgate-reports
formats:
- markdown
- json
- sarif
This manifest answers all five questions: the inventory is narrowed
(the MCP export only contains read tools because the server was
started in read-only mode), the path scope is documented in
declared_purpose and repeated in each tool’s reason, the PII tag
on read_text_file makes the access classification explicit,
prohibitions are written down in prohibited_actions, and there are
no writes left to need approval or idempotency policies.
What bad looks like
The same agent without the manifest narrowing — the surface as the MCP server exports it, with no policies declared:
Status: release_blockers_detected
Critical: 4 · High: 8 · Medium: 2
[critical] filesystem.write_file lacks a declared approval policy
[critical] filesystem.edit_file lacks a declared approval policy
[critical] filesystem.create_directory lacks a declared approval policy
[critical] filesystem.move_file lacks a declared approval policy
[high] filesystem.write_file may be retried without idempotency evidence
[high] filesystem.edit_file may be retried without idempotency evidence
[high] filesystem.write_file lacks declared auth scopes
[high] filesystem.edit_file lacks declared auth scopes
[high] filesystem.create_directory lacks declared auth scopes
[high] filesystem.move_file lacks declared auth scopes
[high] filesystem.read_text_file is not tagged with pii_access risk
[high] Tool surface inventory broader than declared_purpose
[medium] Prompt lacks confirmation language for write/edit actions
[medium] No CI release-gate workflow detected
Fourteen findings, every one of them legitimate. None of them would be caught by an eval suite — the eval set tests behavior on read-only ticket lookups, which is what the prompt asks for, and the model dutifully reads tickets. The unsafe surface lives in the artifact, not in the runtime behavior the evals exercise.
When to run this review
Trigger the review whenever any of the following appear in a PR:
- A new MCP server is added to
tool_sources. - An MCP server’s version is bumped (could add or change tools).
- The server’s allowed paths, scopes, or configuration change.
- The agent’s
declared_purposeis expanded. - The prompt changes in ways that imply broader tool use.
This is the same trigger surface as the broader agent deployment checklist — MCP changes are the most common source of net-new findings on most agent codebases in 2026.
Automating the review
The five questions above are all deterministic. You can run them against your repo:
pipx install agents-shipgate
agents-shipgate init --workspace . --write
agents-shipgate scan -c shipgate.yaml
The scanner reads your MCP export (the JSON snapshot of the server’s declared tools, schemas, and descriptions) plus your manifest, and emits the finding list above. Static analysis only — it does not connect to MCP servers, does not invoke the agent, does not call any model.
For CI, add the GitHub Action in advisory mode first so the team sees MCP-related findings on every PR without blocking merges:
- uses: ThreeMoonsLab/agents-shipgate@v0.8.0
with:
config: shipgate.yaml
ci_mode: advisory
pr_comment: "true"
MCP gateways live downstream of this. Once the surface is reviewed and bounded, runtime enforcement can decide whether any specific call should proceed. The review and the gateway are complementary — see Agents Shipgate vs MCP gateways for the fuller comparison.
The shape of a good MCP review
What you want to leave a review with: a manifest you can hand to a security reviewer, the reviewer can read in three minutes, and they can point at every declared tool, scope, policy, and owner. No wildcards, no “trust the server,” no “the prompt will steer it.” The surface is what the surface is, and it’s the surface the agent gets.
That’s release-readiness for MCP tool surfaces. Five questions, one manifest, one CI check.