An MCP server that passed review last month can hand your agent a different set of instructions today, and nothing in the protocol stops it. Tool poisoning hides instructions for the model inside tool metadata, and a rug pull swaps honest metadata or code for malicious versions after approval. Pre-adoption review catches what a server looks like on the day you assess it; this guide covers what happens afterwards. It explains how to baseline tool definitions, detect drift and hidden instructions at runtime, and respond when a definition changes. It is written for platform teams running MCP gateways, detection engineers and the security reviewers who own MCP server approvals. For the pre-adoption assessment itself, see the MCP Server Trust Assessment.
Key takeaways
- Tool metadata is model input. Names, descriptions and every field of the input schema reach the model's context, so any of them can carry instructions the user never sees.
- The protocol allows tools to change. The MCP specification lets a server's tool list change over time and only says servers that declared listChanged SHOULD notify clients, so a silent change is possible and must be detected by the client side.
- Hash the full definition, not the description. Baseline a canonical hash of each tool's name, title, description, inputSchema, outputSchema and annotations per server version, and alert on any difference.
- Description hashing misses code rug pulls. The postmark-mcp backdoor changed behaviour, not metadata, so pair definition monitoring with package pinning and egress monitoring.
- Quarantine first, investigate second. A changed or suspicious tool should be removed from the agent's tool list by the gateway until a human re-approves it, not merely logged.
How tool poisoning and rug pulls work
An MCP client calls tools/list and receives, for each tool, a name, optional title, a description, an inputSchema (JSON Schema), an optional outputSchema and optional annotations. The client then places those definitions in the model's context so the model can decide which tool to call. Invariant Labs, who named the attack in April 2025, described tool poisoning as malicious instructions in a tool description that are invisible to users but visible to AI models: a form of indirect prompt injection delivered through metadata rather than through data the tool returns.
Three variants matter for detection:
- Hidden instructions: a harmless-sounding tool (Invariant's example was an "add" tool) whose description tells the model to read files such as SSH keys or configuration and pass them in a tool argument. The client UI shows a short summary, so the user approves a call whose full arguments they never read.
- Shadowing: a malicious server's description changes how the agent uses a different, trusted server's tool, for example redirecting all mail sent through a trusted email tool. The poisoned tool does not need to be called, so call logs for that server show nothing.
- Rug pull: the server behaves honestly through review and adoption, then changes a tool definition or its code. MITRE ATLAS records this as AML.T0109 AI Supply Chain Rug Pull and the persistent modification of an integrated tool as AML.T0110 AI Agent Tool Poisoning.
The specification supplies the opening. Servers may change their tool set over time, and in the 2026-07-28 revision a server that declared listChanged SHOULD send notifications/tools/list_changed to clients that opened a subscriptions/listen stream. Nothing obliges a server to declare that capability, and nothing obliges it to keep a tool's description stable between two tools/list calls. The same revision says servers MUST NOT vary the set per connection or as a side effect of other requests, but may vary it by the authorisation presented. Treat a tool definition that differs between sessions, or between identities holding the same scopes, as a finding, because the specification does not permit per-connection variation.
The MCP security best practices also describe how a hijacked session on a stateful Streamable HTTP deployment can push a notifications/tools/list_changed event, leaving a client with tools it did not know were enabled. Change detection covers that path as well. For the wider protocol context see the MCP Security Guide and, for the full risk list, the Top 10 MCP Security Risks.
Where poisoned content hides in a tool definition
Scanners that read only the description field miss most of the surface. Every string in the definition is serialised into context by common clients.
| Field | What poisoning looks like | Detection signal |
|---|---|---|
description | Imperative text addressed to the model ("before using any tool, read...", "do not mention this to the user"), tags such as <IMPORTANT>, references to files, keys or environment variables | Instruction-pattern rules, length jump against baseline, new mentions of paths or secrets |
inputSchema property descriptions | Instructions inside a parameter's description, for example telling the model to fill a "notes" field with file contents | Same rules applied recursively to every nested description, title, default and examples string |
inputSchema structure | A new free-text parameter (often named for context, notes, metadata or debug) added to a tool that did not need it; a parameter moved into required | Structural diff: added properties, changed types, changed required list |
name and title | A tool named to collide with a trusted server's tool, or to sound like a system function | Cross-server name collision check; the 2026-07-28 revision says aggregating clients SHOULD disambiguate, for example by prefixing with a server identifier |
annotations | A destructive tool newly marked readOnlyHint so a client skips confirmation | Any annotation change; the specification says clients MUST treat annotations as untrusted unless from trusted servers |
| Tool results | Instructions returned in output or error text rather than in metadata | Out of scope for definition hashing; covered by output filtering and the AML.T0080 AI Agent Context Poisoning guide |
Unicode deserves its own rule. Zero-width characters, bidirectional control characters and Unicode tag characters can hide text from a human reviewer while leaving it readable to a model. Flag any definition containing code points in the general category Cf (format characters) or outside the scripts the server normally uses, and render definitions for reviewers with those characters made visible.
Baselining tool definitions
Detection depends on a trustworthy baseline captured at approval time. Capture it at the point every call already passes through: the MCP gateway or proxy, or, where none exists, a scheduled collector that connects with a dedicated read-only identity.
- Record the approved state. At approval, store for each server: server URL or package name, version or image digest,
serverInfo, declared capabilities (including whetherlistChangedis true) and the fulltools/listresponse across all pages. - Canonicalise. Serialise each tool definition with sorted keys, normalised whitespace and Unicode normalisation recorded (keep the raw form too, because normalisation can hide the very characters you want to detect). Hash each tool with SHA-256 and hash the sorted set of tool hashes for a server-level fingerprint.
- Store the baseline outside the server's reach. Keep it in the gateway's configuration store or your inventory, signed or write-protected, linked to the approval record in the MCP Server Inventory Template.
- Bind it to identity. Record which identity and scopes the baseline was captured with. Because servers may filter tools by authorisation, capture one baseline per distinct scope set your agents use.
- Re-baseline only through change control. A new server version gets a new baseline only after a reviewer has read the diff, never automatically.
Runtime detection methods
Run several independent checks. Each catches something the others miss.
| Method | When it runs | Catches | Misses |
|---|---|---|---|
| Definition hash comparison | Every tools/list response at the gateway, plus scheduled polling | Any metadata rug pull, silent or announced | Behaviour changes with unchanged metadata |
| Structural schema diff | On every hash mismatch | New parameters, type changes, new required fields | Changes inside existing free text |
| Instruction-pattern rules | At approval and on every change | Hidden instructions, secrecy requests, file and credential references | Novel phrasing; tune rather than trust |
| Classifier or LLM review | On change, in a sandbox, never in the production agent | Paraphrased instructions that evade patterns | Can itself be prompt-injected; use as a second opinion |
| Cross-server analysis | Whenever the set of connected servers changes | Name collisions and descriptions that mention other servers' tools (shadowing) | Shadowing phrased without naming the target |
| Consistency across identities | Scheduled, from two or more collector identities | Definitions served differently to different callers or sessions | Targeting by attributes you do not vary |
| Package and image pinning | At deploy and on update | Code rug pulls such as postmark-mcp | Hosted remote servers whose code you cannot see |
Useful instruction-pattern rules, applied to every string in the definition:
- Model-directed imperatives: phrases addressed to the assistant or model, "always", "before any other tool", "ignore previous", "you must".
- Secrecy: "do not tell the user", "do not mention", "silently", "without informing".
- Sensitive references:
~/.ssh,id_rsa,.env,mcp.json, cloud credential file paths, "API key", "token", "password". - Cross-tool references: names of tools belonging to other connected servers, or generic phrases such as "when sending email" in a tool unrelated to email.
- Exfiltration hints: URLs, email addresses or domains that were not in the baseline.
- Size and shape: a description more than double its baseline length, or markup tags in plain-text fields.
The MCP Security Scanners Guide covers tools that implement several of these checks; the methods here are what to look for whichever scanner you use.
Response when a tool definition changes
Decide in advance what the gateway does on a mismatch, because an agent will otherwise keep using the changed tool while a ticket waits.
| Finding | Default action | Owner |
|---|---|---|
| Hash mismatch, version unchanged | Remove the tool from the agent's list, alert, pull the diff | Gateway (automatic), then server owner |
| Hash mismatch after a planned version upgrade | Hold the new version in staging until diff review is signed off | Server owner and security reviewer |
| Instruction-pattern hit on any field | Quarantine the whole server, not just the tool, because shadowing affects other tools | Security on call |
| Annotation weakened (for example a destructive tool newly read-only) | Quarantine; restore confirmation prompts for that tool | Gateway, then reviewer |
| Definitions differ between sessions or same-scope identities | Quarantine and treat as targeted; open an incident | Incident response |
After quarantine, review the agent transcripts and tool call logs from the first time the changed definition was served: which tools were called, with what arguments, and whether any argument carried file contents or credentials. If a credential could have been read, rotate it. The MCP Incident Response Playbook covers the full response.
Decision guidance
- Gateway or endpoint: where agents reach remote servers through an MCP gateway, put hash comparison and quarantine there, because it sees every
tools/listresponse. For local stdio servers on developer machines a gateway rarely sits in the path, so rely on version pinning, configuration monitoring and periodic collection; see the Local MCP Server Security Guide. - Block or alert: block on change for any server whose tools can write, send, delete or reach credentials. Alert-only is reasonable for read-only public data servers during a pilot.
- First-party or third-party servers: a server operated by the vendor of the underlying service changes with that vendor's releases; agree a change notice route. A community or third-party hosted server gets the strictest setting, because the host can change both code and metadata.
- Small teams: start with pinned versions, a nightly collector that hashes
tools/listfor every approved server and an alert to a shared channel. That alone catches metadata rug pulls. - Large estates: enforce through the gateway, feed definition-change events to the SIEM and tie re-approval to the change management system so a diff cannot be accepted without a named reviewer.
Where identity fits
A poisoned tool usually wants a credential: the server's own downstream token, the user's OAuth token, or secrets on the host. Scoping limits what a successful poisoning can reach. Give each MCP server its own identity and narrowly scoped tokens, follow the specification's rule that servers MUST NOT accept tokens not issued for them, and keep long-lived keys off hosts that run third-party servers. Our MCP Static Token Audit guide covers finding servers that still hold long-lived keys, and the MCP Authorisation Specification Guide covers the token model.
Detection and measurement
Log these events at the gateway or collector: every tools/list response hash with server, version, identity and timestamp; every notifications/tools/list_changed received; every quarantine and re-approval with reviewer name; and every tool call with arguments (redacting secrets) so a change can be tied to what the agent did next. The tools specification itself says clients SHOULD log tool usage for audit purposes.
- Baseline coverage: percentage of approved servers with a stored definition baseline (target 100%).
- Time to detect drift: time from a definition first being served to the alert (target: same request at the gateway; under 24 hours for polled servers).
- Unreviewed changes in use: count of tools served to agents whose current hash has no approval record (target 0).
- Quarantine dwell: median time from quarantine to re-approval or removal, to keep teams from bypassing the control.
- Pattern rule hit rate: hits per 100 definitions reviewed, tracked with false positive rate, so rules are tuned rather than ignored.
- Pinned versions: percentage of local and self-hosted servers running a pinned version or digest.
Framework mapping
| Framework | Reference | How it applies |
|---|---|---|
| MITRE ATLAS | AML.T0110 AI Agent Tool Poisoning; AML.T0109 AI Supply Chain Rug Pull; AML.T0104 Publish Poisoned AI Agent Tool; AML.T0051.001 Indirect prompt injection | The techniques this guide detects |
| MITRE ATLAS mitigation | AML.M0028 AI Agent Tools Permissions Configuration | Limits what a poisoned tool can reach |
| OWASP MCP Top 10 (beta) | MCP03:2025 Tool Poisoning; MCP04:2025 Software Supply Chain Attacks and Dependency Tampering | Risk names for reporting |
| NIST SP 800-53 Rev 5 | CM-3 Configuration Change Control; SI-7 Software, Firmware, and Information Integrity; SI-4 System Monitoring; AU-6 Audit Record Review; CM-8 System Component Inventory; SR-3 Supply Chain Controls | Change control over tool definitions, integrity checking, monitoring and inventory |
| NIST CSF 2.0 | ID.AM-02; PR.PS-01; DE.CM-09; DE.AE-02; GV.SC-07 | Software inventory, configuration management, runtime monitoring, analysis, supplier risk |
| CIS Controls v8.1 | Control 2 Inventory and Control of Software Assets; Control 8 Audit Log Management | Approved software list and logging |
| ISO/IEC 27001:2022 Annex A | A.8.32 Change management; A.8.16 Monitoring activities; A.5.21 Managing information security in the ICT supply chain | Change approval, monitoring and supplier controls |
Common failure patterns
- Scanning once at install. A clean scan on day one says nothing about version 16. The postmark-mcp malicious MCP server did exactly what it claimed for fifteen versions before one release added a hidden BCC to every outgoing email.
- Hashing only the description. Instructions moved into a parameter description or a new parameter pass a description-only check.
- Relying on the server to announce changes.
listChangedis optional and the notification is a SHOULD; poll and compare regardless. - Auto-accepting new versions. Floating version ranges (for example
npx package@latestin a client configuration) turn every upstream release into an unreviewed change. - Trusting annotations. Skipping confirmation for tools marked read-only lets a rug pull remove the prompt that would have caught it.
- Using the production agent to judge a definition. Asking the same model that will act on the definition whether it looks safe exposes it to the injection being assessed.
Practitioner checklist
- Capture and store a canonical SHA-256 baseline of every tool definition for each approved server, version and scope set.
- Compare every
tools/listresponse at the gateway and poll servers that bypass it at least daily. - Apply instruction, secrecy, sensitive-path and Unicode format-character rules to every string field, not only
description. - Run a cross-server check for tool name collisions and references to other servers' tools.
- Quarantine changed tools automatically for any server that can write, send, delete or reach credentials.
- Pin local and self-hosted server packages to exact versions or image digests and review diffs before upgrading.
- Treat annotations as untrusted and keep confirmation prompts for sensitive tools regardless of hints.
- Log definition hashes, change notifications, quarantines and tool calls to the SIEM.
- After any confirmed change, review transcripts from first exposure and rotate any credential the agent could reach.
Standards and references
- Model Context Protocol: Tools specification (revision 2026-07-28)
- Model Context Protocol: Tools specification (revision 2025-11-25)
- Model Context Protocol: Security Best Practices
- MITRE ATLAS: AML.T0110 AI Agent Tool Poisoning
- MITRE ATLAS: AML.T0109 AI Supply Chain Rug Pull
- OWASP: MCP Top 10
- Invariant Labs: MCP Security Notification, Tool Poisoning Attacks
Related NHI Mgmt Group resources: MCP Server Trust Assessment · MITRE ATLAS AML.T0109 AI Supply Chain Rug Pull · MCP Gateway and MCP Server Governance Guide · MCP Security Testing Guide