关于 Agent Receipt Verify
Agent Receipt Verify 为 AI Agent 使用的支付与结果凭证提供独立检查。它可在付款前检查端点符合性,并在付款后关联凭证与结算,同时区分未测结果和失败。
主要功能
- 01检查 x402 端点符合性
- 02关联凭证与结算
- 03运行已发布的验证向量
- 04明确区分无法测量的检查
适用场景
- 01AI Agent 付款前检查 x402 端点
- 02验证凭证是否对应某笔结算
- 03依据协议向量交叉检查验证器行为
MCP 相关
MCP 服务提供付款前检查工具 `check_x402_endpoint`,以及付款后验证工具 `link_receipt_to_settlement`。它通过标准输入输出通信。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
agent-receipt-verify
独立验证 AI Agent 使用的支付与结果证明收据。
一行运行
npx -y agent-receipt-verify https://api.bitrefill.com/x402/gift-cards/search
无需账号、密钥或付款。它会告诉你该端点是否按 x402 的要求索取款项;对于需要实际结算才能验证的级别,它会标记为「未测量」,而不是判定失败。
它不会做什么:它判断的是收据的格式,而非签发方是否诚实。关于哪些内容被刻意留作未测量,请参阅 CONFORMANCE.md。
npx -y agent-receipt-verify attempt-identity # 19 vectors for x402 spec section 5.5
npx -y agent-receipt-verify link-vectors # 12 vectors, receipt to settlement
npx -y agent-receipt-verify kanon # cross-check against iamonuwa/kanon
项目简介
Agent 经济中有两类收据。一类说明谁有权执行操作 (委托:UCAN、Authproof、Agent Passport)。另一类说明支付了什么,以及输出是否匹配(结算与结果:x402 offer-receipt、INAM 联署收据,以及 ERC-8004 消费者计数的证明)。
receipt-commons/receipt-interop 涵盖第一类。本项目涵盖第二类。
通过 Agent 使用:MCP 服务
这两项检查也作为 Model Context Protocol 服务提供,因此 Agent 可以自行运行,而不必由人来执行命令。它通过 stdio 使用 MCP 通信,协议版本为 2025-11-25。消息帧、initialize、tools/list 和 tools/call 均在 src/mcp-server.mjs 中直接实现,不使用 MCP SDK,也没有任何必需依赖。
提供两个工具,分别用于 Agent 实际需要答案的两个时机。
check_x402_endpoint,用于付款前检查。它发送一个请求,并报告该端点达到 CONFORMANCE.md 中阶梯的哪些级别。需要实际结算的级别会返回「未测量」,绝不会返回「失败」。
link_receipt_to_settlement,用于付款后检查。它接收收据、授权盐值和已支付的资产,根据实时链上状态解析加盐承诺,并回答「已关联」、「未关联」或「无法检查」。这是三种不同的答案,绝不会被合并成两种。
将其添加到客户端:
{
"mcpServers": {
"agent-receipt-verify": {
"command": "npx",
"args": ["-y", "github:holistis/agent-receipt-verify", "mcp"]
}
}
}
无需账号或 API 密钥,也没有任何内容发布到 npm。如果你不想从 git 引用运行 Node,可以使用 Dockerfile:
docker build -t agent-receipt-verify . && docker run -i --rm agent-receipt-verify
该服务不会打开端口,也不会写入磁盘。任何非协议帧内容都会写入 stderr,因为 stdout 属于客户端。npm test 会通过真实消息帧驱动该服务,覆盖 21 个用例,包括未知工具名称、必须不回复的通知,以及未知网络;对于未知网络,服务必须回答「无法检查」,而不是「未关联」。
ethers 是可选依赖,仅用于 check_x402_endpoint 中的一项签名检查。如果没有它,该项检查会报告「未测量」而非通过,其他功能则照常运行。
为什么独立实现很重要
由卖家签署的收据,是利益相关方作出的陈述。只有当非签署方能够重新计算并得出相同结论时,它才能成为证据。因此,这里的每个验证器都依据已发布的规范编写,而非参考实现的源代码,并且会使用协议作者发布的向量文件进行测试。
如果两个互不知晓对方代码的实现对相同向量得出一致结果,这就是互操作性。一个实现与自身保持一致,证明不了任何事情。
谁独立开展了这项工作
以下是其他人对本项目的评价,并非我们在此作出的声明。每一行都是可打开的链接。
- INAM 在其自己的验证器文档中将其列为独立实现,说明该实现依据 SPEC.md 编写,而非基于他们的源码;他们还补充说,尚未审计此代码,并建议你自行运行测试向量:docs/VERIFIER.md
- goun7 在他自己的测试向量文档的诚实局限部分引用了本项目的两项测量结果,并保留了我们的原文措辞:TEST-VEKTOR-KOK-COMPUTE-WORK.md
- iamonuwa 决定将本项目从 Kanon 链接到结算的语料集作为互补集合,不作背书,并保持两种格式兼容:kanon#32
- babyblueviper1 部署了此处报告的一项拒绝案例:携带显示名称但没有密钥 ID 的生产者会在付款结算前被拒绝:erc-8004#99
其中两项附带的说明值得重申。Kanon README 中的链接只在 issue 中提及,目前尚未上线;INAM 明确表示,他们尚未审计此代码。两者都是有意如此说明的。有人看过这项工作固然有价值,但同时说明他们没有做什么,价值更大。
状态
| 格式 | 测试向量来源 | 案例数 | 我们的判定是否一致 |
|---|---|---|---|
| INAM 联署 | inamprotocol/inam-protocol @ d5e5f45 | 4 | 4 / 4,失败检查集合完全相同 |
| x402 offer-receipt(commitment §5.6,在 PR 3827 中提出) | x402-foundation/x402 @ 0e492d6,未合入 main | 1 个已发布示例,加 6 个自有边界案例 | 是,字节完全一致 |
| x402 结算链接(§5.6.4,同一提案) | 自有测试,加三笔真实结算 | 22 个离线,3 个实时 | 是,每个链接都解析到对应的结算 |
npm test # all of the above. No account, no key, and nobody else's server
除 keccak256 外,仅使用 Node 内置模块。有两条坦诚的注释,因为“无网络”并不完全属实,而这类声明正是本软件包不该轻率作出的:
- 有一个案例会故意连接 localhost 上一个已关闭的端口,以证明无响应地址会报告未测量,而不是导致工具崩溃。这会建立一个套接字,但不会影响本机,也不会连接任何第三方。
- 不久前,测试套件还会获取属于他人的实时发现索引,约两万条记录,因为有个测试向
census传入了未知标志,而census会忽略未知标志并继续执行。现在该标志会被拒绝,因此测试套件约 11 秒即可完成,且不会联系任何人。过去仅运行一次索引就要超过 30 秒。
只有下方的结算链接会访问真实网络,而且只使用公开的只读 RPC。
来自另一方的确认比表格更重要:x402 规范的作者使用他们自己的实现(与本实现没有共享任何代码)对下方三笔真实收据进行了测试,并从收据、盐值和资产出发,按照 §5.6.4 的要求,解析到了相同的三笔结算。双方的结果见 x402#3827。
无需账户,自行重新计算
其中两项交叉检查可通过一条命令离线运行,因此这里的内容无需盲目信任:
npx -y agent-receipt-verify kanon
npx -y agent-receipt-verify beleid
npx -y agent-receipt-verify link-vectors
第一项会将我们的 EIP-3009 验证器与 iamonuwa/kanon 发布的十个向量进行核对。第二项的存在,是因为结果一致也可能掩盖问题:这十个向量都只涵盖少付,因此无法说明多付时会怎样,也无法说明同一个 nonce 被两个付款方重复使用时会怎样。这正是 x402#2725 中悬而未决的两个问题;该命令会明确打印出两种可能答案在哪些地方存在差异、哪些地方没有差异。
有一项适用范围需要了解。高 s 规则和签名者恢复仅适用于没有代码的付款方。USDC 会根据付款方是否有代码选择检查方式:没有代码时使用 ecrecover(高 s 签名会回滚);有代码时,则在付款方合约上调用 ERC-1271 isValidSignature。因此,不能因智能钱包或带有 EIP-7702 委托的普通地址的签名形式而拒绝它们。在上下文中传入 payerHasCode: true,验证器便会跳过这两项规则,仍然检查时间窗口、金额和 nonce,并且绝不会回答 VALID:它会回答 ongemeten,因为签名本身只能通过对链的调用来检查。现在每个答案都带有 payerScope(code、no-code 或 unknown;unknown 按照 no-code 处理,并会明确说明)。这一点由 robertolocatelli81-dev 在 x402#3925 中指出,而且也适用于我们的第一个版本。
第三项会运行十二个向量,检查付款后的另一半:这张收据是否属于那笔结算。其中十一个完全不需要网络;第十二个会读取链,并被标记后跳过,而不是报告为失败,因为无法运行的检查不能说明收据有问题。同样这十二个向量也以 Kanon 向量的字段格式收录在 vectors/settlement-link-kanon 中,因此两个向量集可以并排对照。
另外还有两个命令,都会测量其他人的端点,而不是我们自己的,并且都只读:
npx -y agent-receipt-verify census [out.json]
npx -y agent-receipt-verify liveness [out.json]
census 按 HOST 而非资源统计发现索引,因此拥有三百条路径的一方不会胜过三百个只有一条路径的一方。liveness 会基于该统计结果,询问每个主机今天是否仍会返回 402,因为索引中的注册并不保证端点仍然存活。按设计,这两个命令都会很慢:它们会在请求之间暂停,因为列表中的主机并未要求接受测量。
将收据关联到结算
如果收据的 transaction 字段为空,则只能通过付款方、收款方、金额和时间来匹配付款。两笔条款相同且发生在同一区块中的付款,完全无法区分。x402 offer-receipt 扩展的第 5.6 节通过对 ERC-3009 nonce 进行加盐承诺解决了这个问题:持有盐值的人可以重新计算出与唯一一笔结算的关联,其他人则一无所知。
第 5.6 节所在的位置,可能并非读者想当然认为的位置。offer-receipt 扩展当前 main 分支的版本中,并不存在第 5.6 至 5.6.5 节。它们只存在于尚未合并的拉取请求 3827 中。因此,这里所有引用第 5.6 节的内容,都是以一份提案为准,并固定到该拉取请求中的提交 0e492d6;该提交比 main 超前四个提交,任何人都可以获取。合并前,编号仍可能变动。阶梯的第 4 级和第 5 级会在配置文件中标明这一点,工具也会在自己的输出中说明,因此结果绝不会悄悄地被呈现为依据已发布文本测量所得。
第 5.6.5 节是让这一点在实践中变得重要的部分。无人揭示的承诺仍然只是签名者的陈述:他们可以针对一笔结算签发两张使用不同盐值的收据,也可以签发一份揭示后什么都得不到的承诺。因此,对于要统计结算数量的消费者,无论是为了去重还是抵御 Sybil 攻击,都只能统计至少有人揭示过的承诺。 以下是 Base Sepolia 上三笔条款完全相同的付款:付款方、收款方、金额、资源均相同,且付款时间相隔数分钟。没有承诺值,就无法区分这些回执。但每笔付款仍会解析到各自的结算:
npm run link-example
默认窗口以回执的 issuedAt 为中心,向前和向后各延伸 800 秒,并根据该时间点附近测得的区块生成;--window=<blocks> 则直接以区块数设置窗口。如果公共节点已裁剪该区块,命令会说明这一点,然后向无需密钥的区块浏览器(Blockscout)查询相同的日志;输出会注明哪个读取方返回了结果(reader :)。区块浏览器是你与链之间的第三方,不是节点:它读取同一条链,但行为可能不同,所以节点仍是首选,区块浏览器则作为第二种参考。使用 --no-explorer 禁止回退查询,或使用 --explorer=<url> 指定另一个兼容 Etherscan 的 API。如果两者都失败,你会得到 LINKED : unknown;这表示检查未完成,绝不是结论。
勘误,于 2026 年 10 月 24 日发现(版本 0.2.11 及更早版本)。 该命令曾假设链尖处的出块速度在整个回溯过程中保持不变,以此将回执时间映射到区块。Base Sepolia 在 10 月 16 日每 2 秒生成一个区块,而一周后变为每 0.2 秒一个,因此估算结果早了 190 万个区块。在拥有完整历史记录的节点上,命令搜索的窗口不包含该笔结算,却返回了 LINKED : no,这是错误结果;在公共节点上则因“历史记录已裁剪”而失败。一位陌生人运行了六次命令,始终没有得到结论,我们就是这样发现问题的。0.2.12 会搜索目标区块,而不是计算它;测量找到的区块附近的出块速度,并使用以秒为单位的窗口。如果早期版本曾针对一张回执返回 LINKED : no,而该回执的时间早于链上出块速度的变化,请重新运行:当时的 no 针对的是错误的窗口。
commitment : 0x5c35a61a09a566a9c284fad18a70ac2ddfff34d01d2a3ce776ce94e8d5b38ff8
candidates : 2 nonce(s) consumed by this payer in that window
LINKED : yes, to exactly one settlement
tx : 0xed87b09bfb20a8c8b69908e163fcea6432d193d389640c0465afcb5561dfeb30
commitment : 0xfecee93e1aec7d6858a218bf324f23e2a4eed07925d8d4542b9662c5026e3d22
candidates : 3 nonce(s) consumed by this payer in that window
LINKED : yes, to exactly one settlement
tx : 0x2898d0ceb97ddf60af98e19c94fc0adddbdf1e64f9032e62454085e37acfe36b
commitment : 0xea8dd34b8beac90f021a4e8d893a689c25249f86947770c8e6647c1cde206008
candidates : 2 nonce(s) consumed by this payer in that window
LINKED : yes, to exactly one settlement
tx : 0x2a9e95d63a5af8210767baa69143b364dad51caffc29a152c77e03bc508d7199
每个窗口都以各自回执的 issuedAt 为中心,因此候选数量会有所不同:中间那张回执的窗口涵盖了三笔付款,但仍能准确找出其中一笔。第 5.6.1 节所述的歧义问题,就这样根据实时状态得到解决。
该输出中有两项会变化,但结论不会变。随着付款方使用更多 nonce,候选数量会增加;在链尖越过窗口上限之前,上方区块编号会被限制为当前链尖。每张回执解析到的交易都是固定的。
examples/ 中的三个文件准确包含了第 5.6.4 节所说的后续验证者所需内容:回执、其盐值,以及已支付的资产。nonce 和交易哈希被刻意排除在外。它们来自代币合约上的 AuthorizationUsed(address,bytes32) 日志;只有恰好一个候选项能揭示承诺值时,工具才会接受结果。匹配项为零或多于一个时,都会报告为未关联,绝不会猜测最可能的结果。
使用 --receipt=<file> 指定你自己的回执;如果盐值和资产没有与回执一同保存,则添加 --salt= 和 --asset=。这三项缺一不可:只有回执和盐值、没有资产就无法关联,这正是第 5.6.4 节特别提醒的内容。
这些付款发给了一个实际运营、会签发版本 2 回执的 x402 卖家。该卖家自行发布了这些案例及每笔付款应解析到的结算,地址为 https://api.tokenizen.nl/api/x402/conformance。你可以让自己的实现查询该地址并进行比对,也可以调用其付费路由,并关联自己获得的回执。测试样例无法验证盐值是否按 §5.6.3 指定的位置到达真实结算信封中;只有付费调用才能验证这一点。
买家和卖家都是我们,因此这表明整条链路已端到端闭合。但这并不能证明还有其他人在使用它。
ERC-8004 反馈是否有支付作为依据?
npx -y agent-receipt-verify ground --feedback=0xa661b2ffd344926ddd5e31dc0c481212ba2d973fdb0c08a461d4c903f15d3936
从链上读取并逐一报告的四项事实:评分者是付款方,收款地址是 Agent 绑定的钱包,付款发生在反馈之前,而且评分者既不是所有者,也不是获准操作员。这些事实构成 AgenticFinanceGraph 在
erc-8004/erc-8004-contracts#99 中采用的定义;这里根据他们的描述重新构建,而非使用他们的代码,与 Kanon 向量的处理方式相同。还有一项单独列出的第五项事实:给定收据及其盐值(--receipt= --salt=),feedbackHash 是否能通过该收据解开,并且恰好对应这笔结算,这样读者就不必相信哈希指向的是正确的转账。
通过两个在此实现之前由他人记录的判定进行验证:它们都记录自 Base 主网,保存在 vectors/grounding 中,并由 test/grounding.mjs 离线重放:Agent 1085288 的四项事实全部成立;Agent 524087 的评分者是收款方,且付款并未转入 Agent 绑定的钱包。付款方和收款方取自 USDC 转账日志,而不是交易本身,因为结算由服务促进方发送。绑定关系在反馈所在区块读取;无法读取该区块的节点会改为读取最新区块,输出也会注明这一点。无法测量的事实标记为 not_measured,绝不会标记为 false。判定依据是链上事实,而非评分是否公正。
一套测试
npx -y agent-receipt-verify suite
按链上顺序进行四项检查,并汇总在一张表中;任何一项失败时,退出代码为 1:x402 第 5.5 节中的尝试身份、针对已发布 EIP-3009 向量的付款授权、必须恰好对应一笔结算的收据,以及必须有支付作为依据的反馈(AgenticFinanceGraph 记录的两个判定,通过 ground --vectors 离线重放)。这是针对
erc-8004/erc-8004-contracts#99 提出的要求;它不会重复实现任何功能,而是调用上述命令,并将结果并列展示。它会检查此实现的行为是否与已发布的向量和他人记录的判定一致。它并不认证任何其他人的端点、收据或反馈。
诚实说明局限
格式检查会验证收据的形式:签名、内容寻址,以及哈希是否与交易对手观察到的字节匹配。它们无法告诉你运营者是否诚实,也无法确认注册表是否将收据视为已最终确认。
结算链接会读取实时链上状态,其实际局限如下:
-
它会搜索收据
issuedAt前后的一段区块范围,而不是整条链,因为公开的 Base Sepolia RPC 每次调用的eth_getLogs上限为 200 个区块(这是实测结果,并非文档所述)。它始终会打印搜索的区块范围,因此“未找到”表示“此范围内未找到”,绝不表示“不存在”。使用--window=扩大范围。卖家可能会先签署收据,再完成结算,因为卖家会先拿到 nonce,所以搜索范围以
issuedAt为中心,而非仅向前搜索。根据实测区块时间推算搜索范围后,以一张实时收据进行测量,代价如下:--windowissuedAt前后的范围RPC 调用次数 耗时 50 1.7 分钟 1 0.7s 200 6.7 分钟 3 0.8s 400(默认值) 13 分钟 5 1.3s 1000 33 分钟 11 1.7s 2000 67 分钟 21 2.9s 调用次数会随搜索范围线性增长,而不会随付款方的历史记录增长;扩大搜索范围就是为此付出的代价:用 21 次请求换取一小时的容差。另一种方法是搜索付款方所有已使用的 nonce,这样完全不需要
issuedAt,也不会因时间偏差而漏掉结果,但代价会随该付款方曾经支付的总额增长。 -
RPC 调用被拒绝时会报错,而不是返回空结果。无法执行的搜索绝不能报告为搜索后未找到结果。
-
目前只实现了
eip3009,因为这是规范目前唯一定义的方法。 -
它会确认收据承诺了一笔结算,但不会确认金额或收款方是否与约定一致;需读取对应交易才能确认。
-
收据版本 2 定义在一个仍未合并的拉取请求中,因此其原像仍可能变更。所依据的提交已固定在上方的源代码和状态表中。
来源与许可证
Apache-2.0,与两个已验证项目使用的许可证相同。它们发布的测试材料在注明出处后以未修改的形式重新分发;确切的提交请参阅 NOTICE。两个项目均未审查或认可本仓库。
一条命令检查你自己的端点
无需安装、无需账号,也不会向 npm 发布任何内容:
npx github:holistis/agent-receipt-verify https://your-endpoint.example/paid
梯级本身是一个有版本的文件:profiles/x402-deployed-ladder-v1.json,其中列出了每个级别对应的条款、每项检查所需的输入,以及检查无法判定时应报告的内容。工具会读取该文件,而不是将规则写在代码中,因此第二种实现无需阅读我们的代码,也能测量同一内容。
它会在不付款的情况下测量 CONFORMANCE.md 中梯级的 0 至 2 级。3 至 5 级需要实际结算,因此结果会显示为“未测量”,而不会显示为“失败”。该文件也列出了它刻意不测量的内容。
为经过测量的端点提供徽章
如果某个版本测量过你的端点,就可以使用对应的徽章:
[](https://github.com/holistis/agent-receipt-verify/blob/main/CONFORMANCE.md)
相同的数据也可通过 /api/x402/badge.json?url=... 以 JSON 格式获取。
关于这个徽章,有两点需要说明,因为这都是刻意设计的。它绝不会进行实时测量:它会根据带日期的测量结果作出响应,因此没人能用该端点探测任意主机,而且数字不会因每次短暂故障而变化。测量日期也会显示在徽章中,因此过期的徽章会明显显示为过时,而不是悄然出错。
没有任何版本测量过的端点会返回 measured: false,而不是 0 级。这代表两种不同的说法:一种表示我们检查过但一无所获,另一种表示我们从未检查过。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。