// Writing
How to Vet an MCP Server Before You Install It
Every MCP server you connect to an agent is a program you didn't write, running with your agent's permissions, on your machine or your network, answering to instructions an AI model will read and probably follow. That sentence should be uncomfortable. Installing one is a trust decision, and most teams currently make it by skimming a README and clicking add.
The timing pressure is real. MCP adoption went from roughly 100,000 monthly SDK downloads in November 2024 to over 97 million by December 2025, with more than 10,000 active public servers and first-class client support across ChatGPT, Claude, Cursor, Gemini, Microsoft Copilot, and VS Code, per Anthropic's own ecosystem update. When Anthropic donated the protocol to the Agentic AI Foundation under the Linux Foundation that same month, it became infrastructure, and infrastructure gets judged by different standards. Meanwhile, the audit numbers are bleak: only about 8.5% of servers use OAuth, the majority still pass credentials through static API keys stored in environment variables, and a 2026 GitGuardian sweep found over 24,000 secrets sitting in MCP-related config files on public GitHub, 2,117 of them still valid. Clutch Security found that 86% of MCP servers run locally on developer machines rather than in managed production environments, which means that in most cases there's no procurement step and no audit log. There's just a person and a config file.
There is no audited MCP registry. No registry publishes a "percentage security reviewed" figure, because for most servers the honest answer is zero. The vetting step, wherever it happens, is you.
This post is that step. It's a vetting workflow for the moment before you wire a server into an agent, and it builds on the agent red teaming guide, whose playbook covers probing a server after you know which one you're considering and checking the config you end up with afterward. The gap in between, deciding whether a specific server deserves to exist in your setup at all, is what nobody documents. Here it is as steps.
Step 1: Establish provenance
The first question isn't "does this server have vulnerabilities." It's "who published this, and what do they lose if it's bad?"
Trace the package to its source. Official servers from the modelcontextprotocol GitHub org, first-party servers from a vendor you already use (Supabase, GitHub, Notion), and widely-adopted community servers with years of history and a real maintainer team are three different risk tiers, and your scrutiny budget should match. A server published last week by an account with no other projects, wrapped around a popular API, is a different animal, no matter how well its README reads.
The supply-chain risk here isn't hypothetical. In September 2025, a malicious version of the postmark-mcp npm package silently BCC'd copies of every processed email to an external domain. It was, according to Snyk, the first tracked case of a deliberately malicious MCP server distributed through the ecosystem's own supply chain. The package worked exactly as documented. It just also did something else, quietly.
The earlier, bigger lesson is mcp-remote. mcp-remote is a bridge package that lets MCP clients talk to remote servers over HTTP, and it had accumulated 437,000+ downloads when JFrog researchers found a command injection flaw in it. Because of how mcp-remote works, connecting to a malicious remote MCP server through a vulnerable version meant code execution on the local machine, and JFrog noted it was the first time full RCE had been demonstrated in that scenario on a real client operating system. One package, bridging every client to every remote server. The lesson: the vetting target is every package and proxy in the chain between your agent and the server, not just the server itself.
What to actually check:
- The package, its repository, and its maintainer history line up (same org, real commit history, real issues traffic, not a vanity repo pushing releases).
- The published package matches the source (a rebuilt diff against the repo tags, when the stakes warrant it, catches the postmark-mcp pattern).
- Install count and age are directionally sane for what it claims to be.
Step 2: Read the manifest like a shell command
Most MCP servers install as a command line in a config file. Read that line as exactly what it is: a command that will be executed on your machine, by your shell, every time your agent starts.
This is not a theoretical framing. In April 2026, OX Security disclosed that the STDIO transport, the default local transport used across the official MCP SDKs for Python, TypeScript, Java, and Rust, processes configuration parameters by passing them directly to the operating system's shell without sanitization or validation. An attacker who could influence an MCP configuration file, through a compromised repository, a malicious registry entry, or social engineering, achieved arbitrary code execution on the target machine, regardless of whether the configured target ever initialized as a valid MCP server. The OX research led to more than thirty responsible disclosures across downstream projects, including LiteLLM, LangChain, Flowise, LibreChat, and Windsurf.
The more uncomfortable detail: Anthropic confirmed the STDIO behavior was intentional, updated the SECURITY.md file to note that STDIO adapters should be used with care, and declined to modify the protocol architecture. Remediation responsibility sits with individual downstream developers. Read that as a design statement. The reference implementation trusts its config input, and the ecosystem standard you're plugging into will do the same unless the local developer does otherwise.
A standard that passes its config straight to the shell, and a governance model where you own the sanitization, means the vetting gate isn't the registry or the SDK. It's the line you paste into your config file.
So read the command line and require justification for every piece of it. Why does this server need that flag? Why that package runner? Anything with a pipe, a redirect, a subshell, or a shell variable should trigger the question "would I type this into a terminal if I had to read it out loud first?" If a README tells you to paste a curl pipe straight into bash, that README is describing, in 2026, exactly the pattern that made mcp-remote a mass RCE.
Step 3: Check the tool descriptions against what the UI shows you
The defining structural weakness of the MCP tool layer lives in an asymmetry. The text of a tool description is what the model consumes as part of deciding when to call the tool and how. It's also the part nobody reads in the product during configuration, because runtime clients don't show you descriptions as primary UI.
That asymmetry has a name, tool poisoning, and it's been demonstrated repeatedly since Invariant Labs' 2025 disclosure. Their proof was elegant: a malicious trivia-game MCP server, connected alongside a legitimate WhatsApp MCP server in the same session, embedded hidden instructions in its own tool description. The agent followed those instructions, had the legitimate WhatsApp server hand over its message history, and routed the data outbound through the trusted server, so the exfiltration looked like the normal behavior of the well-behaved tool you deliberately installed, running above the transport's encryption boundary.
The related failure mode is the rug pull. A server's tool description isn't guaranteed stable. A server whose tools redefine themselves after trust exists, or re-publishes under a mutable package registry that lets a maintainer swap behavior post-hoc, is a server whose Step 1 and Step 2 vetting you'd have to redo every time it released.
Before install, do the thing most people skip: read the tool descriptions with your own eyes. Every text string that the protocol exposes to the model, the server should be willing to also show to you. When you can't inspect them, that's your finding.
If the AI reads it as instructions and I can't read it as a human, it doesn't go in the config.
Step 4: Understand the auth and credential model
The June 2025 MCP spec separates the resource server from the authorization server and recommends OAuth 2.1 with PKCE for HTTP-based servers. The reality on the ground is that OAuth remains the minority. In an Astrix credential audit of 5,200+ MCP servers, 88% required credentials, and of those, 53% relied on static API keys or personal access tokens, only 8.5% used OAuth, and 79% expected keys to be passed through environment variables.
Read that as a description of what you'll be handling. The median MCP server in the wild wants you to provision a long-lived static credential with a broad scope and leave it sitting in a config file or an env var. Then your agent makes tool calls with it. That's a credential management problem as much as a server problem.
Check three things during vetting:
- Does the server's auth posture match what it claims in its docs? A server whose homepage advertises enterprise auth but whose endpoint answers an unauthenticated handshake isn't "confused," it's lying somewhere, and you can run a five-minute probe to find out which.
- What scope does the server demand? A read-only search tool that requests a token with write access across an entire workspace is asking for more than its function requires, and the confused-deputy problem behind Simon Willison's "lethal trifecta" framing is exactly what happens when broad access and unsupervised tool calls coexist.
- Where does the credential live once you've provided it? A config file with 0600 perms on a personal laptop is a different decision than CI logs, a shared volume mount, or any place that gets synced.
The secrets sprawl is a present-tense tax on this whole pattern. GitGuardian's 2026 report, cited earlier, counted 24,008 secrets leaked in MCP-related config files on public GitHub, 2,117 of which were still valid at the time of scan.
Step 5: Size the blast radius before you connect
Vetting's final step is the most skipped. What can this server do, in its worst case, and does my configuration limit it to what I actually need?
The worst-case numbers are now mainstream findings. Endor Labs found that 82% of 2,614 sampled MCP implementations used file operations vulnerable to some form of path traversal, meaning a file-backed server is usually a server that can be steered into reading files outside its intended boundary. Equixly's offensive security assessment found 43% of tested MCP servers vulnerable to command injection, the class of flaw behind several of the highest CVSS CVEs in the ecosystem. And a successful compromise of one server in a multi-server setup rarely stays contained there; academic work on the cascade effect measured cross-server compromise rates around 72% when multiple MCP servers share a host and an agent.
That last number is the reason Step 5 is about your configuration, not the server's. Containment is the only control that doesn't depend on the server being honest. Before you connect:
- Enumerate every tool you're enabling and ask what the worst call is, with worst meaning worst for your data, not for the server's own security posture.
- Scope credentials to the minimum the tool set requires, not the maximum the integration supports.
- Separate agents and servers across trust boundaries deliberately. The server that browses the public internet and the server that holds your production database credentials should never be peers in a single agent's tool list by default.
- Prefer read-only modes when a server offers them, and require an explicit decision to enable anything that writes, deletes, executes, or sends.
- For web apps the agent talks to, containment has a browser-side form too: a strict Content-Security-Policy bounds what an injected script can do even when everything else failed. The CSP Header Analyzer grades a pasted header and explains each finding — paste it before you ship the policy, not after the incident.
This step is why vetting ends in your config file, not in someone else's scan results.
The honest limits of any vetting pass
A vetting pass is a decision aid, not a clearance. Three things it cannot do:
- It cannot catch semantic prompt injection planted in a tool description that reads benign to a human but means something different to a model in context. That class of deception requires behavioral testing, not static review.
- It cannot predict rug pulls. Everything you've vetted is a snapshot; a mutable dependency is a standing invitation to re-audit, and package registries make that a matter of when, not whether.
- It cannot save you from a bad decision you already made. If a server is in your config and you've stopped questioning it, no checklist applies. The vetting pass is only as durable as the habit.
What the pass does give you is a fast, repeatable way to look at the ecosystem's actual baseline numbers and know, for the one server you're about to trust, exactly which of those failure classes you've checked and which you've accepted. That's what "I know the risk" is supposed to mean, and right now, most installs can't say it in one sentence.
Most teams will start with the server they were about to add anyway. Run the manifest step, the tool-description step, and the blast-radius step on that one this week. If any of them comes back ugly, the nice thing about a protocol this portable is that there's usually another server that does the same job, published by someone with more to lose.
Once it passes vetting, the config it becomes needs its own look — the red teaming guide's playbook step on the tool layer covers what to check next. And if you want to be sure what a fresh install is actually exposing, probe it before the next re-vet.
// Read next — more in Agentic AI security
Your AI Gateway Is on the KEV Catalog Now
CISA put LiteLLM, Starlette, and Kestra on the KEV catalog on the same day. The AI plumbing you never inventoried is the part being exploited in the wild.
5 min read
Agentjacking: Fake Errors Can Make Your Coding Agent Run Attacker Code
One fake error event, injected through a public Sentry DSN, can hijack Claude Code, Cursor, or Codex into running attacker-controlled commands. Here's the chain, why your security stack can't see it, and the five controls that actually stop it.
9 min read
How AI Agents Exfiltrate Data — Five Paths Your DLP Was Never Built For
Your DLP stack was built to stop people copying files to USB sticks. Agents leak data through API calls, context windows, and memory stores. Here are the five paths — and the control model that actually closes them.
11 min read