Approved Last Month, Replaced This Morning: Two-Thirds of Public MCP Servers Run Unpinned

An MCP config file decides what code runs on a developer’s machine every time their agent starts — and almost nobody re-reads it after pasting it from a README or letting the agent fill it in. On 8 November 2026, the security-measurement outfit X1Y1 published a census of what those files actually contain: 12,398 config files collected from public GitHub repositories on 7–8 November (the four common locations: claude_desktop_config.json, .mcp.json, .cursor/mcp.json, .vscode/mcp.json), 10,850 unique after dedup, 10,659 containing at least one MCP server, defining 32,223 servers in total. Of those, 14,098 launch from a package registry (11,973 via npm’s npx, 2,125 via PyPI’s uvx/pipx). Registry and OSV vulnerability status is current as of 8 November. Only 5,154 of the 10,659 files (48%) had nothing to fix or review.

The headline number is the pinning gap: 9,465 of the 14,098 registry-based servers (67%) specify no exact version — npx -y some-mcp-server or @latest fetches whatever is newest on every agent start — and 4,040 files (38%) contain at least one unpinned server. Nobody reviewed that code before it ran, and a compromised release reaches every machine the next time the agent restarts. As the study puts it: if a package was approved last month and changes today, is it still approved? With an unpinned config, approval never happens at all. Readers of this site’s MCPhantom coverage will recognize the shape of the problem — agents installing what the registry serves today, not what anyone vetted — now measured at population scale rather than demonstrated per-incident.

Dead servers, wrong registries, and squattable names

Beyond pinning, three findings describe configs rotting in place. 941 files (9%) use a package npm marks deprecated, and 819 of them run one of the original MCP reference servers archived in May 2025 that will never receive security fixes — @modelcontextprotocol/server-github alone appears 580 times, followed by server-postgres (259), server-puppeteer (129), and server-brave-search (126), despite maintained replacements existing (GitHub’s own github/github-mcp-server, Microsoft’s @playwright/mcp). Then there are the 638 files naming packages that do not exist at all (1,381 servers): the most common guesses — @modelcontextprotocol/server-fetch (110), server-git (56), server-sqlite (50) — are the official Python servers mis-guessed as npm names by analogy, by a human or by an agent. Scoped mis-guesses under @modelcontextprotocol/ are squat-proof (only that org can publish there), but 145 servers in 53 files use the @anthropic/ scope — while Anthropic publishes on npm as @anthropic-ai, and the @anthropic scope belongs to an unrelated organization that could publish those exact names. Unscoped guesses like task-master-mcp can be registered by anyone.

The study connects that last pattern to a case where the trap already snapped shut. In June 2026, Amazon Inspector flagged malicious npm packages named mcp-server-fetch, mcp-server-git, mcp-server-notion, mcp-server-postgres, and mcp-server-redis (MAL-2026-5476 and siblings), with advisories stating the names were chosen to catch npx calls from AI coding agents; the mcp-server-git package ran a postinstall script exfiltrating hostname, working folder, platform, and Node version. npm replaced all five with empty security placeholders on 20 August — and 15 files in the November sample still invoke one of those placeholder names (13 of them one of the five), meaning the same config line installed the malicious version before the takedown and installs a dead placeholder today. We confirmed MAL-2026-5476 independently in OSV; the remaining four IDs and the exfil details are carried as reported in the study.

152 files with likely-real tokens, and the rest of the checklist

The secrets count is deliberately conservative and still bad. A first pass flagged 1,122 files with something secret-shaped in env, args, headers, or URL fields — but most are placeholders or local database passwords, so the authors recounted under strict rules (known token formats for OpenAI, Anthropic, GitHub, Google, AWS, Slack, and Stripe; nothing placeholder- or example-shaped). That left 152 files with likely-real tokens: 96 OpenAI-format keys, 26 Google API keys, 18 Anthropic keys, 23 GitHub tokens. The authors state they tested none of them and are not naming the repos — the correct handling, and a number this site’s readers should stack against October’s three independent secrets-in-MCP-config counts, which found the same pattern from different samples. Four studies, same conclusion: the config is the credential store.

The remainder reads like a hardening backlog with numbers attached: 453 remote servers (6% of remote ones) still use legacy SSE transport, superseded by Streamable HTTP in March 2025; 74 servers talk to remote hosts over plain HTTP, sending tokens and tool results unencrypted; 68 servers auto-approve tools whose names say they write, send, or delete; and 684 Docker-based servers float on latest or an untagged image. The study states its limits plainly — public repos include tutorials and templates, and GitHub code search caps at 1,000 results per query (split by file size), so this is a large sample, not a census — and it comes from a vendor that sells the free checker used for the scan, which ran browser-local and sent only package names to public npm/PyPI/OSV APIs. Single-vendor sourcing noted; the methodology is transparent enough to re-run, and the raw failure classes are individually checkable.

What to do

  • Pin every version this week. Exact versions for npm and PyPI, a tag or digest for Docker — then update on purpose, after reading the changelog. Start with the 38% of files carrying at least one unpinned server; that is the population most exposed to the next compromised release.
  • Delete the archived reference servers and verify every name resolves on the right registry. Replace the May-2025 archived servers with vendor-maintained equivalents, confirm Python servers run under uvx (not npx), and check scope ownership (npm view <name> maintainers) before pinning — pinning the wrong publisher’s package locks in the wrong trust decision.
  • Move keys out of the file and rotate anything ever committed. Claude Code expands ${VAR} in .mcp.json; VS Code can prompt once with ${input:id} and store securely. If a config ever touched a repo, gist, or dotfiles backup with a key in it, the key is burned.
  • Stop the agent from adding new servers unreviewed. Put the approved server list in CLAUDE.md or AGENTS.md and block anything else with a pre-tool hook — the same approved-list-plus-hook pattern this site recommended after testmetest armed repos to execute through agent hooks.

Verification note: the X1Y1/DEV Community article of 8 November 2026 was read first-hand in full (12,398-file collection across four config paths on 7–8 November, 10,850/10,659/32,223/14,098 population figures, 67%/38% unpinned rates, 941/819/580 deprecation counts, 638-file/1,381-server nonexistent-name and 145-server/53-file @anthropic-scope figures, MAL-2026-5476–5482 June–August history, 1,122-to-152 secrets recount with per-vendor breakdown, SSE/plain-HTTP/auto-approve/Docker figures, stated sampling limits, and the remediation checklist). MAL-2026-5476 was independently confirmed present in OSV; the remaining advisory IDs and exfil details are carried as reported in the study. Community comment threads on the article were reviewed for reception only. We downloaded no configs, probed no registries beyond the single OSV lookup, tested no tokens, and contacted no vendor.

Sources: