关于 Fetch SCITT receipts and on-chain anchors for offline verification
@forestrie/mcp-resolve 是用于获取 SCITT 收据及其验证材料的 MCP 服务,面向配合 @forestrie/mcp-verify 进行离线验证的用户。它从 SCITT 服务或区块链读取数据,并标明来源及其可支持的信任问题。
主要功能
- 01查询收据注册状态
- 02获取并解码 SCITT 收据
- 03获取日志创世文档和配置
- 04读取累加器与检查点历史
- 05获取合约 KS256 证明
适用场景
- 01为离线收据验证获取所需材料
- 02通过链上读取检查日志累加器
- 03核对收据注册状态与来源
- 04为合约根验证获取签名证明
MCP 相关
此 MCP 服务向 MCP 客户端提供 SCITT 配置、注册状态、收据、创世文档和链上数据的获取工具。它为 @forestrie/mcp-verify 提供验证材料;验证本身由该工具包在本地完成。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y @forestrie/mcp-resolveFORESTRIE_BASE_URLSCRAPI base URL used when a call omits baseUrl. Optional; the package ships no default and names no operator.FORESTRIE_RPC_URLJSON-RPC endpoint used when a call omits rpcUrl: your own chain access. Optional; the package ships no default and names no provider.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
7收录的工具列表 · 更新于 1 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| baseUrl | string | 否 | 任意 SCRAPI 基础 URL;省略时回退到 FORESTRIE_BASE_URL——软件包本身不会设置默认值 |
详细介绍
@forestrie/mcp-resolve
获取 SCITT 收据、其日志的创世文档及其链上累加器,并将它们交给
@forestrie/mcp-verify
进行验证——包括通过链上读取获取分视图答案。这个 MCP 服务会获取验证器要验证的材料:收据、创世文档、累加器快照、注册状态和服务配置。每个结果都会说明其字节的来源,以及它们能支持信任模型中的四个问题中的哪些问题。在 MCP 注册表中列为 dev.forestrie/resolve。
验证器完全在你的进程中运行,无需网络、账户、密钥或后端;它自身的规则是,安装它绝不能意味着存在网络依赖。因此,所有负责获取内容的部分都放在这里。
使用
{
"mcpServers": {
"forestrie-resolve": {
"command": "npx",
"args": ["-y", "@forestrie/mcp-resolve"],
"env": {
"FORESTRIE_BASE_URL": "https://api-a.forest-2.forestrie.dev",
"FORESTRIE_RPC_URL": "https://<your-chain-rpc-endpoint>"
}
}
}
}
两个环境变量都是可选的,且都由你提供。baseUrl 可以是任意 SCRAPI 基础 URL,rpcUrl 是你自己的链访问地址;调用时可以显式传入其中任意一个,只有调用未提供时才会使用环境变量值。该软件包不附带默认运营方或默认链提供方,也不会指定任何运营方或提供方。
有两个公开通道,它们是示例,并非默认值:
| 通道 | 基础 URL | 服务 ID |
|---|---|---|
| A | https://api-a.forest-2.forestrie.dev | canopy-dev-1 |
| B | https://api-b.forest-2.forestrie.dev | canopy-prod-1 |
需要 Node 20.11 或更高版本。
完整示例:一个收据,端到端
以下所有值都是公开的,其中会随版本变化的值来自验证器自身的 tarball。@forestrie/mcp-verify 会将每个版本注册到 Forestrie 日志中,并在 fixtures/self/ 下附带收据(其中的 docs/self-registration.md),因此发布的软件包包含一份声明、一张收据、一份创世文档和一个日志所有者密钥;本软件包可以获取这些内容,并针对在线通道进行验证。下面的运行大约需要四秒,最终通过公共 RPC 端点得出 split-view ok,无需密钥或账户。
| 坐标 | 值 | 来源 |
|---|---|---|
| 基础 URL | https://api-a.forest-2.forestrie.dev | 上文的通道 A(示例,并非默认值) |
| 引导日志 | e22c8d55-3f88-b2b5-f225-5d2c2441bcdd | forest 根;也可从创世文档中解码 |
| 发布日志 | da6f297c-4a0c-4c9a-b2ae-e701e558721d | 验证器注册版本的日志 |
| 内容哈希 | sha256(fixtures/self/statement.cose) | tarball;fixtures/self/manifest.json 列出了相同的摘要 |
| 条目 ID | fixtures/self/entry-id.txt | tarball;query_registration 也会返回该值 |
| massif 高度 | 无需手动输入 | 位于 query_registration 返回的 receiptUrl 中 |
| 日志所有者密钥 | fixtures/self/log-key.xy.b64(base64 文本) | tarball |
| 链 | ID 84532(Base Sepolia),univocity 0xe22c8d553f88b2b5f2255d2c2441bcdd0d50cd58 | 绑定在创世文档中;fetch_genesis 会对其解码 |
要在空目录中获取 tarball:
npm i @forestrie/mcp-verify
ls node_modules/@forestrie/mcp-verify/fixtures/self/
shasum -a 256 node_modules/@forestrie/mcp-verify/fixtures/self/statement.cose
运行日期为 2026-09-22、针对已发布的 0.5.0 bundle 执行时,每次调用返回的序列、JSON 参数和单行结果如下
(其 entry id 为 a0caa672b7030b000000000000000001;你的 entry id 以 entry-id.txt 中的内容为准):
fetch_scitt_configuration {"baseUrl": "https://api-a.forest-2.forestrie.dev"}→从 …/.well-known/scitt-configuration 获取了 SCITT 配置(serviceId canopy-dev-1)query_registration {"baseUrl": …, "bootstrapLogId": "e22c8d55-3f88-b2b5-f225-5d2c2441bcdd", "logId": "da6f297c-4a0c-4c9a-b2ae-e701e558721d", "contentHash": "<sha256 of statement.cose>"}→注册完成;回执位于 https://api-a.forest-2.forestrie.dev/logs/e22c8d55-…/da6f297c-…/14/entries/a0caa672b7030b000000000000000001/receipt—status: "receipt-available",receiptUrl(其中包含 massif 高度 14)以及entryId。fetch_receipt {"receiptUrl": "<from step 2>"}→从 …/receipt 获取了回执(438 B)—receipt.sha256与 tarball 的receipt.cbor摘要相同;receiptLogId指向 publications 日志。fetch_genesis {"baseUrl": …, "logId": "e22c8d55-3f88-b2b5-f225-5d2c2441bcdd"}→从 …/api/forest/e22c8d55-…/genesis 获取了创世文件(160 B):链 84532 上的 univocity 0xe22c8d553f88b2b5f2255d2c2441bcdd0d50cd58— 与 tarball 中的genesis.cbor字节完全一致。请保留它:以下调用会将其作为字节传回,不会在同一次调用中获取。verify_fetched_receipt {"receiptUrl": …, "entryId": "<entry-id.txt>", "payload": {"path": "…/fixtures/self/statement.cose"}, "trust": {"root": "known-log-key", "keyXy": {"b64": "<contents of log-key.xy.b64>"}}}→验证:通过 · 根=known-log-key · 封存有效;此根未回答视图分歧问题;此根未回答追加权限问题;归属有效- 使用
"trust": {"root": "genesis", "genesis": {"path": "…/fixtures/self/genesis.cbor"}}调用相同工具 →验证:在签名处失败(delegation_invalid) · 根=genesis · …这是预期结果,并非篡改:创世根的离线遍历只解析从 forest 根开始的一跳委托,而 publications 日志是其孙级节点。结果中包含genesis_root_reaches_direct_delegates_only,对此作了明确说明。 请像第 5 步和第 7 步那样,在密钥或累加器下验证此回执。 verify_fetched_receipt {"receiptUrl": …, "entryId": …, "payload": …, "trust": {"root": "known-accumulator", "chain": {"genesis": {"path": "…/fixtures/self/genesis.cbor"}, "rpcUrl": "https://sepolia.base.org", "logId": "da6f297c-4a0c-4c9a-b2ae-e701e558721d"}}}→验证:通过 · 根=known-accumulator · 封存有效;视图分歧检查通过;此根未回答追加权限问题;归属有效—anchor.blockNumber、anchor.matchedPeak和provenance.root.from.rpcUrl会说明是哪个链状态提供了答复。如果自回执峰值以来日志有所增长,同一调用会回溯已发布的检查点,并添加root_read_from_chain_history。- 可选:
fetch_accumulator {"chain": {…as in 7…}, "forReceipt": {"receipt": …, "payload": …, "entryId": …}}会将快照作为字节返回,供保存;之后,@forestrie/mcp-verify的verify_receipt在{"root": "known-accumulator", "accumulator": <those bytes>}下再次离线回答视图分歧问题。
链,以及查看位置。 第 7 步需要访问链 84532(Base Sepolia)的 JSON-RPC。本 package 不附带任何 provider,也未指定默认 provider;rpcUrl(或 FORESTRIE_RPC_URL)由你自行设置。以下端点是第三方公共服务,仅作为编写本示例时未经身份验证即可响应的示例列出;除初次尝试外,请使用你自己的 provider。
| 端点 | 2026-09-22 |
|---|---|
https://sepolia.base.org | 已响应 |
https://base-sepolia-rpc.publicnode.com | 已响应 |
https://base-sepolia.drpc.org | 已响应 |
https://base-sepolia.gateway.tenderly.co | 已响应 |
https://1rpc.io/base-sepolia | HTTP 400 |
https://rpc.ankr.com/base_sepolia | HTTP 403 |
https://base-sepolia.g.alchemy.com/v2/demo | HTTP 429 |
两个陷阱。 keyXy: {"path": "log-key.xy.b64"} 会失败:该文件是 base64 文本,而 path 输入会按原始字节读取——请将其内容作为 b64 传入。两个服务中的字节字段都名为 b64(此处也接受 base64 作为别名),因此从验证器中学到的数据结构可以原样用于此处。
test/core/readme-example.test.ts 会根据已记录的 lane-A fixture 校验本节中的坐标,因此重新采集 fixture 不会悄然导致示例失效。
八种工具
| 工具 | 数据来源 | 功能 | 支持能力 |
|---|---|---|---|
fetch_scitt_configuration | 获取 | GET {baseUrl}/.well-known/scitt-configuration | 无 |
query_registration | 获取 | 针对某条 statement 发起一次注册状态请求;绝不轮询 | 无 |
fetch_receipt | 获取 | 获取并解码收据,并报告其证书所指明的日志 | 单独使用时无 |
fetch_genesis | 获取 | 获取 forest 的创世文档,其中包含其链绑定和引导密钥 | sealing,仅适用于你保留的副本 |
fetch_accumulator | 链上读取 | 读取日志已发布的累加器;向历史记录回溯以查找被埋藏的峰值 | split-view、sealing、append-authority |
fetch_checkpoint_history | 链上读取 | 在区块预算范围内,按从新到旧的顺序读取已发布的检查点 | 与 fetch_accumulator 相同 |
fetch_ks256_attestation | 链上读取 | 在指定区块询问合约 KS256 根是否接受收据的证书 | 单独使用时无;mcp-verify 会使用它 |
verify_fetched_receipt | 获取,以及用户提供或链上读取 | 获取收据,并根据你提供或从链上读取的根验证该收据 | 验证器自身的回答,未经改动 |
fetch_ks256_attestation 适用于根为合约(Safe)的 forest。
这样的根无法通过 ecrecover 检查,因此验证器需要合约自身的回答;而获取该回答需要链上读取,验证器绝不会自行执行。将收据和根地址或你持有的 genesis 提供给工具;它会在一个区块上读取 isValidSignature(digest, signature)(默认读取最新区块,然后固定到该区块),并以 canonical-CBOR 证明的形式返回回答:
fetch_ks256_attestation {receipt: {b64: …}, genesis: {b64: …}, rpcUrl: …, block?: 123}
-> attestation {base64, byteLength, sha256}, fields {chainId, address, digest,
signatureHash, blockNumber, blockHash, magic}, provenance, supports
verify_receipt {receipt: …, …, ks256Attestation: {b64: <attestation.base64>}}
结果表示该证书在_该区块_上由合约的签名者验证有效;任何拥有 RPC 的人都可以重新执行调用以确认。对于没有合约代码的根(外部拥有的根无需此验证)、调用回退,或回答不完全是一个 32 字节的字(0x1626ba7e 后跟 28 个零字节;短返回、填充不干净、多余字或回显调用自身数据均会被拒绝),以及 RPC 所连接的链与 genesis 所属链不同时,工具会拒绝并且不返回证明。两次状态读取都通过区块哈希固定(EIP-1898),因此证明中的哈希就是读取状态时所对应的哈希。如果在 genesis 旁提供了 root,那么 root 就是被询问的合约,而 genesis 仅用于提供预期链。
每种工具都标注为只读、幂等且开放世界,因为它们都会与进程外部的对象通信。每项 supports 的含义及原因见
docs/what-fetching-proves.md。
一份获取到的内容能证明什么
从运营方获取的收据,在根据你持有的信任根完成验证之前,只是运营方的说法。forestrie/protocol 中的公开信任模型
spec/receipt-trust-model.md
列出了收据可以回答的四个问题(封存、视图分裂、追加权限、归属),以及调用方可以据此验证的四种信任根(genesis、known-log-key、known-accumulator、checkpoint-chain)。这些信任根没有先后顺序。哪一种合适,取决于你持有什么;获取操作会改变你持有的内容。
因此,这里的每个结果都包含 provenance(对于每项制品,说明它是从 URL 获取、从链上读取,还是由你提供,以及时间)和 supports(说明该材料在何种信任根下可作为哪些问题的证据,并附上一行说明)。这些说明是固定字符串,测试会逐字断言;相关列表和解释见
docs/what-fetching-proves.md。
工具接口中内置了以下三项结论,而不是只在文档中说明:
- 获取到的 genesis 永远不能作为信任根。
verify_fetched_receipt接受你提供的字节作为信任根,或接受从链上读取的累加器;其模式中没有任何形式可以在同一次调用中获取 genesis 并以其为根进行验证。在检查时从运营方获取 genesis,意味着收据和信任根都由运营方提供;这只能证明它与运营方今天选择提供的文档一致,除此之外不能证明任何事。提供fetch_genesis是为了让你获取一次文档、保存下来,此后将其作为字节传入。 - 链绑定属于森林,而非运营方。 不歧义合约地址和链 ID 绑定在森林的 genesis 文档中。
fetch_accumulator和verify_fetched_receipt的链路径从你持有的 genesis 中读取这些值,或者要求你显式提供;绝不会使用默认值、绝不会使用调用内部获取的 genesis,也绝不会使用环境变量。 genesis信任根只能覆盖直接委托对象,结果也会明确说明这一点。 在提供的 genesis 下,验证器的离线遍历只会解析森林根下的一跳委托。来自授权链更下游日志的收据——验证器自己的发布日志就是一个孙级日志——在该信任根下会以delegation_invalid失败,而verify_fetched_receipt会追加genesis_root_reaches_direct_delegates_only,说明这是遍历能力的限制,并非对收据的判定。此类收据应使用known-log-key或known-accumulator验证。- 被折叠的峰值依据同一信任根,从已发布的历史记录中查询。 后续增长将收据的峰值折叠到更大的峰值中时,链路径会在你设置的区块预算内回查已发布的检查点,并说明这一点(
root_read_from_chain_history)。它绝不会进行无界扫描。
验证器自身的 TRANSPARENCY.md(随其 tarball 一同提供)说明了什么是透明度日志以及收据包含什么;本软件包不再重复这些内容。其 docs/trust-roots.md 深入介绍了这些信任根。
此软件包绝不会做的事
- 绝不写入:不注册、不授权、不创建密钥。
- 绝不轮询:
query_registration只发送一次请求,由你决定是否再次调用。HTTP 429 会作为包含retryAfterMs的结构化problem返回,而不是作为错误。 - 绝不替你选择操作员或链提供方。
- 绝不跨调用缓存获取的创世数据或累加器:每个结果都带有一个新的
at。 - 绝不修改验证器的回答。
verify_fetched_receipt会原样返回验证器的stages、questions和diagnostics,并追加两条说明字节来源的诊断信息。not_answered_by_this_root是一个真实回答,会原样传递给你。
目录结构与门禁
src/core/ pure over bytes: URL construction, response classification,
provenance and supports labelling, the compose logic.
No node:*, no fetch, no fs. Exported as ".".
src/net/ the ONLY place fetch is called; every function takes fetchImpl.
Exported as "./net".
src/node/ the MCP adapter: SDK, stdio, {path}/base64 inputs, env defaults.
Exported as "./server".
CI 会根据 AGENTS.md 中所述的纯净性和打包门禁进行阻断。单元测试会在会抛出异常的 fetch 下运行,并重放记录在 test/fixtures/ 中的交互。存在使用真实通道的真实项目,但需选择加入,且绝不会作为必需检查。
pnpm test # purity gates + unit tests
pnpm typecheck
pnpm build
pnpm test:live # FORESTRIE_LIVE=1 plus FORESTRIE_BASE_URL, FORESTRIE_RPC_URL, UNIVOCITY_ADDRESS, CHAIN_ID and GENESIS_CBOR_B64
约定、不变量和发布流程见 AGENTS.md。
依赖项
package.json 中的版本均精确锁定,并会审慎更新。在 @forestrie/mcp-verify 中,只使用 "." 核心导出。每个请求(包括链读取)都通过注入的 fetchImpl 发出。
许可证
MIT。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。