GUIDE · MCP 9 min ·

MCP supply chain hygieneWhat to audit. What to pin. What to verify.

The MCP ecosystem hit 7,000 servers and 150 million downloads before any signature requirement existed. Installing a server today is a trust decision disguised as a config change.

TL;DR· the answer, in twenty seconds

What: The MCP package ecosystem has ~7,000 servers and ~150M downloads (OX Security, early 2026) with no signature requirement, no reputation system, and casual install instructions. Floating latest versions, look-alike package names, and solo-maintainer repos are common.

Fix: Pin every MCP server to an exact version or commit hash. Clone and audit source before first install. Add a CI step that checks installed servers against a verified allowlist.

Lesson: MCP install hygiene is the same playbook as early npm security: pin, vendor, diff, verify. The ecosystem is early enough that most teams skipped step one.

The mcp-postgres server has 40,000 weekly downloads. mcp-postgressql has 200. Both exist on npm.

The typo version is newer, and most people have not looked at the name since they pasted the config snippet. The MCP ecosystem in early 2026 looks like npm circa 2014: fast growth, casual install culture, no tooling to verify what you are running.

OX Security counted roughly 7,000 MCP servers in the wild with around 150 million total downloads by early 2026. None of those installs required a signed release. Most came from one-line config snippets in README files: "just add this to claude_desktop_config.json."

The risk is not theoretical. Snyk published a full walkthrough of the MCP prompt-injection data heist in early 2026, showing how a malicious tool description can redirect an agent that already has live credentials. That attack requires the server to be running. Supply chain compromise is how the server gets malicious in the first place.

What to know in 60 seconds

  • Every MCP server you install runs as a process that inherits your agent's credential environment. There is no sandbox.
  • Floating latest versions mean a maintainer push or a registry compromise updates your running server without any review.
  • Look-alike names (mcp-github is community, not GitHub-official) are already present on npm and PyPI.
  • AI coding tools sometimes suggest package names that do not exist. Real packages with those names can be registered by anyone. Lasso's late-2025 slopsquatting research documented this pattern across multiple ecosystems.
  • Signed releases are rare. Anthropic signs its own packages. Most MCP servers do not.

The state of the MCP supply chain

npm took about five years to develop meaningful package signing culture after the first major incident. PyPI is still working on it. MCP is in year one.

The distribution path is unusually casual. Install instructions are typically a JSON snippet the user pastes into a config file. No install step, no lockfile, no integrity hash. The package manager handles the download, but the agent host does not verify what it received before granting the server process a live connection to your tools and credentials.

Maintainer concentration makes this worse. Browse the first page of MCP servers on npm and you will find most of them list a single maintainer with admin rights on both the registry org and the GitHub repo. One compromised GitHub account, one malicious package push, and every pinned-to-latest install updates silently on the next agent restart.

GitGuardian's 2026 State of Secrets Sprawl puts AI-service token leaks at +81% year-over-year. The MCP runtime model, where the server process inherits the full credential environment, creates an unusually wide blast radius if a server is compromised. The agent's GitHub token, Stripe key, database URL, and OpenAI API key are all in scope simultaneously.

How to run clean installs

Pin versions explicitly

Every MCP server in your config should reference an exact version. Not latest. Not a semver range.

For npm-based servers, pin in package.json and commit the lockfile:

{
  "dependencies": {
    "@modelcontextprotocol/server-github": "0.6.2"
  }
}

For PyPI-based servers, pin in requirements.txt:

mcp-server-sqlite==0.4.1

For GitHub-sourced servers, pin to a specific commit hash, not a branch name:

pip install git+https://github.com/example/mcp-server-thing@a3f9c1d2e8b7f06e1234567890abcdef12345678

Branch names move. Tags can be deleted and recreated. Commit hashes are permanent.

Vendor the source

For servers your team depends on for production workflows, clone the repo, review the code, and install from your own copy.

git clone https://github.com/modelcontextprotocol/servers.git ./vendor/mcp-servers
cd ./vendor/mcp-servers
git checkout 0.6.2
npm ci --workspaces

Keep your vendored copy on a separate branch. When you want to update, run a diff of the incoming changes before merging:

