Skip to content
Cloud Security DeskSearch
Menu

Technical guideAI systems

Admit MCP servers before agents can call their tools

An MCP server's tool descriptions can change after approval with no new release. Record provenance and execution context, hash the definitions per authorization context, enforce the pin at a gateway or allowlist, and watch for drift.

Published
Sources checked
Next review
Reading time
20 minutes
Coverage
MCP · AWS · Anthropic · GitHub · Microsoft Azure · Google Cloud · OWASP
A low barrier with a raised arm and a clipboard panel on its post. Four small circles wait in a queue on the left. Past the barrier on the right, one shape that entered as a circle has become a jagged red form, while a thin blue line from the clipboard still points to the dashed outline of the circle it was approved as.
Conceptual illustration: the approval record still describes the tool that passed review, while the tool itself has changed shape since.

An admission procedure for platform and security engineers who approve MCP servers for agents, built on MCP specification revision 2026-07-28, MCP Registry and OWASP guidance, three arXiv preprints and AWS, Anthropic, GitHub, Microsoft and Google documentation reviewed October 9, 2026. It covers the evidence to collect, canonical hashing of tool definitions per authorization context, what each enforcement point actually holds, drift detection and how far scanner results can be trusted.

At a glance

Key findings

  • Under MCP revision 2026-07-28 a server's tools/list may change over time and may differ by the authorization on each request, deterministic ordering is only a SHOULD, and the Tool type has no signature or integrity field, so pinning tool definitions is work the consumer adds. [1][2]
  • The official MCP Registry is still in preview. Its namespace authentication shows who published the metadata, while security scanning is delegated to package registries and aggregators and consumers are told to assume minimal-to-no moderation. [3][4]
  • AgentCore Gateway's default listing mode serves a catalog captured when a target is created and refreshed only when it is updated or SynchronizeGatewayTargets runs; Claude Code and GitHub Copilot allowlists match server URLs and launch commands, and their MCP administration pages describe no setting that pins tool descriptions. [5][6][7]
  • In the MCPZoo preprint, eight scanners flagged 96.89 percent of 37,288 runnable servers yet none was flagged by all eight, and manual validation put mean precision at 45.53 percent and mean recall at 24.17 percent. [8]
  • A listChanged notification is something the server chooses to send, so drift detection has to re-list each approved authorization context and compare hashes rather than wait to be told. [1][9]

What admitting a server decides

Admitting an MCP server approves three things at once: the code or endpoint that runs, the authority it runs with, and the text the model reads when it chooses a tool. The first two behave like any third-party dependency. The third does not. Under the MCP revision dated July 28, 2026, a server answers tools/list with whatever set is currently available, that set may change over time and may differ with the authorization on the request, and the tool definition has no field for a signature or digest. A server can change what every connected agent reads with no new package version, no deploy on your side and no registry update. [1][2]

The admission record built here has six parts. Provenance says who published the server and which bytes or endpoint were reviewed. Execution context says what the server can reach: a local stdio process inherits the client's privileges, while a remote server needs an authorization review. A snapshot of the tool definitions, canonicalized and hashed once per authorization context, records exactly what the model was shown. Scanner and manual findings enter as evidence, each with its known limits. The enforcement point names the component that makes the approval binding. Re-admission triggers list the changes that send the server back to review, and a drift monitor watches for them.

Two neighboring subjects stay out of scope because other guides already cover them: token audience, resource indicators and consent for remote servers, and the mapping of agentic supply-chain risk to cloud controls under OWASP's ASI04 entry. Per-call authorization of arguments and destinations, which still applies after a server is admitted, belongs to the agent's tool policy. Sources were reviewed on October 9, 2026, against MCP revision 2026-07-28; product behavior is described only as far as the cited documentation states it.

Figure 01

Six layers of an MCP server admission record

Only the tool-definition layer captures what the model reads, and it can change without any change to the layers above it. [1][3]

Flowchart of six admission layers in order from publisher to runtime: registry entry, package or endpoint, execution context, tool definitions, enforcement point and drift monitor, each with what it establishes and what the record stores.

Source. Conceptual model based on the MCP 2026-07-28 tools page, the security guidance page in the MCP documentation, the MCP Registry documentation and OWASP's third-party MCP server guide. [1][12][3][14]

Method. Conceptual. Layers run from the publisher toward runtime; each row names what the layer establishes and what the admission record stores for it.

Accessible table and figure data
Figure 1 accessible table
LayerEstablishesRecord
Registry entryWho published the metadataServer name, registry version, status
Package or endpointWhich code or URL runsDigest, exact command, endpoint, operator
Execution contextWhat the server can reachSandbox, paths, egress, credentials
Tool definitionsWhat the model readsCanonical hashes per authorization context
Enforcement pointWhat agents can reachGateway snapshot, allowlist or allowed_tools
Drift monitorWhether anything changedRe-list diffs, registry status, expiry
Figure 1 accessible table
LayerEstablishesRecord
Registry entryWho published the metadataServer name, registry version, status
Package or endpointWhich code or URL runsDigest, exact command, endpoint, operator
Execution contextWhat the server can reachSandbox, paths, egress, credentials
Tool definitionsWhat the model readsCanonical hashes per authorization context
Enforcement pointWhat agents can reachGateway snapshot, allowlist or allowed_tools
Drift monitorWhether anything changedRe-list diffs, registry status, expiry

