Skip to main content
Glama

vet402

ci

Independent Verification of the x402 Agent-Payment Economy

vet402 buys. It settles. It publishes the measurements.

Chains vet402 buys on

vet402 pays x402 sellers on ten chains with its own funds and publishes every attempt. Counts below were read from https://vet402.com/api/v1/observatory/state at 2026-10-23 09:10 UTC (whole ledger). Avalanche C-Chain started on 2026-10-26, after that read, so it has no row here; its count is in l1.byChain of the same URL. They grow daily, so read the live page for current numbers.

Chain

Network id

Asset paid

Paid attempts

Settled

Settled and answered 2xx

Base

eip155:8453

USDC

15,208

9,616

8,912

Solana

solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp

USDC

983

724

683

Tempo

eip155:4217

USDC.e

312

282

79

XRPL

xrpl:0

RLUSD

195

81

61

Celo

eip155:42220

USDC

168

140

135

Arc

eip155:5042

USDC

138

68

68

Stellar

stellar:pubnet

USDC

77

70

70

BNB Chain

eip155:56

USD1

59

41

38

Arbitrum One

eip155:42161

USDC

30

14

14

BNB Chain. vet402 pays sellers that accept USD1 (0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d) by EIP-3009 on eip155:56, and every settled BNB Chain row is bound to the nonce vet402 signed. To list them:

curl -sL "https://vet402.com/api/v1/observatory/export.csv?days=90" | grep "eip155:56"

Related MCP server: trust-mcp

Arbitrum Open House Singapore (vet402 /rwa)

Paste a Robinhood Chain wallet and see its Stock Token track record rebuilt from public chain data, including look-alike tokens it was sent and the corporate actions (share multiplier changes) it held through. Live at https://vet402.com/rwa . Start with docs/rwa/README.md (30-second path, verify-it-yourself, what is not trustless).

ETHGlobal Tokyo 2026 (ENS and Intercepta partner prizes)

For Tokyo, an agent pays an x402 seller only while vet402's draft ENSIP-29 attestation in the seller's ENSv2 name still matches the seller's offer. Check it with one line (Node 22.18 or later, no key):

git clone --depth 1 --branch tokyo-2026-submission https://github.com/kzmttkc/vet402 && cd vet402/examples/tokyo-2026-demo && npm ci && npm run verify -- seller-a.eth
  • Page from the event, where a judge could change one character of a seller's offer on-chain and watch the agent refuse: https://vet402.com/tokyo?name=seller-a.eth

  • Status on 2026-11-02: since 2026-10-01 the page and the verify command end at step 1 with ens_evidence_unavailable for any valid name, because ENS redeployed on Sepolia and the two contracts the SDK reads (UniversalResolver and UniversalHelper) now report different root registries. The demo's attestations, valid for seven days, also expired on 2026-10-03. No seven-step result is produced. The recorded demo and the transactions in for-reviewers are from before that date.

  • Demo, commands and the Intercepta screening notes: examples/tokyo-2026-demo/README.md

  • Every requirement, transaction and one-line check: docs/tokyo-2026/for-reviewers.md

  • What existed before the event: vet402 and @vet402/sdk, up to the tag pre-tokyo-2026. Everything in git log pre-tokyo-2026..tokyo-2026-submission was made during it. Who did what, human and AI: AI_USAGE.md and docs/tokyo-2026/human-log.md

Judges: 60 seconds (ETHOnline 2026, September)

