Eight scored checks. One advisory. Zero secrets about how grading works.
Methodology is a trust product. Every check below is documented publicly, weighted transparently, and enforced identically for every server: vendors know exactly what an A requires before they submit, and buyers know exactly what a grade means before they rely on it.
How the grade is computed
Each scored check returns 0–100 and a status (PASS, WARN, FAIL, or N/A when the surface is behind authentication). The overall score is the weighted average over scored checks, renormalized so auth-gated N/A checks never dilute the grade. Letter thresholds: A at 90+, B at 80+, C at 70+, D at 60+; below 60 is an F.
Four findings are severe enough that an average must never wash them out. They hard-cap the grade regardless of everything else:
- CFM-01: an unauthenticated non-read-only surface caps the grade at F
- CFM-05: credential material disclosed to clients caps the grade at F
- CFM-06: unguarded destructive tools caps the grade at D
- CFM-08: plaintext transport caps the grade at D
Every report is anchored
A grade that can be edited is marketing. A grade that is anchored becomes evidence. Each report's canonical body is hashed (SHA-256) and the digest is written to the Sigstore/Rekor public transparency log at issue time. The report page carries the digest, the Rekor log index, and an independent verify link. Grades are append-only: a vendor can improve and re-scan, but history stays checkable forever.
Authentication
weight 20Roughly 40% of internet-exposed MCP servers ship with no authentication (Adversa AI, Jun 2026). An unauthenticated non-read-only surface hard-caps the overall grade at F, no matter how well everything else scores.
The scanner sends the standard MCP initialize handshake with no credentials. A hardened endpoint refuses it. An exposed endpoint completes an anonymous session and, in the worst case, lists its tools to a stranger.
The endpoint rejects unauthenticated MCP initialize outright. Anonymous-by-design servers can still score, but only when every exposed tool is read-only or stateless generation with no data-mutation surface.
- Endpoint completes an anonymous MCP session: open to the internet
- Write- or destructive-class tools exposed to an anonymous caller
- Legacy SSE stream opened without credentials
OAuth Correctness
weight 10Broken OAuth flows in MCP servers have produced multiple CVEs. Auth that clients cannot discover gets worked around, and workarounds become the vulnerability.
When the endpoint does require auth, the scanner checks how discoverable that auth is: a 401 with a WWW-Authenticate challenge, and RFC 9728 protected-resource metadata at /.well-known/oauth-protected-resource.
Both discovery mechanisms present: spec-conformant OAuth 2.1 wiring that any MCP client can negotiate without out-of-band setup.
- Rejects anonymous calls but returns no WWW-Authenticate header
- No protected-resource metadata served, forcing manual configuration
Tool Poisoning
weight 15Tool descriptions are trusted input to the calling agent. A poisoned description executes in the agent's context on every session, before any tool is ever invoked.
Every tool description and the server's instructions field are analyzed for embedded directives aimed at the calling agent: instruction overrides, exfiltration prompts, tool-shadowing language, and oversized descriptions that commonly carry hidden payloads.
Tool metadata that describes tools and nothing else. No imperative language aimed at the model, no cross-tool references, descriptions sized for humans.
- Injection patterns matched inside a tool description
- Server instructions attempting to steer client behavior
- Oversized description: a common carrier for embedded instructions
The honest boundary: This check covers the injection surface in a server's own metadata. It cannot sanitize live response content an authorized tool relays back after connection; response poisoning (the Agentjacking class) is an ecosystem-wide frontier no pre-connect grade can honestly claim to stop.
Scope Minimization
weight 10A single over-scoped tool turns a compromised session into a database-wide incident. Least privilege is the difference between a finding and a breach.
The exposed tool surface is classified: raw-execution parameters (query, command, sql), administrative-scope tools, read/destructive mixes on one anonymous endpoint, and very large tool surfaces that inflate blast radius.
A scoped tool surface where each tool's effective privilege matches its stated purpose, destructive scope is separated or gated, and no tool accepts an arbitrary-execution parameter.
- Tool accepts a raw execution parameter: effective scope is whatever the backing system allows
- Administrative-scope tool exposed on a connector surface
- Read-only and destructive tools sharing one unauthenticated endpoint
Secret Exposure
weight 10Disclosed credential material hard-caps the grade at F. A key in tool metadata is not a leak risk; it is already leaked to every client that has ever connected.
Tool definitions, server instructions, and server metadata are scanned for credential material: API-key patterns, bearer tokens, connection strings. Everything a server hands to every connecting client.
Zero credential patterns in disclosed metadata. Anything a client receives on handshake is public by definition.
- Credential pattern matched inside tool definitions or server instructions: every connecting client receives it
Destructive Guardrails
weight 15Unguarded destructive tools cap the grade at D. Annotations are the only signal a host has to decide which invocations deserve a human in the loop.
Destructive- and execute-class tools are checked for the MCP annotations (destructiveHint, readOnlyHint) that let host applications require human confirmation before invocation, and for annotation mismatches that would suppress those confirmations.
Every destructive-class tool declares destructiveHint honestly; no tool claims readOnlyHint while its behavior class says otherwise.
- Destructive-class tool without destructiveHint: hosts cannot require confirmation
- readOnlyHint declared on a destructive-looking tool: hosts will skip confirmation for a risky tool
- Write-class tool with no safety annotations at all
Spec Conformance (retired — folded into the readiness axis)
advisory · unscoredFolding this check corrected a conflation: a well-authenticated server on deprecated transport is a migration risk for 2027-07-28, not a security defect today. Scoring both on one axis mispriced both. The identifier is retired rather than reused — published reports reference CFM-07, and renumbering would break them.
Nothing, as of battery 2.0.0. The three signals this check measured — transport generation, negotiated protocol revision, and session posture — are now RDY-01, RDY-02 and RDY-03 on the readiness axis, measured with more signal than a single weighted check could carry.
Not applicable. Migration posture is not a security property, so it no longer contributes to the A–F grade; it is reported separately as readiness.
The honest boundary: Reports anchored under battery 1.x still contain a scored CFM-07 and remain valid at their stated version. Grades are comparable within a major battery version, not across one.
Transport Security
weight 10Plaintext transport caps the grade at D. Tool calls carry session identifiers and often tokens; unencrypted transit makes every other control moot on a hostile network.
Whether the endpoint serves MCP over TLS or plaintext HTTP.
TLS transport, full stop.
- Plaintext HTTP transport: sessions and tool traffic are interceptable
Exposed Configuration Surface
weight 10SANS ISC observed internet-wide scanning for mcp.json config files and AI-assistant credentials across 49 distinct source IPs over two weeks (reported August 2026). Attackers enumerate this surface today. Served credential material hard-caps the grade at F on the same principle as CFM-05: it is not at risk of leaking, it has already leaked.
A short list of well-known paths at the origin (.env, mcp.json, .mcp.json, .cursor/mcp.json, claude_desktop_config.json) is requested read-only. A path counts as exposed only when the body actually parses as the document type it claims to be; page shells returned by catch-all routing are rejected and reported as noise, never as exposure.
No configuration or credential document served from a public path. A clean result carries zero weight — not exposing your environment file is the expected baseline, not something a grade rewards.
- Credential material served at a public path: already disclosed to every client that has requested it
- Configuration document served unauthenticated, disclosing deployment structure
The honest boundary: The check is asymmetric on purpose. A clean result contributes nothing to the weighted average, so adding it cannot promote a server that earned a lower grade on real findings. It can only ever cost.
Command-Injection Exposure
advisory · unscoredOne command-injection root cause in the MCP SDK layer produced 12 CVEs across the ecosystem, mostly unpatched. A trust registry either maps a class like this or it is decoration.
The remotely-observable sink surface of OX Security's MCP STDIO command-injection class (published 2026-04-15): command-execution tools, command-shaped parameters, and server content that tries to steer a client's transport to STDIO (exploit family 4).
Nothing: CFM-09 is advisory and never affects the A–F grade. It exists so the class is mapped on every report instead of marketed around.
- Command-execution sink observed: the surface this class ultimately targets
- Transport-steering content referencing STDIO or process spawning (family 4 pattern)
The honest boundary: Three of the four exploit families are local STDIO config-injection: they live in client configuration and on the host, not at a URL. A remote probe cannot honestly test them; they are covered by signed, Rekor-anchored operator attestation instead.
Checks we evaluated and did not ship
Three attack classes published in 2026 map cleanly to MCP servers and are absent from the public battery on purpose. A registry that implies coverage it does not have is worth less than one that names its edges, so the edges are named here.
Requires invoking tools/call. The public scanner is technically incapable of invoking a tool and that invariant is enforced by a test, because unsolicited exploitation of a third party's endpoint carries CFAA and state computer-crime exposure regardless of intent. This belongs to the authenticated battery, run with the vendor's written consent.
The defect is that delimiting of third-party-authored content was applied to some tools and not others. Observing it means reading live tool responses that relay attacker-authored content, which again requires invoking tools. Nothing in the pre-connect handshake reveals it.
A property of a locally running process, not of a hosted URL. There is no honest way to observe a remote server's bind address from the far side of a load balancer, and inferring it would be fabrication.
What the scanner never does
The public scanner performs the standard MCP client handshake (initialize, notifications/initialized, tools/list) against a publicly reachable endpoint and nothing more. It never calls tools/call, so nothing is exploited, nothing is changed, and no access control is circumvented. A grade is an automated observation at a timestamp. It is not a certification, and we say so on every surface that shows one.