Threats that arrive with a server

Invariant Labs named the core attack on April 1, 2025. In a tool poisoning attack, instructions that the user never sees sit inside a tool description that the model reads in full. Their demonstration used an addition tool whose description told the model to read the client's MCP configuration file and an SSH private key and pass both through an extra argument, while it explained arithmetic to the user. When they ran it in Cursor, the confirmation dialog showed a summarized call and hid the argument carrying the key. [10]

The same disclosure described two variants that shape admission. A rug pull is a server changing a tool description after the client approved it, so a clean review at installation says nothing about the following week. Shadowing is a description that changes how the agent uses a different server's tool; their test redirected mail sent through a trusted send_email tool to the attacker. Combined, the two let a malicious server steer an agent without ever being called, which means reviewing one server in isolation can miss what it does to the servers connected beside it. [10]

Models do not reliably resist this. MCPTox, an arXiv preprint whose second version was posted on September 29, 2026, built 1,348 malicious test cases on 353 real tools from 45 live servers and ran them against 20 LLM agent settings. The highest attack success rate it reports is 72.8 percent, for o1-mini; the highest refusal rate is under 3 percent; and the authors observed that more capable models were often more susceptible, because the attack exploits instruction following. A reviewer who expects the model to notice an odd description is counting on behavior the benchmark did not find. [11]

Local servers bring a threat that comes before any text. The MCP project's security guidance page, which the MCP site publishes with its documentation rather than as part of the 2026-07-28 specification, lists a malicious startup command planted in a client configuration, a malicious payload inside the server, and DNS rebinding against a server left listening on localhost, and it notes that such a server can run commands with the MCP client's privileges. For a stdio server, admission is first a decision to run someone else's code on a developer workstation or an agent host. [12]

What revision 2026-07-28 specifies and leaves open

The July 28, 2026 revision made MCP stateless. It removed protocol-level sessions and the Mcp-Session-Id header, dropped the initialize handshake so that every request carries its protocol version and client capabilities in _meta, required servers to implement server/discover, and replaced the HTTP GET stream and resource subscriptions with a single subscriptions/listen stream. It also made ttlMs and cacheScope required on list results and asked servers, as a SHOULD, to return tools in a deterministic order. [13]

Read together, these rules give a client the means to keep a tool list fresh and none to tell whether it is still the list someone approved. No page of the 2026-07-28 specification, including the tools and schema pages, addresses tool poisoning, rug pulls or the integrity of descriptions. That security guidance page, which sits in the MCP documentation rather than in the specification, has sections on confused deputies, token passthrough, server-side request forgery, state handle hijacking, local server compromise and several OAuth attacks, and it is silent on those topics too. Pinning guidance comes instead from Invariant Labs and from OWASP's GenAI Security Project. [1][2][12][10][14]

Parts of the OWASP material predate the stateless revision, so translate them rather than apply them literally. The guide to using third-party servers, version 1.0 dated October 23, 2025, suggests new connections or sessions for distinct operations, and the server development guide, version 1.0 from February 2026, recommends resetting MCP sessions between tasks and keeping state in a session-keyed store. Revision 2026-07-28 has no protocol session to reset, and a tool list may no longer vary per connection. The nearest equivalent now is the authorization context on each request plus explicit state handles, which is why the snapshot below is taken per authorization context. [14][15][1]

The development guide also asks server authors for a signed manifest per tool, covering its description, schema, version and required permissions, verified at load time. The Tool type defines no field for that signature and the protocol defines no way to verify one. A publisher could put a signature under its own key in the open-ended _meta object, but no client is required to look for it. Until a revision standardizes a field, a signature has to travel by an arrangement outside the specification, for example beside the publisher's release artifact, and the working control stays a hash that you compute yourself. [15][2]

What MCP revision 2026-07-28 says about the properties an admission review relies on, from the specification pages reviewed October 9, 2026; the local launch row comes from the security guidance page in the MCP documentation, which is not part of the specification. [1][9][2][16][12]
PropertyWhat the revision specifiesWhat it leaves to you
Tool list contentsMAY change over time and MAY vary by authorization; MUST NOT vary per connectionAny version or revision number for the list
OrderingSHOULD be deterministicA canonical form for hashing
Change signalSHOULD notify a subscriptions/listen streamA diff, or any signal from a silent server
FreshnessttlMs is a hint; data MAY change before expiryAny trust meaning
Shared cachespublic results may be served to any userChecking that a public list is the same for everyone
IntegrityNo signature or digest field on ToolPinning and verification
AnnotationsMUST be treated as untrusted unless the server is trustedDeciding which servers count as trusted
Server identityserverInfo is self-reported; SHOULD NOT drive security decisionsBinding the server to an operator
Local launchDocs page, not the specification: one-click setup MUST show the commandBuilding the sandbox