# Node 24.x (package.json engines; Node 22 cancels the timeout tests)
git clone https://github.com/kzmttkc/vet402 && cd vet402
npm run judge-check   # sdk → mcp-server → demo → root → ab: builds, tests, mutation runs. No key, nothing live. About 25–30 s on a clean clone (24–27 s across three measurements on 2026-09-07, warm npm cache)
GRAPH_API_KEY=… node examples/ethonline-2026-demo/src/run.ts judge https://kronossignals.com/api/v1/price/btc
  • Line 2 prints one table, one exit code per step, and unsets every key before it starts — a green run is proof that none is needed.

  • Line 3 reads the seller's real 402 challenge, vet402's /decision and The Graph's x402 Base subgraph (_meta.block.number, live) side by side, then prints verdict: ALLOW|REFUSE, reason_codes[], signed: false. Dry run — the signing module is never loaded. About 4 s.

  • Keys: one free Graph key from https://thegraph.com/studio → API Keys. /decision answers key-less (10/min per IP); nothing else is required.

  • Everything else a judge can run, in dependency order, with the recorded output: SKILL.md.

  • Continuity Track boundary — what predates the hackathon window, what does not, and the git commands that separate them: docs/ethonline-2026/README_CONTINUITY_SECTION.md.

  • Language. Source comments are written in Japanese, my working language; commit subjects are Japanese, and the rule from 20:00 JST on 2026-09-08 is to write new ones in English (docs/ethonline-2026/GIT_RULES.md) — a rule going forward, not a property of the log behind it. It took effect mid-evening, not at the start of that day, and work already in flight kept landing in Japanese for a few commits after. Nothing is rewritten to tidy this up, so count it rather than take my word: TZ=Asia/Tokyo git log origin/main --since='2026-09-08 20:00:00 +0900' --pretty=%s. The English route through the work is SKILL.md, AI_USAGE.md and docs/ethonline-2026/CHANGED_FILES.md. Seven source files carry an English header above the Japanese one — the SDK's decision and payment path, the MCP bridge, and the two example front-ends: packages/sdk/src/pay-or-refuse.ts, packages/sdk/src/subgraph-evidence.ts, packages/sdk/src/x402-pay.ts, packages/mcp-server/src/pay-if-trusted.ts, examples/ethonline-2026-demo/src/run.ts, examples/ethonline-2026-demo/src/render.ts, examples/ethonline-2026-ab/src/mcp.mjs. Count them yourself: grep -rln "English header for judges" packages/ examples/ | grep -v dist.

vet402 buys from x402 endpoints, re-reads each payment on-chain, records what came back, checks it against the seller's output declaration when there is one, and publishes the results.

Formerly Vouch. The repository is https://github.com/kzmttkc/vet402 (renamed 2026-08-18). The npm scope is @vet402/*; the API key prefix (vouch_) and some internal identifiers retain the old name for backward compatibility — renaming them would break every key already issued.

Site: https://vet402.com · Live demo: https://vet402.com/playground · API reference: https://vet402.com/docs/api · Accuracy ledger: https://vet402.com/accuracy

日本語の概観は docs/ja/README.md。

This repository is the source of the vet402 service and of the three npm packages published from packages/.

ETHOnline 2026 (Continuity) — what I claim from this window