git fetch origin
git diff HEAD..origin/main -- src/

Any update that adds a network call, reads new filesystem paths, or touches environment variables warrants a closer look. Most updates do not. The diff is fast. The alternative is running code you have never read.

Track maintainers

Check who controls each server you install.

For npm packages:

npm access list collaborators @modelcontextprotocol/server-github

For PyPI packages, look at the project's "History" page and "Maintainers" section on pypi.org.

A solo maintainer with admin rights on both the registry account and the GitHub repo is a single point of compromise. One phishing email or session token theft exposes every downstream install on the next agent restart.

Pin tightly. Diff every update before applying it.

For servers your team uses daily, set a quarterly calendar reminder to re-audit maintainer lists. Maintainers leave projects. They transfer npm org ownership informally. The maintainer who wrote the initial code in 2025 may have handed off admin rights by the time you read this.

Check signed releases when available

Anthropic signs releases for its own MCP servers. A small number of other organizations do as well. For packages that publish signatures, verify before installing.

For packages with GitHub Releases and attached checksums:

# Download the checksum file from the release page
curl -fsSL https://github.com/modelcontextprotocol/servers/releases -o SHA256SUMS
# Verify the downloaded tarball
sha256sum --check SHA256SUMS

Most MCP servers do not publish signatures. Treat unsigned packages as requiring source review rather than binary trust.

Diff dependency updates before applying

MCP servers have their own dependencies. An update to mcp-server-postgres might pin to a new version of a database driver that has never been reviewed. Run a full dependency diff when you update a server.

# Before updating
npm list --all > deps-before.txt
# After updating
npm list --all > deps-after.txt
diff deps-before.txt deps-after.txt

Flag any new transitive dependency. AI coding tools sometimes suggest package names that plausibly fill a gap in the dep tree but do not exist on the registry. A real package with that name can be registered by anyone. Lasso documented this "slopsquatting" pattern across npm and PyPI in late 2025. The mitigation is the same as for typosquatting: review new packages before they reach a running server.

Three failure modes that are already happening

Typosquatting

mcp-postgressql versus mcp-postgres. mcp-githb versus mcp-github. These names exist or can exist. The install instruction is a paste-from-README operation, and README text can be manipulated.

Before adding any MCP server to a config, verify the exact package name against the organization's own documentation, not a third-party tutorial or a search result. For community servers not published by the upstream service, check the npm or PyPI org page to confirm the publisher matches who you expect.

mcp-github on npm is published by the community MCP project, not by GitHub. The name looks official. It is not a typosquat, just a naming ambiguity, but easy to misjudge if you expect official provenance.

Slopsquatting

AI coding tools generate package names. When a tool suggests mcp-server-notion-sync or mcp-linear-integration, check whether the package actually exists before running the install command. If it does not exist and you install the suggestion anyway, npm will fail, which is the good outcome. If someone registered that name in advance, you just installed their code.

Lasso's research from late 2025 showed this pattern across multiple package ecosystems. The MCP ecosystem is a particularly attractive target because installs go directly into an agent that may already hold credentials for every service the package name references.

Solo maintainer risk

A package with a single maintainer, no CI, and no second reviewer on releases ships updates on one person's judgment and one person's account security. The maintainer may be entirely trustworthy. The account is still a single point of failure that compound events can exploit.

The Knostic February 2026 Claude Code disclosure involved a different failure mode (state files, not supply chain), but the disclosure itself triggered a wave of audits of MCP server installs. Several teams found they were running servers where the last commit was two years old, the maintainer had changed employers twice, and the npm account had no 2FA enforced.

Check npm 2FA status for any package you depend on: the package page shows whether the maintainer account requires 2FA for publishes.

What the conventional advice gets wrong

Most "MCP security" posts focus on prompt injection. Snyk's early 2026 MCP heist writeup is good, and it is the right thing to publish. But prompt injection requires the attacker to control input to your agent. Supply chain compromise gives the attacker control of the server binary itself.