Evidence to collect before approval

Start with provenance, and be exact about what it proves. The official MCP Registry is still in preview, with breaking changes or data resets possible before general availability. Its trust mechanism is namespace authentication: a name such as io.github.example-org/docs-search can be published only by whoever controls that GitHub account, and a reverse-DNS name only by whoever proves control of the domain through a DNS record or an HTTP challenge. The registry says it focuses on that authentication and on hosting metadata, and it delegates security scanning to package registries and downstream aggregators. [3]

The moderation policy is blunter still. Consumers should assume minimal-to-no moderation; maintainers remove illegal content, malware, spam and servers that do not work, but not buggy servers or servers with security vulnerabilities, and a removed server keeps its metadata with status set to deleted. Because each published version string is unique and its metadata cannot be edited afterwards, the registry version you reviewed is a stable reference worth recording, and its status is worth polling later. [4][17]

Then fix the bytes. For npm, PyPI, NuGet, Cargo and OCI packages, the registry checks that the package points back to the server name, through mcpName in package.json, an mcp-name: string in the README or an io.modelcontextprotocol.server.name image annotation. That ties a package to a namespace; it says nothing about the code. An OCI identifier may be given as a digest instead of a tag, and an MCPB bundle must declare a fileSha256, which the registry does not validate but clients check before installing. Record the digest or exact version that was reviewed, never a range or latest. [18]

Execution context comes next. For a local server, record the exact launch command and arguments, the runtime, the directories and network destinations the process may use, and every credential it receives through its environment. The MCP documentation's security guidance page requires clients that offer one-click local setup to display that command without truncation and to get explicit approval, and recommends a sandbox with minimal default privileges plus explicit grants for any directory or network access. For a remote server, record the endpoint, the operator behind it and the credentials it uses downstream; the token and consent review for that relationship is separate work. [12]

Then capture everything the model will read: the full tools/list result, including the nested property descriptions inside each inputSchema, any outputSchema and the annotations, plus the optional instructions string that server/discover returns as guidance for LLMs. The serverInfo name and version in that response are self-reported, and the specification tells clients not to base security decisions on them, so they go in the record as labels rather than evidence. [1][16]

Descriptions can read as harmless while the server demands far more. Hasan and colleagues' study of 1,899 servers, an arXiv preprint last revised on April 13, 2026, reports two servers found during installation: a notes server that required full disk access on macOS and a game-engine server set to auto-approve sensitive operations. The MCP scanner they used missed both because it reads tool descriptions through the protocol, not configuration or code. OWASP's development guide makes the matching demand: flag any tool that performs actions its description does not mention. [19][15]

What each piece of admission evidence establishes and what it leaves open. Editorial synthesis of the cited registry, specification and OWASP documents reviewed October 9, 2026. [3][18][1][14]
EvidenceEstablishesDoes not establish
Registry namespace checkWho controls the publishing account or domainCode safety, or who runs the endpoint later
Package digest or fileSha256The exact bytes that were reviewedBehavior, or any remote tool list
Exact launch commandWhat starts on the hostWhat the process does once running
Tool-definition hashThe text the model was shown at reviewServer behavior, or other authorization contexts
Scanner reportWhich patterns that scanner matchedThat no vulnerability exists
Manual reviewOne reviewer's reading on one dateAnything that changes afterwards

Pin what you approved

A package pin covers code that runs locally. A remote server offers no package to pin, so its definitions are the only artifact under your control, and even a local server's definitions deserve their own pin because the server may generate them at runtime. As with a commit SHA on a third-party action, a definition hash proves that the text is the text you reviewed, not that the text is safe. Both OWASP guides and Invariant's disclosure recommend pinning definitions with a hash, and the third-party guide adds a version history, alerts on unauthorized changes and a hash of the tool descriptions with every submission. [14][15][10]

Hashing raw responses produces false alarms, so canonicalize first. Deterministic order is only a SHOULD, and paginated lists carry no cross-page consistency guarantee; the caching rules tell a client that needs a consistent snapshot to fetch again from the first page. Sort tools by name, serialize each with sorted keys and fixed separators, hash each tool, then hash the sorted map of per-tool hashes. Do not trim whitespace or normalize Unicode first. An added zero-width character is part of what the model reads and should change the hash, and listing format-control characters separately lets the reviewer see them. Use one implementation for every hash you compare, since JSON libraries format numbers differently. [1][9]

Take one snapshot per authorization context. The specification lets a server return different tools depending on the authorization on the request, such as only the tools a token's scopes permit, so a list captured with a read-only token says nothing about what an administrator's token will see. Capture each role or scope set you intend to approve, and treat a tool that appears only for a broader token as a separate admission. Capture at the layer the model reads: a client on Streamable HTTP must drop any tool whose x-mcp-header values are invalid, and a proxy that aggregates servers is told to disambiguate names, for example with a server prefix, so the list the agent receives can differ from the server's raw response. [1]

Keep freshness signals out of the trust decision. ttlMs says how long a client may skip fetching again, and the caching page states that a server may change the data before it expires. cacheScope matters more. A public result may be stored by a shared gateway or proxy and served to any user, even when it came from an authenticated call. A server whose list varies by authorization but marks it public lets one caller's list reach another caller's agent, so compare the scope with the per-context snapshots during review and reject the mismatch. [9]

Example Python 3 fragment using only the standard library. It hashes a captured tools/list result for one authorization context and lists format-control characters per tool; it does not contact the server.
# Example fragment: canonicalize a captured tools/list result and hash it.
# One input file per page of the JSON-RPC response, all captured with the
# same authorization context. File names and the context label are placeholders.
import hashlib
import json
import sys
import unicodedata


def canonical(value) -> bytes:
    # Sorted keys and fixed separators. Text is neither trimmed nor
    # Unicode-normalized, so an added zero-width character changes the hash.
    return json.dumps(value, sort_keys=True, separators=(",", ":"),
                      ensure_ascii=False).encode("utf-8")


def format_chars(tool: dict) -> list:
    text = json.dumps(tool, ensure_ascii=False)
    return sorted({f"U+{ord(c):04X}" for c in text if unicodedata.category(c) == "Cf"})


def snapshot(pages: list) -> dict:
    tools = [tool for page in pages for tool in page["result"]["tools"]]
    names = [tool["name"] for tool in tools]
    if len(names) != len(set(names)):
        raise SystemExit("duplicate tool names across pages: re-fetch from the first page")
    per_tool = {}
    for tool in sorted(tools, key=lambda t: t["name"]):
        # Hash the whole definition the server returned, not a subset.
        per_tool[tool["name"]] = {
            "sha256": hashlib.sha256(canonical(tool)).hexdigest(),
            "format_chars": format_chars(tool),
        }
    set_hash = hashlib.sha256(
        canonical({name: entry["sha256"] for name, entry in per_tool.items()})
    ).hexdigest()
    return {"set_sha256": set_hash, "tools": per_tool}


if __name__ == "__main__":
    # python3 snapshot.py role-docs-reader page-1.json [page-2.json ...]
    context, paths = sys.argv[1], sys.argv[2:]
    pages = []
    for path in paths:
        with open(path, encoding="utf-8") as handle:
            pages.append(json.load(handle))
    print(json.dumps({"authorization_context": context, **snapshot(pages)}, indent=2))

Make the approval binding at one enforcement point

An approval that no component enforces is documentation. Decide where it binds before running the review, because the enforcement point determines what a pin can hold. The options documented today differ in what they fix: some fix which server an agent can reach, some fix which tool names it may call, and only some fix the definitions the model reads.

Amazon Bedrock AgentCore Gateway offers the clearest snapshot. For an MCP server target in DEFAULT listing mode, which applies unless changed, the gateway fetches tools/list, prompts/list and the resource lists when the target is created or updated and serves them from a catalog cached at the control plane. The catalog changes again only when the target is updated or someone calls SynchronizeGatewayTargets, which AWS says to do whenever the server's definitions change. DYNAMIC mode forwards each listing request to the server and, per the same page, does not work with semantic search or outbound three-legged OAuth. A target can instead carry a static mcpToolSchema, supported for all credential providers, which disables synchronization, so the model sees only definitions an administrator supplied. The gateway lists 2026-07-28 among its supported protocol versions. [5][20]

It follows that in DEFAULT mode a synchronization is a re-admission, and the right to run one belongs to the admission pipeline alone. The example policy below expresses that as a service control policy. The snapshot fixes what the model reads, not what the server does: tools/call still reaches the live server, so a server that keeps its descriptions and changes its behavior passes straight through.

Managed client configuration fixes which servers run, not what they say. In Claude Code, managed-mcp.json gives an organization exclusive control of the server set, and allowedMcpServers entries match a remote server by serverUrl pattern or a stdio server by serverCommand, an exact match on every argument in order. The allowlist becomes authoritative only when allowManagedMcpServersOnly is set in a managed settings source, since otherwise users can broaden it in their own settings, and the documentation warns that a serverName entry is not a security control because users choose the names. GitHub Copilot reads allowedMcpServers and deniedMcpServers, matching by name, server URL or command, from an enterprise managed-settings.json; its registry-only policy, in public preview, matches server names or IDs alone, which GitHub notes can be bypassed by editing configuration files. [6][7][21]

Two consequences belong in the admission record. An exact serverCommand doubles as a package pin only when the command names an exact version; GitHub's own example allowlists @playwright/mcp@latest, which matches exactly while the code behind it moves. And neither product's MCP administration pages describe a setting that pins tool descriptions, so when a client allowlist is the only enforcement point, definition drift is visible only to a separate monitor. [6][7]

Agent tool configuration fixes names. In Microsoft Foundry, allowed_tools defaults to every tool the server exposes and require_approval defaults to always. A list of names stops a newly added tool from being called but leaves a rewritten description under an allowed name in place. Microsoft's guidance supplies a trigger worth copying: review allowed_tools, approval settings and connection permissions when the server's operator, exposed tools or behavior changes. [22]