The boundary tag pre-ethonline-2026 is commit c42daca, cut 2026-09-04 00:05:36 UTC, and it is pushed, so anyone can verify the split themselves. The window opened at 2026-09-04 16:00 UTC (ETHGlobal's published schedule, hacking-begins), so the tag sits 15 h 54 min before the start, not five minutes after it as this file said until 2026-09-08. Three commits in the range I claim were made before the window opened — their SHAs, times and contents, and the command that lists them, are in docs/ethonline-2026/DISCLOSURE_2026-09-05.md, which also carries the message I sent ETHGlobal on 2026-09-05 disclosing the volume of work that predates the tag, and why its commit count reads 207 where this file says 214.

One caveat. main is also this product's production branch, so the range pre-ethonline-2026..main contains work that has nothing to do with this submission — grant applications for other programmes, SEO and marketing, a secret rotation, operational fixes. I am not claiming all of it. What I submit is the payment gate and what it needed:

git log pre-ethonline-2026..main -- packages/sdk packages/mcp-server \
    examples/ethonline-2026-demo examples/ethonline-2026-ab \
    SKILL.md AI_USAGE.md docs/ethonline-2026    # the submission
git log pre-ethonline-2026..main                # everything on main in the same days

Both commands are worth running. The first is my claim; the second is the full picture, including the parts I am not claiming.

Who wrote the code, by area and representative file — and what I did: AI_USAGE.md. An excerpt of the instructions I gave, by day (Japanese; the folder's README opens in English): docs/ethonline-2026/PROMPTS/. The payment gate itself: SKILL.md.

Start here — four doors, all reachable from this page:

Try it (clean clone; the first line needs no API key):

  • npm run judge-check — one command from the repo root: sdk → mcp-server → demo → root npm ci → A/B harness, every test, exit codes recorded per step (scripts/judge-check.sh).

  • examples/ethonline-2026-demo — refuse (two sources side by side, no signature), pay (dry run: fetches the real 402, signs nothing), judge <url> (bring your own x402 URL).

  • examples/ethonline-2026-ab — the Bazantic A/B harness: Recipe vs no Recipe, mock run without keys, live run with a model.

Install

npm i @vet402/sdk          # TypeScript API client
npm i @vet402/middleware   # x402 transaction gate (Express / Next.js / Hono)
npm i @vet402/mcp-server   # MCP tools for Cursor / Claude Desktop

Use the scoped names exactly as written above. The unscoped npm package vouch-sdk is an unrelated project published by a different vendor and has nothing to do with vet402. Only @vet402/* packages are vet402's.

On npm since 2026-09-12 (06:41 UTC, the day before the 2026-09-13 16:00 UTC deadline). @vet402/sdk is 0.6.0 (published 06:41:38 UTC) and exports payOrRefuse; @vet402/mcp-server is 0.3.0 (06:45:12 UTC) and answers tools/list with 7 tools, pay_if_trusted among them. The releases that stood through the window contained neither: the @vet402/sdk@0.5.0 tarball (2026-08-25) ships index and spend-guard only, and @vet402/mcp-server@0.2.0 (2026-08-24) answers tools/list with 5 tools. Publishing is not part of the claimed work; what is judged is the clone, not the tarball. To read or run this window's code from source, clone this repository and build from packages/ (SKILL.md → Install).

import { createVouchClient } from "@vet402/sdk";

Get a free API key at https://vet402.com/signup (1,000 lookups/month, no card required).

Verification levels

A result never moves up a level: an L0 probe cannot report settlement, and an L3 opinion is never folded into an L0–L2 fact.

Level

Question

How

Output

L0

Liveness — does the endpoint answer correctly?

Probe, no purchase

pass / fail / unverified

L1

Settle-through — does the payment settle, and does a non-empty 2xx come back?

Real purchase

n of m settled, latency

L2

Conformance — does the response match the seller's own declaration?

Purchase + machine diff

conform / mismatch / undeclared / not_checked

L3

Quality — is the content any good?

Not built

none

The 0–100 trust score this API returns today (banded ALLOW / WARN / BLOCK) predates these levels. It stays available to API and SDK callers during the transition, and is never reported as an L0–L2 result. Methodology: https://vet402.com/#methodology.

Since 2026-09-02 (product spec v1.0), the canonical integration is GET /api/v1/resources/{resource_id}/decision: the L0–L2 facts and the ALLOW / WARN / BLOCK recommendation arrive in the same document, with reason_codes, freshness, evidence and the rules_version that produced it. GET /api/v1/resolve?q=<url|domain|address|tx> turns whatever you hold into the §5 object ids. The score endpoints remain as a thin compatibility layer.

Docs

Start here, in this order:

Grant / hackathon reviewers: docs/applications/ has the one-page impact summary and per-ecosystem materials.

Customer-facing: Quickstart · OpenAPI · Deployment

Self-hosting and development

The rest of this file covers running the service yourself. Using vet402 as a customer needs none of it — see Install above.

Stack

  • Next.js 16 (App Router) + TypeScript

  • viem (Base mainnet)

  • Drizzle ORM + PostgreSQL (Neon)

  • ERC-8004 Identity & Reputation Registry

Quick start

cp .env.example .env.local
# Set DATABASE_URL, secrets, DEV_API_KEY, BASE_RPC_URL

npm install
./scripts/dev-setup.sh   # local Postgres + db:push (Docker or Homebrew)
npm run dev

Health check

curl http://localhost:3000/api/health

Score an agent (dev)

curl -H "Authorization: Bearer $DEV_API_KEY" \
  http://localhost:3000/api/v1/agents/1/score

Score by wallet (x402 integration path)

curl -H "Authorization: Bearer $DEV_API_KEY" \
  http://localhost:3000/api/v1/wallets/0x1234567890123456789012345678901234567890/score

Attest settlement

curl -X POST -H "Authorization: Bearer $DEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"wallet":"0x...","txHash":"0x..."}' \
  http://localhost:3000/api/v1/payments/x402

Resolve, then decide (product spec §7.3 / §9.1)

# what is this URL? → resource_id, endpoint_id, payee_id (key-less)
curl -L "http://localhost:3000/api/v1/resolve?q=https://api.example.com/v1/quote"

# should my agent pay it? facts + recommendation in one document
curl -L -H "Authorization: Bearer $DEV_API_KEY" \
  "http://localhost:3000/api/v1/resources/<resource_id>/decision?role=payer"

# should this seller serve this payer? (seller mode, post-settlement)
curl -L -H "Authorization: Bearer $DEV_API_KEY" \
  "http://localhost:3000/api/v1/resources/<resource_id>/decision?role=payee&payer=0x..."

# The rest of the §7.3 / §9.1 family — all key-less:
#   GET /api/v1/resources/<resource_id>                    the Resource, payees, links
#   GET /api/v1/endpoints/<endpoint_id>                    the Endpoint (sha256 id or observatory uuid)
#   GET /api/v1/endpoints/<endpoint_id>/payees             endpoint → payees[]
#   GET /api/v1/payees/<address>/endpoints                 payee → endpoints[]
#   GET /api/v1/observatory/endpoints/<id>/facts           L0–L2 seller facts, no score
#   GET /api/v1/census/summary?chain=&window=7d|30d        settlements raw and real, never merged
#   GET /api/v1/observatory/corrections                    the correction log as JSON

Database setup (M2)

# Set DATABASE_URL in .env.local, then push schema
npm run db:push

# Create a production API key
npm run api-key:create -- --plan free --name "my app"

Production: set APP_ENV=production explicitly (do not rely on NODE_ENV alone).

Production required env vars: DATABASE_URL, API_KEY_PEPPER, DASHBOARD_SESSION_SECRET, ADMIN_SECRET (min 32 chars, no placeholders).
Set PROXY_HEADER_SOURCE to name the proxy in front of the app (vercel | generic | none); per-IP rate limits depend on it, and the default none believes no forwarded header.
Never set in production: DEV_API_KEY, SKIP_CHAIN_READS.

Deep health check: GET /api/health?deep=1 with Authorization: Bearer $ADMIN_SECRET (development allows unauthenticated deep checks).

Dashboard uses httpOnly session cookies only (Bearer API keys are not accepted on dashboard routes).

Whitelist override: responses include manualOverride: true when customer WL/BL changed the outcome. Global operator blacklist is applied opaquely (blockReason: operator_policy, signals.manual.list: none). WL is not applied when sybil risk is high.

Customer WL/BL lists are shared across all API keys under the same owner account.

Score cache invalidation uses DB-backed epochs (cache_epochs table) so list changes propagate across serverless replicas.

After enabling API_KEY_PEPPER, recreate API keys (hashes change).

API keys are stored as SHA-256 hashes. Monthly quotas are enforced per owner (all API keys under the same account share one quota). Up to 10 active API keys per owner.

Plan

Monthly limit

free

1,000

pro

50,000

scale

500,000

Responses include X-RateLimit-Limit, X-RateLimit-Used, and X-RateLimit-Remaining headers.

Dashboard (M3)

npm run dev
# Open http://localhost:3000/dashboard
# Sign in with a database-backed API key (not DEV_API_KEY)

Dashboard features:

  • Overview — monthly usage and plan quota

  • Lookup — agent / wallet score search (counts against API quota)

  • WL/BL — customer whitelist/blacklist + CSV import (max 500 rows)

  • Logs — recent query history

  • API Keys — create keys (inherits current plan only) and revoke

Sign-in uses httpOnly session cookies (API key is not stored in the browser).

Signup: /signup creates a free account + API key (invite code required when BETA_INVITE_CODE is set).

Billing: Dashboard → Billing (Stripe Checkout for Pro/Scale).

Funder indexer (F-03)

Background job populates funder_wallets for sybil cluster detection (read-only from scoring path):

npm run indexer:funders
# or Vercel cron: GET /api/cron/index-funders (daily 04:00 UTC)

Owner agent indexer (sybil F-03)

Indexes ERC-8004 Registered / Transfer events into owner_agents for multi_agent_owner sybil checks:

npm run indexer:owners
# or Vercel cron: GET /api/cron/index-owners (daily 05:00 UTC)

Partial sync is supported — scores ship while the indexer catches up. Responses include dataCoverage.ownerIndexer (synced only at tip; otherwise partial + staleRisk). Sybil multi_agent_owner uses ERC-721 balanceOf (authoritative) cross-checked with max(index, balanceOf). Full catch-up can take weeks; do not gate product launch on it.

Monitor critical outages: GET /api/cron/monitor-health (503 = env/DB/RPC only). Indexer lag is reported in the payload without forcing 503.

Log retention

trust_events are purged by plan: 90 days (free) / 1 year (pro, scale). Expired dashboard sessions and stale IP rate-limit buckets are also cleaned.

npm run cron:purge-logs
# or Vercel cron: GET /api/cron/purge-logs (daily 03:00 UTC)

MCP Server (M4 / M7)

cd packages/mcp-server
npm install && npm run build

Tools: check_agent_trust, check_wallet_trust, check_payee_trust, explain_trust_score, attest_x402_payment, check_resource_decision, pay_if_trusted, diagnose_x402_url

See MCP setup for Cursor / Claude Desktop configuration.

TypeScript SDK (M7)

cd packages/sdk && npm install && npm run build
import { createVouchClient } from "@vet402/sdk";

x402 sample middleware (M4)

cd examples/x402-trust-gate
npm install
export VOUCH_API_KEY=your_key
npm run demo

Express middleware that blocks BLOCK recommendations before serving paid routes. See x402 integration guide.

Project structure

src/
  app/api/v1/          # REST API routes
  app/docs/api/        # Hosted API reference page
  lib/
    chain/             # viem client, ERC-8004 reads, wallet metrics, agent resolver
    scoring/           # Score engine, sybil detection
    db/                # Drizzle schema
    api/               # Auth, rate limits (M2)
docs/
  README.md            # which remaining file to open
  quickstart.md
  deployment.md
  openapi.yaml
packages/
  mcp-server/          # @vet402/mcp-server — MCP tools for Cursor / Claude
  sdk/                 # @vet402/sdk — thin TypeScript API client
  middleware/          # @vet402/middleware — x402 gate (Express / Next.js / Hono)
examples/
  x402-trust-gate/     # Express middleware sample

Milestones

Phase

Status

Scope

M0

✅ Done

Base RPC, ERC-8004 reads, API skeleton

M1

✅ Done

Score engine v1, sybil standard, cache

M2

✅ Done

API keys, rate limits, DB persistence

M3

✅ Done

Dashboard, WL/BL

M4

✅ Done

MCP server, x402 sample, docs

M5

✅ Done

Closed β deploy, funder indexer, signup + Stripe, log retention

M6

✅ Done

x402 payment attestations + 10% score weight

M7

✅ Done

Parallel channels: SDK, MCP attest, settlements UI, API docs

License

MIT © KIZUNA Creation. The published @vet402/* packages carry the same license.

The measurements are licensed separately: CC BY 4.0 — the observatory snapshot (/api/v1/observatory/state), the daily series (/api/v1/observatory/history), the L1 purchase ledger (/api/v1/observatory/export.csv), the L0 export (/api/v1/observatory/l0/export.csv) and the sellers export (/api/v1/sellers/export.csv) may be redistributed and built on, commercially or not, provided the source is named:

KIZUNA Creation. vet402 observatory. Dataset, retrieved YYYY-MM-DD. https://vet402.com/api/v1/observatory/state

The retrieval date is part of the citation — every one of these numbers moves. The JSON carries license / retrievedAt / cite in the body; the CSV carries them in x-vet402-* and Link response headers.

Available Tools

7 tools
attest_x402_paymentB

Record an x402 payment attestation after settlement verification (idempotent on txHash).

Inputs: txHash (required), wallet (required), amount, network, +1 more.

check_agent_trustA

ERC-8004 agent trust check on Base. Decide on decision (ALLOW_PAY | REFUSE) and safe_to_pay (boolean); pay only on ALLOW_PAY. An error result is REFUSE too — no answer is not an ALLOW. measurement carries the full score body as evidence, not as the decision.

Inputs: agentId (required), wallet.

check_payee_trustA

Buyer-side check before paying a wallet.

Read these two fields and nothing else to decide:

  • decision: ALLOW_PAY | REFUSE

  • safe_to_pay: boolean (always equals decision === ALLOW_PAY)

Pay only on ALLOW_PAY. REFUSE already accounts for a degraded read, a partial measurement, a stale score, and a non-ALLOW recommendation; refuse_reasons names which. An error result is also REFUSE with safe_to_pay false — no answer is not an ALLOW.

measurement carries the full score body (score, dataDepth, signals) for explanation and logging. It is evidence, not the decision.

Inputs: payee (required).

check_resource_decisionA

Pre-payment decision for an x402 resource (role=payer) or pre-service decision for a payer (role=payee). Decide on decision (ALLOW_PAY | REFUSE) and safe_to_pay (boolean); pay only on ALLOW_PAY. WARN and BLOCK are both REFUSE under the default allow-only policy. An error is REFUSE too. measurement carries the full decision body: facts (L0 liveness, L1 settle-through, L2 conformance), reason_codes, freshness, evidence, and the rules_version that produced the recommendation. The L1 reason codes name whose gap it is: l1_not_attempted (vet402 signed no paid attempt), l1_inconclusive (settled purchases exist, but each paid response was a 4xx vet402 attributes to its own request shape - a gap in vet402's measurement, not evidence against the seller; facts.l1.n_inconclusive carries the count and those rows do not count toward a BLOCK), l1_never_delivered (a conclusive paid response existed and nothing was delivered), l1_delivered.

Each row of evidence[] names its own source: vet402 for a row from vet402's own L0-L2 record, subgraph for a row read from The Graph's x402 subgraph with the caller's own Graph key (that row carries subgraphId, block.number, deployment and queriedAt, which is what shows live index data rather than a static snapshot). This route returns vet402 rows. Do not add counts across sources: they count different things, and the two can disagree about the same wallet.

Your own policy (role=payer only): pass amountUsd (what the 402 asks), maxPerTxUsd (your ceiling, default 1), minL1Deliveries (floor on vet402's delivered L1 purchases) and/or requireVet402Allow (default true: a WARN refuses with payee_recommendation_not_allow, as in the SDK; false waives the WARN and needs minL1Deliveries >= 1 or the server answers 400 invalid_policy). The server then adds measurement.caller_policy with verdict ALLOW | REFUSE and reason_codes in the SDK's own words: price_above_ceiling, insufficient_delivery_evidence, payee_recommendation_block, payee_recommendation_not_allow, evidence_unavailable. A caller_policy REFUSE makes this tool REFUSE and those words are in refuse_reasons. caller_policy never rewrites recommendation; not_evaluated lists what the server did not check (min_subgraph_receipts is always there - The Graph is read only with your own key, through pay_if_trusted).

Inputs: resourceId (required), role, payer, amountUsd, +4 more.

check_wallet_trustA

Trust check for a wallet address. Resolves ERC-8004 agents when registered. Decide on decision (ALLOW_PAY | REFUSE) and safe_to_pay (boolean); pay only on ALLOW_PAY. An error result is REFUSE too — no answer is not an ALLOW. measurement carries the full score body as evidence, not as the decision.

Inputs: wallet (required).

explain_trust_scoreB

Explain a trust score breakdown in plain language for an agent or wallet.

Inputs: wallet, agentId.

pay_if_trustedA

Pay an x402 resource ONLY if vet402's decision allows it. Unlike check_resource_decision, which returns a verdict for you to act on, this tool holds the signer: on anything other than ALLOW the payment module is never even loaded, so no signature can exist.

Read these two fields and nothing else to know what happened:

  • decision: PAID | REFUSE | FAILED

  • safe_to_pay: boolean (always equals decision === PAID)

REFUSE means it stopped BEFORE a signature existed; refuse_reasons carries the server's own reason_codes unchanged, plus one of evidence_unavailable, payee_recommendation_not_allow, payment_target_unknown, payer_not_configured, payee_mismatch, chain_or_asset_mismatch, price_above_ceiling (the 402 asks more than maxPerTxUsd), price_above_declared (the 402 asks more than the amountUsd you named). FAILED means it signed and the seller did not settle — signed and nonce are returned, not hidden, because the authorization stays live until validBefore.

settlement is at most settle_claimed: the seller's PAYMENT-RESPONSE header is a claim, not an on-chain confirmation. measurement carries the /decision body verbatim, including evidence[] with each row's own source (vet402 or subgraph) so you can check which ledger answered. A subgraph row also carries subgraphId, block.number, deployment and queriedAt - the proof it was read from a live index. Counts on rows from different sources are not added together.

A seller outside vet402's catalogue (/decision answers 404) is not a dead end: when resource is given, the 404 is handed to the SDK, which judges from the 402's payTo, the payee score for that address and your evidence floors (decision_record.verdict_source payee_score or caller_policy, reason_codes resource_uncatalogued). The same boundary holds there: payTo must equal payee, a BLOCK payee score refuses even with requireVet402Allow:false. Without resource a 404 refuses with evidence_unavailable - there is nothing to judge from.

Omit resource/payee/amountUsd to get the pre-payment checks alone (Graph-key presence, degraded, the server's caller_policy, ALLOW): it refuses with payment_target_unknown, and the evidence floors and The Graph read are not evaluated (they live inside the SDK's payOrRefuse, which is only reached with a payment target). Paying also requires VOUCH_PAYER_PRIVATE_KEY in this server's env and viem installed; without them resource/payee/amountUsd are dropped before the SDK, the same checks run, and the tool refuses with payer_not_configured (floors not evaluated on this path either).

policy is passed to the SDK's payOrRefuse unchanged. policy.evidence.source "subgraph" or "both" reads The Graph's x402 Base subgraph through the Graph Gateway and puts that read on decision_record.evidence[] as its own row (source subgraph, receipts, block.number). The Gateway key is read from GRAPH_API_KEY in this server's env, never from tool input; without it the tool refuses with graph_key_not_configured before reading anything. policy.requireVet402Allow:false waives only a WARN, and only if every declared floor (minSubgraphReceipts / minL1Deliveries) is met - BLOCK and degraded still refuse. decision_record then carries verdict_source caller_policy and policy_override (what was waived, which floors were met with which numbers).

Inputs: resourceId (required), payee, method, policy, +3 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 7 tools

    Disambiguation2/5

    Three tools — check_agent_trust, check_wallet_trust, and check_payee_trust — all perform essentially the same address/agent trust lookup, and their descriptions repeat the identical "decide on decision and safe_to_pay" boilerplate rather than drawing a clear boundary. check_resource_decision also overlaps with all three while adding policy, leaving an agent unsure which entry point to use. Only explain_trust_score, attest_x402_payment, and pay_if_trusted are clearly distinct.

    Naming Consistency4/5

    All names are snake_case with a leading verb (check_*, explain_*, attest_*, pay_if_*), which reads predictably. Minor deviations like pay_if_trusted (conditional phrasing) and attest_x402_payment (embedded protocol token) are still legible and do not break the pattern.

    Tool Count5/5

    Seven tools is well within a comfortable range for a trust-gating server, and each remaining tool (explain, attest, decide, pay) maps to a distinct stage of the x402 flow. The only count concern is redundancy among the three check_* tools, not raw volume.

    Completeness4/5

    The surface covers check → explain → attest → decide → pay, which is the full payment-gating lifecycle. Gaps exist around reading back attestations or querying score history, but an agent can work around them via the check tools.

    Maintenance

    ActivityActive
    ResponsivenessSyncing

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      MCP server for AI agent trust verification, enabling agents to verify identities, check trust scores, and build reputation across multiple blockchain and web platforms.
      12
      20 npm
      1
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      MCP server that enables AI agents to verify each other's trust scores, register, submit reviews, and find trusted agents before transacting.
      4
      20 npm
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      MCP server for verifying agent payment integrity on x402, providing tools to check payTo mismatches, market stats, and lookup endpoints.
      1
      MIT