The Trust-Readiness Rubric
v0.6.2The open standard behind every TrustScore. One scan of a property — a domain and any MCP server it runs — produces one signed scorecard, scored on three co-equal axes: Trust (can an agent believe who this is?), Access (can an agent find, read, and connect to it?), and MCP (if it runs an MCP server, is it a quality capability to build against?). The three are reported side by side and never blended into one number. A property with no MCP server simply scores the MCP-server checks as N/A — never as a failure.
Every check is labelled standard (a formal third-party spec exists) or convention (an emerging community/vendor convention — no standards body yet), independent of how widely adopted it is. Stability: The rubric is versioned with semver (the `rubricVersion` on every result). Immutable result permalinks (/r/<domain>/<scanId>) freeze the rubric version they were graded under, so a published historical grade never silently re-grades when the rubric changes; a fresh scan always states its current version.
Trust
Can an agent believe who this is? Trust is the headline axis. Each website Trust category also folds in the matching MCP-server checks (signing, provenance, honest tool behaviour, fail-closed auth) — those run only when the property has an MCP server, and are N/A otherwise.
How Trust scoring works
- Each check scores pass = 1.0, partial = 0.5, or fail / not-served = 0.
- A check is N/A — excluded from scoring, not failed — when the property honestly doesn't offer that capability (e.g. the MCP-server checks when there is no MCP server, or the OAuth-discovery checks when
/auth.mddeclares no delegated authorization). We never reward faking a surface you don't have. - A category's score is the mean of its applicable checks, scaled to 0–100.
- The overall score is the weighted mean of the category scores; a fully-N/A category is dropped and the remaining weights are renormalized.
- Grades: A+ ≥ 90 (with a signed agent card), A ≥ 85, B ≥ 70, C ≥ 55, D ≥ 40, else F.
- Caps: a 0 in the identity category caps the grade at F; an unsigned or missing agent card caps it at B.
| Category | Weight |
|---|---|
| Verifiable identity | 25% |
| Alignment & values | 20% |
| Attestation & provenance | 20% |
| Accountability | 20% |
| Delegation & auth | 15% |
Verifiable identity
weight 25% · 5 checksCan an agent prove who this is — and is that proof cryptographic?
Publishes an A2A agent card
conventionemergingidentity.agent-cardInspects /.well-known/agent-card.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: Create a file served at https://<your-domain>/.well-known/agent-card.json with Content-Type: application/json. Minimal valid A2A card: { "protocolVersion": "0.3.0", "name": "<Your service name>", "description": "<What this agent/service does>", "url": "https://<your-domain>", "capabilities": { "streaming": false }, "skills": [{ "id": "search", "name": "Search", "description": "<one concrete skill>" }], "securitySchemes": {} } Verify: `curl -sI https://<your-domain>/.well-known/agent-card.json` returns 200 + application/json. Spec: https://a2a-protocol.org/latest/specification/
Reference standardAgent card is cryptographically verifiable (signed / DID / VC)
standardemergingidentity.agent-card-signedInspects /.well-known/agent-card.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: Make /.well-known/agent-card.json tamper-evident by adding a cryptographic binding the scanner recognizes — ANY of: (a) a JWS in a top-level `signatures` array (A2A AgentCardSignature[]), (b) a W3C VC `proof` object, (c) a top-level `jws` string, or (d) a `did:`-prefixed `id`/`issuer`/`controller`. Example (a): "signatures": [{ "protected": "<base64url JWS header>", "signature": "<base64url sig>" }] Generate the JWS over the card with your signing key (e.g. an Ed25519/ES256 JWK). Verify: re-fetch the card and confirm the `signatures`/`proof`/`did:` field is present and validates. Spec: https://www.w3.org/TR/vc-data-model-2.0/
Reference standardPublishes an MCP server card (if it runs MCP)
conventionemergingidentity.mcp-server-cardInspects /.well-known/mcp/server-card.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: If you run an MCP server, advertise it at https://<your-domain>/.well-known/mcp/server-card.json (Content-Type: application/json). Minimal card: { "name": "<Your MCP server>", "version": "1.0.0", "transport": "streamable-http", "url": "https://<your-domain>/mcp", "tools": [{ "name": "<tool_name>", "description": "<what it does>" }] } Verify: `curl -s https://<your-domain>/.well-known/mcp/server-card.json` returns the card with a name + tools. (If you don't run MCP, skip — this check is upside-only.) Spec: https://modelcontextprotocol.io/
Reference standardinitialize advertises server identity
standardestablishedMCP servermcp.identity.server-infoSeeded from MCP spec; Gauntlet C20
Scoring scored — counted equally; a partial/fail docks
To satisfy: Make your MCP `initialize` response advertise a clear identity — a non-empty `serverInfo.name` and `serverInfo.version`, plus a top-level `instructions` string telling an arriving agent what the server is for. The result should look like: { "protocolVersion": "2025-06-18", "serverInfo": { "name": "<Your MCP server>", "version": "1.2.0" }, "instructions": "<what this server is for + how to start>", "capabilities": { "tools": {} } } Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' | jq '.result | {name:.serverInfo.name, version:.serverInfo.version, instructions}'` shows all three non-empty. Spec: https://modelcontextprotocol.io/specification
Reference standardExposes cryptographically verifiable artifacts
standardestablishedMCP servermcp.identity.signed-artifactsSeeded from Gauntlet C17/C29
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish at least one signed resource an agent can verify in-band. Advertise a JWKS resource (Ed25519: kty=OKP, crv=Ed25519) and serve resources whose body carries a detached Ed25519/JCS signature (a `signature` or `jws` field) OR an embedded W3C Data Integrity proof (`eddsa-jcs-2022`, did:key), each verifiable against that JWKS. Envelope shape: { "payload": { /* your resource */ }, "jws": "<base64url JWS over the JCS-canonicalized payload>" } // JWKS advertised at e.g. https://<your-domain>/.well-known/jwks.json Verify: `curl -s https://<your-domain>/.well-known/jwks.json` returns your Ed25519 key; then fetch a signed resource, re-canonicalize (JCS) the stripped payload, and confirm the `jws`/`signature` verifies against that key — then flip one byte and confirm the tampered copy FAILS. Spec: https://www.w3.org/TR/vc-data-integrity/
Reference standard
Alignment & values
weight 20% · 5 checksAre the operator's declared values, scope, and capabilities published and machine-readable?
Publishes an alignment / values card
conventionemergingalignment.cardInspects /.well-known/alignment-card.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish https://<your-domain>/.well-known/alignment-card.json (application/json) declaring your values AND scope AND refusals. Recognized shapes: top-level `values`/`principles` plus EITHER `scope`/`permitted_scope`/`boundaries` and `must_refuse`/`prohibited`, OR an AAP `autonomy_envelope` with `bounded_actions`+`forbidden_actions`. Example: { "values": ["honesty", "user-safety"], "autonomy_envelope": { "bounded_actions": ["read public data", "answer questions"], "forbidden_actions": ["make payments", "delete user data"] } } Verify: `curl -s https://<your-domain>/.well-known/alignment-card.json` shows values + scope + refusals. Spec: https://github.com/mnemom/aap
Reference standardDeclared capabilities are present and coherent
conventionemergingalignment.capabilitiesInspects /.well-known/agent-card.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: In /.well-known/agent-card.json, populate either a non-empty `capabilities` object or a non-empty `skills` array so a calling agent knows what this one can do. Example: "skills": [ { "id": "search", "name": "Search", "description": "Full-text search over docs" }, { "id": "summarize", "name": "Summarize", "description": "Summarize a URL" } ] Verify: `curl -s https://<your-domain>/.well-known/agent-card.json` shows ≥1 concrete skill/capability. Spec: https://a2a-protocol.org/latest/specification/
Reference standardDeclares values / scope (alignment card or equivalent)
conventionemergingMCP servermcp.alignment.cardSeeded from Mnemom alignment-card convention (vendor-neutral; A2A card or equivalent also passes)
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish a machine-readable declaration of what this server is allowed to do — values/principles plus scope and/or refusals. Any ONE satisfies it: an A2A agent card with capabilities, an alignment/values card, or an equivalent documented resource (open-sourcing the implementation is NOT required). Example at https://<your-domain>/.well-known/alignment-card.json: { "values": ["honesty", "user-safety"], "scope": ["read public data"], "must_refuse": ["make payments", "delete user data"] } Verify: `curl -s https://<your-domain>/.well-known/alignment-card.json | jq '{values, scope, must_refuse}'` (or your A2A agent card) returns a structured values + scope/refusals declaration. Spec: https://a2a-protocol.org/latest/specification/
Reference standardDeclares known limitations / non-goals
conventionemergingMCP servermcp.alignment.known-limitationsSeeded from Mnemom signed-known-limitations; IITR /non-goals.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish an honest, machine-readable statement of what this server does NOT do or guarantee — its known limitations / non-goals (ideally signed). This is on-brand transparency, not a demerit — declaring a limit SCORES; hiding it does not. Example at https://<your-domain>/non-goals.json (or /.well-known/non-goals.json): { "nonGoals": ["does not execute payments", "no real-time data (cached up to 24h)", "English-only responses"] } Verify: `curl -s https://<your-domain>/non-goals.json | jq '.nonGoals'` lists explicit known-limitations / non-goals. Spec: https://isittrustready.ai/non-goals.json
Reference standardTools do what they say (output matches declared schema)
standardestablishedMCP servermcp.honesty.descriptions-accurateSeeded from Gauntlet C11/C12
Scoring scored — counted equally; a partial/fail docks
To satisfy: Make each tool's behaviour match its declared `outputSchema`: every readable tool's live `structuredContent` must validate (strict) against the schema it declares, and descriptions must not overclaim what the tool returns. Fix by tightening the schema to the real output (or the output to the schema). Verify: call each public-read tool, e.g. `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"<tool>","arguments":{}}}'`, then run its `.result.structuredContent` through a JSON-Schema validator (AJV) against that tool's `outputSchema` — no drift. Spec: https://json-schema.org/
Reference standard
Attestation & provenance
weight 20% · 3 checksAre claims backed by signed attestations and verifiable provenance?
Publishes signed attestations / verifiable credentials
standardemergingattestation.signedInspects /.well-known/attestations.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish https://<your-domain>/.well-known/attestations.json (application/json) — an array (or {credentials:[…]}/{attestations:[…]}) of W3C Verifiable Credentials, each carrying a `proof` object (or `jws`/`signatures`). Example: [{ "type": ["VerifiableCredential"], "issuer": "did:web:<your-domain>", "credentialSubject": { "id": "https://<your-domain>", "claim": "<what you attest>" }, "proof": { "type": "DataIntegrityProof", "proofValue": "<sig>" } }] Verify: `curl -s https://<your-domain>/.well-known/attestations.json` returns ≥1 credential WITH a proof/jws. Spec: https://www.w3.org/TR/vc-data-model-2.0/
Reference standardBuild / behaviour provenance (Sigstore / SLSA)
standardemergingattestation.provenanceInspects /.well-known/provenance.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish https://<your-domain>/.well-known/provenance.json (application/json) — a SLSA/in-toto statement, ideally Sigstore/DSSE-signed. Recognized: a `predicateType`+`predicate` (in-toto) or a `dsseEnvelope`, plus `dsseEnvelope`/`signatures` to be a PASS (unsigned = partial). Example: { "_type": "https://in-toto.io/Statement/v1", "predicateType": "https://slsa.dev/provenance/v1", "predicate": { "buildDefinition": { /* … */ } }, "dsseEnvelope": { "payload": "<base64>", "signatures": [{ "sig": "<sig>" }] } } Generate with `slsa-generator` or `cosign attest`. Verify: `curl -s …/provenance.json` shows the predicate + a signature/DSSE envelope. Spec: https://slsa.dev/
Reference standardOffers verifiable provenance / attestation
standardemergingMCP servermcp.provenance.attestationSeeded from Gauntlet C19/C25
Scoring scored — counted equally; a partial/fail docks
To satisfy: Expose a verifiable provenance path an agent can check in-band: EITHER a signed scorecard/certificate whose Ed25519 signature verifies against a published key, OR an append-only attestation (hash-chain + merkle root) proving a claim derives from an unbroken checkpoint chain. Minimal shape: { "claim": "<what you attest>", "checkpoints": [{ "hash": "<h0>" }, { "prevHash": "<h0>", "hash": "<h1>" }], "merkleRoot": "<root>", "signature": "<Ed25519 sig over the root>" } Verify: `curl -s https://<your-domain>/.well-known/provenance.json` (or your attestation resource) returns the artifact; confirm it verifies (signature ok / hash_chain_valid=true), then submit a bogus/altered one and confirm it is REJECTED. Spec: https://modelcontextprotocol.io/specification
Reference standard
Accountability
weight 20% · 7 checksIs there a way to report, audit, and verify behaviour over time?
Serves security.txt
standardestablishedaccountability.security-txtInspects /.well-known/security.txt
Scoring scored — counted equally; a partial/fail docks
To satisfy: Create https://<your-domain>/.well-known/security.txt (Content-Type: text/plain). Set a real Contact and an Expires < 1 year out (ISO 8601). Template: Contact: mailto:security@<your-domain> Expires: 2027-01-01T00:00:00Z Canonical: https://<your-domain>/.well-known/security.txt Verify: `curl -s https://<your-domain>/.well-known/security.txt` shows the Contact: and Expires: lines. Spec: https://www.rfc-editor.org/rfc/rfc9116
Reference standardPublishes a dated, signed re-verification status
conventionemergingaccountability.statusInspects /agent-readiness-status.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish https://<your-domain>/agent-readiness-status.json (application/json) carrying BOTH a date field (lastVerified/verifiedAt/updatedAt/generatedAt) and a verdict field (status/grade/verdict/ready/score), refreshed on a schedule (e.g. a daily CI job). Example: { "status": "ready", "grade": "A", "lastVerified": "2026-06-11T00:00:00Z", "rubric": "https://www.isittrustready.ai/rubric" } Verify: `curl -s https://<your-domain>/agent-readiness-status.json` shows a recent date + a status/grade. Spec: https://www.mnemom.ai/for-agents/
Reference standardPublishes advisories / a STIX IoC feed
standardemergingaccountability.advisoriesInspects /.well-known/advisories.json
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish https://<your-domain>/.well-known/advisories.json (application/json) — a JSON array of advisories, an `{advisories:[…]}` object, or a STIX 2.1 bundle. Example (STIX 2.1): { "type": "bundle", "objects": [{ "type": "indicator", "name": "<advisory>", "pattern": "[…]", "valid_from": "2026-06-11T00:00:00Z" }] } An empty array is valid (no current advisories). Verify: `curl -s …/advisories.json` parses as a feed/STIX bundle. Spec: https://oasis-open.github.io/cti-documentation/stix/intro
Reference standardVerification discriminates real from tampered
standardestablishedMCP servermcp.accountability.tamper-evidenceSeeded from Gauntlet C19/C21/C25/C29
Scoring scored — counted equally; a partial/fail docks
To satisfy: If your server offers any verify/attestation tool, it MUST discriminate: a valid artifact returns `verified:true`, a tampered one returns `verified:false` as a SUCCESSFUL answer (`isError:false`, not an error). A verifier that 'confirms' a forgery is worthless. Verify: call the verify tool with a real artifact (→ verified:true), then with a one-byte-altered copy — `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"<verify_tool>","arguments":{"artifact":"<tampered>"}}}' | jq '{verified:.result.structuredContent.verified, isError:.result.isError}'` returns verified:false, isError:false. Spec: https://www.w3.org/TR/vc-data-integrity/
Reference standardWell-formed errors, no internal leakage
standardestablishedMCP servermcp.accountability.error-hygieneSeeded from MCP / JSON-RPC spec; Gauntlet C13
Scoring scored — counted equally; a partial/fail docks
To satisfy: Return well-formed JSON-RPC 2.0 errors (unknown method/tool → a proper `error` object with code+message; tool failures → an `isError:true` result envelope) and never leak internal conformance signals (no `undocumented_status_code` / `spec_deviation` fields) to clients. Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"no_such_method"}' | jq '.error'` returns a JSON-RPC error object (code+message), and no response body contains spec-deviation fields. Spec: https://www.jsonrpc.org/specification
Reference standardDiscloses caching / freshness honestly
conventionemergingMCP servermcp.accountability.cache-transparencySeeded from Gauntlet C30
Scoring scored — counted equally; a partial/fail docks
To satisfy: If a tool can serve cached results, disclose it with a `cached` field and honour a freshness override (`fresh:true` ⇒ a live result, `cached:false`). Agents must be able to tell a cached answer from a live one. Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"<tool>","arguments":{"fresh":true}}}' | jq '.result.structuredContent.cached'` returns false (and a normal call may return true). Spec: https://modelcontextprotocol.io/specification
Reference standardAccountable maintainer contact
conventionemergingMCP servermcp.accountability.maintainer-contactSeeded from Braille-discovered (Glama listing failure); Cloudflare public-MCP-repo practice
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish a real, reachable maintainer contact (e.g. a role email like `mcp@<your-domain>`) in your canonical home / server metadata, so an agent or user can report problems and reach an accountable owner. Add to your `server.json` / server card: { "maintainers": [{ "name": "<team>", "email": "mcp@<your-domain>" }] } Verify: `curl -s https://<your-domain>/.well-known/mcp/server-card.json | jq '.maintainers // .contact'` (or your server.json home) names a reachable contact. Spec: https://github.com/modelcontextprotocol/registry
Reference standard
Delegation & auth
weight 15% · 7 checksCan an agent discover how to authenticate and act on a user's behalf, correctly?
OAuth protected-resource metadata
standardestablisheddelegation.protected-resourceInspects /.well-known/oauth-protected-resource
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish https://<your-domain>/.well-known/oauth-protected-resource (application/json) naming your authorization server(s) so agents discover how to authenticate. Example: { "resource": "https://<your-domain>", "authorization_servers": ["https://auth.<your-domain>"] } Verify: `curl -s https://<your-domain>/.well-known/oauth-protected-resource` lists a non-empty `authorization_servers`. (If your service has NO delegated/OAuth auth, declare that in /auth.md instead — this check then becomes N/A, not a fail.) Spec: https://www.rfc-editor.org/rfc/rfc9728
Reference standardOAuth authorization-server metadata
standardestablisheddelegation.auth-serverInspects /.well-known/oauth-authorization-server
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish https://<your-auth-server>/.well-known/oauth-authorization-server (application/json) with issuer, endpoints, and jwks_uri so token issuance is discoverable. Example: { "issuer": "https://auth.<your-domain>", "authorization_endpoint": "https://auth.<your-domain>/authorize", "token_endpoint": "https://auth.<your-domain>/token", "jwks_uri": "https://auth.<your-domain>/.well-known/jwks.json" } Verify: `curl -s …/.well-known/oauth-authorization-server` has issuer + an endpoint + jwks_uri. (N/A if you declare no delegated/OAuth auth in /auth.md.) Spec: https://www.rfc-editor.org/rfc/rfc8414
Reference standardDocuments agent authentication
conventionemergingdelegation.auth-docInspects /auth.md
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish https://<your-domain>/auth.md (Markdown, ≥200 chars) documenting your real auth schemes (OAuth/token/bearer) and the agent registration flow — OR, if you have none, state that explicitly (the phrase "no delegated" or "no OAuth" makes the OAuth-discovery checks N/A instead of failures). Template: # Agent authentication This service uses <OAuth 2.1 / API keys / NO delegated auth>. - Register: <how an agent obtains credentials> - Token endpoint: <url or 'n/a'> - Scopes: <list or 'n/a'> Verify: `curl -s https://<your-domain>/auth.md | head` shows a substantive auth guide. Spec: https://github.com/workos/auth.md
Reference standardServes coherent RFC 9728 protected-resource metadata
standardestablishedMCP servermcp.auth.prmSeeded from RFC 9728; Gauntlet C5
Scoring scored — counted equally; a partial/fail docks
To satisfy: Serve protected-resource metadata at https://<your-domain>/.well-known/oauth-protected-resource/mcp (application/json) whose `resource` EQUALS your MCP endpoint and whose `authorization_servers` names the real issuer, and serve the AS metadata at that issuer. Template: { "resource": "https://<your-domain>/mcp", "authorization_servers": ["https://auth.<your-domain>"] } Verify: `curl -s https://<your-domain>/.well-known/oauth-protected-resource/mcp | jq '{resource, authorization_servers}'` shows resource=https://<your-domain>/mcp and a non-empty authorization_servers. Spec: https://www.rfc-editor.org/rfc/rfc9728
Reference standardAuthed / write tools fail closed
standardestablishedMCP servermcp.auth.fail-closedSeeded from RFC 9728 / OAuth; Gauntlet C6
Scoring scored — counted equally; a partial/fail docks
To satisfy: An anonymous call to any tool that mutates state or reads private data MUST return transport HTTP 401 with a `WWW-Authenticate` header pointing at your protected-resource metadata — never a 200 with an error body, never silent success. Header to emit: WWW-Authenticate: Bearer resource_metadata="https://<your-domain>/.well-known/oauth-protected-resource/mcp" Verify: `curl -s -o /dev/null -D - -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"<authed_tool>","arguments":{}}}'` returns `401` and a `WWW-Authenticate: Bearer resource_metadata=...` line. Spec: https://www.rfc-editor.org/rfc/rfc9728
Reference standardPublic reads leak no private/owner data
conventionestablishedMCP servermcp.auth.least-privilege-readsSeeded from Gauntlet C8/C9
Scoring scored — counted equally; a partial/fail docks
To satisfy: Every tool reachable WITHOUT credentials must return only public, no-PII fields — an anonymous caller must never receive owner/account metadata (key prefixes, internal ids, emails). Scope private detail behind auth; return the minimal public projection to anon. Verify: call each anon-reachable read tool with NO credentials — `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"<read_tool>","arguments":{}}}'` — and confirm the response carries no owner/private fields. Spec: https://modelcontextprotocol.io/specification
Reference standardDynamic Client Registration handles loopback safely
standardemergingMCP servermcp.auth.dcr-loopbackSeeded from RFC 7591/8252; Gauntlet C10
Scoring scored — counted equally; a partial/fail docks
To satisfy: If you support OAuth Dynamic Client Registration, accept loopback redirect URIs (`http://localhost`, `http://127.0.0.1`) as public clients (HTTP 201, no client_secret) and REJECT non-loopback/insecure redirects (HTTP 400 invalid_redirect_uri). Verify: `curl -s -o /dev/null -w '%{http_code}\n' -X POST https://auth.<your-domain>/register -H 'Content-Type: application/json' -d '{"redirect_uris":["http://127.0.0.1/cb"]}'` returns 201, and the same request with `"https://evil.example/cb"` returns 400. (N/A if you do not support DCR.) Spec: https://www.rfc-editor.org/rfc/rfc8252
Reference standard
Access
Trust asks can an agent believe who this is? — Access asks can an agent find, read, and connect to it at all? The two are scored separately and never blended. Access spans five website discoverability categories plus three MCP-server categories (protocol hygiene, discoverability, breadth) that score only when a server is present.
How Access scoring works
- Established checks dominate. Widely-adopted checks (robots.txt, sitemaps, structured data, OpenAPI) set each category's score.
- Emerging checks are upside-only. Adopting a low-penetration draft (markdown negotiation, llms.txt, Content Signals, MCP cards, registry breadth) can raise a category — but not adopting one never lowers it.
- Policy-neutral. The AI-access-policy checks reward declaring an explicit, parseable policy — they never reward openness nor penalize a site for choosing to disallow AI. We score the declaration, not the stance.
- Not applicable. A property with no APIs and no MCP server isn't penalized for lacking OAuth metadata or MCP protocol hygiene — those categories go N/A and the remaining weights renormalize.
- Access grades use plain thresholds (A+ ≥ 90, A ≥ 85, B ≥ 70, C ≥ 55, D ≥ 40, else F) — no signing or identity caps.
| Category | Weight |
|---|---|
| Crawl discoverability | 25% |
| Agent content access | 20% |
| AI access policy | 15% |
| Capability discovery | 25% |
| Authorization discovery | 15% |
| Protocol hygiene | 35% |
| Discoverability | 40% |
| Breadth of publishing | 25% |
Crawl discoverability
weight 25% · 3 checksCan a crawler find the site's content and map (robots, sitemap, link relations)?
Serves a parseable robots.txt
standardestablishedaccess.robots_presentInspects /robots.txt
Scoring sets the category floor — a partial/fail docks
To satisfy: Serve a valid RFC 9309 file at https://<your-domain>/robots.txt (Content-Type: text/plain). Minimal: User-agent: * Allow: / Sitemap: https://<your-domain>/sitemap.xml Verify: `curl -s https://<your-domain>/robots.txt` returns text/plain robots rules (not an HTML page). Spec: https://www.rfc-editor.org/rfc/rfc9309.html
Reference standardPublishes a sitemap
standardestablishedaccess.sitemapInspects /sitemap.xml
Scoring sets the category floor — a partial/fail docks
To satisfy: Publish https://<your-domain>/sitemap.xml (Content-Type: application/xml) and reference it from robots.txt (`Sitemap: https://<your-domain>/sitemap.xml`). Minimal: <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url><loc>https://<your-domain>/</loc></url> </urlset> Verify: `curl -s https://<your-domain>/sitemap.xml | head` is XML with <urlset>. Spec: https://www.sitemaps.org/protocol.html
Reference standardEmits HTTP Link relations
standardestablishedaccess.link_headersInspects /
Scoring sets the category floor — a partial/fail docks
To satisfy: Emit an RFC 8288 `Link` response header on your homepage (https://<your-domain>/) pointing to typed relations. Add at your edge/server, e.g.: Link: </.well-known/api-catalog>; rel="api-catalog", </openapi.json>; rel="service-desc" Verify: `curl -sI https://<your-domain>/ | grep -i ^link` shows a Link header with rel values. Spec: https://www.rfc-editor.org/rfc/rfc8288.html
Reference standard
Agent content access
weight 20% · 3 checksCan an agent read the content efficiently and structured (markdown, llms.txt, JSON-LD)?
Serves markdown via content negotiation
conventionemergingaccess.markdown_negotiationInspects /
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: Make your homepage content-negotiate Markdown: when a request sends `Accept: text/markdown`, return the page AS Markdown with `Content-Type: text/markdown` (agents read it with ~80% fewer tokens than HTML). Implement at your edge/server (check the Accept header → serve a .md rendering). Verify: `curl -s -H 'Accept: text/markdown' https://<your-domain>/ -D - | grep -i content-type` shows text/markdown and the body is Markdown, not HTML. Spec: https://blog.cloudflare.com/agent-readiness/
Reference standardPublishes an llms.txt index
conventionemergingaccess.llms_txtInspects /llms.txt
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: Publish https://<your-domain>/llms.txt (Content-Type: text/plain or text/markdown) — an H1 site name, an optional summary blockquote, then curated link sections. Template: # <Your site> > <One-line summary of what you offer agents> ## Docs - [Getting started](https://<your-domain>/docs): how to begin ## API - [OpenAPI](https://<your-domain>/openapi.json): machine-readable API Verify: `curl -s https://<your-domain>/llms.txt | head` starts with an H1 (not HTML). Spec: https://llmstxt.org/
Reference standardEmbeds structured data (JSON-LD / OpenGraph)
standardestablishedaccess.structured_dataInspects /
Scoring sets the category floor — a partial/fail docks
To satisfy: Embed schema.org JSON-LD in your homepage HTML <head> (a real @type) plus OpenGraph tags, so agents parse entities, not prose: <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Organization", "name": "<Your org>", "url": "https://<your-domain>" } </script> <meta property="og:title" content="<Your title>"> <meta property="og:type" content="website"> Verify: `curl -s https://<your-domain>/ | grep -i 'application/ld+json'` finds the block. Spec: https://schema.org/
Reference standard
AI access policy
weight 15% · 2 checksHas the operator published an explicit, parseable AI-access policy (any stance)?
Declares explicit AI-bot rules
standardestablishedaccess.ai_bot_rulesInspects /robots.txt
Scoring sets the category floor — a partial/fail docks
To satisfy: In https://<your-domain>/robots.txt add explicit groups for the major AI user-agents. YOUR STANCE IS YOURS — the rubric only checks that you STATE it; Allow and Disallow score identically (we never reward openness nor penalize blocking). Example (substitute your own Allow/Disallow per agent): User-agent: GPTBot Disallow: / User-agent: ClaudeBot Allow: / User-agent: Google-Extended Disallow: / User-agent: PerplexityBot Allow: / User-agent: CCBot Disallow: / Verify: `curl -s https://<your-domain>/robots.txt` lists explicit AI user-agent groups. Spec: https://www.rfc-editor.org/rfc/rfc9309.html
Reference standardPublishes Content Signals
conventionemergingaccess.content_signalsInspects /robots.txt
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: Add a Content-Signal directive to https://<your-domain>/robots.txt with explicit search / ai-input / ai-train values (yes/no) so your AI-usage preferences are machine-readable. Example (set each value to your own policy): User-agent: * Content-Signal: search=yes, ai-input=yes, ai-train=no Allow: / Verify: `curl -s https://<your-domain>/robots.txt | grep -i content-signal` shows the directive. Spec: https://contentsignals.org/
Reference standard
Capability discovery
weight 25% · 5 checksCan an agent programmatically discover the site's APIs and tools (catalog, OpenAPI, MCP)?
Publishes an API catalog
standardestablishedaccess.api_catalogInspects /.well-known/api-catalog
Scoring sets the category floor — a partial/fail docks
To satisfy: Publish https://<your-domain>/.well-known/api-catalog (Content-Type: application/linkset+json) linking your API endpoints + their OpenAPI docs. Example: { "linkset": [{ "anchor": "https://<your-domain>", "service-desc": [{ "href": "https://api.<your-domain>/openapi.json", "type": "application/json" }] }] } Verify: `curl -s https://<your-domain>/.well-known/api-catalog` returns a linkset citing your OpenAPI. Spec: https://www.rfc-editor.org/rfc/rfc9727.html
Reference standardPublishes an OpenAPI document
standardestablishedaccess.openapiInspects /openapi.json
Scoring sets the category floor — a partial/fail docks
To satisfy: Publish your OpenAPI document and make it discoverable. Easiest: serve it at https://<your-domain>/openapi.json (Content-Type: application/json, top-level "openapi": "3.1.0"). If it lives on an api. subdomain, advertise it so the RFC 9727 chain finds it — add to your homepage <head>: <link rel="service-desc" type="application/json" href="https://api.<your-domain>/openapi.json"> or list it in /.well-known/api-catalog. Verify: `curl -s https://<your-domain>/openapi.json | head -c 200` shows an openapi/swagger version field. Spec: https://spec.openapis.org/oas/latest.html
Reference standardPublishes an MCP server card
conventionemergingaccess.mcp_server_cardInspects /.well-known/mcp/server-card.json
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: If you run an MCP server, advertise it at https://<your-domain>/.well-known/mcp/server-card.json (application/json) with transport, url, and tools so agents can connect: { "name": "<Your MCP server>", "transport": "streamable-http", "url": "https://<your-domain>/mcp", "tools": [{ "name": "<tool>", "description": "<what it does>" }] } Verify: `curl -s https://<your-domain>/.well-known/mcp/server-card.json` returns the card. (Upside-only — skip if you don't run MCP.) Spec: https://modelcontextprotocol.io/
Reference standardForegrounds the primary agent channel (agents.txt → MCP)
conventionemergingaccess.agent_entrypointInspects /agents.txt
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: Publish a real text/plain https://<your-domain>/agents.txt (an agent-facing companion to robots.txt) that points arriving agents at your primary programmatic channel — reference your MCP endpoint URL (or the /.well-known/mcp/ server card), or surface it from /llms.txt, so an agent hitting the convention path finds the channel without guessing. Example (agents.txt): # agents.txt — <your-domain> ## Use this service MCP (preferred): https://<your-domain>/mcp (streamable-http) first call: <your_first_tool>({ ... }) server card: https://<your-domain>/.well-known/mcp/server-card.json Verify: `curl -s https://<your-domain>/agents.txt` (or /llms.txt) references your /mcp endpoint or MCP server card. (Upside-only — never lowers your score.) Spec: https://llmstxt.org/ + https://modelcontextprotocol.io/
Reference standardDNS-AID agent discovery (DNSSEC-validated)
conventionemergingaccess.dns_aidInspects _index._agents
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: Publish a DNS-AID ServiceMode SVCB record (RFC 9460) at _index._agents.<your-domain> (and/or _a2a._agents, _mcp._agents) pointing agents at your endpoints, then sign the zone with DNSSEC (resolver AD=true) so it's tamper-evident. Example zone record: _index._agents.<your-domain>. 3600 IN SVCB 1 <your-domain>. ( alpn="h2" port=443 ) Enable DNSSEC at your DNS provider. Verify: `dig +dnssec SVCB _index._agents.<your-domain>` returns the SVCB record with the AD flag set. Spec: https://datatracker.ietf.org/doc/draft-mozleywilliams-dnsop-dnsaid/
Reference standard
Authorization discovery
weight 15% · 2 checksIf the site exposes APIs, can an agent discover how to authenticate (OAuth metadata)?
OAuth protected-resource metadata
standardestablishedaccess.oauth_protected_resourceInspects /.well-known/oauth-protected-resource
Scoring sets the category floor — a partial/fail docks
To satisfy: Publish https://<your-domain>/.well-known/oauth-protected-resource (application/json) naming your authorization server(s): { "resource": "https://<your-domain>", "authorization_servers": ["https://auth.<your-domain>"] } Verify: `curl -s https://<your-domain>/.well-known/oauth-protected-resource` lists a non-empty authorization_servers. (If your APIs need NO delegated/OAuth auth, declare that in /auth.md — this whole category then goes N/A, not fail.) Spec: https://www.rfc-editor.org/rfc/rfc9728
Reference standardOAuth authorization-server metadata
standardestablishedaccess.oauth_as_metadataInspects /.well-known/oauth-authorization-server
Scoring sets the category floor — a partial/fail docks
To satisfy: Publish https://<your-auth-server>/.well-known/oauth-authorization-server (application/json) with issuer, endpoints, and jwks_uri: { "issuer": "https://auth.<your-domain>", "authorization_endpoint": "https://auth.<your-domain>/authorize", "token_endpoint": "https://auth.<your-domain>/token", "jwks_uri": "https://auth.<your-domain>/.well-known/jwks.json" } Verify: `curl -s …/.well-known/oauth-authorization-server` has issuer + endpoints + jwks_uri. (N/A if /auth.md declares no delegated/OAuth auth.) Spec: https://www.rfc-editor.org/rfc/rfc8414
Reference standard
Protocol hygiene
weight 35% · 3 checksDoes the server speak MCP correctly — initialize, advertised capabilities, well-formed lists?
initialize returns protocolVersion + capabilities
standardestablishedMCP servermcp.proto.initializeSeeded from MCP spec; Gauntlet C20
Scoring sets the category floor — a partial/fail docks
To satisfy: Your `initialize` response must return a `protocolVersion` and a `capabilities` object declaring what you support (`tools`, plus `resources`/`prompts` if offered). Result shape: { "protocolVersion": "2025-06-18", "capabilities": { "tools": {}, "resources": {} }, "serverInfo": { "name": "<server>", "version": "1.0.0" } } Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' | jq '.result | {protocolVersion, capabilities}'` shows both present. Spec: https://modelcontextprotocol.io/specification
Reference standardAdvertised capabilities are actually served
standardestablishedMCP servermcp.proto.capabilities-honestSeeded from MCP spec
Scoring sets the category floor — a partial/fail docks
To satisfy: Only advertise capabilities you actually serve: if `initialize` declares `resources`, then `resources/list` must work; if it declares `prompts`, `prompts/list` must work. Don't advertise an empty/erroring capability. Verify: for each capability in `initialize`, confirm its list works — e.g. `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"resources/list"}' | jq '.result.resources'` returns an array (not an error). Spec: https://modelcontextprotocol.io/specification
Reference standardtools/resources/prompts lists are well-formed
standardestablishedMCP servermcp.proto.lists-well-formedSeeded from MCP spec
Scoring sets the category floor — a partial/fail docks
To satisfy: Return well-formed `tools/list` (and `resources/list` / `prompts/list` if advertised): a JSON-RPC result with the documented array shape, each tool carrying at least a `name` and an `inputSchema`. Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[] | {name, hasSchema: (.inputSchema|type=="object")}'` lists named tools each with an inputSchema. Spec: https://modelcontextprotocol.io/specification
Reference standard
Discoverability
weight 40% · 4 checksCan an arriving agent self-onboard with zero context, and is there one canonical public home?
Zero-auth orientation for arriving agents
conventionemergingMCP servermcp.disco.orientationSeeded from Gauntlet C16/C22
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: Give a zero-context agent a way to self-onboard with NO credentials: a `get_started`-style tool (or a rich `instructions` string) that returns who you are, what you offer, and a surface map. Declare it in `tools/list` and make it callable anonymously. Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_started","arguments":{}}}' | jq '.result'` returns a usable surface map / value-prop with no auth. Spec: https://modelcontextprotocol.io/specification
Reference standardagents.txt / llms.txt surface the server door
conventionemergingMCP servermcp.disco.agent-native-docsSeeded from Gauntlet C22/C23
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: Publish https://<your-domain>/agents.txt and/or /llms.txt (text/plain or text/markdown) that name your MCP endpoint URL and orientation entrypoint in the first screenful, so an arriving agent finds the door without guessing. Template (agents.txt): # agents.txt — <your-domain> ## Use this service MCP (preferred): https://<your-domain>/mcp (streamable-http) first call: get_started({}) server card: https://<your-domain>/.well-known/mcp/server-card.json Verify: `curl -s https://<your-domain>/agents.txt` (or /llms.txt) references your /mcp endpoint near the top. Spec: https://llmstxt.org/
Reference standardExample calls execute verbatim, anonymously
conventionemergingMCP servermcp.disco.executable-examplesSeeded from Gauntlet C24
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: Provide runnable examples (e.g. a `try_now` array of {tool, args}, or examples in your `instructions`) that an agent can execute EXACTLY as written, anonymously, with NO placeholders (`<...>`, `YOUR_`, `example.com`) and no errors. Verify: take each advertised example verbatim and run it — `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"<example_tool>","arguments":<example_args>}}' | jq '.result.isError'` returns false. Spec: https://modelcontextprotocol.io/specification
Reference standardOne canonical public home, resolvable from the registry
conventionestablishedMCP servermcp.disco.canonical-homeSeeded from Braille-discovered (Glama); Cloudflare public-MCP-repo practice
Scoring sets the category floor — a partial/fail docks
To satisfy: Publish ONE canonical, public, documented home — a public repo (e.g. github.com/<org>/mcp) and/or docs page — carrying your `server.json`/manifest, connect instructions (URL + transport), and the auth model, and point your registry listings at it so they resolve to one source of truth. Open-sourcing the IMPLEMENTATION is NOT required — only this public metadata home. Verify: your canonical MCP-registry id resolves to a public home containing server.json + connect + auth docs — e.g. `curl -s https://<your-domain>/.well-known/mcp/server-card.json | jq '{url, transport}'` resolves and links back to that home. Spec: https://github.com/modelcontextprotocol/registry
Reference standard
Breadth of publishing
weight 25% · 1 checksMeaningful presence in the canonical registry + reachable in-product. Upside-only; never a trust signal.
Meaningful presence in the canonical MCP registry
conventionemergingMCP servermcp.breadth.canonical-registrySeeded from Canonical MCP registry (registry.modelcontextprotocol.io)
Scoring upside-only — a pass raises the score, a partial/fail never docks
To satisfy: List your server in the canonical MCP registry (registry.modelcontextprotocol.io) with a resolvable id whose remote URL points at THIS endpoint. This measures MEANINGFUL presence, not raw count — being in many registries adds nothing here, and it never affects your Trust score. Publish a `server.json` and submit it per the registry's publish flow. Verify: `curl -s 'https://registry.modelcontextprotocol.io/v0/servers?search=<your-server>' | jq '.servers[].remotes[].url'` includes https://<your-domain>/mcp. Spec: https://registry.modelcontextprotocol.io/
Reference standard
MCP
If the property runs an MCP server, is it a quality capability to build against? The MCP axis measures capability quality — described and typed tools, parameters, and output schemas, accurate behaviour annotations, and complete, consistent server metadata. The whole axis is N/A (not a failure) for a property with no MCP server.
How MCP scoring works
- Each check scores pass = 1.0, partial = 0.5, or fail = 0; an inapplicable check is excluded, never counted as a failure.
- A category's score is the mean of its applicable checks; the axis score is the weight-renormalized mean of its categories.
- The axis grades on plain thresholds (A+ ≥ 90, A ≥ 85, B ≥ 70, C ≥ 55, D ≥ 40, else F) — there are no website-specific identity or signing caps.
- Vendor naming preferences are flagged, never penalized; nothing on this axis rewards being listed on Mnemom.
| Category | Weight |
|---|---|
| Tool & schema quality | 40% |
| Annotations | 30% |
| Server metadata | 30% |
Tool & schema quality
weight 40% · 5 checksAre tools, parameters, and output schemas described and typed (no opaque bodies)?
Every tool has a meaningful description
conventionemergingMCP servermcp.cap.tool-descriptionsSeeded from Smithery: Capability Quality
Scoring scored — counted equally; a partial/fail docks
To satisfy: Give every tool a clear, non-empty `description` that says what it does AND when to use it (not just a restated name). Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '[.result.tools[] | select((.description // "") | length < 15)]'` returns an empty array (no thin/missing descriptions). Spec: https://modelcontextprotocol.io/specification
Reference standardEvery input parameter is described
conventionemergingMCP servermcp.cap.param-descriptionsSeeded from Smithery: Capability Quality
Scoring scored — counted equally; a partial/fail docks
To satisfy: Describe every input parameter in each tool's `inputSchema` (a `description` per property) so an agent knows what to pass. Shape: "inputSchema": { "type": "object", "properties": { "query": { "type": "string", "description": "<what to pass>" } }, "required": ["query"] } Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '[.result.tools[].inputSchema.properties // {} | to_entries[] | select((.value.description // "")=="")]'` returns an empty array. Spec: https://json-schema.org/
Reference standardEvery tool declares a real outputSchema
standardestablishedMCP servermcp.cap.output-schemasSeeded from Smithery; Gauntlet C11/C12
Scoring scored — counted equally; a partial/fail docks
To satisfy: Declare a real, non-empty `outputSchema` on every tool that returns structured content, matching the live `structuredContent` shape. Shape: "outputSchema": { "type": "object", "properties": { "results": { "type": "array" } }, "required": ["results"] } Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '[.result.tools[] | select(.outputSchema|not)]'` is empty, and live output validates against it. Spec: https://modelcontextprotocol.io/specification
Reference standardTyped request bodies (no opaque blobs)
standardestablishedMCP servermcp.cap.typed-bodiesSeeded from OpenAI ChatGPT-Apps preflight; Gauntlet C15
Scoring scored — counted equally; a partial/fail docks
To satisfy: Type your request arguments: declare the real properties in each `inputSchema` instead of an opaque `additionalProperties: true` / free-form object blob, and ensure a payload built strictly from the published schema is accepted. Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '[.result.tools[] | select((.inputSchema.properties|length)==0 or .inputSchema.additionalProperties==true)]'` is empty (no untyped blobs). Spec: https://json-schema.org/
Reference standardTools carry human-readable titles
conventionemergingMCP servermcp.cap.titlesSeeded from OpenAI ChatGPT-Apps preflight
Scoring scored — counted equally; a partial/fail docks
To satisfy: Give each tool a human-readable `title` (or `annotations.title`) so client UIs can present it cleanly to users. Shape: `{ "name": "search_docs", "title": "Search documentation", ... }`. Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '[.result.tools[] | select((.title // .annotations.title // "")=="")]'` is empty. Spec: https://modelcontextprotocol.io/specification
Reference standard
Annotations
weight 30% · 2 checksDo tools carry accurate behaviour annotations (read-only / destructive / idempotent / open-world)?
Tools carry behaviour annotations
standardestablishedMCP servermcp.cap.annotations-presentSeeded from MCP spec; Smithery; Gauntlet C1/C2
Scoring scored — counted equally; a partial/fail docks
To satisfy: Annotate every tool with the four behaviour hints — `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint` — so clients can reason about side effects. Shape: "annotations": { "readOnlyHint": true, "destructiveHint": false, "idempotentHint": true, "openWorldHint": false } Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '[.result.tools[] | select(.annotations.readOnlyHint==null or .annotations.destructiveHint==null or .annotations.idempotentHint==null or .annotations.openWorldHint==null)]'` is empty. Spec: https://modelcontextprotocol.io/specification
Reference standardAnnotations are accurate
conventionemergingMCP servermcp.cap.annotations-justifiedSeeded from OpenAI per-annotation justification
Scoring scored — counted equally; a partial/fail docks
To satisfy: Make annotations TRUE, not just present: a tool marked `readOnlyHint:true` must not mutate state; a `destructiveHint:false` tool must not delete. Align each hint with the tool's REAL behaviour (and its method — a GET-style read ⇒ readOnlyHint:true). Verify: spot-check — call each `readOnlyHint:true` tool and confirm no state changed, e.g. `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"<read_tool>","arguments":{}}}'` leaves state unchanged, and confirm the tools that actually mutate are the ones marked destructive. Spec: https://modelcontextprotocol.io/specification
Reference standard
Server metadata
weight 30% · 3 checksDisplay name, description, homepage, icon, sensible config, and consistent naming.
Complete server metadata
conventionemergingMCP servermcp.cap.server-metadataSeeded from Smithery: Server Metadata
Scoring scored — counted equally; a partial/fail docks
To satisfy: Publish complete server metadata — a display `name`, a `description`, a `homepage`, and an `icon` — in your `server.json` / registry listing. Shape: { "name": "<Your MCP server>", "description": "<what it does>", "homepage": "https://<your-domain>", "icons": [{ "src": "https://<your-domain>/icon.png", "sizes": "512x512" }] } Verify: `curl -s https://<your-domain>/.well-known/mcp/server-card.json | jq '{name, description, homepage, icons}'` (or your server.json) shows all four present. Spec: https://github.com/modelcontextprotocol/registry
Reference standardConsistent tool naming (preferences flagged, not penalized)
conventionemergingMCP servermcp.cap.namingSeeded from Smithery naming PREFERENCE (flagged as preference, not a spec requirement)
Scoring scored — counted equally; a partial/fail docks
To satisfy: Use a consistent naming scheme across your tools (all snake_case, OR all dot.notation — pick one). NOTE: a vendor preference (e.g. Smithery's dot-notation) is a PREFERENCE, not an MCP spec requirement — we flag it, we never penalize you for choosing your own consistent scheme. Verify: `curl -s -X POST https://<your-domain>/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '[.result.tools[].name]'` shows one consistent convention. Spec: https://modelcontextprotocol.io/specification
Reference standardClear configuration UX
conventionemergingMCP servermcp.cap.config-uxSeeded from Smithery: Configuration UX
Scoring scored — counted equally; a partial/fail docks
To satisfy: Declare required configuration explicitly with descriptions and sensible defaults in your `server.json` (env/inputs), so a user can connect without guessing. Shape: { "config": { "API_KEY": { "description": "<where to get it>", "required": true }, "REGION": { "description": "deployment region", "default": "us" } } } Verify: `curl -s https://<your-domain>/.well-known/mcp/server-card.json | jq '.config'` (or your server.json) lists each required input with a description/default. Spec: https://github.com/modelcontextprotocol/registry
Reference standard
What we deliberately don’t measure
A check earns a place only when it’s evaluable from a static, unauthenticated fetch (or a DNS lookup, or a read-only MCP probe) of a neutral, third-party-specified surface, and rewards a real capability — not a vendor’s product, popularity, or licence. For transparency, here’s what we leave out on purpose:
- WebMCP (navigator.modelContext) and other JS-runtime capabilities — Detecting them requires executing the page in a headless browser. We scan with static fetches only — no code execution — so the scan stays cheap, safe, and reproducible. Absence here is not evidence of absence.
- Performance / TTFB / Core Web Vitals — Speed is real but orthogonal to whether an agent can find, read, and trust a service. Blending it in would muddy a trust+access signal with an unrelated one.
- Web Bot Auth (inbound bot verification) — It governs whether a site can verify bots calling IT — the opposite direction from whether the site is discoverable/usable BY an agent. It belongs to a future identity axis, not access.
- Commerce protocols (x402 / UCP / ACP) — Payment rails apply to a small slice of sites; scoring them would penalize the majority for not being storefronts. We may add an opt-in commerce axis later.
- Whether a site ALLOWS vs BLOCKS AI (the stance) — We score publishing an explicit, parseable AI policy — never the choice itself. Rewarding openness (or blocking) would impose our preference instead of measuring readiness. See the policy-neutrality note.
- Raw content quality, SEO, or backlinks — These are search-marketing signals, not agent-readiness. We measure machine-readability and discoverability of structure, not editorial merit.
- Whether the server IMPLEMENTATION is open-source — Leading teams keep the implementation private while publishing a public metadata home + contact (canonical-home / maintainer-contact score the metadata, never the source). Requiring open source would punish legitimate private servers and has nothing to do with trust-readiness.
- Being listed on Mnemom (or any single vendor's directory) — Neutrality is the whole asset. Breadth-of-publishing measures meaningful presence in the canonical MCP registry + in-product reachability; it is upside-only, never a trust signal, and explicitly excludes Mnemom's own directory. We never reward 'listed on us' nor punish a competitor.
- Raw registry count / number of directories listed — A malicious server can spam ten registries. We measure MEANINGFUL presence (canonical registry + reachable in-product), not count, so we don't incentivize registry-spam.
- Tool count, popularity, or download numbers — Popularity is not trust-readiness, and download counts are gameable and vendor-specific. We score verifiable properties of the server itself.
- Latency / performance — Speed is real but orthogonal to whether an agent can trust and safely use a server. Blending it would muddy the trust+access+DX signal.
- In-product reachability (Claude Connectors / ChatGPT Apps listings) — There is no neutral, automatable way to verify a first-party in-product listing without each vendor's private surface — and we won't let one vendor's directory be decisive. Deferred from v0.1; may return post-v1 as an owner-attested signal with right-of-response.
- A vendor naming PREFERENCE as a spec requirement — Naming conventions like dot-notation are vendor preferences, not MCP spec. We flag them as preferences and never penalize a server for a different, internally-consistent scheme.