Cloud IAM fixes calls. Google Cloud can deny mcp.googleapis.com/tools.call with a condition on the tool.isReadOnly attribute, only for Google's own MCP servers, and its documentation notes that tools/list still returns every tool, including the read-write tools the policy blocks. A denied tool's description still reaches the model. A reasonable reading is that the condition holds because Google writes both the server and its annotations, which is the trusted-server case the specification allows; annotations from a third-party server cannot carry the same weight. [23][1]

What each documented enforcement point holds fixed after approval, from AWS, Anthropic, GitHub, Microsoft and Google documentation reviewed October 9, 2026. [5][20][6][7][22][23]
Enforcement pointHolds fixedStill changes
AgentCore Gateway, DEFAULT listingDefinitions served to agents until a syncServer behavior behind tools/call
AgentCore Gateway, mcpToolSchemaDefinitions an administrator suppliedServer behavior behind tools/call
AgentCore Gateway, DYNAMIC listingWhich endpoint is reachedDefinitions, on every listing
Claude Code or Copilot allowlistServer URL pattern or exact launch commandDefinitions, and the package if the command floats
Foundry allowed_toolsWhich tool names can be calledDescriptions under allowed names
Google IAM deny on tools.callCalls to denied Google toolsListings, which still show denied tools
Example service control policy fragment with placeholder account and role. It denies creating, updating or synchronizing AgentCore Gateway targets to every principal except the admission pipeline role. Test it in a non-production organizational unit first: it also blocks administrators and any break-glass role you do not exempt.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "OnlyAdmissionPipelineRefreshesMcpTargets",
      "Effect": "Deny",
      "Action": [
        "bedrock-agentcore:CreateGatewayTarget",
        "bedrock-agentcore:UpdateGatewayTarget",
        "bedrock-agentcore:SynchronizeGatewayTargets"
      ],
      "Resource": "*",
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalArn": "arn:aws:iam::111122223333:role/mcp-admission-pipeline"
        }
      }
    }
  ]
}

Detect a server that changes after approval

Drift detection means listing again with each authorization context used at admission, recomputing the canonical hashes and comparing them with the record. Classify every difference: a tool added or removed, a description rewritten, a schema or nested parameter description changed, annotations changed, or new instructions from server/discover. Each one alters what the model reads, so each is a re-admission event even though most will turn out to be routine releases. Run the comparison on your own schedule and spread it out, since the caching page says clients should not treat ttlMs as a polling interval and requires jitter and backoff from implementations that poll. [9][16]

Notifications help but cannot carry the load. A server that declared listChanged SHOULD send notifications/tools/list_changed to clients that opened a subscriptions/listen stream asking for it. That is a recommendation the server chooses whether to follow, and the server is the party whose change you are trying to catch. Treat a notification as a reason to list again early, and its absence as no evidence of anything. [1][13]

Where a gateway holds the snapshot, watch both sides. Compare the server's live list, fetched with the admission pipeline's own credentials, with the catalog the gateway serves. A difference while the catalog is unchanged means the server drifted and agents are still reading approved text; a change in the catalog itself means someone created, updated or synchronized the target outside the process. GetGatewayTarget returns a lastSynchronizedAt timestamp to reconcile against the record. Neither case calls for an automatic sync. [5][24]

Registries need watching as well. On the official registry, poll for a new version or a status of deleted. AWS Agent Registry can copy a server's tool definitions into its record by synchronizing from the endpoint, and synchronizing an approved record again creates a new draft revision while the approved one stays discoverable, which turns a periodic re-sync into a diff that waits for a curator. The record remains a catalog entry, though: an agent that finds the server there and connects to its endpoint reads the live list. [4][25]

When a hash changes, freeze first and investigate second. With a gateway snapshot the freeze is already in place, because agents keep reading the approved definitions. With a client allowlist, add a deny entry until the diff is reviewed; in both Claude Code and Copilot a denylist match blocks a server even when it also matches the allowlist. Review the diff as carefully as the original: read the new text in full, look for references to other tools, files or credentials, scan the new snapshot, and approve a new record instead of editing the old one. [6][7]

Figure 02

A tool keeps its name and schema while its description changes

A hash of the approved definition exposes the change; only a snapshot at the gateway keeps agents reading the approved text. [10][5]

Illustration. On the left, an approved tool card named search_docs with a one-line description and hash a3f1. On the right, the same tool thirty days later with the same name and schema, an added amber line marking a hidden instruction and hash 9c07. Below, three lanes: a live listing leads to the model reading the new text, a gateway snapshot keeps the model on the approved text, and a drift monitor raises a hash mismatch alert.

Source. Conceptual illustration of the rug pull described by Invariant Labs and of AgentCore Gateway's DEFAULT listing mode. Tool name and hashes are hypothetical. [10][5]

Method. Conceptual hand-authored illustration. It simplifies the gateway to one target and the monitor to a single comparison of hashes.

