关于 vet402-check
vet402-check 可在 Agent 支付 x402 或 MPP 卖家前查询 vet402 的公开购买记录,并检查签名记录与付款故障。它为自动付款流程提供只读事实,不持有密钥、不发起付款,具体处理策略由调用方决定。
主要功能
- 01查询卖家的购买与交付记录
- 02验证签名、Merkle 证明及链上付款
- 03比较收款地址、金额和资产
- 04诊断付款未交付的故障方
- 05通过 fetch 钩子在签名前检查付款
- 06提供 CLI、HTTP 查询和 MCP 工具
适用场景
- 01付款前核查 x402 或 MPP 卖家
- 02为自动付款设置拦截或人工审批规则
- 03验证最新卖家记录及链上凭据
- 04分析付款失败或无响应的原因
MCP 相关
提供基于 stdio 的 MCP 服务,包含付款前查询、记录验证和付款故障诊断三个只读工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
3收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| url | string | 是 | 即将付款的资源 URL |
| asset | string | 否 | 402 中的资产(铸币或代币地址);会与 vet402 向该卖家付款时使用的资产进行比较(原因 `asset_unseen`) |
| chain | string | 否 | solana、tempo、base、algorand,或 CAIP-2 ID,例如 eip155:8453 |
| amount | string | 否 | 402 中的金额,以原子单位表示;会与 vet402 为此 URL 支付的金额进行比较(原因 `price_jump`) |
| pay_to | string | 否 | 你持有的 402 中的 payTo(接收方) |
| explain | boolean | 否 | 同时列出答复所依据的购买记录,包括日期和 tx |
| verify_newest_record | boolean | 否 | 同时验证该卖家最新的签名记录(签名、Merkle 证明、链上付款和锚定) |
详细介绍
@vet402/check(命令:vet402-check)
在 Agent 向 x402 或 MPP 卖家付款前,先在 vet402 的公开记录中查询该卖家。
此软件包会读取 vet402 自身购买记录的公开数据(vet402-delivery 仓库)。它与 @vet402/mcp-server 相互独立,后者是用于 vet402 评分 API 的 MCP 工具。
vet402 使用自己的资金,在 Solana、Tempo 和 Base(以及其专属页面上的 Algorand)向 x402 和 MPP 卖家购买,逐笔在链上核验付款,并公布收到的内容:排名(rank.json)以及已签名记录;每日 Merkle 根会写入 Solana 备忘录。vet402-check 会读取这些记录,并返回事实:vet402 尝试向该卖家购买了多少次、成功结算了多少次、收到了多少次回复、最近一次购买及其 tx、计入卖家的失败次数和未计入的失败次数、rank.json 中显示的评级(购买次数过少时为 measuring),以及该卖家的已签名记录。若 vet402 从未向该卖家购买过,回答会是“vet402 没有该卖家的记录”。如何处理这些事实,由调用方决定。
仅读取公开文件:公开网站上的 rank.json 和 records/<id>.json,以及此仓库中的 data/records/index.json。核验记录时也会读取公开 RPC。无需密钥、钱包或付款,也不会写入任何内容。
60 秒内试用
需要 Node 22 或更高版本。首次运行会从 npm 下载软件包(@vet402/check);之后运行会立即启动。
npx -y @vet402/check https://api.xona-agent.com/token/pumpfun-trending
同时核验该卖家最新的已签名记录(签名、Merkle 证明、链上付款、Solana 备忘录锚定):
npx -y @vet402/check https://api.xona-agent.com/token/pumpfun-trending --verify
在 Claude Code 中作为 MCP 服务运行(先运行上面那一行一次,以便在客户端启动服务前缓存安装包):
claude mcp add vet402-check -- npx -y @vet402/check --mcp
不使用 Node:Python 和 curl
通过 HTTP 进行相同的查询,免费且无需密钥:GET https://vet402-delivery.vercel.app/v1/check?url=<seller URL>(可选参数 chain 和 payTo)。回答以 verdict 和 why 开头(了解 verdict 的含义)。
- Python,仅使用标准库:
examples/python/check_before_paying.py。提供一个命令、一个pay_after_check(url, pay)函数,以及一个适用于 x402 Python HTTP 客户端的钩子(x402HTTPClientSync(client).on_payment_required(vet402_on_payment_required));该钩子会在每次收到 402 时、客户端签名前,查询请求的 URL。 - curl:使用 jq 和不使用 jq 的单行命令:use.html#curl。
命令行
vet402-check <url> [--chain solana|tempo|base|algorand|<CAIP-2>] [--pay-to <address>] [--amount <atomic units>] [--asset <address>] [--explain] [--verify] [--offline] [--json]
vet402-check verify <record id | https URL | file> [--offline] [--json]
vet402-check diagnose <tx signature or hash> --url <seller URL> [--chain solana|base] [--status <http>] [--json]
vet402-check --mcp
--chain将回答限定为一个链(eip155:8453是 Base,eip155:4217是 Tempo)。--pay-to会将你持有的 402 中的 payTo 与 vet402 为该卖家记录的 payTo 地址进行比较。--amount和--asset会将 402 中的金额(原子单位,与 x402 的写法一致)和资产与 vet402 为该卖家支付的内容进行比较(参见将 402 与 vet402 的付款进行比较)。--explain会列出回答所依据的购买记录,按时间从新到旧排列;每条记录包含日期、链、结果、HTTP 状态和 tx,以及卖家已签名记录中的付款信息(金额、资产、payTo)。--json会输出完整结果:sellers[](卖家所在的每个页面各一个)、records、payTo、asOf、sources。- 查询后无论是否找到记录,均以状态码 0 退出;记录未通过核验时以 1 退出;输入错误或数据无法读取时以 2 退出。
从克隆的仓库运行:npm ci,然后运行 npm run check-before-paying -- <url>。
MCP 工具
三个只读工具(stdio,按行分隔的 JSON-RPC):
| 工具 | 输入 | 返回值 |
|---|---|---|
check_before_paying | url、可选的 chain、pay_to、amount、asset、explain、verify_newest_record | 上述事实和列出的原因,以文本和 structuredContent 的形式返回 |
verify_record | record(id 或 https URL)、可选的 offline | OK、OK_NOT_ANCHORED、OK_OFFLINE 或 FAIL,每项检查占一行 |
diagnose_failed_payment | tx、url、可选的 chain、status | seller_side、facilitator_side、buyer_side 或 undetermined,附带原因;若为 seller_side,还会提供证据包 |
Claude Desktop(claude_desktop_config.json):
{ "mcpServers": { "vet402-check": { "command": "npx", "args": ["-y", "@vet402/check", "--mcp"] } } }
支付时:x402 fetch 钩子
封装 x402 客户端通过其发起支付的 fetch。在客户端签名前,会先检查每个 402:
import { wrapFetchWithPayment } from "@x402/fetch";
import { wrapFetchWithCheck } from "@vet402/check"; // npm install @vet402/check
const fetchWithPay = wrapFetchWithPayment(
wrapFetchWithCheck(fetch, {
onCheck: (e) => {
console.log(e.checks.map((c) => c.summary).join("\n"));
// The rule is yours. To stop the payment, throw here:
// if (e.checks.some((c) => c.sellers.some((s) => s.settled > 0 && s.delivered === 0))) throw new Error("not paying");
},
}),
client,
);
此钩子应放在支付包装器内部,因为 @x402/fetch 会调用传入的 fetch,且仅在其返回 402 后才创建支付。该钩子会通过克隆响应读取 402(从响应正文中的 x402 accepts 或 PAYMENT-REQUIRED 标头,以及 MPP WWW-Authenticate: Payment 挑战中读取),针对其提供的每个链和 payTo 运行 check_before_paying,将结果传递给 onCheck,并原样返回 402。它不会接触密钥或签名器,也不会更改任何标头。已携带支付信息(X-PAYMENT、PAYMENT-SIGNATURE、Authorization: Payment)的请求是客户端的重试请求,会直接通过。如果无法读取 vet402 的记录,onCheck 会收到已设置的 error,且不会执行任何检查。
rank.json 和记录索引只读取一次,并在内存中保留 10 分钟(new PublicData({ ttlMs })),因此繁忙的 Agent 不必针对每个 402 重新下载它们。用于比较金额的已签名记录也会以相同方式保留。
列出的原因和策略
每个回答都包含 reasons[],其中每项原因都有一句说明,以及据此判断的购买记录或其他记录。这些事实与判定结果一同提供,且不会改变判定结果。
| 原因 | 触发条件 |
|---|---|
paid_not_delivered | vet402 从卖家处最新的一笔已付款购买已结算,但未收到答复(结算后按 rank.json 的卖家侧规则判定)。只有 vet402 已向卖家告知其结果时才会给出此原因;这与判定结果采用的条件相同。 |
payto_differs | 402 中的 payTo 并非 vet402 曾向该卖家付款的对象(且 vet402 至少记录过一次此类付款)。 |
price_jump | 402 要求的金额超过 vet402 为同一 URL、同一资产支付过的最高金额的 10 倍。恰好 10 倍不算 price_jump。 |
asset_unseen | 402 要求的资产,是 vet402 从未在该链上向此卖家支付过的资产。 |
never_bought | vet402 从未在所请求的链上向此卖家购买过。 |
stale | vet402 从此卖家处进行的最新一次购买已超过 7 天。 |
price_jump 和 asset_unseen 根据卖家的已签名记录得出(该链上最新的 8 条记录,同一 URL 的记录优先)。如果没有可供比较的记录,则不会给出这两个原因。
为钩子提供策略,按原因决定如何处理:
const fetchWithPay = wrapFetchWithPayment(
wrapFetchWithCheck(fetch, {
policy: { paid_not_delivered: "block", payto_differs: "block", price_jump: "block", asset_unseen: "ask_human", never_bought: "warn" },
askHuman: async (e) => confirmWithPerson(e.reasons), // true pays, anything else stops
}),
client,
);
block会在客户端签名前抛出CheckBlockedError;其reasons会列出阻止支付的原因。ask_human会调用askHuman(event),仅当其返回true时才会支付。如果没有askHuman,则会以CheckNeedsApprovalError(一种CheckBlockedError)停止。warn和allow会继续执行。策略未列出的原因按allow处理。- 在执行上述任何操作之前,
onCheck都会收到event.reasons和event.decision(每项原因对应的操作及其中最严格的操作)。 - 如果没有
policy,钩子的决定和等待方式与 0.1.2 完全一致:event.reasons仅包含来自 rank.json 的原因,不会读取已签名记录,因此不会检查price_jump和asset_unseen。 - 如果无法读取 vet402 的记录,则会设置
event.error,没有任何原因,支付会继续执行,与 0.1.2 一致。
将 402 与 vet402 的付款记录进行比较
npx -y @vet402/check https://api.xona-agent.com/token/pumpfun-trending --chain solana --amount 1000000 --asset EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
答案会添加 comparison:读取的已签名记录、vet402 向该卖家支付过的资产、vet402 为此 URL 支付过的最高金额及其记录和交易,以及 402 金额与该金额的比值。提供 policy 时,此钩子会对 402 中的每个 x402 报价进行相同的比较。
排查付款后未收到答复的问题
npx -y @vet402/check diagnose <tx signature or hash> --url <seller URL> --status 500
在 TypeScript 中:diagnose({ tx, url, status }, new PublicData())。作为 MCP 工具:diagnose_failed_payment。
- 从公共 RPC 读取付款信息(Solana 或 Base;
SOLANA_RPC_URL和BASE_RPC_URL可用于指定其他 RPC):是否已结算、payTo、金额、资产和时间。不会签名或发送任何内容。 - 使用 vet402 自身的故障判定规则对尝试进行分类(rank.json 正是使用同一套代码生成的)。
- 列出 vet402 在该笔付款前后 24 小时内从同一卖家、同一链上购买的记录;如果没有,则列出时间最近的记录。rank.json 会保留每个卖家的最新购买记录,因此较早的付款可能没有落在其时间范围内的记录。
答案会给出 fault 和 reasons[]:
seller_side仅在以下条件全部满足时才会给出:付款已在链上结算;付款对象是 vet402 曾向该卖家付款的 payTo;资产是 vet402 曾用于向该卖家付款的资产,且金额不低于 vet402 支付过的最低金额;HTTP 状态属于 vet402 规则中已结算付款后归责于卖家的状态(5xx,再次返回 402);rank.json 中该卖家的最新购买记录列表覆盖至该时间范围的起始时间之前;vet402 在该 24 小时内向该卖家的已付款记录也均已结算但未收到答复,且没有任何已交付记录;这些日期的已签名记录中也没有显示从该卖家成功交付的购买记录。buyer_side仅适用于因付款方的代币账户余额不足而导致链上付款失败的情况。facilitator_side仅适用于因其他原因导致链上付款失败,且 vet402 在该时间范围内向该卖家的付款也都未能结算的情况。- 其他所有情况均为
undetermined:无法读取 RPC、没有对应交易、未提供状态、状态为 4xx、payTo 是 vet402 从未付款的对象、资产是 vet402 从未用于向该卖家付款的资产、金额低于 vet402 支付过的最低金额、vet402 在该时间范围内收到了卖家的答复(记录在 rank.json 或这些日期的已签名记录中)、rank.json 中的记录未覆盖至该时间范围,或 vet402 在该时间范围内没有购买记录。未通过完整测试时,不会指名卖家。
对于 seller_side,evidence 会包含一份可交给卖家的证据包,提供 JSON 和 Markdown 两种格式:交易及其区块浏览器链接、付款方、payTo、金额和资产、买家报告的 HTTP 状态、vet402 对此次付款的规则判定、vet402 在该时间范围内的购买记录及其交易,以及这些日期的已签名记录(如果此次付款属于 vet402 自身,则会从记录中排除)。证据只会打印出来,不会发送。
框架示例
此文件夹中的 examples/(不包含在 npm 包中)提供 Coinbase AgentKit、OpenAI Agents SDK、Vercel AI SDK(TypeScript)和 LangChain(Python)的示例。每个示例都会为 Agent 提供一个工具,通过 check 并附带策略来获取付费的 x402 URL。packages/check/test/examples.test.ts 会在不连接网络的情况下运行每个示例,并使用本地替代函数模拟框架的工具函数。
阅读答案
- 已尝试 / 已结算 / 已返回答案:付款结算后,答案为 2xx 且正文非空。vet402 不会检查答案是否符合 listing 的承诺。
- 计入卖家责任的失败:
rank.json的method.faultRules中归咎于卖家的规则(例如paid_not_delivered:vet402 的付款已结算,但卖家返回 5xx 或没有响应)。 - 不计入卖家责任的失败:vet402 或 facilitator 一方的失败,以及无法判断原因的失败。
- 评级:以
rank.json打印的评级为准。measuring表示购买次数太少,无法评级。 - 已签名记录:对 DELIVERED 购买发布;对于其他判定结果,则在 vet402 通知卖家后发布。拥有购买记录但没有负面记录的卖家,在
rank.json中仍可能存在失败。 - 截至:指
rank.json和读取的记录日期。vet402 会按计划再次购买;卖家在最近一次购买后可能有所变化。
为什么可以验证记录
每条记录都由 vet402 的观察密钥签名(EIP-712)。每天的记录会组成一棵 Merkle 树,其根由 vet402 的 anchor 钱包写入 Solana memo,因此之后若要向某一天添加或删除记录,根就会发生变化。verify_record 会检查字节是否与记录索引列出的内容一致,以及密钥、签名、判定结果是否由记录的检查结果得出、Merkle 证明、Solana、Base 或 Tempo 上通过公共 RPC 查询的付款记录,以及 memo。它使用与 scripts/verify-receipt.ts(src/receipt/)相同的代码。RPC:SOLANA_RPC_URL、BASE_RPC_URL、TEMPO_RPC_URL 或公共端点。
其他来源
VET402_CHECK_RANK、VET402_CHECK_RECORDS_INDEX 和 VET402_CHECK_RECORDS_BASE 可将检查指向其他副本(HTTPS URL 或本地路径),例如网站的本地构建版本。
测试
npm test 会在不访问网络的情况下运行 packages/check/test/check.test.ts,使用由 packages/check/test/make-fixtures.ts 从公开数据中截取的固定数据:存在卖家责任失败和负面记录的卖家、已交付的卖家、未知卖家、链和 payTo 筛选、共享主机、通过的记录验证,以及因单字符修改、Merkle 证明和来自其他钱包的 memo 而验证失败的记录。
packages/check/test/v020.test.ts 涵盖 diagnose(针对 Solana 和 Base 的固定 RPC 响应:已结算、链上失败、未找到、RPC 故障)、将 402 与 vet402 的付款进行比较、原因以及钩子在真实 @x402/fetch 和 spy signer 下的策略、--explain 和 MCP 工具。packages/check/test/examples.test.ts 会运行框架示例。
packages/check/test/no-node.test.ts 会在回环地址上,对 /v1/check 自身的处理程序运行 Python 示例以及 use.html 中的 Python 和 curl 命令。packages/check/test/package.test.ts 会构建 npm 包(scripts/build.mjs),并在仓库之外的文件夹中运行该包。
前期工作:将钩子放在付款封装器之下的做法参考了 probe402-check,后者检查的是另一种公开记录(probe402 的记录)。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。