Agent tools and badge
These are the public tools around the free verification path: two tools an agent calls before it pays, a badge you embed on any page, and the path that publishes the keys verdicts are signed with. None of them carries a charge, none needs an account, and every one keeps working when no verdict is available: the answer is unknown and nothing blocks.
MCP server
The Model Context Protocol server is built in the Apache-2.0 tools repository, next to the copy-in snippets. It gives an agent harness the free verification path as two tools, so the agent can check an x402 endpoint before paying it.
- Transport. JSON-RPC 2.0 over stdio, one message per line (the standard
MCP stdio transport), protocol revision 2025-06-18. Your MCP client starts the server as a local process.
- Methods.
initialize,tools/list,tools/callandping. - Dependencies. None at runtime. The server reuses the verdict snippet for
fetching, freshness and the local signature check.
- Configuration. Two optional environment variables.
| Variable | Meaning | When unset |
|---|---|---|
VERIFICATION_BASE_URL | Base URL of the free verification path | Every lookup answers unknown with outcome no_service; the server still answers |
VERIFICATION_PUBLIC_KEY | Verifier public key, base64 (see verification keys) | The signature is reported as not_checked |
lookup_verdict
When to use this. Use this when you need the full picture on an x402 endpoint before you pay it or recommend it. It tells you whether a verified operator backed by a guarantee deposit stands behind the endpoint, the assurance grade, when the verdict was last re-checked, whether that re-check sits inside its promised cadence, and whether the signature checks locally. Pass chain, payTo and resourceUrl from the 402 response. An unknown result is normal: carry on under your own policy.
Input: the endpoint the payment would unlock. All three fields are required strings; no other field is accepted.
{
"type": "object",
"properties": {
"chain": { "type": "string", "description": "Chain identifier, e.g. \"solana\" or \"base\"." },
"payTo": { "type": "string", "description": "The endpoint's payTo address." },
"resourceUrl": { "type": "string", "description": "The resource URL the payment unlocks." }
},
"required": ["chain", "payTo", "resourceUrl"],
"additionalProperties": false
}
Output: a text line for the model to read and the same signal as structured content.
Verification: human-backed; outcome human_backed; freshness fresh; assurance G; last re-checked 2026-09-01T08:00:00Z; signature valid.
endpoint_status
When to use this. Use this for a quick routing decision before paying, when one word per endpoint is enough: human-backed, revoked or unknown. Call it once per candidate endpoint with chain, payTo and resourceUrl from its 402 response. Prefer human-backed, avoid revoked, and read unknown as the absence of a signal, never as a failure; what you do with unknown endpoints stays your decision. Call lookup_verdict when you need the grade, the re-check time or the signature result.
Input: the same schema as lookup_verdict (chain, payTo, resourceUrl, all required, nothing else).
Output: one compact text line, Verification signal: human-backed. (or revoked, or unknown), with the same structured content as lookup_verdict.
The structured result
Both tools return this object as structuredContent, with isError: false.
| Field | Values | Meaning |
|---|---|---|
state | human-backed, revoked, unknown | The routing signal. Only a fresh positive verdict whose signature is not invalid reads human-backed |
outcome | human_backed, revoked, stale, no_signal, no_service, no_response | The verdict's wire outcome, or why none arrived |
freshness | fresh, overdue, unknown | Last re-check measured against the cadence the verdict promises |
signature | valid, invalid, unverifiable, not_checked | The local Ed25519 check over the canonical payload |
assuranceLevel | for example G | Assurance grade of a positive claim, when present |
verifiedAt | RFC 3339 time | Last re-check, when present |
recheckEveryHours | integer | The cadence promise the verdict carries, when a verdict arrived |
A missing service URL, an unreachable service, a timeout or an absent verdict all resolve to state: "unknown", never to an error. The only error result is a call without the three required strings: it comes back with isError: true and a line naming the missing arguments.
Badge embed
The badge is a copy-in snippet that renders the verdict for one x402 endpoint on any page: a seller's site, an explorer listing, a dashboard. You give it a container element, the base URL of the free path, the endpoint (chain, payTo, resourceUrl), and optionally the verifier public key and a timeout (3000 ms by default). It fetches the verdict, checks it locally and renders one small inline element.
It reads one of three words.
| Badge | Snippet state | When |
|---|---|---|
| verified | human-backed | The verdict is human_backed, and its signature does not fail the local check. The label carries the assurance grade |
| not verified | revoked | The verdict is revoked: the attestation was withdrawn or lapsed past its renewal deadline |
| unknown | unknown | The verdict is stale or no_signal, no verdict arrived, or a positive claim failed the signature check |
The hover text states when the verdict was verified and its cadence promise ("re-checked every N h"), and flags a re-check that is overdue. With no verdict it says that nothing in the flow depends on it.
Graceful degradation. The badge always renders. When the service is unreachable, when there is no verdict or when the signature check fails, it reads unknown and nothing else on the page is affected. The embed resolves to the state it rendered and never rejects. When the runtime cannot check signatures, the verdict shows as delivered.
Verification keys
GET /v0/verification-keys publishes the Ed25519 public keys verdict signatures are checked against. It is free, needs no authentication and is cacheable.
{
"keys": [
{
"keyId": "3f9a1c07b2e4d856",
"algorithm": "Ed25519",
"publicKey": "<raw 32-byte public key, base64>",
"status": "current"
}
]
}
| Field | Required | Meaning |
|---|---|---|
keyId | yes | First 8 bytes of the key's SHA-256, hex. Matches the keyId a verdict carries |
algorithm | yes | Always Ed25519 |
publicKey | yes | Raw 32-byte Ed25519 public key, base64 |
status | no | current signs new verdicts; previous is a key a rotation retired, kept published for at least the re-check window so verdicts signed before the rotation keep verifying |
Select the key by keyId, never by its position in the list. A consumer without the key keeps working: a signature it cannot check reads unverifiable, never an error.
Published metrics
GET /v0/metrics publishes the adoption series the service computes from its own records: sellers attested (cumulative, active, and outside the operator's related-party circle), 90-day retention as a ratio, verifications served on the free path, independent registered verifiers, and revenue as context. Free, needs no authentication, cacheable; complete months only, every point dated YYYY-MM. The report and every series carry relatedParty: true: the operator runs a related-party pilot, and nothing measured on it counts as independent adoption. A series the records hold no source for answers available: false with the reason instead of a figure. Counts and ratios only — no identifier, no endpoint, nothing personal, and no central record of individual verifications exists behind it.
{
"schema": "published-metrics/v1",
"asOf": "2026-08-31",
"period": { "from": "2026-05", "to": "2026-08" },
"relatedParty": true,
"operator": { "operator": "CONECTIA group", "relatedParty": true, "disclosure": "…" },
"series": [
{
"id": "sellers_attested",
"discipline": "registry-based",
"relatedParty": true,
"available": true,
"points": [{ "period": "2026-08", "cumulative": 3, "active": 2, "independent": 0 }]
},
{
"id": "verifications_free_path",
"discipline": "aggregate",
"relatedParty": true,
"available": false,
"unavailableReason": "…",
"points": []
}
]
}
The metrics page is built from a committed snapshot of this read, once a month; the page never fetches, and while no complete month holds a record it says there is nothing to publish yet. When the read is unreachable, a consumer treats it as no dataset: nothing about a verdict or a badge depends on it.
Next: buyer verification describes the path these tools sit on, and the SDK reference lists the snippets behind them.