Skip to main content
mcpscore’s value rests on its score being trustworthy. This page explains exactly how the score is computed, what is and is not checked, and why — so you can verify every claim yourself, dispute a rule you disagree with, or propose a new one.

Principles

  1. Deterministic. No LLM judging, no API keys, no sampling. The same server state always produces the same score, in seconds. This makes mcpscore usable as a CI gate and its results reproducible by anyone.
  2. Spec-anchored. Every rule enforces something the MCP specification or its published best practices actually says. Readiness rules cite the SEP (Specification Enhancement Proposal) they enforce, and the citation is included in every result.
  3. Fair across spec versions. MCP is a versioned protocol; servers on different revisions are scored against the revision they negotiated, never punished for a spec they don’t speak yet (see Multi-spec-version scoring).
  4. Honest about limits. Anything mcpscore cannot verify from the outside is documented as untested, not silently skipped.

The score

Every rule has a severity, and each passing rule contributes its weight to the score. The final score is reported as earned/maximum.
SeverityWeightMeaning
CRITICAL5Spec violations that break interoperability (protocol version, server name, TLS)
HIGH3Strong spec expectations (server version, valid tool schemas)
MEDIUM2Recommendations that improve agent UX (titles, descriptions, error hygiene)
LOW1Nice-to-haves (capability extras, transport recommendations)
Only applicable rules count toward the maximum: a rule that cannot meaningfully run against your server is skipped and excluded from both the earned and the maximum score — never failed. Every skip appears in the JSON report (skipped_rules) with one of three reasons:
Skip reasonMeaning
not-applicableThe rule’s spec-version range excludes the negotiated version (e.g. a rule for behavior your revision doesn’t define)
insufficient-dataThe observations the rule needs are unavailable (e.g. a network probe errored, or probes don’t run for this transport) — the rule can neither pass nor fail
requires-modern-supportA readiness detail check on a server with no modern-lifecycle support — the gateway rules already carry that verdict; repeating it per detail would pile on noise

How an audit runs

  1. Connect — stdio (local .py/.js servers), Streamable HTTP, or SSE, with automatic transport detection. A connection only counts once the server completes a real MCP handshake.
  2. Collect — protocol version, server info, capabilities, tools, resources, prompts, TLS details.
  3. Probe (HTTP/HTTPS servers only) — a set of sessionless requests observing behavior outside the negotiated session, e.g. whether the server also speaks the next spec revision’s stateless lifecycle. Probes are read-only by design: mcpscore never invokes tools/call against your server, so an audit can never trigger tool side effects.
  4. Evaluate — every registered rule runs against the collected data and probe observations. See the Rules Reference for the full list.

Multi-spec-version scoring

MCP publishes dated spec revisions (2024-11-052025-11-25, with 2026-07-28 in release-candidate stage), and the 2026 revision replaces the stateful initialize lifecycle with a stateless one. The spec itself describes servers as Legacy (stateful), Modern (stateless), or Dual-era (both). mcpscore treats the spec version as a property of your server that it detects, not a mode of the tool:
  • Your server is scored against the spec revision it negotiated. Rules declare the version range they apply to; outside it they are skipped, keeping scores comparable across servers on different revisions.
  • The report states the era your server was observed to support (legacy, modern, or dual-era), following the spec’s own detection guidance.
  • Modern-only servers are fully supported: if the legacy handshake fails but the server answers stateless requests, mcpscore audits it through its probe layer instead of reporting a connection failure.

The readiness score (separate, informative)

Ahead of a new spec revision, mcpscore answers a second question: “is this server ready for the upcoming spec?” This is deliberately a separate score (readiness.score / readiness.max_score in the report), never mixed into the main score — a fully compliant 2025-11-25 server keeps its clean main score, and readiness is guidance, not punishment. Readiness rules for the 2026-07-28 revision, each anchored to normative spec language (MUST/MUST NOT unless noted):
RuleSeverityNormative basis
readiness_2026_server_discoverCRITICALServers MUST implement server/discover (SEP-2567)
readiness_2026_stateless_requestCRITICALRequests carry their context in _meta; the initialize handshake is removed (SEP-2575)
readiness_2026_meta_validationHIGHRequests missing required _meta fields MUST be rejected with -32602 + HTTP 400
readiness_2026_header_validationHIGHHeader/body mismatches MUST be rejected with -32020 (HeaderMismatch) + HTTP 400 (SEP-2243)
readiness_2026_cache_metadataHIGHList/discover results MUST carry ttlMs >= 0 and cacheScope (SEP-2549)
readiness_2026_unsupported_version_errorMEDIUMUnknown protocol versions MUST be rejected with -32022 naming the supported versions
readiness_2026_error_code_migrationMEDIUMMissing resources yield -32602; the legacy -32002 MUST NOT be emitted (SEP-2164)
readiness_2026_result_typeMEDIUMAll results carry the resultType discriminator (SEP-2322)
readiness_2026_deprecated_featuresMEDIUMFeatures deprecated in 2026-07-28 (logging; SHOULD NOT adopt) (SEP-2577)
readiness_2026_tool_schema_dialectLOWTool schemas valid under the default JSON Schema 2020-12 dialect; no network-URI $ref (SEP-2106)
readiness_2026_no_session_idHIGHNo Mcp-Session-Id minted or echoed on modern requests — leaked legacy session bookkeeping
readiness_2026_removed_methodsMEDIUMRemoved methods (e.g. ping) rejected with 404 + -32601, not served — leaked legacy surface
Two things worth knowing about how these run:
  • The last two are legacy-leakage checks: they only run when your server shows modern support, because a legacy-only server keeping sessions is correct behavior, not leakage. A dual-era server (serving both lifecycles) is spec-legal and scores normally — what’s flagged is legacy artifacts inside modern interactions.
  • When the 2026-07-28 revision goes final and adoption normalizes, these rules migrate into the main score for servers that negotiate it — their rule_ids never change, so CI integrations and report consumers are unaffected.
Draft-spec citations — the 2026-07-28 revision is final-pending (release candidate locked 2026-05-21). All citations above will be re-verified against the dated spec URLs when the revision is published, before the corresponding mcpscore release.

What mcpscore deliberately does not test

Honesty about limits is part of the methodology. The following are currently out of scope, either because they are not externally observable or because testing them safely from the outside is not yet possible:
  • True statelessness (no hidden cross-request state) — only probabilistically testable; the spec itself concedes this.
  • requestState integrity enforcement (MRTR) — only tamper-rejection is observable, and active tampering probes are deferred.
  • subscriptions/listen behavior and SSE stream semantics.
  • Tasks and MCP Apps extensions — they version independently of the core spec.
  • Client-side OAuth obligations (issuer validation, credential binding, scope accumulation) — not observable from a server audit.
  • Tool behavior — mcpscore never calls your tools, so it cannot judge what they do, only how they are described and typed.

Stability contract

  • rule_id is stable. Display names and messages may be reworded; the rule_id in the JSON report is a machine contract and never changes meaning.
  • The JSON report schema is versioned (schema_version), bumped only on backwards-incompatible changes. New fields are added additively.
  • Rule additions and severity changes are documented in the changelog.

Disagree with a rule?

The methodology is open by design. If a rule seems wrong for your server, open an issue with the rule_id and the spec text you believe contradicts it — rules are required to be defensible by citation, and we treat a rule that can’t be defended as a bug. To propose a new rule, see CONTRIBUTING.