I've been building out heuristics for scanning static MCP server manifests (mcp.json) for the issue classes people keep hitting in the wild, and mapped each one against the OWASP MCP Top 10 (beta) plus the existing OWASP LLM Top 10, since a lot of MCP-specific findings don't have a clean home in an existing framework yet. Sharing the mapping table here as reference material in case it's useful for anyone auditing their own configs or building similar tooling.
The mapping
| Heuristic | OWASP MCP Top 10 | OWASP LLM Top 10 | What it catches |
|---|---|---|---|
tool_description_injection |
MCP01: Prompt Injection via Tool Descriptions | LLM01 | imperative/override language, fake system tags, zero-width chars, base64 blobs hidden in a tool's description field |
hidden_unicode_instructions |
MCP01 | LLM01 | invisible unicode steering text inside descriptions (tag-block smuggling, bidi overrides) |
tool_name_shadowing |
MCP02: Tool Poisoning / Shadowing | LLM01 | tool names that collide with sensitive/builtin names, or descriptions claiming to override another tool |
indirect_injection_surface |
MCP01 | LLM01 | manifests that both ingest untrusted content (fetch/browse/read-inbox) and can act (send/write/execute), the toxic-flow combo |
hardcoded_credential |
MCP03: Credential / Secret Exposure | LLM02 | API keys/tokens/passwords literal in a server's env block or CLI args instead of an env-var placeholder |
unpinned_remote_source |
MCP04: Supply Chain Risk | LLM03 | npx/uvx/pip server entries with no pinned version, or plaintext http:// transports |
missing_provenance |
MCP04 | LLM03 | remote-sourced server entries with no signature/checksum/publisher field |
excessive_agency_schema |
MCP06: Excessive Agency / Permissions | LLM06 | free-form shell/command params, admin/bypass/sudo flags, wide-open schemas |
overbroad_tool_scope |
MCP06 | LLM06 | wildcard/blanket scopes ("*", "all", "admin") instead of enumerated lists |
missing_hitl_confirmation |
MCP06 | LLM06 | sensitive-capability tools (exec, filesystem write/delete, outbound send) with no human-in-the-loop gate |
Only five OWASP MCP Top 10 categories show up here: MCP01, MCP02, MCP03, MCP04, MCP06. That's not a gap in the table, it's a real limit of what a purely static, offline manifest scan can see. Categories like runtime tool poisoning after install, cross-origin session hijacking, or DoS need a live server connection to detect at all. A static text/JSON parse of the manifest structurally can't reach them.
How this was verified
This table is pulled directly from the OWASP_MCP_TOP10 dict in the scanner's source, cross-checked against the heuristic comment block above it. No category was assigned from memory or guessed after the fact:
OWASP_MCP_TOP10 = {
"tool_description_injection": "MCP01: Prompt Injection via Tool Descriptions",
"tool_name_shadowing": "MCP02: Tool Poisoning / Shadowing",
"excessive_agency_schema": "MCP06: Excessive Agency / Permissions",
"indirect_injection_surface": "MCP01: Prompt Injection via Tool Descriptions",
"unpinned_remote_source": "MCP04: Supply Chain Risk",
"hardcoded_credential": "MCP03: Credential / Secret Exposure",
"overbroad_tool_scope": "MCP06: Excessive Agency / Permissions",
"missing_provenance": "MCP04: Supply Chain Risk",
"missing_hitl_confirmation": "MCP06: Excessive Agency / Permissions",
"hidden_unicode_instructions": "MCP01: Prompt Injection via Tool Descriptions",
}
Try it against your own manifest
This runs as a static, offline analysis, no network calls, no server execution, just parsing the manifest text and JSON structure. It's open source (MIT) if anyone wants to point it at their own mcp.json or extend the ruleset:
pip install sentinel-scan-cli
sentinel-scan mcp --demo
or, no install:
pipx run sentinel-scan-cli mcp --demo
Repo: github.com/Ventrova/sentinel-scan-cli
Curious if others tracking against the MCP Top 10 beta have found gaps in this mapping, or heuristics worth adding.
Top comments (0)