# I looked at 1,384 tools across 28 MCP servers. Here's what the attack surface actually looks like.

> Source: <https://dev.to/dishant_desle/i-looked-at-1384-tools-across-28-mcp-servers-heres-what-the-attack-surface-actually-looks-like-4il0>
> Published: 2026-10-22 23:04:48+00:00

Quick context if you're new to this: an **MCP server** is a plug-in that gives an AI agent a set of **tools** it can call, like "fetch this URL," "run this database query," or "send this email." There are hundreds of open-source MCP servers now. This writeup measures what those tools actually let an agent do, and how often the risky combinations sit side by side.

Method note. The numbers are a **snapshot taken on 2026-10-20** of 28 popular Python MCP servers (plus the reference-servers repo), produced with a [reproducible harness](https://github.com/agentsploit/agentsploit/tree/main/examples/mcp-survey). Upstream repos change over time, so re-running this later gives somewhat different counts. Everything below is read from **public source code**. No server was connected to or executed. The capability labels are rough heuristics (see the caveats), and this writeup deliberately does **not** call anything a vulnerability.

I kept running into MCP servers where one tool fetches a web page and another tool runs a command, both handed to the same agent, with nothing telling the model which one is dangerous. Instead of guessing how common that is, I decided to measure it.

So I pulled the tool definitions straight from the source code of the most popular Python MCP servers on GitHub (AWS Labs, Google Workspace, Atlassian, Home Assistant, Snowflake, the Neo4j / Redis / Qdrant / Chroma / Milvus database servers, the Office / Excel document servers, Telegram, and more), and ran [AgentSploit](https://github.com/agentsploit/agentsploit)'s inventory checks plus a simple source/sink classifier over them. That comes to **1,384 tools across 28 servers.** The reproducible harness is at the bottom.

A tool's description is just text, and the AI reads it. The "tool poisoning" attack is built on that fact: if an attacker can slip instructions into a description, the model may follow them. (Think of it like hiding a note in a manual that the reader obeys without question.)

What the survey shows is that **normal, legitimate servers already write their descriptions as direct orders to the model.** These are real examples, and all of them are completely harmless:

"Before using this tool, provide a 1-3 sentence explanation…" (AWS Labs)

"CRITICAL: YOU MUST CALL inspect_doc_structure FIRST…" (Google Workspace)

"🔴 PREREQUISITE: Before calling this tool, you MUST first call `…`" (AWS Labs)

Those are good-faith usage notes. The problem is that, at the level of plain text, they look exactly like an injected `IGNORE ALL PREVIOUS INSTRUCTIONS`. A pattern-based scanner (AgentSploit's included) flags both the same way. I went through all **9** matches by hand, spread across **4 independent vendors** (AWS, Google, Home Assistant, arXiv), and every single one was **benign**. That is the real finding: there's no clean way to tell a harmless instruction-style description from a malicious one, because the whole ecosystem already uses that same channel on purpose.

I sorted every tool into two buckets:

`url`, `uri`, `path`, or `document`, or its name looks like `fetch` / `read` / `search` / `crawl`.` exec` / `run` / `query` / `write` / `send` / `delete`, or an argument like `command`, `code`.
The danger isn't either bucket on its own. It's having both in front of the same agent, so that something the agent *read* can flow into something the agent *does*.

Across 1,384 tools:

|  | count | 
|---|---|
| untrusted-content **sources** | 239 | 
| high-impact **sinks** | 384 | 
| tools that are **both** (they ingest*and* act) | **59** | 
| servers that expose at least one source **and** one sink in the same context | **14 / 28** | 

The 59 "both" tools are the sharpest edge. A single tool that pulls in a URL or a file and also runs a query or a command is a one-call bridge from untrusted input straight to a real action, if the content it pulls is attacker-controlled. For example `search_table(url)` that also takes a `query`, or `run_query(db_endpoint, sql)`, or the Google Workspace and Office document tools that read a document and write it back. And 14 of the 28 servers put sources and sinks in the same agent context with nothing marking one as untrusted or the other as dangerous. (The harness prints the full list so you can check it yourself. A few are weak matches. For instance AWS IAM's `create_user` trips the filter on an IAM `path` argument, which isn't really outside content.)

Plenty of the sink tools take a raw string with **no `enum`, `pattern`, or `format` constraint** on it, like `executeQuery(query)`, `run_opencypher_query(query)`, or `read_documentation(url)`. (Those constraints are the schema's way of saying "only these specific values are allowed." Without them, the field accepts anything.) That's by design for a query tool. It's also exactly the kind of wide-open sink that turns "the agent read a poisoned page" into "the agent ran the attacker's SQL."

To be clear: none of the above is a bug in these servers. A source-to-sink chain only becomes a real exploit when two things both happen: untrusted input actually reaches a source, and the agent actually carries it over to a sink. Both of those depend on how the server gets deployed and wired together, not on the code itself. Proving a real chain needs a live agent, which is what AgentSploit's permission-graph mapper and canary runner are for. That's out of scope for a static survey like this one.

`enum` / `pattern` / `format`) rather than taking any raw string.
The quickest way to run this against a server yourself (shipped in v1.7):

```
pip install agentsploit
agentsploit survey ./path/to/mcp-server   # a repo directory or a single .py file
```

Or reproduce the full multi-repo survey with the harness:

```
git clone https://github.com/agentsploit/agentsploit && cd agentsploit
pip install -e .
cd examples/mcp-survey            # edit servers.yaml to pick targets
python survey.py collect          # read tool definitions from public source
python survey.py scan             # run agentsploit's inventory checks
python survey.py aggregate        # print the findings table
python survey.py surface          # run the source/sink classification
```

`scrape` names), and the matching is token-based. It's still a guess at what a tool can do, not a traced data-flow. So read the counts as "could plausibly be," and trust the overall shape more than any single number. The shape is the sturdy part: most servers mix ingestion and action, and 59 tools do both in one call.
The tool is Apache-2.0: [github.com/agentsploit/agentsploit](https://github.com/agentsploit/agentsploit). Ideas and pull requests welcome.
