a2ally™ Sign up

Answers · MCP security

How do I detect when an MCP server changes its tool descriptions after I approved it (a rug pull)?

Last updated

TL;DR: An MCP rug pull, a term from Invariant Labs' April 2025 tool-poisoning research, is a server changing its tool definitions after you approved them: the description you reviewed was harmless, and the one your model reads today carries injected instructions. Approving a server once is therefore not enough. At approval, record a hash of each tool's name, description and input schema (plus output schema and annotations if present); every time the client fetches tools/list, recompute and compare; and when anything differs, fail closed: disable the changed tool, show the diff, and require a fresh approval. Pin package and image versions for local servers so updates are deliberate, and remember a remote server can change at any time, so for remote servers the hash comparison is your only signal.

What should I check, step by step?

1. At approval, fetch tools/list and store, per tool, a SHA-256 over a canonical serialization of name, title, description, inputSchema, outputSchema and annotations. Canonical JSON (for example RFC 8785, JSON Canonicalization Scheme) keeps key order and whitespace from changing the hash.

  1. Store a server-level hash over the sorted set of tool hashes too, so an added or removed tool is caught, not only an edited one.
  2. Re-check on every tools/list response, at session start and whenever the server sends notifications/tools/list_changed. Don't assume a server without that notification never changes.
  3. Fail closed: if a tool's hash differs, do not expose that tool to the model until a person reviews the diff and approves the new version. A new tool starts unapproved.
  4. Show the diff, not just "changed": the old and new description and schema side by side, with invisible or zero-width characters made visible.
  5. Pin local servers: an exact package version (no @latest or unpinned npx/uvx), a lockfile, or a container image by digest. An update is then a decision you make, followed by a re-approval.
  6. Log every approval and every detected change with the server, tool, old and new hash and time, so you can tell when a definition changed and which sessions saw it.

Why does hashing the definition work?

The model reads the full tool description and schema, including text the user interface may truncate or hide, so that text is what must not change unseen.

What does it not catch?

A server can keep its definitions identical and change what the tool does on the server side, or return injected text in tool results. Treat tool results as untrusted data and keep confirmations on for actions that write, send, pay or delete.

How do I ask a2ally about this (free, no key)?

Add the MCP server https://www.a2ally.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “Has this MCP server's tool list changed since it was recorded, and what exactly changed?”

One-click setup

{
  "tool": "precall_check",
  "arguments": {
    "url": "<the server's https URL>",
    "tool": "<optional tool name>"
  }
}

a2ally compares tool definitions with the fingerprints it has on record and scans for invisible text. An unchanged answer means the definitions match the record, not that a server is safe.

What are the sources?

Related

Not legal advice. JSON · Markdown · All answers

Get a free API key in one call

No card and no email needed. 100 free credits; the license and policy checks stay free without a key.

curl -s -X POST https://www.a2ally.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export A2ALLY_API_KEY=rs_...   # the api_key from the reply (shown once)
curl -s https://www.a2ally.com/v1/licenses/check -H "Authorization: Bearer $A2ALLY_API_KEY" \
  -H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'