Rightshold™ Sign up

Answers · MCP security

I build an MCP server. How do I change my tool descriptions or schemas without clients flagging it as a rug pull?

Last updated

TL;DR: Rug-pull defenses hash the tool definitions a user approved and flag any difference, so to a client your honest edit and an attacker's injected instruction look the same. You can't make a change invisible, and you shouldn't try. What you can do is make every change deliberate, versioned and quick to review. Ship definition changes only in releases and bump the server version you report in the initialize result (serverInfo.version). Never vary definitions per user, per session or per experiment, because each variant looks like a new definition to a client that pins hashes. Add a new tool rather than rewording or repurposing one people already approved. If your tool list can change mid-session, declare tools.listChanged and send notifications/tools/list_changed so clients re-fetch it. Publish a changelog that quotes the old and new text. In CI, snapshot your own tools/list and fail the build on any diff nobody reviewed, so no change ships by accident.

What should I check, step by step?

1. Treat every field a client may hash as a published contract: name, title, description, inputSchema, outputSchema and annotations. Clients and scanners hash different subsets. mcp-scan's tool pinning hashes tool definitions, and Rightshold's published fingerprint covers each tool's name, description and inputSchema plus the server's name and version. Assume a one-word edit to any of them will be flagged.

  1. Snapshot the full tools/list (every page) in CI as canonical JSON with keys sorted (RFC 8785 is one scheme) and fail the build on a diff nobody reviewed. A reviewed change updates the snapshot in the same pull request.
  2. Change definitions only in versioned releases, and bump serverInfo.version in the initialize result with each one, plus the version in server.json if you publish to the MCP Registry, so every change a client sees matches a version a user can look up.
  3. Serve identical definitions to every user and session. Don't template user names, dates, locale, feature flags, experiment arms or per-account lists (an enum of the user's projects, say) into descriptions or schemas. Return dynamic values from a read tool instead.
  4. Add rather than edit: a new tool shows up as one addition, which a client can hold for approval while the tools a user already approved keep working. Editing an existing tool flags the tool people depend on.
  5. Never reuse a tool name for different behaviour. Deprecate the old tool (say so in its description in one release, remove it in a later one) and give the new behaviour a new name.
  6. If your tool list can change while a session is open, declare "tools": {"listChanged": true} and send notifications/tools/list_changed when it does. If it can't, don't declare it.
  7. Publish a changelog entry for each release that changes definitions, quoting the old and new description and schema and saying why. That is what a reviewer reads when their client shows the diff.
  8. Keep descriptions plain: no zero-width, bidirectional-control or Unicode tag characters, HTML comments or base64 runs. Scanners flag them, and they make the diff unreadable.
  9. After each deploy, fetch tools/list from the live endpoint and compare it with the CI snapshot. Proxies, i18n layers and config can change what is actually served.

Why do clients flag an MCP server's legitimate tool description change as a rug pull?

Invariant Labs named the rug pull in its April 2025 tool-poisoning research: a server changes its tool descriptions after a user has approved them. Clients and scanners defend against it by recording what was approved and treating any difference as unapproved until a person looks. A hash can't tell intent, so a typo fix and an injected instruction raise the same alarm.

What should MCP server builders avoid when changing tool definitions?

Don't hot-patch descriptions in production to steer model behaviour. Fix the wording in a release, with a version bump and a changelog line.

How do I check what my MCP server's tool definitions look like to clients after a deploy?

Rightshold's tool-definition fingerprint is an open specification at /security/spec: SHA-256 over canonical JSON of the server's name and version and each tool's name, description and inputSchema, sorted by tool name. You can compute it in CI without an account. Because the server version is part of it, a release reads as one change rather than a silent drift.

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

Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “I just deployed a new release of my MCP server. Do the tool definitions it serves now match what was recorded, what exactly changed, and does any definition carry hidden text?”

One-click setup

{
  "tool": "precall_check",
  "arguments": {
    "url": "<your MCP server's https URL>"
  }
}

Rightshold doesn't run in your CI, publish your releases or notify your users' clients. precall_check and check_tool_integrity compare what your endpoint serves with a recorded fingerprint. Other clients and scanners hash their own choice of fields, so they may flag edits this fingerprint doesn't cover, such as a title, outputSchema or annotation change.

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.rightshold.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export RIGHTSHOLD_API_KEY=rs_...   # the api_key from the reply (shown once)
curl -s https://www.rightshold.com/v1/licenses/check -H "Authorization: Bearer $RIGHTSHOLD_API_KEY" \
  -H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'