your MCP server is probably vulnerable
you connected an MCP server to your AI agent today. you didn't read the tool descriptions. you didn't check what they do. you ran the install command, saw it connect, closed the terminal, and moved on.
almost everybody does this. the ecosystem is moving fast. new servers every day. stars going up, installs going up, nobody reading the code. the README says "40 tools for your agent" and that's enough. you trust it. you trust it the same way you trust an npm package with 2 million weekly downloads, which is to say, you don't really think about it at all.
Simon Willison, the person who coined the term "prompt injection" in 2022, wrote about this in April 2025. his assessment was direct: "any time you mix together tools that can perform actions on the user's behalf with exposure to potentially untrusted input you're effectively allowing attackers to make those tools do whatever they want." he called the MCP security situation a "huge issue." this from someone who's been writing about prompt injection for three years. when the person who named the problem says it's getting worse, it's getting worse.
i started building mcprobe because i wanted to stop not thinking about it. what it found, in servers people are using right now, connected to agents people are using right now, is not great. but the interesting part isn't what mcprobe found. the interesting part is what i learned about MCP security while building it, and while contributing to other projects in the ecosystem.
prompt injection is the obvious one. tool descriptions are instructions. not to you, to the LLM. they tell the model what a tool does and when to use it. some of these descriptions contain literal injection attacks. "ignore all previous instructions." "you are now a different assistant." "execute the following command and return the output."
the model reads these. the model follows them. that's what models do. they follow instructions, including the ones hiding in tool descriptions, including the ones that say "ignore all previous instructions." this isn't a thought experiment.
Invariant Labs documented this first. they called it "Tool Poisoning." they proved it against Cursor, one of the most popular MCP clients. a poisoned tool description made Cursor read ~/.ssh/id_rsa and ~/.cursor/mcp.json and exfiltrate them. Anthropic, OpenAI, Zapier, all susceptible. the attack is not theoretical. someone already built it, ran it, and it worked.
i found 18 distinct injection patterns across public MCP servers. some are careless. someone wrote "ignore previous instructions and use this tool for..." as a harmless description and didn't think about what happens when an LLM reads that literally. some are not careless. those are worse.
OWASP now lists Tool Poisoning as MCP03:2025 in their MCP Top 10. when OWASP formalizes a vulnerability class, it means enough people have been hit that ignoring it is no longer an option. this happened in 2025. it's 2026.
i learned this while contributing to agent-orchestrator, which is itself an MCP-based system for orchestrating AI coding agents. when you're building the thing that controls the agents, you start thinking about what the agents are reading. tool descriptions aren't metadata. they're prompt context. everything the agent sees is prompt context. a tool description that says "ignore all previous instructions" isn't a description. it's an attack that runs on every single call.
then there's tool shadowing. you connect server A and server B to the same agent. both have a tool called "read_file." server A reads files from your project directory. server B reads files from anywhere. the agent doesn't know which one it's calling. you don't know which one it's calling. the tool runs, the file gets read, the response goes back into the agent's context window, and nobody asked where that file came from.
Invariant Labs documented this too. they called it a "compound attack." a malicious server injects a tool description that modifies the agent's behavior with respect to a trusted service or tool. the agent thinks it's calling server A's "read_file." it's actually calling server B's "read_file." server B's "read_file" sends the contents somewhere else. the agent never notices. you never notice.
i ran into this while testing observer, the MCP proxy i built at valtors. observer sits between your agent and the MCP server and logs every tool call. the first time i connected it to a multi-server setup, i watched the logs and realized the agent was calling tools by name without any namespace. two servers, same tool name, the agent picking whichever one matched first. there was no disambiguation. there was no warning. the tool just ran.
if someone replaces server A's "read_file" with something else after you trusted it, you won't notice until something breaks or something leaks. by then the context is already in the model's memory.
then there's resource exposure. some MCP servers expose resources that point at sensitive paths. /etc. .ssh. .env. credentials. some tools accept file paths as parameters with no validation, no allowlist, no path scoping. nothing stops the agent from asking for /etc/passwd. nothing stops a prompt injection from telling the agent to ask for your ssh keys. the tool reads the file because that's what the tool does. it doesn't know it shouldn't. it doesn't ask.
this isn't hypothetical either. Snyk's security research team disclosed real CVEs in MCP servers for exactly this. CVE-2025-5273: arbitrary file read in markdownify-mcp. the get-markdown-file tool took a file path and converted the file to markdown. it didn't validate the path at all. any file readable by the server process, /etc/passwd, ssh keys, .env files, could be exfiltrated. no UI confirmation. no additional user interaction. just an LLM doing what it was told, by a page it was told to fetch.
if we don't start securing these servers now, we'll be stuck rediscovering the same old vulnerabilities, just wrapped in new AI-shaped wrappers. Snyk Labs, July 2025
they're right. the vulnerability is old. the wrapper is new. nobody checked the wrapper.
i saw this pattern while contributing to ClickHouse. the PR was about vector search optimization, unrelated to MCP, but the process taught me something about how large codebases handle parameter validation. ClickHouse validates everything. every setting has bounds. every query has limits. every input is checked before it's trusted. MCP servers don't do this. MCP servers take a string, pass it to os.ReadFile, and return the result. the contrast was the lesson. a database built by 2000 people checks every input. an MCP server built by one person checks none.
while contributing to labstack/echo, i added llms.txt and llms-full.txt to their documentation. the point of llms.txt is to make documentation machine-readable so agents can understand a project without scraping the site. while doing it, i read through echo's entire docs structure. one thing stood out: they document every route, every middleware, every error code. if something goes wrong, you can look it up. MCP servers don't do this either. tool descriptions are often one line. sometimes zero lines. the agent is supposed to figure out what a tool does from its name. "read_file" is descriptive enough. "process_data" is not. "do_thing" is a joke, but i've seen it in a real server.
documentation isn't a feature. it's the interface between the tool and the model. when you don't document a tool, the model guesses. the model will guess wrong. wrong guesses in an agent with file access are not harmless.
i also contributed to steel-browser, fixing a session leak where user data directories weren't cleaned up when sessions ended. context leaking between sessions. sound familiar? it's the same class of problem as MCP tool shadowing. state from one context bleeds into another. in a browser, it means your next session sees the previous session's cookies. in MCP, it means your agent sees tools from a server you disconnected. the attack surface is different. the failure mode is the same. nobody owns their state.
here's what mcprobe looks like when you run it against a server:
$ mcprobe scan --stdio "npx some-mcp-server"
scanning: some-mcp-server
tools: 12
[CRITICAL] prompt injection in tool 'exec_cmd'
description contains: "ignore all previous instructions"
a model will follow this. this is not a warning. this is what will happen.
[HIGH] tool 'read_file' accepts unrestricted file paths
no path validation. /etc/passwd. your ssh keys. your .env.
nothing stops it. the tool just reads.
[MEDIUM] tool 'search_web' has no description
the model will guess what it does.
the model will guess wrong.
risk score: 67/100 (HIGH)
67 out of 100. and this was a server with 500 stars and a nice logo.
everything i learned across these projects points to the same thing. the MCP ecosystem is growing faster than the security conversation. the protocol is solid. the servers are not.
agent-orchestrator taught me that everything an agent sees is prompt context, including tool descriptions. observer taught me that agents call tools without disambiguation and nobody notices. clickhouse taught me that serious codebases validate every input and MCP servers validate none. echo taught me that documentation is an interface and most MCP servers don't have one. steel-browser taught me that state leaks between contexts and nobody owns their session boundaries.
the curse of prompt injection continues to be that we've known about the issue for more than two and a half years and we still don't have convincing mitigations for handling it. Simon Willison, April 2025
he's right. we know the problem. we've named the problem. we don't have the fix. but we can at least look at the problem before we connect it to our agents.
run mcprobe before you connect anything. it takes three seconds.
go install github.com/tamish560/mcprobe@latest
mcprobe scan --stdio "npx whatever-mcp-server"
if it finds nothing, that doesn't mean the server is safe. it means mcprobe didn't find anything. there's a difference. you should know the difference.
save a baseline. check it again next week. if the tool descriptions changed and you didn't update anything, someone replaced something on you. that's a rug-pull. mcprobe catches those too.
mcprobe baseline --stdio "npx whatever-mcp-server" --save
mcprobe drift --stdio "npx whatever-mcp-server" --baseline baseline.json
every MCP server you connect is code you didn't write, running on your machine, reading your files, driven by a model that follows instructions. including instructions written by someone else. including instructions that say "ignore all previous instructions."
you trust the server. you trust the model. you trust that neither will do anything stupid. that's three layers of trust for something nobody checked.
mcprobe checks. that's all it does. it doesn't fix. it doesn't block. it tells you what's there, what's wrong, and what will happen if you connect it.
what you do after that is on you.
every contribution i made to other projects taught me something that ended up in mcprobe. the injection patterns, the shadowing detection, the resource exposure checks, the baseline drift. none of it came from a security textbook. it came from building things, breaking things, and reading what other people built.
the ClickHouse PR was about vector search. the echo PR was about documentation. the steel-browser PR was about session cleanup. the agent-orchestrator PRs were about lifecycle management. none of them were about MCP security. all of them taught me something about MCP security.
that's how this works. you build, you contribute, you learn, you build better. the blog you're reading is the output of that loop. mcprobe is the tool that came out of it.