Accessible table and figure data
Figure 2 accessible table
ElementWhat it represents
Approved cardTool definition captured at review
Day 30 cardSame name and schema, new description
Amber lineInstruction added after approval
Hash tagsCanonical hashes of each definition
Live listingClient that reads tools/list on each use
Gateway snapshotCatalog that serves approved definitions
Hash mismatchMonitor comparing live and approved hashes
Figure 2 accessible table
ElementWhat it represents
Approved cardTool definition captured at review
Day 30 cardSame name and schema, new description
Amber lineInstruction added after approval
Hash tagsCanonical hashes of each definition
Live listingClient that reads tools/list on each use
Gateway snapshotCatalog that serves approved definitions
Hash mismatchMonitor comparing live and approved hashes

What scanners can and cannot tell you

MCPZoo, which its authors describe as the largest collection of MCP servers for dynamic analysis to date, is an arXiv preprint by Pei Chen and colleagues submitted on July 13, 2026 and not peer reviewed. The authors collected servers from ten markets and package ecosystems, deployed them automatically in containers and kept the 37,288 that answered a real tools/list request. They ran eight publicly available scanners with more than 200 GitHub stars, nine configurations once A.I.G's static and dynamic modes are counted apart, using default settings, a 1,500-second timeout and a fixed local Qwen3-235B-A22B-Instruct model for scanners that need one. [8]

The scanners disagreed about almost everything. At least one flagged 96.89 percent of the servers, no server was flagged by all eight, and the share each scanner flagged ran from 0.54 percent to 80.04 percent. Average pairwise overlap between the sets of flagged servers was 15.66 percent. Popularity barely mattered: servers with at least 5,000 GitHub stars were flagged at 97.66 percent, servers with none at 96.27 percent. [8]

Validation shows why the flags cannot settle anything. For precision, two reviewers inspected alerts drawn from 100 sampled servers and counted a true positive only when the evidence showed concrete vulnerable behavior or a reachable unsafe flow. For recall, the authors mapped 10 published CVEs to 38 servers in the corpus. The two most precise tools, mcp-gateway at 96.88 percent and MCPSafetyScanner at 74.03 percent, found none of the known vulnerable servers, while MCPScan, with the highest recall at 74.29 percent, was right on fewer than half of its sampled alerts. The overall 45.53 percent precision and 24.17 percent recall the paper reports are unweighted means of the nine configurations, and some denominators are small: nova-proximity's recall rests on four servers. [8]

The authors trace the errors to the evidence each tool uses. Metadata scanners read only descriptions and schemas, so they mistake capability for vulnerability, in one case reporting credential leakage because a schema field was named token, and they cannot see a flaw in code. Static scanners read code without testing whether a path is exploitable, and LLM-driven probes are unstable from run to run. The headline prevalence figure in the 1,899-server study shows the same dependence on the instrument: its 5.5 percent tool-poisoning rate comes from one scanner run on the 73 servers it could scan, out of a random sample of 83 drawn from the 583 servers with at least ten GitHub stars. [8][19]

Use scanner output as evidence with a named source. Record the scanner, its version, its mode, what it saw (the snapshot, the source or a live endpoint) and the date. Treat each finding as a question for the reviewer and a clean result as a statement that this scanner's rules matched nothing. A metadata scan is cheap and catches crude hidden instructions, so run one on every snapshot and every diff; for local servers whose code you will execute, add a source review. Never let a scanner verdict approve a server by itself. The MCPZoo authors call scanner reports useful triage signals and note that results depend on scanner versions and model configurations, so treat the numbers as a picture of mid-2026 tools rather than a rating of current releases. [8]

Figure 03

The most precise MCP scanners found none of the known vulnerable servers

Precision ran from 10.40 to 96.88 percent and recall from 0 to 74.29 percent across nine configurations, and the two most precise tools recalled nothing. [8]

Grouped horizontal bar chart of precision and recall in percent for nine MCP scanner configurations from the MCPZoo preprint, sorted by precision: mcp-gateway 96.88 and 0, MCPSafetyScanner 74.03 and 0, A.I.G static 56.98 and 47.06, nova-proximity 52.78 and 0, MCPScan 45.53 and 74.29, Agent-Scan 28.21 and 50.00, MCP-Scanner 24.64 and 21.21, mcp-armor 20.34 and 19.05, A.I.G dynamic 10.40 and 5.88.

Source. MCPZoo arXiv preprint 2607.11086v1, Table 7, submitted July 13, 2026; one study, values copied. Precision is measured on alerts from 100 sampled servers; recall on 10 CVEs mapped to 38 servers. [8]

Method. Values copied from Table 7 and sorted by precision. Alerts reviewed and recall base are the per-configuration denominators the paper reports. The paper's overall row (45.53 and 24.17 percent) is the unweighted mean of the nine configurations and is not plotted. Scanner versions and default settings are those used in the study, not current releases.

