verify402-mcp
Provides pre-signing safety and on-chain truth checks for Ethereum and EVM networks. Validates x402 payment challenges and transaction intents, including chain ID, recipient, data, value, asset, amount, payer balance, and block freshness, before signing. Also uses Ethereum on-chain data to measure swap routes, depth, safety, and quote fairness via Uniswap V3 QuoterV2 and V2 router getAmountsOut.
Supports preflight validation of Solana x402 payment challenges, checking scheme, network, asset, amount, payTo, resource, timeout, and authorization window before signing.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@verify402-mcppreflight this x402 payment challenge before I sign it"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
verify402
Machine-payable on-chain truth & pre-signing safety for AI agents. Free fail-closed preflight gate + five verifiable truth checks paid per call via x402 in USDC. No API key, no account, no wallet required for the free gate.
30-Second Quickstart
1. Free Preflight Gate (No wallet, no key)
Verify an x402 payment challenge before signing it:
curl -sS -X POST https://verify402.onrender.com/preflight \
-H 'content-type: application/json' \
-d '{
"kind": "payment",
"payment": {
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "10000",
"payTo": "0xAeEF086DD4e436c69d6d0F684b4c9F43392df9cb",
"resource": "https://verify402.onrender.com/verify"
},
"policy": { "maxAmountUsd": 0.25 }
}'Returns PASS / CAUTION / BLOCK, typed reasons, exact checked and notChecked lists, pinned block number, and a verifiable 90-second HMAC receipt.
2. Connect via MCP (Claude Desktop, Cursor, AI Agents)
Run the MCP server directly via npx (stdio transport):
npx -y --package=https://github.com/a32116150-ctrl/verify402/releases/download/mcp-v1.0.2/verify402-mcp-1.0.2.tgz verify402-mcpAdd to Cursor or Claude Desktop (mcp.json):
{
"mcpServers": {
"verify402": {
"command": "npx",
"args": [
"-y",
"--package=https://github.com/a32116150-ctrl/verify402/releases/download/mcp-v1.0.2/verify402-mcp-1.0.2.tgz",
"verify402-mcp"
],
"env": { "VERIFY402_URL": "https://verify402.onrender.com" }
}
}
}Note: With no EVM_PRIVATE_KEY configured, only the free preflight_gate is registered. Configure EVM_PRIVATE_KEY with a dedicated hot wallet holding USDC to unlock the 5 paid checks.
Related MCP server: SENTINEL
Machine-Readable Discovery
Service Manifest:
/.well-known/verify402.json(Documentation)OpenAPI 3.1 Specification:
/openapi.jsonMCP Registry Manifest:
server.json(Publishing Guide)x402 Bazaar Discovery:
docs/x402-discovery.mdPlain Text Contract:
/llms.txtDiscovery Audit:
docs/discovery-audit.mdDirectory Submission Checklist:
docs/distribution-directory-checklist.md
Human Pages: Home · Start · Preflight Gate · MCP Server · Checks · Live Findings · Proof & Receipts · Docs · Donate
Why an agent would pay for this
Models hallucinate profit. Most arb "opportunities" an LLM surfaces are stale, ignore fees, or come from a quote that was never checked against the venue that would execute the trade. This service exists to be the check an agent cannot do itself.
The measurement discipline
Quotes come from the canonical on-chain sources — never our own reimplementation of venue math:
V3: Uniswap's deployed
QuoterV2(its answers are the router's answers by construction)V2: each router's own
getAmountsOut
Both legs are measured against one block number, so the round trip reflects a single instant rather than two prices that never coexisted.
Three bugs were caught by the test suite before this ever served a paid response, and they are the reason the tests exist:
Invented profit (the big one).
decodeFunctionResultongetAmountsOut— a single declared output, which viem unwraps — was indexed withdecoded[0], silently grabbing the array's first element: the input amount. On the sell leg that turned 41 WETH into 41 trillion USDC of "profit". Now the decoder finds theuint256[]structurally and refuses a result equal to the input.Basis points off by 100x. −292 USDC on a 100,000 USDC round trip rendered as −0.29 bps instead of −29. A rounding trick multiplying by
10_000ninstead of1_000_000nwhen keeping two decimals of precision.Venue that doesn't exist still gets reported. Started as an unasserted assumption;
all configured venues exist on-chainis now a test.
node test/measure.test.mjs # 5 checks, needs a mainnet RPCOne check is the headline property: a 100k USDC round trip on Ethereum must NOT come out profitable. If it ever does, the engine is wrong until proven otherwise.
Run it
npm install
npm test # engine checks (network required)
node src/server.mjs # testnet payment mode
BYPASS_PAYMENT=1 node src/server.mjs # local dev only - loud warning, no billing
X402_MODE=mainnet FACILITATOR_URL=... node src/server.mjsEndpoints
Route | Price | What it does |
| free | pre-signing gate: validate a payment request or transaction, return PASS / CAUTION / BLOCK + a short-lived receipt |
| free | verify a preflight receipt (HMAC, intent hash, expiry) |
| $0.05 (x402) | the measurement, with verdict + evidence |
| $0.10 (x402) | pre-trade gate: TRUST / CAUTION / AVOID + evidence |
| $0.03 (x402) | what would the chain ACTUALLY pay for this swap? best route + every quoted venue |
| $0.05 (x402) | audit a quote someone else gave you: fair / overpaying / too_good_to_be_true |
| $0.05 (x402) | position sizing: how big can I trade before the round trip eats me? |
| free | live no_edge verdicts on Ethereum majors |
| free | machine-readable service description |
| free | liveness |
POST /depth is the pre-trade sizing question nothing on-chain answers.
Pool-reserve "liquidity: $2.7M" tells an agent nothing about the block it
will actually trade in. The depth engine buys the target with the best route
at each rung of a USD ladder, immediately sells the exact position back, and
reports per-rung impact %, round-trip cost %, and capacityUsd — the largest
size that stays under the caller's cost tolerance. Live example (SHIB,
2.5% tolerance): "$2.7M liquidity" resolves to a real capacity of $1,000
($100 → 1.20% round trip, $10k → 6.57%). The ladder is the honest answer.
The preflight gate
POST /preflight is the free control an agent runs immediately before signing.
It accepts one of two intents:
payment — an x402 402 challenge (EVM or Solana): scheme, CAIP-2 network, asset, raw amount,
payTo, resource, timeout, authorization window.transaction —
chainId,to,data,value, optionalfrom(EVM: Ethereum 1, Arbitrum 42161, Base 8453).
Response: PASS / CAUTION / BLOCK, reasons[], checked[],
notChecked[], and a receipt:
{
"verdict": "CAUTION",
"sign_allowed": true,
"requires_ack": true,
"reasons": [{ "code": "unlimited_approval", "severity": "caution", "message": "..." }],
"checked": ["scheme", "network", "asset", "amount", "payee", "resource", "payer_balance", "block_freshness"],
"notChecked": ["recipient reputation", "sanctions screening", "payment signature / signed payload", "facilitator settlement", "off-chain promises"],
"receipt": {
"spec": "verify402/preflight-receipt@1",
"intentHash": "…",
"blockNumber": 48277717,
"blockHash": "0x…",
"expiresAt": "2026-10-19T…Z",
"signature": "…"
}
}The receipt is HMAC-SHA256 signed, bound to the canonical normalized intent, a fresh pinned block where the chain is readable, and an expiry (default 90s, max 300s).
POST /preflight/verifyrecomputes it.Fresh block, not just any block: on EVM chains, a latest block older than 30s is
CAUTION; older than 120s isBLOCK. The response carriesblock.timestampandblock.ageSeconds. A block with no timestamp is a503, not a PASS.Fail-closed: if the chain cannot be read, the endpoint returns
503and no receipt. An executor must refuse to sign without a verifiable receipt.Not checked, on purpose: recipient reputation, sanctions, contract audits, full balance-delta simulation, MEV, the payment signature/signed payload, gas price/limit chosen by the signer, raw signed-transaction binding, and facilitator settlement. They are named in every response instead of being implied.
Free is rate-limited (60/min per IP, 8 concurrent) so it cannot become a public RPC proxy.
The MCP server exposes it as preflight_gate. It runs with no wallet (free gate
only); add EVM_PRIVATE_KEY for the five paid tools. If sign_allowed is
false, do not sign; if requires_ack is true, surface the reasons to the
operator. Client configs and the install command are at
/mcp.
Reference executor
examples/preflight-guard.mjs is a dependency-free, deny-by-default wrapper:
it calls the gate, refuses BLOCK, requires an explicit acknowledgement for
CAUTION, verifies the receipt, and only then calls your signer. It holds
no key and sends nothing itself. The intent is cloned and deep-frozen before the
gate, and that same object is passed to the signer. If you do not want to trust
the public instance, run your own and pin baseUrl.
POST /verify request:
{ "chainId": 1, "target": "WETH", "base": "USDC", "size": 100000, "minNet": 0 }Response (abridged):
{
"verdict": "no_edge",
"blockNumber": 26214368,
"buy": { "venue": "uni-v3-0.05%", "amountOutHuman": 41.366 },
"sell": { "venue": "uni-v3-0.05%", "amountOutHuman": 99707.9 },
"gross": { "human": -292.08, "bps": -29.23 },
"net": { "human": -342.14, "bps": -34.23 },
"costs": { "flashLoanPremiumBps": 5, "gasUsd": 0.0561 },
"venues": { "quoted": 12, "buySide": [...], "sellSide": [...], "unavailable": [...] },
"caveats": ["..."]
}verdict is one of edge (net cleared minNet), no_edge_costs_ate_it (gross
positive, costs larger), or no_edge (unprofitable before costs).
POST /safety request (accepts any ERC-20 address, not just registry symbols):
{ "chainId": 1, "tokenAddress": "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", "intendedUsd": 500 }Response (abridged):
{
"verdict": "TRUST",
"checked": ["contract", "market", "sellability", "liquidity"],
"notes": ["sellable via uni-v3-0.3% via hop, uniswap-v2 via hop, ...",
"round trip in and out of $500 costs 1.42%"],
"evidence": { "contract": {...}, "market": {...},
"sellability": { "canSell": true, "probedInUsd": 25 },
"liquidity": { "roundTripCostPct": 1.42 } },
"elapsedMs": 1434
}verdict is one of:
AVOID— positively dangerous: cannot be exited at a realistic size, or the contract is not a token at allCAUTION— tradable, but with a stated cost or problem (thin pool, manufactured price, expensive round trip)TRUST— every check passed; still not a promise of profit, only a record that nothing measured was wrong
Probes are USD-sized on purpose: an earlier version probed "half a token" and flagged SHIB — a $1B token — as a honeypot, because half a SHIB is dust no pool can quote. The test suite and live calls caught that before it launched; every future verdict change must be explainable, not silent.
A request without payment gets 402Payment Required with instructions in the
PAYMENT-REQUIRED header; the client pays and retries with proof — that is the
entire x402 flow, and it is what makes the caller a program rather than a person.
Deploying
See DEPLOY.md — free-tier container deployment, ERC-8004 identity mint
(npm run register, owner's key, on-chain data URI), mainnet facilitator
wiring, and the distribution order.
MCP: how AI agents consume this
mcp-bridge/ is packaged as verify402-mcp (stdio). It exposes the free
pre-signing gate (preflight_gate) and the five paid endpoints
(verify_arbitrage, check_token_safety, get_best_route,
verify_quote_fairness, get_trade_depth) to Claude Desktop, Cursor, OpenCode,
or any MCP client.
Free gate only: omit
EVM_PRIVATE_KEY; the server registers onlypreflight_gateand nothing can be charged.Paid tools: add a dedicated hot wallet with USDC. The bridge signs the x402 payment only — never a trade — and enforces
MAX_PAYMENT_USD(default $0.25) inside the x402 client before a signature exists. It also requires an allowlisted EVM network, the verify402 payee, and a known paid resource on the configured server, and refuses redirects.MAX_DAILY_USDis optional.Install:
npx -y --package=<release-tarball> verify402-mcp. The versioned GitHub release tarball is the install; no npm account is required. Client configs, the exact OpenCode V2 shape, and the release command are at/mcp. See alsomcp-bridge/README.md.
Settlement, response delivery, and recovery (honest contract)
Delivery: the verdict is returned inside the settlement response — the paid answer arrives together with the payment confirmation (testnet receipt: tx
0x11121007bafec47f132763f42e2f58af6ebba7fba9c7ed1b5980d3f595a9324a).Recovery — implemented (
x-idempotency-key): send the header on the FIRST call of an operation, minting ONE key per operation (not per retry — though with double-keying even fresh ids per retry are protected, see below). If the response is lost after settlement, retrying returns the SAME verdict without requiring payment again, under two locks: 10 minutes (max 3 replays). Caveats stated plainly: the key is the caller's secret (whoever presents it replays for free), the window is short by design so the 402 paywall stays intact, and replayed responses carryrecovery: { replayed: true, responseHash, replays }.Settlement-gated (fail-closed): a verdict record only becomes replayable after the facilitator confirms the settlement (official x402
onAfterSettlehook). Promotion is keyed on a server-generatedoperationIdcarried in the response (recovery.operationId, excluded from the projection) and requires a confirmed settle result with a non-empty transaction reference; the projection hash of the served bytes is an additional check. Equal output does not identify an operation — two requests that both resolve to{"verdict":"no_match"}are still distinct, and settling one never promotes the other. A failed, aborted or pending settlement promotes nothing. The replay cap (3) is per operation, shared between the caller key and the content-hash companion key — two aliases are ONE budget, not two.Key/scope conflicts (rejected before payment or replay): a key is bound to one operation — its canonical request content plus, when the rail exposes it (
payload.authorization.from), the payer's scope. Presenting the same key with changed request content or from a different payer returns409 recovery_conflict(conflict: "request_mismatch" | "scope_mismatch") before the paywall — never the other operation's verdict, never a charge.Double-keying: the verdict is recorded under the caller's key and an implicit key derived from the canonical request content. A caller that mints a fresh id on each retry still replays free under the content hash. Two distinct asks that canonicalise identically are the same question — sharing one verdict inside the window is determinism, not a bug. The companion is a same-caller recovery alias, not a public cache: a companion hit is only returned to a caller whose declared scope matches the scope recorded at staging. Equal content alone is never permission to see another account's result.
After the window (typed, not silent): expired verdicts leave a tombstone carrying the previous
responseHashfor 6 hours. A retry within the tombstone window re-measures with the paywall re-armed (there is no free measure after the window — anything else would be a loophole), but the response carriesrecovery: { remeasured: true, previousResponseHash, expiredAt }, so the calling agent can branch on "this operation was charged before and could not be replayed" instead of guessing.Conformance controls:
test/recovery.test.mjsincludes the review-requested controls — two staged operations with byte-identical output do not cross-promote; promotion is refused on failed/pending settle, a missing/empty transaction, or a missingoperationId; same key + changed content or scope is a conflict; a companion requires a matching scope.
Evidence bundle — implemented (
verify402/evidence-bundle@1): every paid response carriesevidence_bundle: { requestHash, responseHash, pinnedBlock, mode, createdAt, payment.in }. Both hashes are sha256 over canonical JSON (keys sorted recursively, no whitespace).requestHashcovers{ route, body };responseHashcovers the response body excludingevidence_bundle,recovery,cache,elapsedMs. Reference implementation of the canonical form:src/recovery.mjs(canonicalStable), tests intest/recovery.test.mjs.Payment caps (bridge/client):
MAX_PAYMENT_USDcaps one payment at signature time;MAX_DAILY_USD(optional,mcp-bridge/budget.mjs) caps the whole UTC day across restarts via a local ledger. Attempted charges are counted before the payment is built — a spending guard should over-count, never under-count.
First paid call, in ten lines
GET https://verify402.onrender.com/info— prices, endpoints, recovery contract.GET https://verify402.onrender.com/findings— free verdicts on majors; see the honestno_edgebaseline.POST /verifywith your question ({"chainId":1,"target":"WETH","size":100000}) and no payment → HTTP 402.Read the payment instructions from the
payment-requiredresponse header.Build an x402 payment for ≤ the stated price (Node example:
mcp-bridge/first-payment.mjs; MCP users:mcp-bridge/).Re-send the same request with the
paymentheader. The verdict arrives with the settlement response.On a retry/restart, add
x-idempotency-key: <random-32-hex>to step 3 so lost responses replay free instead of paying twice.Verify the receipt:
evidence_bundle.responseHashmust equal sha256(canonical JSON of the body minusevidence_bundle,recovery,cache,elapsedMs).Cap your own wallet:
MAX_PAYMENT_USD(per call) andMAX_DAILY_USD(per day) in the bridge.env.Repeat the call in the same block and compare
evidence_bundle.responseHash— determinism of the measurement. Anedgeverdict should be raced, not trusted.
Honesty
No profit is promised. The measured answer for mainstream pairs is almost always
no_edge— that is the market's fee boundary, not a failure of the tool.The service reports what it measured, not what it guarantees.
Not financial advice.
License
Apache License 2.0. See LICENSE and NOTICE.
You may use, modify, and distribute this software, including in commercial agent
systems and bots. If you redistribute it or a product containing it, keep the
copyright notice, the license text, and the NOTICE file. The verify402 name
and logo are not licensed for endorsement.
This server cannot be deployed
Maintenance
Related MCP Connectors
Pre-trade token safety checks for AI agents on Solana and Base. x402 USDC per call, no key.
Free token-safety scans + paid x402 verdicts, signals, radar & EVM swap quotes for AI agents.
Free token-safety scans + paid x402 verdicts, signals, radar & EVM swap quotes for AI agents.
Live uptime and source-URL verification for AI agents, with per-call x402/USDC paid endpoints.
1
Related MCP Servers
- AlicenseAqualityDmaintenancePay-per-call tools for AI agents including trust checks, due diligence, market data, and human-verified approvals, settled in USDC on Base via the x402 protocol.16MIT
- FlicenseAqualityCmaintenanceAgent-Level Transaction Safety Oracle. Before an AI agent signs a blockchain transaction, it returns a SAFE/UNSAFE verdict with a SENTINEL Score (AAA-D) and risk flags. Pay-per-call $0.005 USDC via x402 on Base.1-
- AlicenseNot gradedqualityBmaintenancePay-per-call checks an AI agent runs before it moves money: token safety verdicts and wallet risk profiles on Base, on-chain payment verification, IBAN/VAT/BIC/LEI/ISIN validation, and live TLS and email-spoofing posture for a domain. Paid in USDC over x402 with no API key or account; the free payment_info tool explains the pricing.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to obtain an independent review of their drafts, returning a pass/fail verdict with specific issues and suggested fixes, plus a signed receipt. Payments are made per check over x402, with no account or API key needed.MIT