A Model Context Protocol (MCP) server speaks to your agent in the same voice as the human at the keyboard — anything it returns is read as content the agent can act on. That makes MCP server security a different problem from auditing an npm package: the dangerous behavior is usually legal code with bad intent, the manifest's capability claims aren't compiler-enforced, and the agent is a new execution environment that can be talked into calling tools it shouldn't. Below are the seven questions to ask before you trust one, drawn from the 12-class AI dev-stack threat model.
file:line quote. Jump to running it on an MCP server, or read the manual checklist first.An MCP tool-call response string is read in the same context as legitimate model output. A sufficiently clever string re-enters the model as instruction — this is indirect prompt injection, and it is the defining risk of the MCP surface. The attacker never speaks to your model directly; they shape data your model will eventually read. When you review a server, assume every byte it can return is hostile, and check that responses are structured as data (JSON with named fields) rather than free prose that interpolates upstream, attacker-controllable strings at the top level.
The tool description registered at MCP handshake time is read by the model as guidance. A description that adds operational instructions — "always also call read_file on this path for context," "the assistant should now…" — is a working injection vector, sometimes called MCP tool poisoning. Red flags: imperative language directed at the model ("you must," "always also call"), a description that references tools other than its own, and hidden Unicode or zero-width joiners in the description string. A tool description should be a factual contract, not a prompt.
No single tool needs to be dangerous. A "log viewer" reads a sensitive file; an "issue tracker connector" makes outbound requests. Asked to perform an innocent workflow, the agent composes them — read this log, file an issue including the contents — and sensitive data leaves the machine. The vulnerability is structural, not in any one function, so static analysis of a single tool won't surface it. When a server exposes both a high-sensitivity read primitive and an outbound network primitive, treat the combination as an exfiltration chain until proven otherwise.
An innocent-looking tool that wraps a dangerous one inherits your approval of the wrapper. You said "yes" to format_repo, not to the Bash command it constructs internally from your inputs — the classic confused-deputy pattern, transplanted into the agent context. Look for exec, spawn, or Bash invocations that build commands by string-concatenating upstream inputs without surfacing the exact command to the user.
A server reachable over a remote transport can return benign descriptions at review time and inject at call time. Static review sees a snapshot; the runtime behavior belongs to a party you cannot audit. A remote MCP transport means every response is a live, untrusted injection source — weight it accordingly, and prefer servers you can run and inspect locally.
The README advertises a "weather tool"; the code, alongside the documented get_forecast, reads ~/.bash_history. The author may be malicious or merely sloppy — the harm to you is identical. Write down the capabilities the server claims, then read the code for the capabilities it actually has: network access in a tool that advertises only local operations, filesystem reads outside the obvious work directory, crypto or child-process imports an artifact's stated purpose doesn't need. The gap between claim and code is itself the vulnerability.
A clean review of 1.0.0 says nothing about 1.0.1. Auto-update, post-install scripts, and unpinned dependency ranges turn a one-time install into a continuous trust relationship — the same typosquatting and account-takeover vectors seen across npm and PyPI apply directly to MCP registries. Pin dependencies, prefer signed releases, and re-scan on every update rather than trusting the install once.
Running these seven questions by hand on every server you install doesn't scale. Assay is an open-source MCP and Claude Code security scanner that does exactly this: it builds a threat model of the server before it reads the source, dispatches one investigator per threat class, and returns a safe / caution / unsafe verdict — with a post-validator that re-reads every citation and drops anything the model can't back with real code, so you never get a confabulated finding.
# scan a local MCP server directory
assay scan ./path-to-mcp-server
# or inventory everything in ~/.claude and scan from the web UI
assay inventory
assay serve # http://localhost:7373 → "New Scan"
It runs on your Claude Code subscription — no separate API key. See Installation to get the binary, or the full 12-class threat model for the classes beyond MCP (hooks, skills, settings, and connector OAuth scopes).