A malicious MCP server does not need to inject into the agent's prompt. It runs as a process with your credentials already in scope. It can exfiltrate silently via DNS, HTTP, or subprocess. The agent sees normal tool call results. The audit log shows normal operations. Nothing looks wrong until you notice the outbound traffic.

A broker changes what "in scope" means. The compromised server process inherits a reference string, not a raw key. Exfiltrating that reference sends the attacker something with a session ceiling and an audit trail, not a long-lived credential they can use indefinitely. Source review and version pinning remain the right controls for keeping bad code out. A broker limits what bad code can take if it gets in anyway.

The other conventional advice that misses the mark: "read the code before you install." For a 200-line server, yes. For a server with 40 transitive dependencies across three ecosystems, reading the code before install is aspirational. Vendoring, pinning, and diffing updates is what actually scales.

A checklist you can paste into a PR

## MCP server install audit

- [ ] Package name verified against official documentation, not a tutorial
- [ ] Version pinned to exact semver or commit hash in config
- [ ] lockfile committed (package-lock.json / uv.lock / pip freeze)
- [ ] Maintainer list reviewed; 2FA status checked on npm/PyPI
- [ ] Source cloned and reviewed before first install
- [ ] Transitive dependencies diffed against known-good baseline
- [ ] Signed release verified if publisher provides checksums
- [ ] CI step added that checks installed servers against allowlist with hashes
- [ ] Dependency update diff reviewed before merge (not auto-merged)
- [ ] Incident plan documented: who rotates credentials if this server is compromised?

The CI workflow

Drop this into .github/workflows/verify-mcp.yml:

name: Verify MCP servers

on:
  push:
    paths:
      - 'package.json'
      - 'package-lock.json'
      - 'requirements.txt'
      - 'claude_desktop_config.json'
  pull_request:

jobs:
  mcp-allowlist:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: npm ci

      - name: Check MCP servers against allowlist
        run: |
          # List installed MCP packages with exact versions
          npm list --json | jq -r '
            .dependencies
            | to_entries[]
            | select(.key | startswith("@modelcontextprotocol/") or startswith("mcp-"))
            | "\(.key)@\(.value.version)"
          ' > installed-mcp.txt

          # Compare against allowlist
          echo "Installed MCP servers:"
          cat installed-mcp.txt

          while IFS= read -r pkg; do
            if ! grep -qF "$pkg" .mcp-allowlist.txt; then
              echo "FAIL: $pkg is not in .mcp-allowlist.txt"
              exit 1
            fi
          done < installed-mcp.txt

          echo "All MCP servers verified against allowlist."

Commit .mcp-allowlist.txt to the repo. It contains one package@version entry per line for every approved MCP server. Any PR that adds a new server without updating the allowlist fails the check.

What this means for your stack

The MCP install problem is the same problem as ambient credential access in AI agent runtimes generally. An MCP server process that runs with your full credential environment is a high-value target. Tightening who can supply that server, and verifying that the code running matches the code you reviewed, removes the easiest entry point.

hasp is one working implementation of the broader fix. curl -fsSL https://gethasp.com/install.sh | sh, hasp setup, connect a project, and the agent receives scoped, time-limited references rather than raw credentials. A compromised MCP server that exfiltrates your environment captures references with a 24-hour ceiling, not long-lived keys. Source-available (FCL-1.0), local-first, macOS and Linux, no account.

The supply chain problem does not fully go away with credential scoping, but the blast radius shrinks to what the agent actually needed for that session, not everything in your shell.

Sources· cited above, in one place

NEXT STEP~90 seconds

Stop handing the agent your real keys.

hasp keeps secrets in one local encrypted vault, brokers them into the child process at exec, and never lets the agent read the value.

  • Local, encrypted vault — no account, no cloud, no telemetry by default.
  • Brokered run — agent gets a reference, the child process gets the value.
  • Pre-commit + pre-push hooks catch managed values before they ship.
  • Append-only HMAC audit log answers "did the agent touch the prod token?" in seconds.
→ okvault unlocked · binding ./api
→ okgrant once · pid 88421
→ okagent never read

macOS & Linux. Source-available (FCL-1.0, converts to Apache 2.0). No account.

Browse all clusters· eight threads, one index