Checks catalogue · v0.1
Every check, what it probes, and how often it is wrong.
37 catalogue entries, generated from the scanner itself (mcpprobe 0.3.7) and mapped to OWASP MCP Top 10 (beta), Agentic Top 10 and LLM Top 10 (2026 and 2025). The 24 passive checks run today and never call a tool. The 8 active checks are registered behind the authorisation gate and coming soon. Governance items are listed so you know what a scan cannot see.
Showing 37 of 37 checks
Authentication
AUTH-01 … AUTH-06Unauthenticated endpoints, RFC 9728 protected-resource metadata, PKCE S256 and HTTPS on the authorization server, and tokens with the wrong audience or issuer.
| ID | Check | What it does | Mode | Severity | Maps to | FP rate |
|---|---|---|---|---|---|---|
| AUTH-01 | Remote MCP endpoint accepts requests without authentication | POSTs initialize (or server/discover) and tools/list with no Authorization header.Details and fixPOSTs initialize (or server/discover) and tools/list with no Authorization header. If the server answers with a tool list, anyone who can reach the endpoint can drive its tools. When the operator supplies credentials with --header, one extra request is sent with those credentials removed, so a protected server is not misreported. Passes only when the endpoint answers 401/403 without credentials. Evidence: The unauthenticated request (Authorization shown as not sent) and the response. False positives: Servers protected by a network boundary (VPN, mTLS at a proxy, IP allow-list) will still be flagged because the scanner only sees what the endpoint returns. Local development servers bound to loopback are reported at reduced severity but are still exposed to DNS-rebinding and browser-based attacks (see CVE-2025-49596 class). Fix: Put the endpoint behind OAuth 2.1 as described in the MCP authorization spec: return 401 with WWW-Authenticate: Bearer resource_metadata="<RFC 9728 metadata URL>", validate each token's signature, issuer, expiry and audience, and reject requests without a valid bearer token. Authentication does not make a dangerous tool safe. | passivehttp | High | MCP07:2025 (beta) · ASI03 · LLM04:2026 · LLM03:2026 · LLM03:2025 · LLM06:2025 | Pending |
| AUTH-02 | Protected resource metadata (RFC 9728) missing or invalid | Parses WWW-Authenticate for resource_metadata (when the server answers 401), then tries /.well-known/oauth-protected-resource[/path] and the host root.Details and fixParses WWW-Authenticate for resource_metadata (when the server answers 401), then tries /.well-known/oauth-protected-resource[/path] and the host root. The document must be JSON with a non-empty authorization_servers list. Without it, conformant clients cannot start the OAuth flow and users are pushed toward pasting long-lived static tokens. A server that requires no authentication and serves no metadata is reported too (medium, the discovery half of AUTH-01). Evidence: The 401 response headers and each metadata GET with its status. False positives: Servers that use a non-OAuth scheme (API keys at a gateway) will be flagged; the MCP spec still expects RFC 9728 metadata for HTTP transports. A deliberately public, read-only server with no authentication will also be flagged; AUTH-01 is the root cause. Fix: Serve /.well-known/oauth-protected-resource (path-aware variant preferred) with 'resource' and 'authorization_servers', and include WWW-Authenticate: Bearer resource_metadata="<url>" on every 401. | passivehttp | High | MCP07:2025 (beta) | Pending |
| AUTH-03 | Authorization server metadata weak or missing (PKCE S256 / HTTPS) | For each authorization server listed in the protected resource metadata, fetches /.well-known/oauth-authorization-server (RFC 8414, path-aware) then /.well-known/openid-configuration.Details and fixFor each authorization server listed in the protected resource metadata, fetches /.well-known/oauth-authorization-server (RFC 8414, path-aware) then /.well-known/openid-configuration. Flags a missing document, a code_challenge_methods_supported list without S256 (MCP clients MUST refuse to proceed), and non-HTTPS endpoints. This may make one GET to a third-party host; disable with --no-follow-as. Evidence: Each metadata GET with status and body. False positives: Some authorization servers support PKCE but omit code_challenge_methods_supported; per the MCP spec that is still a conformance failure for clients. Fix: Publish RFC 8414 metadata with code_challenge_methods_supported including S256 and HTTPS-only endpoints. If you use a third-party identity provider, confirm it exposes the metadata document at the issuer URL you advertise. | passivehttp | High | MCP07:2025 (beta) · MCP01:2025 (beta) | Pending |
| AUTH-04 | Server accepts a self-minted token with the wrong audience/issuer | Mints two syntactically valid JWTs the server cannot have issued (HS256 with a random scanner key, and alg=none) with a foreign issuer and audience, and presents them as Bearer tokens.Details and fixMints two syntactically valid JWTs the server cannot have issued (HS256 with a random scanner key, and alg=none) with a foreign issuer and audience, and presents them as Bearer tokens. Any non-401/403 answer means the server is not validating signature or audience (RFC 8707). Also presents the token in the access_token query parameter, which the spec forbids. Only self-minted tokens are used; no real credentials. Evidence: Each probe request (token redacted) and the response status/body. False positives: A proxy that strips Authorization and answers 200 for health reasons could be flagged; check the response body in the evidence actually contains an MCP result. Fix: Validate the token signature against the authorization server's keys, require 'aud' to equal this server's canonical URI (RFC 8707), reject alg=none, and never read tokens from query strings. | passivehttp | Critical | MCP07:2025 (beta) · MCP01:2025 (beta) · ASI03 | Pending |
| AUTH-05 | Authorization server does not advertise RFC 9207 iss support (2026-07-28) | 2026-07-28 says MCP authorization servers SHOULD include the RFC 9207 'iss' parameter in authorization responses and, when they do, MUST advertise authorization_response_iss_parameter_supported: true in their metadata; clients reject a response without iss only when that flag is true.Details and fix2026-07-28 says MCP authorization servers SHOULD include the RFC 9207 'iss' parameter in authorization responses and, when they do, MUST advertise authorization_response_iss_parameter_supported: true in their metadata; clients reject a response without iss only when that flag is true. Reads the authorization server metadata AUTH-03 already fetched and reports a flag that is absent or not true. Sends nothing. The revision says this SHOULD is expected to become a MUST. Evidence: The authorization server metadata document AUTH-03 fetched. False positives: The spec allows a server to emit iss before it updates its metadata; such a server is reported until the flag is published, because clients key their rejection on the flag. Fix: Include iss in every authorization response, error responses too (RFC 9207 section 2), and publish "authorization_response_iss_parameter_supported": true in the authorization server metadata (https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization#authorization-response-validation). With a hosted identity provider, enable its issuer-identification option. | passivehttp | Low | MCP07:2025 (beta) | Pending |
| AUTH-06 | Deprecated Dynamic Client Registration offered (2026-07-28) | 2026-07-28 deprecates OAuth Dynamic Client Registration (RFC 7591) in favour of Client ID Metadata Documents, which authorization servers SHOULD support.Details and fix2026-07-28 deprecates OAuth Dynamic Client Registration (RFC 7591) in favour of Client ID Metadata Documents, which authorization servers SHOULD support. Reads the authorization server metadata AUTH-03 already fetched: a registration_endpoint without client_id_metadata_document_supported: true is low (clients have only the deprecated path); a registration_endpoint next to CIMD support is informational (kept for backwards compatibility). No registration request is sent. Evidence: The authorization server metadata document AUTH-03 fetched. False positives: Keeping DCR for older clients is allowed; the informational finding is a reminder to plan its removal, not a defect. An authorization server that supports neither DCR nor CIMD (pre-registration only) is not reported here. Fix: Support Client ID Metadata Documents and advertise "client_id_metadata_document_supported": true; keep registration_endpoint only while older clients still need it, and plan its removal (https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/client-registration). Open registration endpoints also deserve rate limits and redirect-URI validation. | passivehttp | Low | MCP07:2025 (beta) | Pending |
Transport
TRANS-01 … TRANS-09Origin validation (DNS rebinding), protocol-version handling, session-id quality and forgery, deprecated HTTP+SSE, TLS and security headers.
| ID | Check | What it does | Mode | Severity | Maps to | FP rate |
|---|---|---|---|---|---|---|
| TRANS-01 | Origin header not validated (DNS-rebinding exposure) | Sends a normal negotiation request with 'Origin: https://evil.example' and one with 'Origin: null'.Details and fixSends a normal negotiation request with 'Origin: https://evil.example' and one with 'Origin: null'. The transport spec says servers MUST validate Origin and return 403 on an invalid one; otherwise a web page can drive the server through the victim's browser (DNS rebinding). Requests without an Origin header are not tested: non-browser clients do not send one. Evidence: Each POST with a foreign Origin header and the non-403 response. False positives: If the server answers 401 (or 429) before looking at Origin the check is not applicable and says so. Reverse proxies that strip Origin will cause a finding against the proxied endpoint even if the application validates it. Fix: Reject any request whose Origin header is present and not on an allow-list with HTTP 403, before processing the body, including 'Origin: null'. Allow requests with no Origin header. | passivehttp | Medium | MCP07:2025 (beta) | Pending |
| TRANS-02 | Unsupported MCP-Protocol-Version header not rejected | Sends a request with 'MCP-Protocol-Version: 1999-01-01'.Details and fixSends a request with 'MCP-Protocol-Version: 1999-01-01'. The spec requires HTTP 400 (or, on 2026-07-28, an UnsupportedProtocolVersionError). Accepting any version string suggests the server ignores version negotiation entirely. Evidence: The request with the bogus version header and the response. False positives: Pre-2025-06-18 servers never defined the header, so an older server that ignores it is noted at low severity only. A 401/429 before the header is read makes the check not applicable. Fix: Validate the MCP-Protocol-Version header against the versions you support and reply 400 when it is unknown. Separately, when initialize asks for a protocolVersion you do not support, answer with the latest version you do support and let the client decide; never echo back whatever the client asked for. | passivehttp | Low | MCP spec conformance / hygiene (no OWASP item) | Pending |
| TRANS-03 | Weak, predictable or unvalidated Mcp-Session-Id | Collects three session IDs from repeated initialize requests and checks length, character set, Shannon entropy and sequentiality.Details and fixCollects three session IDs from repeated initialize requests and checks length, character set, Shannon entropy and sequentiality. Then sends tools/list with a random forged session ID, expecting 400 or 404. Only runs on handshake revisions that issue Mcp-Session-Id (sessions were removed in 2026-07-28). Evidence: The initialize responses (session header) and the forged-session request. False positives: Entropy is estimated from three samples, so the test is conservative: only IDs shorter than 16 characters, obviously sequential, or with very low per-character entropy are flagged. Fix: Generate session IDs with a CSPRNG (at least 128 bits, e.g. UUIDv4 or 32 random bytes base64url), store them server-side, and answer 404 for unknown IDs. | passivehttp | High | MCP07:2025 (beta) | Pending |
| TRANS-04 | Deprecated HTTP+SSE (2024-11-05) transport in use | Reports when the endpoint answers a GET with an SSE 'endpoint' event, i.e.Details and fixReports when the endpoint answers a GET with an SSE 'endpoint' event, i.e. the 2024-11-05 HTTP+SSE transport that was deprecated in 2025-03-26 and formally classified Deprecated in 2026-07-28. Also noted when the negotiated revision predates 2025-06-18. Evidence: The GET request and the first SSE event. False positives: Informational only. Fix: Migrate to Streamable HTTP (single endpoint, POST per message) and current protocol revision. | passivehttp | Info | MCP spec conformance / hygiene (no OWASP item) | Pending |
| TRANS-05 | Plain HTTP, weak TLS or missing security headers | Flags non-loopback endpoints served over plain HTTP (bearer tokens in clear), TLS versions below 1.2 observed on the connection, and missing Strict-Transport-Security / X-Content-Type-Options headers on the MCP endpoint responses.Details and fixFlags non-loopback endpoints served over plain HTTP (bearer tokens in clear), TLS versions below 1.2 observed on the connection, and missing Strict-Transport-Security / X-Content-Type-Options headers on the MCP endpoint responses. Evidence: Response headers of the negotiation/tools/list exchange and the observed TLS version. False positives: Loopback plain-HTTP is reported as informational. Header checks look at the MCP endpoint only; a CDN may add headers on other paths. Fix: Serve the endpoint over HTTPS with TLS 1.2+ only, add Strict-Transport-Security and X-Content-Type-Options: nosniff. Where an edge (API Gateway, CloudFront, Cloudflare, Vercel) is detected the fix text says where to configure it. | passivehttp | High | MCP01:2025 (beta) | Pending |
| TRANS-06 | server/discover result incomplete (2026-07-28) | A 2026-07-28 server MUST implement server/discover.Details and fixA 2026-07-28 server MUST implement server/discover. The scanner's first request is server/discover; when it is answered, its result is checked for what the revision requires: supportedVersions (a list of version strings that includes the 2026-07-28 revision the request was served under), a capabilities object and resultType 'complete'. A missing _meta['io.modelcontextprotocol/serverInfo'] (SHOULD) is reported as informational. Caching hints on the same result are checked by TRANS-09. Not applicable to servers that speak an earlier revision through initialize. Evidence: The fingerprinting server/discover request and its complete result. False positives: A server that answers server/discover under an earlier result shape (for example a 'protocolVersions' key from a pre-release draft) is reported, because 2026-07-28 clients read 'supportedVersions'. Fix: Return the DiscoverResult defined in the 2026-07-28 schema: {"resultType":"complete","supportedVersions":["2026-07-28", ...],"capabilities":{...},"_meta":{"io.modelcontextprotocol/serverInfo":{"name":...,"version":...}},"ttlMs":...,"cacheScope":"public"} (https://modelcontextprotocol.io/specification/2026-07-28/server/discover). | passivehttp, stdio | Low | MCP spec conformance / hygiene (no OWASP item) | Pending |
| TRANS-07 | Protocol version the server does not advertise is accepted (downgrade) | Takes supportedVersions from the server/discover result and sends one more server/discover declared (in _meta and in the MCP-Protocol-Version header) as the newest earlier revision the server does not list (2025-11-25, 2025-06-18 or 2025-03-26).Details and fixTakes supportedVersions from the server/discover result and sends one more server/discover declared (in _meta and in the MCP-Protocol-Version header) as the newest earlier revision the server does not list (2025-11-25, 2025-06-18 or 2025-03-26). The spec says a server that does not implement the requested version MUST answer HTTP 400 with UnsupportedProtocolVersionError (-32022). Serving the request anyway means a client or intermediary can push the server onto a revision it claims not to support, without the header validation and other protections of 2026-07-28. One request, no tool is called. Evidence: The server/discover result (supportedVersions) and the downgraded request with its response. False positives: A dual-era server that does serve the older revision but forgot to list it is reported too; listing it in supportedVersions is then the fix. Not applicable when every earlier revision is advertised. Fix: Reject any request whose declared protocol version is not in your supportedVersions with HTTP 400 and {"code":-32022,"message":"Unsupported protocol version","data":{"supported":[...],"requested":"..."}}, before doing any work (https://modelcontextprotocol.io/specification/2026-07-28/basic/versioning). If you do serve the older revision, list it in supportedVersions. | passivehttp | Low | MCP spec conformance / hygiene (no OWASP item) | Pending |
| TRANS-08 | Mcp-Method header not validated against the request body (2026-07-28) | 2026-07-28 requires clients to mirror the JSON-RPC method into an Mcp-Method header, and servers that process the body MUST reject a request whose headers are missing or do not match the body with HTTP 400 and HeaderMismatch (-32020); otherwise a gateway that routes, rate-limits or authorises on the header can be bypassed by a body that says something else.Details and fix2026-07-28 requires clients to mirror the JSON-RPC method into an Mcp-Method header, and servers that process the body MUST reject a request whose headers are missing or do not match the body with HTTP 400 and HeaderMismatch (-32020); otherwise a gateway that routes, rate-limits or authorises on the header can be bypassed by a body that says something else. Sends two server/discover requests: one with 'Mcp-Method: tools/list' and one with no Mcp-Method header. Mcp-Name is not tested, because it is only defined for tools/call, resources/read and prompts/get, which a passive scan does not send. Evidence: Both probe requests (headers shown) and their responses. False positives: A server that rejects the probes with another 4xx is reported as informational only. When a gateway in front rejects mismatches itself, the finding concerns the server behind it only if the gateway can be bypassed. Fix: Before dispatching, compare Mcp-Method (and, for tools/call, resources/read and prompts/get, the decoded Mcp-Name) with the body; when a required header is missing or differs, answer HTTP 400 with {"code":-32020,"message":"Header mismatch: ..."} (https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http#server-validation). | passivehttp | Medium | MCP spec conformance / hygiene (no OWASP item) | Pending |
| TRANS-09 | ttlMs / cacheScope caching hints missing or invalid (2026-07-28) | 2026-07-28 servers MUST include caching hints on complete results of server/discover, tools/list, prompts/list and resources/list (among others): ttlMs, a number of milliseconds that MUST be >= 0, and cacheScope, either 'public' or 'private'.Details and fix2026-07-28 servers MUST include caching hints on complete results of server/discover, tools/list, prompts/list and resources/list (among others): ttlMs, a number of milliseconds that MUST be >= 0, and cacheScope, either 'public' or 'private'. Checks the results the scan already received; sends nothing. A missing cacheScope leaves shared gateways to guess whether a list may be served to other users. Evidence: The server/discover and list results the scan already received. False positives: Only the first page of each list is checked. A public cacheScope on an authenticated server is allowed by the spec when the list is the same for every user, so it is not reported. Fix: Add "ttlMs": <milliseconds, >= 0> and "cacheScope": "public" (identical for every user) or "private" (depends on the caller's authorization) to every cacheable result (https://modelcontextprotocol.io/specification/2026-07-28/server/utilities/caching). | passivehttp, stdio | Low | MCP spec conformance / hygiene (no OWASP item) | Pending |
Tool metadata
META-01 … META-07Tool-description poisoning, rug-pull diffs against a stored baseline, annotations that contradict capability, schema hygiene and secrets in metadata.
| ID | Check | What it does | Mode | Severity | Maps to | FP rate |
|---|---|---|---|---|---|---|
| META-01 | Possible tool-description poisoning | Scans every tool, prompt and resource description, every inputSchema parameter description and the server 'instructions' for: prompt-style markup (<IMPORTANT>, <system>, HTML comments), instructions aimed at the model (conceal from the user, ignore previous instructions, claim precedence over every other tool or over what the user asks, redirect/replace), requests to send the model's context out (conversation, chat history, system prompt, previous messages), requests to harvest secrets (the user's API key, token, password, credentials, .env), credential file paths next to a read/send verb, wording that changes what another tool does (shadowing), URLs and e-mail addresses with send/BCC wording, base64 blobs, zero-width/bidi/tag Unicode, mixed-script homoglyphs and excessive length.Details and fixScans every tool, prompt and resource description, every inputSchema parameter description and the server 'instructions' for: prompt-style markup (<IMPORTANT>, <system>, HTML comments), instructions aimed at the model (conceal from the user, ignore previous instructions, claim precedence over every other tool or over what the user asks, redirect/replace), requests to send the model's context out (conversation, chat history, system prompt, previous messages), requests to harvest secrets (the user's API key, token, password, credentials, .env), credential file paths next to a read/send verb, wording that changes what another tool does (shadowing), URLs and e-mail addresses with send/BCC wording, base64 blobs, zero-width/bidi/tag Unicode, mixed-script homoglyphs and excessive length. Ordinary usage guidance ("always use this tool to...", "you must call X first") is not an indicator on its own; it raises a finding to high when combined with another instruction category. Findings carry tiered confidence and are never rated Critical on heuristics alone. Precision is guarded in the test-suite by thousands of real descriptions from published MCP servers (docs/benchmark.md). Evidence: The tools/list (or prompts/resources/initialize) response and the matched excerpts. False positives: Legitimate text that claims precedence over every other tool ('call this before any other tool whenever...') is reported at medium. Plain URLs, sibling-tool mentions and usage guidance are not reported; a credential path without a read/send verb is low. Review the matched excerpts; a second-opinion judge can be plugged in through the Python API (ScanOptions.judge); mcpprobe itself makes no LLM calls. Fix: Rewrite the description so it only says what the tool does. Remove every instruction addressed to the model (when to call it, what to call first, what to hide from the user) and every request to pass along conversation, prompts, files or credentials. Strip invisible Unicode and HTML comments, and never reference other servers' tools. Pin and hash approved descriptions in clients (see META-02). | passivehttp, stdio | High | MCP03:2025 (beta) · ASI01 · ASI04 · LLM01:2026 · LLM01:2025 | Pending |
| META-02 | Tool surface changed since the stored baseline (rug-pull indicator) | Computes SHA-256 over the canonical JSON of each tool, prompt and resource definition and the server instructions.Details and fixComputes SHA-256 over the canonical JSON of each tool, prompt and resource definition and the server instructions. With --baseline FILE the current snapshot is compared to the stored one and any added, removed or changed definition is reported (a changed description after approval is the classic 'rug pull'). The snapshot is then written back to the file. Also notes when the server advertises tools.listChanged. Evidence: The current tools/list response plus per-item hashes and the diff. False positives: Any legitimate release that edits a description will trigger the diff; the finding says what changed so the reviewer can decide. Servers that embed timestamps or random data in descriptions will change on every scan. Fix: Treat tool definitions as code: version them, review changes, and have clients pin and re-approve definitions when the hash changes. | passivehttp, stdio | Medium | MCP03:2025 (beta) · ASI04 | n/a (diff) |
| META-03 | Dangerous tool capability exposed | Classifies each tool by name, description and parameter names into capability classes.Details and fixClassifies each tool by name, description and parameter names into capability classes. Tools that advertise arbitrary command execution, arbitrary file read or arbitrary file write (a shell/file capability selected by a free-form string argument with no enum, const or pattern) are rated high on their own and critical when the remote endpoint needs no authentication. The remaining high-impact surface (network, e-mail, credentials, payments) is summarised at low severity. Annotations that contradict the capability are reported by META-06 (until 0.2 they were reported here). Passive scanning sees the advertised capability, not whether the implementation really executes: the rating is based on the name, description and schema, and no tool is called. Evidence: The tools/list response; each affected tool's complete definition is shown as proof. False positives: Classification is keyword-based: a tool named 'delete_draft' that only removes the user's own draft will be classed 'delete'. A tool that advertises a shell but whose implementation is a stub is still reported, because clients and models act on the advertised capability. Since 0.3.4 lookup tools (names built from get/search/describe and no action verb, such as 'get_cli_command') are only rated as command execution or file read when their description says they execute something or read files. The critical rating assumes tools/call is as open as tools/list; a server that gates tools/call with an API key the handshake does not require (and declares it in its registry entry) is rated high, not critical, in the registry survey since 0.3.5. Fix: For arbitrary command execution or file access: remove the tool, or sandbox it and replace the free-form argument with an allow-list (enum) of commands or files. Annotations come after that (META-06); they help a careful client warn the user but do nothing about the risk. | passivehttp, stdio | High | MCP02:2025 (beta) · MCP05:2025 (beta) · ASI02 · ASI05 · LLM03:2026 · LLM06:2025 | Pending |
| META-04 | Tool schema hygiene problems | Checks tool names against [A-Za-z0-9_.-]{1,128} and for duplicates; checks that inputSchema is a JSON Schema object of type 'object'; flags free-form string arguments whose names suggest paths, commands, URLs or queries and that carry no enum, pattern, format or maxLength constraint.Details and fixChecks tool names against [A-Za-z0-9_.-]{1,128} and for duplicates; checks that inputSchema is a JSON Schema object of type 'object'; flags free-form string arguments whose names suggest paths, commands, URLs or queries and that carry no enum, pattern, format or maxLength constraint. One finding lists every affected tool, with each tool's complete inputSchema and its specific problems (including arguments missing from 'required' and additionalProperties not set to false). Missing outputSchema cannot be judged without calling the tool and is not reported. Evidence: The tools/list response; each affected tool's complete inputSchema is shown as proof. False positives: An unconstrained 'query' string is often unavoidable (search tools); the finding is Low severity and meant as a review prompt, not a defect. Fix: Constrain every risky string argument (enum for a fixed set, pattern for a format, maxLength to bound size), list required arguments in 'required', set additionalProperties: false, and validate arguments on the server against the same schema instead of trusting the client to have done it. | passivehttp, stdio | Low | MCP05:2025 (beta) | Pending |
| META-05 | Secret material in server metadata or configuration | Pattern and entropy scan for AWS/GitHub/Slack/Stripe/Google keys, private key blocks, JWTs, bearer tokens, connection strings with passwords and generic 'key=...' assignments across tool/prompt/resource descriptions, parameter descriptions, resource URIs, server instructions and, for stdio targets, the command line and environment supplied to the scanner.Details and fixPattern and entropy scan for AWS/GitHub/Slack/Stripe/Google keys, private key blocks, JWTs, bearer tokens, connection strings with passwords and generic 'key=...' assignments across tool/prompt/resource descriptions, parameter descriptions, resource URIs, server instructions and, for stdio targets, the command line and environment supplied to the scanner. Matched values are redacted in the report. Evidence: The listing response that contained the match; values redacted. False positives: Published sample keys (the AWS keys ending in EXAMPLE, the Stripe and jwt.io documentation samples) and obvious placeholders are skipped since 0.3.4; other example values that look real can still match. High-entropy strings are reported at Low severity and low confidence; identifiers such as content hashes trigger them. Secrets in a stdio server's environment are expected (the spec says stdio servers take credentials from the environment) and are reported as Low so the operator can confirm least privilege. Fix: Remove secrets from descriptions and resources, rotate anything that was exposed, and pass credentials through the environment or a secret store, never through metadata. | passivehttp, stdio | High | MCP01:2025 (beta) · LLM02:2026 · LLM02:2025 | Pending |
| META-06 | Tool annotations missing or inconsistent with the tool | Reads each tool's annotations from tools/list.Details and fixReads each tool's annotations from tools/list. Reported as missing: readOnlyHint, openWorldHint and a title (tool.title or annotations.title) on every tool, and destructiveHint and idempotentHint on every tool not marked readOnlyHint:true (the spec says those two are meaningful only when readOnlyHint is false). Reported as inconsistent: readOnlyHint:true on a tool that looks like it executes, writes, deletes, sends or pays; destructiveHint:false on a tool that looks like it executes, deletes or overwrites; openWorldHint:false on a tool that reaches the network; a hint that is not a boolean; and, as low-severity noise, destructiveHint:true on a tool that only appears to read. Medium when a dangerous-class tool is mislabelled, low when annotations are merely missing. One finding lists every affected tool. Anthropic's directory policy (5E) and OpenAI's app review both require accurate annotations. Evidence: The tools/list response; each affected tool's name, title and annotations are shown as proof. False positives: The capability behind a contradiction is inferred from keywords in the tool's name, description and parameter names, so a narrowly scoped tool (for example 'delete_draft' that only removes the caller's own draft) may be misread. Missing annotations are a plain observation. Annotations are hints that clients must treat as untrusted; correcting them does not change what a tool can do. Fix: Give every tool a title and set readOnlyHint and openWorldHint explicitly; for tools that are not read-only also set destructiveHint and idempotentHint. Make each value true to the implementation: readOnlyHint:true only when nothing is modified, destructiveHint:true for anything that executes, deletes or overwrites, openWorldHint:true for tools that reach the internet or other external systems. Anthropic's Software Directory Policy (5E, https://support.claude.com/en/articles/13145358-anthropic-software-directory-policy) requires all applicable annotations, and OpenAI's app review (https://developers.openai.com/plugins/deploy/app-review.md) checks readOnlyHint, destructiveHint and openWorldHint for accuracy. | passivehttp, stdio | Medium | MCP spec conformance / hygiene (no OWASP item) | Pending |
| META-07 | Toxic flow: private data, untrusted content and external send on one server | Classifies every advertised tool into three roles from its capability class and wording: access to private data (files, databases, credentials, the user's mail), intake of untrusted content (fetching URLs, web search, reading mail or messages written by others) and a way to send data out (sending e-mail or messages, HTTP posts, webhooks, uploads).Details and fixClassifies every advertised tool into three roles from its capability class and wording: access to private data (files, databases, credentials, the user's mail), intake of untrusted content (fetching URLs, web search, reading mail or messages written by others) and a way to send data out (sending e-mail or messages, HTTP posts, webhooks, uploads). A tool advertising arbitrary command execution counts for all three; arbitrary file read counts as private data. When one server offers all three roles, text planted in the untrusted content can instruct the model to read the private data and send it away: the 'lethal trifecta' of prompt injection. Medium, high when the HTTP endpoint needs no authentication. One finding lists the tools in each role. No tool is called. Evidence: The tools/list response; each role lists its tools and the wording that put them there. False positives: Roles are inferred from names, descriptions and capability keywords, not from behaviour. A 'fetch' tool restricted to one internal API, or a 'send' tool that only notifies the signed-in user, is still counted. Conversely a fetch tool with a free-form URL can leak data through the URL itself; that path is not counted as a send role. Fix: Break the trifecta: split the tools across separate servers (or separate client configurations) so no single agent session combines private data, untrusted content and an outbound channel; or gate the send tool behind explicit per-call user confirmation and an allow-list of destinations; or constrain the intake tool to trusted sources. Keep the server authenticated so nobody else can drive the combination. | passivehttp, stdio | Medium | ASI01 · LLM01:2026 · LLM01:2025 | Pending |
Robustness
ERR-01 · DOS-01Verbose errors, and three bounded probes for size, nesting and rate limits. Never more than one oversized request, one deep request and 20 paced list calls.
| ID | Check | What it does | Mode | Severity | Maps to | FP rate |
|---|---|---|---|---|---|---|
| ERR-01 | Verbose error responses leak stack traces, paths or versions | Sends three malformed requests: truncated JSON, an unknown method and a request whose params is a string instead of an object.Details and fixSends three malformed requests: truncated JSON, an unknown method and a request whose params is a string instead of an object. Error bodies and the Server header are inspected for stack traces, internal file paths and framework version strings. Evidence: Each malformed request and the error response body. False positives: A Server header with a version is reported as Info. Framework names without versions are not reported. Fix: Return generic JSON-RPC errors (-32700/-32600/-32601/-32602) without 'data' containing exceptions, log details server-side, and strip the Server header version at the proxy. | passivehttp, stdio | Medium | MCP08:2025 (beta) · LLM02:2026 · LLM02:2025 | Pending |
| DOS-01 | No visible limits on request size, nesting or rate | Three bounded probes, never more: (1) one tools/list whose 'cursor' is a 1 MB string, (2) one tools/list with params nested 64 levels deep, (3) 20 tools/list requests sent at the scan rate cap (5 req/s by default).Details and fixThree bounded probes, never more: (1) one tools/list whose 'cursor' is a 1 MB string, (2) one tools/list with params nested 64 levels deep, (3) 20 tools/list requests sent at the scan rate cap (5 req/s by default). A 5xx, a transport failure or a response slower than 5 s on (1)/(2) is reported; absence of any 429 during (3) is reported as informational. No tools are called. Evidence: The oversized/nested requests (truncated) and timing; the burst responses' statuses. False positives: The burst runs inside the scanner's own rate cap, so 'no 429 observed' is indicative only (low confidence) and many well-run servers legitimately allow 5 req/s. A 413 or connection reset on the oversized probe is treated as a correct defence. Fix: Enforce a request body limit (e.g. 1 MB) and JSON depth limit at the edge, return 413/400 promptly, and rate-limit per client/session with 429 and Retry-After. | passivehttp, stdio | Low | ASI08 · LLM06:2026 · LLM10:2025 | Pending |
Injection (active, coming soon)
INJ-01 … INJ-05Registered in v0.1.0 behind the authorisation gate and reported as not implemented. No payloads are sent yet.
| ID | Check | What it does | Mode | Severity | Maps to | FP rate |
|---|---|---|---|---|---|---|
| INJ-01 | Command/code injection (safe canaries) | Will call exec-class tools with time-based, non-destructive canaries and compare response timing.Details and fixWill call exec-class tools with time-based, non-destructive canaries and compare response timing. requires --active and verified ownership. Evidence: tools/call request with the canary and the timed response. False positives: Network jitter can mimic a timing delta; the probe will repeat and require consistency. Fix: Never pass tool arguments to a shell; use argument vectors and allow-lists. | active · coming soonhttp, stdio | Critical | MCP05:2025 (beta) · ASI05 · LLM10:2026 | n/a |
| INJ-02 | Path traversal / arbitrary file read | Read-only traversal probes on path-typed arguments.Details and fixRead-only traversal probes on path-typed arguments. requires --active and verified ownership. Evidence: tools/call request with the traversal path and the response excerpt. False positives: Tools that intentionally expose the whole filesystem are a design decision, not a bug. Fix: Canonicalise paths, resolve symlinks, and enforce a root directory check after resolution. | active · coming soonhttp, stdio | High | MCP05:2025 (beta) · ASI02 | n/a |
| INJ-03 | Server-side request forgery | Callback-based SSRF probes on URL-typed arguments.Details and fixCallback-based SSRF probes on URL-typed arguments. requires --active and verified ownership. Evidence: tools/call request with the callback URL and the callback log entry. False positives: Fetch tools are often meant to fetch arbitrary URLs; the finding focuses on internal/metadata addresses. Fix: Resolve and validate destination addresses, block link-local and private ranges, use an egress proxy. | active · coming soonhttp, stdio | High | MCP05:2025 (beta) · ASI02 | n/a |
| INJ-04 | SQL injection (safe markers) | Boolean and timing markers on query/filter arguments, plus error fingerprinting.Details and fixBoolean and timing markers on query/filter arguments, plus error fingerprinting. requires --active and verified ownership. Evidence: tools/call requests and differential responses. False positives: Tools that accept raw SQL by design are reported as a capability (META-03), not an injection. Fix: Use parameterised queries; never interpolate tool arguments into SQL. | active · coming soonhttp, stdio | High | MCP05:2025 (beta) | n/a |
| INJ-05 | Tool output returned without sanitisation (contextual payload path) | Read-only tool calls against scanner-controlled canary content.Details and fixRead-only tool calls against scanner-controlled canary content. requires --active and verified ownership. Evidence: tools/call request and the returned content. False positives: Returning fetched content verbatim is normal; the finding documents the path for the client's risk assessment. Fix: Mark tool output as untrusted data in content blocks and strip instruction-like markup where feasible. | active · coming soonhttp, stdio | Medium | MCP06:2025 (beta) · MCP10:2025 (beta) · ASI06 · LLM01:2026 · LLM01:2025 | n/a |
Supply chain and sandbox (active, coming soon)
SUPPLY-01 … SANDBOX-01For stdio packages and client configs. Registered in v0.1.0 and reported as not implemented.
| ID | Check | What it does | Mode | Severity | Maps to | FP rate |
|---|---|---|---|---|---|---|
| SUPPLY-01 | Package provenance and known vulnerabilities (stdio) | Registry metadata, OSV lookup and install-script review for the stdio package.Details and fixRegistry metadata, OSV lookup and install-script review for the stdio package. requires --active and verified ownership. Evidence: Registry metadata documents and OSV responses. False positives: Unpinned versions are common in examples; severity will depend on install-script presence. Fix: Pin versions, commit lockfiles, publish with provenance attestations, avoid install scripts. | active · coming soonstdio | Medium | MCP04:2025 (beta) · ASI04 · LLM04:2026 · LLM03:2025 | n/a |
| SUPPLY-02 | stdio configuration launches risky commands | Static review of a submitted client configuration file.Details and fixStatic review of a submitted client configuration file. requires --active and verified ownership. Evidence: The configuration entry. False positives: npx -y with a pinned version is acceptable; unpinned is flagged. Fix: Pin packages, avoid shell wrappers, keep secrets out of config files. | active · coming soonstdio | High | MCP05:2025 (beta) · MCP04:2025 (beta) · ASI05 | n/a |
| SANDBOX-01 | Unexpected runtime behaviour during listing (stdio) | Sandboxed execution with egress and syscall logging.Details and fixSandboxed execution with egress and syscall logging. requires --active and verified ownership. Evidence: Sandbox egress/file/process log. False positives: Telemetry and update checks are common; the finding will list hosts for review. Fix: Do not contact the network or touch files outside the package during start-up and listing. | active · coming soonstdio | High | MCP04:2025 (beta) · ASI10 · ASI04 | n/a |
Attestation only
GOV-01 … GOV-05Cannot be assessed by a black-box scan. Every report lists them with a checklist and never marks them passed or failed.
| ID | Check | What it does | Mode | Severity | Maps to | FP rate |
|---|---|---|---|---|---|---|
| GOV-01 | Audit logging and telemetry for tool invocations | Whether a server logs and alerts on tool calls is not observable from outside.Details and fixWhether a server logs and alerts on tool calls is not observable from outside.
| attestation | n/a | MCP08:2025 (beta) | n/a |
| GOV-02 | Shadow MCP servers (inventory of servers in use) | An inventory problem across workstations and repositories, not an endpoint property.Details and fixAn inventory problem across workstations and repositories, not an endpoint property.
| attestation | n/a | MCP09:2025 (beta) | n/a |
| GOV-03 | Data and model poisoning in training or fine-tuning pipelines | Training-pipeline integrity is not black-box testable.Details and fixTraining-pipeline integrity is not black-box testable.
| attestation | n/a | LLM05:2026 · LLM04:2025 | n/a |
| GOV-04 | Inter-agent communication, cascading failures, human trust, rogue agents | Architecture and process items; multi-agent paths are not externally visible.Details and fixArchitecture and process items; multi-agent paths are not externally visible.
| attestation | n/a | ASI07 · ASI08 · ASI09 · ASI10 | n/a |
| GOV-05 | Memory and context poisoning persistence | Needs a customer-supplied test session across two conversations; not a server-side scan.Details and fixNeeds a customer-supplied test session across two conversations; not a server-side scan.
| attestation | n/a | ASI06 | n/a |
False-positive rates are measured against the public “state of MCP server security” dataset and published per check once there are at least 100 samples. Until then the column reads “Pending”.
Read the methodology