RingZero

Blog

How to vet an MCP server before your agents load it

Every entry in a .mcp.json is third-party code — or a third-party endpoint — that runs with your agent’s privileges. Most teams review a new npm dependency more carefully than a new MCP server, even though the MCP server usually gets more access: filesystem roots, environment secrets, and a direct line into the model’s context.

Here is the checklist we use. It works for a one-off manual review; at the end, notes on what changes when you need to do this continuously across an org.

1. Confirm you have the official package

Typosquats thrive on install-command muscle memory. Before installing, find the server’s name from the vendor’s own documentation or repository — not from a search result or a pasted snippet — and compare character by character. One transposition is all it takes (the npm ecosystem spent a decade proving this).

2. Check the publisher, not just the package

  • How old is the publishing account, and what else has it published?
  • Does the package link to a real repository, and does that repository actually contain the code being published?
  • Does the resolved version carry a provenance attestation tying the artifact to a source commit and build? An unsigned package isn’t automatically malicious — but combined with a young publisher and a familiar-looking name, it should stop the review.

3. Read the tool descriptions

Tool descriptions are injected into your agent’s context every session. Read every one, in full, before approving the server. You are looking for imperative instructions aimed at the model, references to files or credentials unrelated to the tool’s purpose, directives about other tools, and any variant of “do not tell the user”. This is the tool poisoning channel — the part of the package a code review never touches.

4. Check the install-time behavior

A package that runs scripts at install time deserves the same suspicion as any dependency with a postinstall hook. The malicious versions of well-known typosquats overwhelmingly do their damage before the tool is ever invoked.

5. Pin the version

latest re-resolves every launch — which means the server you reviewed is not necessarily the server your agent loads tomorrow. Pin an exact version. This single line of config converts “silent compromise” into “a diff in a pull request”.

6. Scope it to the minimum

If the server takes a filesystem root, point it at the project directory, not $HOME. If it takes API scopes, grant read-only unless writes are the point. A compromised or poisoned server can only reach what the session hands it.

7. Keep secrets out of the config

Credentials belong in environment references (${env:API_KEY}), never inline in the server’s env block. Inline secrets end up in git history, in every checkout, and in every tool that reads the config — and they’re the first thing a scanner (or an attacker) finds.

8. For remote servers: verify the domain

An HTTP MCP server is an endpoint someone else operates. Confirm the URL is the vendor’s official MCP domain, not a lookalike, and route it through your org’s allowlist process so the next team doesn’t have to re-derive the answer.

9. Re-review on every change

A review is a snapshot. Version bumps, changed tool lists, and changed descriptions all invalidate it — remote servers can change without any version bump at all. Diff tool descriptions on change and treat unexpected differences as findings, not curiosities.

Doing this at org scale

The checklist is manageable for one server. The realistic situation is dozens of servers across dozens of repos, added by teams who never saw this list, changing underneath you. That’s an inventory-and-automation problem, not a diligence problem: something has to discover every configured server, run these checks continuously, and hand back the exact config change when one fails.

That is what RingZero’s MCP security does — each server cross-checked against the registry, descriptions scanned, trust derived from open findings, and every fix proposed as a reviewable .mcp.json PR through the same human-in-the-loop remediation pipeline as the rest of your findings.

← All posts