Accessible table and figure data
Figure 3 accessible table
Scanner configurationPrecision (%)Recall (%)Alerts reviewedRecall base (servers)
mcp-gateway96.8803235
MCPSafetyScanner74.0307726
A.I.G static56.9847.068634
nova-proximity52.780364
MCPScan45.5374.2912335
Agent-Scan28.21507832
MCP-Scanner24.6421.216933
mcp-armor20.3419.055921
A.I.G dynamic10.45.8812534
Figure 3 accessible table
Scanner configurationPrecision (%)Recall (%)Alerts reviewedRecall base (servers)
mcp-gateway96.8803235
MCPSafetyScanner74.0307726
A.I.G static56.9847.068634
nova-proximity52.780364
MCPScan45.5374.2912335
Agent-Scan28.21507832
MCP-Scanner24.6421.216933
mcp-armor20.3419.055921
A.I.G dynamic10.45.8812534

Running the review as a process

OWASP's third-party guide describes a workflow that fits this record with little change. A submitter provides documentation and a hash of the tool descriptions, automated scanning follows, security and domain reviewers sign off, approved servers are version-pinned into a registry, deployment goes to staging for a probation period, and servers are re-validated on a schedule or when versions change. The roles are a submitter, a security reviewer, a domain owner who confirms the server is needed and approves its scopes, an approver step that needs both security and domain sign-off, and an operator who owns rollout, monitoring and the kill switch. [14]

The decision tree orders the questions so that the cheapest disqualifiers come first. Keep the resulting record in a form that both the enforcement point and the monitor can read. The example below shows a hypothetical remote server approved for a single read-only context behind an AgentCore Gateway target; its field names are illustrative rather than any standard schema.

Give every approval an expiry date. A time-boxed approval forces a fresh snapshot and a fresh scan even for a server that never announces a change, and it limits how far a record can drift from a server nobody is watching. These events should send a server back to review before the expiry arrives:

  • Any change to a per-tool hash, the set hash or the server's instructions in an approved authorization context.
  • A new package version or image digest, a changed launch command, or a new endpoint URL.
  • A change of operator or namespace owner, or a registry status of deleted.
  • A new authorization context, such as a broader scope or role that lists tools the original review never saw.
  • A scanner finding on the current snapshot, or a published vulnerability in the package.
  • A protocol revision change at the client or gateway, because listing and caching rules changed between revisions.
Example admission record for a hypothetical server, with placeholder names, addresses, account and hashes. The structure is illustrative and not a published schema.
{
  "server": "io.github.example-org/docs-search",
  "registryVersion": "1.4.2",
  "transport": "streamable-http",
  "endpoint": "https://mcp.example.com/docs/mcp",
  "operator": "Example Org documentation team",
  "protocolVersion": "2026-07-28",
  "executionContext": {
    "type": "remote",
    "outboundCredential": "OAuth client credentials, scope docs.read",
    "downstreamData": [
      "team wiki, read only"
    ]
  },
  "snapshots": [
    {
      "authorizationContext": "role docs-reader, scope docs.read",
      "capturedAt": "2026-10-09T14:00:00Z",
      "cacheScope": "private",
      "setSha256": "sha256-of-sorted-per-tool-hashes",
      "tools": {
        "get_doc": "sha256-of-canonical-definition",
        "search_docs": "sha256-of-canonical-definition"
      },
      "instructionsSha256": "sha256-of-discover-instructions"
    }
  ],
  "evidence": [
    {
      "type": "scanner",
      "name": "example-metadata-scanner",
      "version": "0.0.0",
      "input": "snapshot only",
      "result": "1 finding, reviewed as false positive",
      "date": "2026-10-09"
    },
    {
      "type": "manual",
      "reviewer": "security-reviewer@example.com",
      "scope": "full descriptions and schemas; source not available",
      "date": "2026-10-09"
    }
  ],
  "approvals": [
    {
      "role": "security",
      "by": "security-reviewer@example.com",
      "date": "2026-10-09"
    },
    {
      "role": "domain owner",
      "by": "docs-owner@example.com",
      "date": "2026-10-09"
    }
  ],
  "enforcement": {
    "point": "AgentCore Gateway target, listingMode DEFAULT",
    "refreshRole": "arn:aws:iam::111122223333:role/mcp-admission-pipeline"
  },
  "monitor": {
    "schedule": "daily with jitter",
    "contexts": [
      "role docs-reader, scope docs.read"
    ]
  },
  "expiresAt": "2027-01-09"
}
Figure 04

Decide whether to admit an MCP server

Enforcement and execution context come before reading descriptions, because a pin that nothing enforces and a process that runs unsandboxed both defeat the review. [12][1][14]

Decision tree with five questions: whether an enforcement point can hold the approval, whether the server runs as a local process, whether tools/list differs by authorization context, whether any text mentions other tools, files, secrets or hidden steps, and whether evidence shows behavior the descriptions do not mention.

Source. Conceptual decision aid based on the MCP 2026-07-28 tools page, the security guidance page in the MCP documentation and OWASP's third-party MCP server guide. [12][1][14]

