A Security Review Checklist for MCP Servers: What to Audit Before You Install

Written by the Rafter Team

The MCP server ecosystem grew faster in the past nine months than any developer-tool category in recent memory. The security posture of the category grew at a fraction of that speed. Most MCP servers are installed by developers from a one-line shell command, run as a local daemon with whatever permissions the host shell has, and integrate directly into a tool-using AI agent's capability surface. That combination — unscrutinized origin, local execution, agent-tier privilege — has produced the disclosures Rafter has covered over the past quarter, from the MCP protocol design RCE to the sandworm-mode MCP worm.
This post is the audit checklist a senior engineer or security reviewer should run before approving any MCP server for a developer environment.
The checklist below is not a substitute for not installing an MCP server you have not audited. If you have time to read it but not time to run it, the safer default is "do not install."
Why "just install it" is the bug
An MCP server is a process. It runs on your machine, under your user account, with your shell's privileges. It speaks a protocol that exposes tools to an AI agent. The agent, having tools, takes actions. The action surface includes anything the MCP server chose to expose: shell, file system, network, database, your other developer tools.
The trust chain in a typical install is: developer runs npx some-mcp-server from a README, the package is fetched from npm, the package may or may not have been published by the project it claims to come from, the package runs at install time and at every subsequent invocation, the package now has agent-driven access to your environment. There are at least four trust hops in that sequence. Most installs check zero of them.
The relevant prior incidents — the sandworm-mode MCP worm, the Codex branch injection, and the MCP protocol design RCE — each exploit a different one of those trust hops. The defenses are different per hop. The audit has to cover all of them.
The checklist
1. Who publishes it, and how do you know?
Find the canonical publisher. For an MCP server claiming to integrate with a specific service (Slack, GitHub, Notion, Stripe), is the server published by that service, or by a third party? Third-party integrations are not automatically untrustworthy, but they are operating without the publisher's namespace authority, and the namespace gap is the same one exploited in the VS Code fork OpenVSX namespace attack.
Verify the publisher matches the project. Cross-reference the npm or PyPI publisher account against the project's GitHub organization, signed commits, and stated maintainer list. A mismatch is not always malicious — maintainers do move — but it always demands an explanation before installation.
2. What is the install-time behavior?
Read the package.json (or setup.py, or container Dockerfile). Does it have a postinstall script? If so, what does the script do? A postinstall script in an MCP server is the postinstall hook bug class applied to a privileged surface — execute it during install in a sandbox, never on a developer host.
3. What tools does the server expose to the agent?
This is the question most defenders skip. The MCP protocol lets a server expose any number of tools. Each tool is a function the agent can call. Each function is a privilege. Read the tool definitions.
The tool inventory should answer:
- Does the server expose shell, file write, or network egress directly?
- If it exposes a database tool, what is the database connection's privilege scope? Read-only? Schema-scoped? Production-credentialed?
- If it exposes an HTTP-fetch tool, does it allow arbitrary URLs, or does it restrict to a known origin?
- If it exposes a "search" or "query" tool, does the search return results from a source the agent will then re-invoke other tools against?
The last bullet is the lesson from the sandworm-mode MCP worm — a tool's response can be the input that triggers the next tool. A server with even one tool returning untrusted text and other tools accepting text as a parameter has the precondition for a chained attack.
4. How is the server authenticated to its backing service?
If the MCP server bridges an agent to a real service (Slack, your database, your CI), it must have credentials. Where do those credentials live? An MCP server that asks for a long-lived API token, stores it in plaintext in a config file in your home directory, and uses it for every invocation has the same security posture as an SSH key on the desktop. That is not automatically wrong, but it has to be deliberate.
Prefer servers that use short-lived tokens, OAuth with refresh, or per-invocation credentials over servers that ask for static long-lived secrets. Audit the credential lifecycle the same way you would audit any production-credential lifecycle.
5. What is the update model?
When the MCP server updates, what changes? Is the update channel pinned? Will you get the latest version on every invocation (via npx), or is there a lockfile? An MCP server pulled fresh on every invocation is a single compromise of the upstream publisher away from running attacker code in your agent context. This is identical to the Trivy TeamPCP supply-chain compromise pattern, applied to a privileged surface.
Pin the version. Update on a deliberate cadence. Treat every version bump as a re-audit of the tool inventory, not a no-op.
6. What is the audit log?
When the agent invokes a tool on the MCP server, what is logged? The default in most servers is "nothing visible to the user." An MCP server that has access to your file system or database and does not surface a per-invocation log of what it did is a forensics gap. Either the server logs, or you instrument the wrapper.
7. What is the kill switch?
If the server is compromised, how do you stop it? Most MCP servers run as a child process of an editor or terminal. Killing the editor stops the server. That is the bare minimum. The next-level kill switch is a revocation of the credentials the server holds — if the server has a long-lived API token, you need to know which token, where, and how to revoke it in under five minutes.
What "passing the checklist" looks like
A server that passes the checklist has:
- A verifiable publisher matching the project's canonical namespace.
- No postinstall script, or a postinstall script that is trivially auditable.
- A tool inventory that is read-only or per-tool sandboxed, with no tool capable of triggering another tool from untrusted input.
- Short-lived or per-invocation credentials, not static long-lived secrets.
- A pinned version with explicit re-audit on update.
- Per-invocation audit logs visible to the developer.
- A documented kill switch and credential-revocation procedure.
Most servers in the current ecosystem fail at least three of these. That is the calibration to walk into the audit with.
The Rafter angle
Rafter does not currently scan MCP servers in isolation — they live in the agent runtime, not in your code. What Rafter's code review does catch is the inverse: the code in your repository that exposes a tool to an agent and the data flow from untrusted input to that tool's invocation. The MCP-server-side audit is operational; the MCP-client-side audit is code, and code is what rafter run reads.
The category is not going to slow down. The audit discipline has to scale at the same rate the install discipline does, or it will lose.