Answers · MCP security
How do I pin MCP server versions and review what changed before upgrading?
Short answer: Run every local MCP server at an exact version, make each upgrade a deliberate change, and review the published package and its tool list, not just the changelog, before you switch. In practice: replace npx some-server or @latest with some-server@1.4.2 (uvx some-server@1.4.2 for Python, or a container image by digest); add a release-age delay so brand-new versions aren't installed the day they appear; when an update is offered, read the release notes, diff the two published versions with npm diff, run the new version in a sandbox and compare its tools/list with the one you approved; then change the pin. The changelog alone is not enough: in September 2025 the npm package postmark-mcp, which impersonated Postmark, shipped 15 working versions and then a 1.0.16 whose one added line BCC'd every email to the attacker. Nothing in a version number tells you that; a diff of the published code does.
Checklist
- Pin exactly in your client config: an exact version in the args ("some-server@1.4.2", never @latest or a bare package name), uvx some-server@1.4.2, or a Docker image referenced by @sha256 digest. For servers you install as dependencies, commit the lockfile.
- Add a release-age delay: npm's min-release-age (days), pnpm's minimumReleaseAge (minutes; 1440, one day, by default in current pnpm), and uv's exclude-newer (accepts durations such as "7 days") refuse versions newer than the window, which gives the registry and researchers time to catch a malicious release.
- Let a bot open the upgrade, not your agent: Dependabot or Renovate turns a new version into a pull request you review. Dependabot applies a 3-day cooldown to version updates by default and lets you set your own with cooldown; security updates skip the cooldown.
- Read the release notes and the compare view between the two tags, and look for new tools, new network destinations, new environment variables or credentials, and new install scripts.
- Diff what is actually published: npm diff --diff=some-server@1.4.2 --diff=some-server@1.5.0 compares the two registry tarballs. The source repository can look clean while the published package differs, and the package is what runs.
- Check provenance: npm audit signatures verifies registry signatures and provenance attestations of installed packages; a package that suddenly loses provenance or changes publisher deserves a closer look.
- Diff the tool list before you switch: start the new version in a sandbox with no real credentials, fetch tools/list (the MCP Inspector can do this), and compare names, descriptions and schemas with the hash you stored at approval. A new or changed tool needs a fresh approval.
- Change the pin in one reviewed commit, keep the old version one line away for rollback, and log which version each environment runs.
What pinning does and doesn't cover
- Pinning protects you from a bad release only until you upgrade to it, so the review at upgrade time is the real control; the pin just makes sure there is an upgrade time.
- A remote (HTTP) MCP server has no version you can pin: its operator can change its code and tool definitions at any time. For remote servers, re-check the tool-definition hash on every tools/list and keep confirmations on for actions that write, send, pay or delete.
- Dependencies of the server change too. A lockfile or a container image by digest pins the whole tree; an exact version in npx args pins only the top-level package.
Why a delay window helps
- pnpm's documentation notes that in most cases malicious releases are discovered and removed from the registry within an hour. A delay of a day or more means you install a version other people have already run, not the one published an hour ago.
- Keep a way around the delay for urgent security fixes: npm's min-release-age-exclude, pnpm's minimumReleaseAgeExclude, and Dependabot's security updates, which ignore cooldown.
Ask Rightshold (free, no key)
Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “This MCP server just released a new version. Has its tool list changed since it was recorded, and what exactly changed?”
{
"tool": "check_tool_integrity",
"arguments": {
"server": "<the server's Rightshold slug or id>"
}
}- The answer is unchanged, changed (with a plain diff of tools added, removed, description changed or schema changed) or unreachable, which is never reported as unchanged. It works for servers registered on Rightshold.
- precall_check gives a one-read summary for any https MCP server: registry entry, live integrity probe, invisible-text scan of the definitions it serves now, security flags and tool-name collisions.
- The tool-definition fingerprint construction is published at /security/spec, so you can compute the same hash for a local server in your own CI.
Rightshold compares remote servers' tool definitions with recorded fingerprints. It does not diff npm or PyPI packages, review changelogs, or certify that a version is safe.
Sources
- Koi Security: First malicious MCP in the wild: the postmark-mcp backdoor
- Postmark: Information regarding the malicious postmark-mcp package
- npm docs: config (min-release-age)
- npm docs: npm diff
- npm docs: npm audit (audit signatures)
- pnpm docs: minimumReleaseAge
- uv docs: exclude-newer
- uv docs: running tools at a specific version
- GitHub docs: Dependabot options reference (cooldown)
- MCP Inspector
Related
- How to detect MCP tool description changes (rug pulls)
- How to vet an MCP server before installing it
- How to tell if a Claude Code or Cursor plugin is safe to install
- Tool-definition fingerprint spec
Not legal advice. Updated 2026-10-29. 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}}'