Method. Conceptual ordering of documented guidance. Each question is answered for the server as a whole and again for each authorization context.

Accessible table and figure data
Figure 4 accessible table
QuestionYes, thenNo, then
Can a gateway, allowlist or tool setting enforce the approval?name it in the record and continuefix enforcement first; approval would be advisory
Does the server run as a local process?require a sandbox, exact command and package digestreview the operator and authorization relationship
Does tools/list differ by authorization context?snapshot and approve each context separatelykeep one snapshot and confirm cacheScope
Does any text mention other tools, files, secrets or hidden steps?reject, or serve reviewed definitions from the gatewaycontinue to scanner and code evidence
Does evidence show behavior the descriptions do not mention?reject or narrow the allowed toolsapprove with an expiry and start the monitor
Figure 4 accessible table
QuestionYes, thenNo, then
Can a gateway, allowlist or tool setting enforce the approval?name it in the record and continuefix enforcement first; approval would be advisory
Does the server run as a local process?require a sandbox, exact command and package digestreview the operator and authorization relationship
Does tools/list differ by authorization context?snapshot and approve each context separatelykeep one snapshot and confirm cacheScope
Does any text mention other tools, files, secrets or hidden steps?reject, or serve reviewed definitions from the gatewaycontinue to scanner and code evidence
Does evidence show behavior the descriptions do not mention?reject or narrow the allowed toolsapprove with an expiry and start the monitor

The order that makes a pin hold

Choose the enforcement point first, because it decides whether a definition pin is enforced or only monitored. Classify the execution context next: a local server needs a sandbox, an exact launch command and a package digest before anyone reads its descriptions, and a remote server needs its authorization relationship reviewed. Capture and hash a snapshot for each authorization context you will approve, check cacheScope against those snapshots, then run the scanners and read the full text. Approve with an expiry, restrict who can refresh the enforcement point, and start the monitor the same day. [12][9][14]

One test decides whether the review achieved anything: name the component that would stop an agent from reading a changed description. If it is a gateway snapshot or a static schema, the tools are pinned. If it is a monitor that alerts after the fact, they are pinned with a delay, and the record should say how long that delay can be. If no component can be named, the organization has approved a server and agreed to trust whatever it says next.

The procedure shrinks only where the text cannot move without you, as with a server you build whose definitions ship in your own reviewed release, and even then the runtime list should be compared with that release. A future MCP revision that adds signed tool definitions would change the integrity step and leave the rest in place: provenance, execution context, per-context snapshots and an enforcement point would still be the work.

Method and provenance

Source-led technical analysis of the Model Context Protocol specification revision 2026-07-28, MCP Registry documentation, OWASP GenAI Security Project guides, the Invariant Labs disclosure, three arXiv preprints, and AWS, Anthropic, GitHub, Microsoft and Google documentation, with original conceptual figures. Sources were reviewed on October 9, 2026.

No MCP server, gateway, client or scanner was configured, run or measured. Scanner figures come from one unrefereed preprint and reflect the scanner versions and settings it used. Product behavior is limited to the cited documentation as of the review date, and the MCP Registry and GitHub Copilot's registry-only enforcement policy were in preview.

AI assistance. AI assisted research synthesis, drafting, diagram planning and visual production, with deterministic editorial checks. No personal deployment experience, independent human review or live test is claimed.

Published under the Cloud Security Desk organizational byline. Read the practitioner guide policy.

References

  1. Tools, Model Context Protocol specification revision 2026-07-28 Model Context Protocol. Published . Accessed .
  2. Schema reference, MCP specification revision 2026-07-28 Model Context Protocol. Published . Accessed .
  3. The MCP Registry (preview) Model Context Protocol. Accessed .
  4. The MCP Registry Moderation Policy Model Context Protocol. Accessed .
  5. MCP servers targets, Amazon Bedrock AgentCore Developer Guide Amazon Web Services. Accessed .
  6. Caching, MCP specification revision 2026-07-28 Model Context Protocol. Published . Accessed .
  7. MCP Security Notification: Tool Poisoning Attacks Invariant Labs. Published . Accessed .
  8. Security Best Practices, Model Context Protocol documentation (2026-07-28 version) Model Context Protocol. Published . Accessed .
  9. Key changes in MCP specification revision 2026-07-28 Model Context Protocol. Published . Accessed .
  10. A Practical Guide for Securely Using Third-Party MCP Servers, version 1.0 (PDF) OWASP GenAI Security Project. Published . Accessed .
  11. A Practical Guide for Secure MCP Server Development, version 1.0 (PDF) OWASP GenAI Security Project. Published . Accessed .
  12. Discovery (server/discover), MCP specification revision 2026-07-28 Model Context Protocol. Published . Accessed .
  13. Versioning Published MCP Servers Model Context Protocol. Accessed .
  14. MCP Registry Supported Package Types Model Context Protocol. Accessed .
  15. Prevent read-write MCP tool use, Google Cloud MCP servers Google Cloud. Accessed .
  16. Synchronize records from external sources, AWS Agent Registry Amazon Web Services. Accessed .