4,500 MCP Servers, 7,840 Tools, Zero Input Validation. Here's the Attack Surface.
MCP tools define input schemas. An LLM reads the schema, generates the parameters, and the server executes the call. But schemas aren’t validation. The schema says “this parameter is a string.” It doesn’t say “this string must not contain ../ or ; rm -rf /.”
I parsed the tool definitions of 8,216 MCP servers to map the actual attack surface.
How I did it
I pulled tool definitions from the ToolSDK MCP Registry, which indexes 4,548 servers with 8,010 tool definitions across 30 categories. Each tool has a name, description, and (sometimes) an input schema.
For tools with schemas, I checked every string parameter for validation constraints: enums, patterns, format specifications, maxLength. A string parameter with none of these is a freeform input — the tool will accept whatever the LLM sends.
For tools without detailed schemas (the majority), I analyzed tool names and descriptions for high-risk patterns. A tool called execute_command that accepts a command parameter is a command injection surface regardless of whether the schema is documented.
I categorized high-risk parameters into six injection categories:
| Category | Pattern | What it enables |
|---|---|---|
| SQL injection | sql, query, statement, where_clause |
Direct database manipulation |
| Path traversal | path, file_path, directory, filename |
Filesystem escape |
| Command injection | command, cmd, shell, exec, script |
Arbitrary code execution |
| SSRF | url, uri, endpoint, host, webhook |
Internal network probing |
| XSS | html, template, content, body, message |
Content injection |
| Code injection | code, expression, eval, formula, regex |
Runtime code execution |
The numbers
7,840 tools across 8,216 servers. 4,512 high-risk input parameters detected across 2,432 servers (29.6% of the ecosystem).
Risk breakdown
| Category | Flagged Parameters | What these tools do |
|---|---|---|
| XSS / content injection | 1,650 | Accept HTML, templates, message bodies — content the user or agent generates |
| SSRF | 831 | Accept URLs, endpoints, webhook addresses — anywhere the server will make outbound requests |
| Code injection | 740 | Accept code, expressions, regex, formulas — input that gets evaluated |
| SQL injection | 566 | Accept raw SQL, query strings, filter expressions — direct database input |
| Command injection | 368 | Accept shell commands, scripts, terminal input — system-level execution |
| Path traversal | 357 | Accept file paths, directory names — filesystem navigation |
The most common dangerous tool patterns:
| Tool pattern | Occurrences |
|---|---|
search / search_* |
14+ |
execute_command / run_command |
6+ |
send_message / add_comment |
5+ |
run_code / eval |
5+ |
search_files / search_codebase |
5+ |
browser_navigate |
4+ |
FileWriteOrEdit |
4+ |
The worst offenders
Servers with the most high-risk input surfaces:
| Server | High-Risk Params |
|---|---|
| hostinger-api-mcp | 119 |
| @configcat/mcp-server | 89 |
| Graphlit | 87 |
| IT Tools MCP | 58 |
| @smartbear/mcp | 50 |
| Tree Sitter MCP Server | 34 |
| XcodeBuild MCP | 32 |
| Phone MCP | 30 |
These aren’t necessarily vulnerable — a server can accept a query parameter and sanitize it internally. But the schema doesn’t tell you that. From the outside, the only contract is “string.” What happens after the string arrives is implementation-dependent and undocumented.
Why this matters now
In February 2026, researchers cataloged 30+ CVEs in MCP servers within 60 days. 82% involved path traversal. These weren’t theoretical — they were real exploits in real servers that people were running.
The attack path is straightforward:
- An agent connects to an MCP server
- The server exposes a tool that accepts a file path
- A prompt injection (or just a careless LLM generation) sends
../../etc/passwd - The server reads or writes the file because it trusted the input
This is the same class of vulnerability that web apps solved twenty years ago with input validation, parameterized queries, and allowlists. MCP servers are reliving the early 2000s.
The difference: in a web app, the attacker is a human crafting malicious input. In an MCP context, the attacker can be a prompt injection in any upstream data source. The LLM reads a malicious instruction embedded in a document, a web page, or a previous tool result, and faithfully passes it as a parameter to the next tool call.
This is indirect prompt injection meeting a validation-free attack surface. The LLM is the unwitting vector. The MCP server is the target. And nothing in between is checking the input.
The MCP spec doesn’t help
The MCP specification defines tool input schemas using JSON Schema. That’s a description format, not a validation framework. A server can declare that a parameter is a string — but JSON Schema’s validation keywords (pattern, format, enum, maxLength) are optional and rarely used.
Looking at the tool definitions in ToolSDK: of 8,010 tools, those that include input schemas almost never use constraining keywords. The schema says “type: string” and stops there. It’s a hint for the LLM about what to generate, not a contract about what the server will accept.
This means:
- The LLM has no constraints beyond the parameter name and description
- The server receives whatever the LLM generates
- Validation — if it exists — is ad hoc, per-server, undocumented
What deny-by-default validation looks like
The fix isn’t asking every MCP server author to implement input validation. There are 8,000+ servers and the ecosystem is growing. The fix is validating at a layer that sits in front of all of them.
A control plane that intercepts every tool call can:
- Parse the tool’s input schema and enforce constraints the schema should have specified — regex patterns for paths, allowlists for URLs, parameterization for SQL
- Apply category-specific rules — file path parameters get
../stripping, SQL parameters get parameterization, URLs get allowlist checks - Detect injection patterns — the same patterns that show up in CVEs (path traversal sequences, shell metacharacters, SQL injection markers) get caught before they reach the server
- Log every parameter value for forensic analysis — even if a novel injection technique gets through, you have the audit trail
This is the validatabl pattern. The server doesn’t need to validate. The control plane does.
Methodology
Tool definitions from ToolSDK MCP Registry (4,548 servers, 8,010 tools). High-risk parameter detection via regex matching on tool names, descriptions, and schema property names. Server descriptions analyzed for servers without explicit tool definitions.
This is static analysis of metadata. It identifies the surface area — tools that accept high-risk input types. It does not prove that any specific server is vulnerable. A server that accepts a path parameter may sanitize it internally. We can’t tell from the schema.
What we can tell: 2,432 servers expose input surfaces where a single unsanitized parameter is a CVE. The question is whether you want to trust each server’s internal validation — or enforce it at the control plane.
Read the reference architecture → · Get started free →