关于 AgentRead
AgentRead 是一个 Cloudflare Worker,可将公开网页转换为供大模型和 Agent 使用的结构化 Markdown 与元数据。它提供免费的 URL 检查,并按成功请求收费;通过 Base 上的 USDC 和 x402 支付,无需账户或 API 密钥。
主要功能
- 01免费检查 URL 可访问性
- 02网页 Markdown 提取
- 03结构化提取元数据和链接
- 04HTTP API 与远程 MCP 访问
- 05x402 按请求支付
- 06URL 与目标地址验证
适用场景
- 01提取前检查网页是否可访问
- 02为 Agent 提供网页的简洁 Markdown
- 03提取页面元数据、标题和链接
- 04通过 MCP 客户端调用网页提取
MCP 相关
通过无状态的 Streamable HTTP MCP 端点提供 `inspect_url`、`extract_url` 和 `extract_url_structured`。付费工具遵循 x402 MCP 约定:客户端提交支付元数据,随后收到结算凭据。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://agentread.omar-cv.workers.dev/mcpclaude mcp add --transport http omarhassanatef-agentread https://agentread.omar-cv.workers.dev/mcp{"mcpServers":{"omarhassanatef-agentread":{"type":"http","url":"https://agentread.omar-cv.workers.dev/mcp"}}}在 Cursor 中打开{"servers":{"omarhassanatef-agentread":{"type":"http","url":"https://agentread.omar-cv.workers.dev/mcp"}}}在 VS Code 中打开{"mcpServers":{"omarhassanatef-agentread":{"type":"http","url":"https://agentread.omar-cv.workers.dev/mcp"}}}{"mcpServers":{"omarhassanatef-agentread":{"serverUrl":"https://agentread.omar-cv.workers.dev/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
详细介绍
AgentRead
为 AI Agent 清理网页内容,按请求付费。
AgentRead 是一个 Cloudflare Worker,可将公开网页转换为适合大模型和自主 Agent 使用的简洁、结构化 Markdown。它同时作为 HTTP API 和远程 MCP 服务提供。付费操作按成功请求收费,使用 x402 微支付(Base 上的 USDC)。无需账户、API 密钥或订阅。
| 操作 | HTTP | MCP 工具 | 价格(默认) |
|---|---|---|---|
| 检查 URL | POST /v1/inspect | inspect_url | 免费 |
| 提取简洁 Markdown | POST /v1/extract | extract_url | $0.003 |
| 结构化提取 | POST /v1/extract/structured | extract_url_structured | $0.005 |
价格通过配置设置,而非写死在代码中(参见配置)。
立即使用
服务: https://agentread.omar-cv.workers.dev · MCP: https://agentread.omar-cv.workers.dev/mcp · 已列入官方 MCP 注册表,名称为 io.github.omarhassanatef/agentread,也已列入 x402scan
试用免费检查:
curl -s -X POST https://agentread.omar-cv.workers.dev/v1/inspect \
-H 'content-type: application/json' -d '{"url":"https://example.com/"}'
将其添加到 MCP 客户端(任何客户端都能使用 inspect_url;付费工具需要支持 x402 的客户端,例如 @x402/mcp):
claude mcp add --transport http agentread https://agentread.omar-cv.workers.dev/mcp
{ "mcpServers": { "agentread": { "url": "https://agentread.omar-cv.workers.dev/mcp" } } }
使用 x402 客户端通过代码按次付费:参见使用 x402 客户端付款和 MCP。面向 Agent 的文档:/llms.txt、/openapi.json、/.well-known/x402。
状态:MVP,已上线。
- 生产环境(Base 主网,真实 USDC): https://agentread.omar-cv.workers.dev。付款通过 CDP facilitator 结算至
0xb866dD8C8401438Efb29b538ec4df11b6b407Ce6。- 测试网: 未托管测试网。
npm run dev会在本地运行,使用 Base Sepolia 和免费的 x402.org facilitator。已验证的内容:
- 自动化测试: 全部通过(237 项测试,在 Workers 运行时中执行)。
- 目录检查: 两个付费端点均通过 CDP Bazaar 验证器(所有预检检查)和 AgentCash/x402scan 发现审核,并已登记在 x402scan。
- 本地冒烟测试(9/9): 使用线上 x402.org facilitator,通过
wrangler dev执行。来自未注资钱包的真实签名付款通过了签名和要求检查,并因insufficient_balance被拒绝,因此已验证线上支付链路,直至实际转账环节。- 生产环境部署: 通过 GitHub Actions 中的所有冒烟检查(
gh workflow run CI)。线上 CDP facilitator 会验证 AgentRead 的身份并校验签名付款。由于开发网络会阻止workers.devIP 范围,因此 CI 从该网络执行。- 可在 Workers Free 计划上运行。 在 Cloudflare 边缘(Free 账户)的约 180 次基准测试请求中,已部署的
lite引擎没有因 CPU 限制而被终止;readability引擎在最多 84% 的请求中被终止。参见提取引擎。- 尚未在已部署的边缘环境中实际测试: 通过产品 Worker 进行的付费提取。同一提取代码已在边缘环境中进行基准测试,但尚未有注资付款成功完成。
为什么 Agent 愿意为此付费
原始 HTML 大多是噪声:导航、Cookie 横幅、广告、脚本、样式。Agent 自己抓取页面会浪费令牌,还必须维护 HTML 清理、SSRF 防护、编码检测、重定向处理和大小限制。AgentRead 一次性完成这些工作,并返回格式稳定、带版本号的 JSON。
x402 允许 Agent 使用自己的钱包,每次调用支付几分之一美分。无需注册,也不会存储凭据。提取失败不会收费。
这是一个有待验证的假设,而非已经证实的市场。服务会记录用于验证这一假设的数据(参见可观测性)。
架构
AI agent ──HTTP (x402 headers)──┐ ┌── POST /v1/inspect ──────────────┐
▼ │ │
Cloudflare Worker (Hono) ▼
AI agent ──MCP Streamable HTTP──► /mcp ────┤ validate input + URL policy + DNS guard
(x402 in _meta) │ (rejects bad input BEFORE any 402)
│ │
├── POST /v1/extract[/structured] │
│ └─ @x402/hono paymentMiddleware ──► facilitator /verify
│ │
└── MCP paid tools ▼
└─ @x402/mcp wrapper shared operations
(inspectUrl / extractUrl /
extractUrlStructured)
│
bounded fetch (manual redirects, re-validated
each hop, timeout, byte cap) → linkedom →
boilerplate removal → Readability → Turndown
│
handler OK (2xx) → facilitator /settle → result + PAYMENT-RESPONSE
handler failed → settlement cancelled → error (not charged)
- 单个 Worker,无数据库、队列或容器。
- HTTP API 和 MCP 工具调用相同的操作函数,并使用相同的 zod 架构进行验证。
- 支付使用官方 x402 v2 SDK:HTTP 使用
@x402/hono,MCP 使用@x402/mcp,exactEVM 方案使用@x402/evm。这里不会重新实现任何 x402 协议逻辑。
更多详情:docs/architecture.md · docs/payments.md · docs/security.md · docs/demo.md
API
机器可读文档:GET /(发现:操作、价格、限制)和 GET /openapi.json(OpenAPI 3.1,根据服务用于验证的 zod 架构生成)。
所有错误都采用相同格式:
{ "error": { "code": "blocked_destination", "message": "Internal, reserved, or loopback hostnames are not allowed.", "details": {} } }
GET /health
curl -s http://localhost:8787/health
# {"status":"ok","service":"agentread","version":"0.1.0","environment":"development",
# "payments":{"enabled":true,"network":"eip155:84532","networkName":"Base Sepolia"}}
POST /v1/inspect:免费
在付费前检查 URL 是否可访问并可提取内容。最多下载 64 KB,且不会构建 DOM。上游问题(超时、404、非 HTML)会在 200 响应正文中以 reachable: false 或 limitation 的形式报告。只有无效或被拦截的 URL 会返回 4xx。
curl -s -X POST http://localhost:8787/v1/inspect \
-H 'content-type: application/json' \
-d '{"url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402"}'
{
"schemaVersion": "1.0", "operation": "inspect_url",
"requestedUrl": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402",
"normalizedUrl": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402",
"hostname": "developer.mozilla.org", "reachable": true, "statusCode": 200,
"finalUrl": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/402", "redirects": 1,
"contentType": "text/html", "contentLength": 209180, "isHtml": true,
"title": "402 Payment Required - HTTP | MDN", "mayRequireJavaScript": false,
"extractionSupported": true, "limitation": null
}
POST /v1/extract:付费
请求正文(JSON,最大 8 KB;不接受未知字段):
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url | string | 必填 | 绝对的公开 http(s) URL,使用默认端口,不含凭据,不得为 IP 字面量,≤ 2048 个字符 |
maxCharacters | integer | 20000 | 100 – 100000;Markdown 会在段落边界处截断 |
includeLinks | boolean | true | false 会保留链接文本,但移除 URL 和图片 |
未付款时,响应为 HTTP 402。PAYMENT-REQUIRED 标头携带 base64 编码的 x402 PaymentRequired 对象;JSON 正文提供便于人类和 Agent 阅读的摘要:
curl -si -X POST http://localhost:8787/v1/extract \
-H 'content-type: application/json' -d '{"url":"https://example.com/"}'
# HTTP/1.1 402 Payment Required
# payment-required: eyJ4NDAyVmVyc2lvbiI6Miwi...
# {"error":{"code":"payment_required","message":"This operation costs $0.003 (USDC on Base Sepolia). ...",
# "details":{"operation":"extract_url","price":"$0.003","network":"eip155:84532","x402Version":2}}}
解码后的 PAYMENT-REQUIRED(accepts[0]):{"scheme":"exact","network":"eip155:84532","amount":"3000","asset":"0x036CbD53842c5426634e7929541eC2318f3dCF7e","payTo":"<your address>","maxTimeoutSeconds":120,...}。3000 相当于 0.003 美元的 6 位小数 USDC。
提供有效的 PAYMENT-SIGNATURE 标头后,响应为 200,并包含带有结算凭据的 PAYMENT-RESPONSE 标头:
https://example.com/ 的实际输出(2026 年 10 月直接运行操作代码时捕获):
{
"schemaVersion": "1.0", "operation": "extract_url",
"requestedUrl": "https://example.com/", "finalUrl": "https://example.com/",
"title": "Example Domain", "description": null,
"markdown": "This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.",
"headings": [], "truncated": false,
"extraction": { "method": "readability", "statusCode": 200, "contentType": "text/html", "charset": "utf-8",
"redirects": 0, "bytesFetched": 577, "characters": 156, "totalCharacters": 156, "words": 25,
"warnings": [], "durationMs": 854 }
}
extraction.method 的值为 readability、main-content-fallback、body-fallback 或 plain-text。
POST /v1/extract/structured:付费
请求正文与 /v1/extract 相同。分别返回元数据、Markdown、标题和链接:
{
"schemaVersion": "1.0", "operation": "extract_url_structured",
"requestedUrl": "...", "finalUrl": "...",
"metadata": { "title": "...", "description": "...", "canonicalUrl": "...", "language": "en-GB",
"siteName": "...", "byline": "...", "publishedTime": "2026-03-14T09:00:00Z" },
"markdown": "...",
"headings": [{ "level": 2, "text": "Zones of the intertidal" }],
"headingsTruncated": false,
"links": [{ "text": "official tide tables", "url": "https://example.org/tide-tables" }],
"linksTruncated": false,
"truncated": false,
"extraction": { "...": "same as extract_url" }
}
缺失的元数据为 null;不会进行猜测。includeLinks 只影响 Markdown;始终会返回 links(最多 200 个,去重后仅保留绝对 http(s) URL)。
状态码(付费端点)
| 状态 | 含义 | 是否收费? |
|---|---|---|
| 200 | 提取成功且付款已结算 | 是 |
| 400 | 输入无效/目标地址被拦截(在任何 402 之前返回) | 否 |
| 402 | payment_required(未付款)、payment_invalid(已拒绝:details.reason 给出 Facilitator 的原因,例如 invalid_exact_evm_insufficient_balance),或 payment_settlement_failed | 否 |
| 413 / 415 | 请求体过大/不是 JSON | 否 |
| 422 | unsupported_content_type、content_too_large、empty_content、javascript_required、too_many_redirects、host_not_found | 否 |
| 429 | 请求频率受限 | 否 |
| 502 / 504 | 上游页面错误/无法访问、DNS 故障、Facilitator 错误/上游超时 | 否 |
| 503 | 此部署未配置付款 | 否 |
使用 x402 客户端付款(HTTP)
官方 @x402/fetch 封装会处理 402 → 签名 → 重试流程。以下摘自 scripts/testnet-smoke.mjs:
import { wrapFetchWithPayment, x402Client, x402HTTPClient } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { toClientEvmSigner } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY); // TESTNET-ONLY wallet
const client = new x402Client().register("eip155:84532", new ExactEvmScheme(toClientEvmSigner(account)));
client.registerPolicy((_v, reqs) => reqs.filter((r) => BigInt(r.amount) <= 10_000n)); // never pay > $0.01
const payFetch = wrapFetchWithPayment(fetch, client);
const res = await payFetch("http://localhost:8787/v1/extract", { // `npm run dev` (Base Sepolia)
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ url: "https://example.com/" }),
});
const result = await res.json();
const receipt = new x402HTTPClient(client).getPaymentSettleResponse((n) => res.headers.get(n));
console.log(result.markdown, receipt.transaction);
MCP
端点:POST /mcp,采用 MCP Streamable HTTP,无状态(无会话,使用 JSON 响应)。按照传输规范,未启用服务端主动流的服务可以让 GET/DELETE 返回 405。
MCP 上的付款遵循 @x402/mcp 传输约定,而非 HTTP 标头:
- 未付款时调用付费工具,会返回
isError: true的工具结果。其structuredContent是 x402PaymentRequired对象,也会以 JSON 编码形式放在content[0].text中。 - 客户端进行签名,并在
params._meta["x402/payment"]中附上付款信息后重试。 - 服务端验证付款、运行工具,然后结算。收据位于
_meta["x402/payment-response"]中。
无效输入和被拦截的 URL 会在付款挑战发出之前被拒绝。工具执行失败(isError)的结果不会结算。
兼容性说明:付费工具有意不声明 outputSchema。MCP SDK 客户端即使在错误结果中也会根据 outputSchema 验证 structuredContent。这会导致 x402 付款挑战被拒绝;我们已在官方 TypeScript 客户端中观察到此问题。工具的输出结构会在工具描述和 /openapi.json 中说明。inspect_url 确实声明了 outputSchema。
使用官方客户端连接并付款(scripts/mcp-client-example.mjs):
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { wrapMCPClientWithPayment } from "@x402/mcp";
import { x402Client } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { toClientEvmSigner } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY);
const payments = new x402Client().register("eip155:84532", new ExactEvmScheme(toClientEvmSigner(account)));
const mcp = wrapMCPClientWithPayment(new Client({ name: "my-agent", version: "1.0.0" }), payments, {
autoPayment: true,
onPaymentRequested: async ({ paymentRequired }) => BigInt(paymentRequired.accepts[0].amount) <= 10_000n,
});
await mcp.connect(new StreamableHTTPClientTransport(new URL("http://localhost:8787/mcp")));
console.log((await mcp.listTools()).tools.map((t) => t.name)); // inspect_url, extract_url, extract_url_structured
const r = await mcp.callTool("extract_url", { url: "https://example.com/" });
console.log(r.paymentMade, r.paymentResponse?.transaction, r.content[0].text);
付费 MCP 工具还会在付款挑战中携带 x402 Bazaar 发现元数据(输入和输出架构),因此启用 Bazaar 的 Facilitator 可以将其编入目录。HTTP 路由不提供这些元数据:@x402/hono 2.28 使用 ajv 验证 Bazaar 路由元数据,而 ajv 会在运行时编译代码。Workers 禁止此操作,因此每个请求都会记录错误。HTTP 发现入口为 GET / 和 /openapi.json。
通用 MCP 客户端(IDE、桌面应用)可以连接到 /mcp 并使用 inspect_url。付费工具仅适用于实现了 x402 MCP 付款流程的客户端,例如 @x402/mcp。其他客户端会将付款挑战视为工具错误。不承诺与通用 Agent 框架兼容。
本地开发
要求:Node.js ≥ 20 和 npm。
npm install # see Troubleshooting if npm 10 fails with "edgesOut"
cp .dev.vars.example .dev.vars # set X402_PAY_TO to your testnet receiving address
npm run dev # wrangler dev on http://localhost:8787 (Base Sepolia, x402.org facilitator)
npm run smoke:local # health, inspect, 402, SSRF rejection, MCP discovery (no payment)
| 脚本 | 功能 |
|---|---|
npm run dev | 通过 wrangler dev 在本地运行 Worker(真实 workerd、真实出站网络) |
npm run typecheck | tsc --noEmit(严格模式) |
npm test | 通过 @cloudflare/vitest-plugin 在 workerd 中运行完整 Vitest 测试套件 |
npm run build | 通过 wrangler deploy --dry-run --outdir dist 生成生产构建包 |
npm run check | typecheck + test + build |
npm run smoke:local | 针对 AGENTREAD_URL 运行免费冒烟测试,包括零成本实时 Facilitator 检查(必须拒绝来自空钱包的签名付款,原因是余额不足) |
npm run smoke:testnet | 实时付费测试网测试(需要 EVM_PRIVATE_KEY) |
npm run smoke:mcp | MCP 发现 + 免费工具;如果设置了 EVM_PRIVATE_KEY,则调用付费工具 |
npm run cf-typegen | 编辑 wrangler.jsonc 后重新生成 worker-configuration.d.ts |
配置
非敏感配置存放在 wrangler.jsonc(vars,按环境配置)。机密信息使用 wrangler secret put(已部署)或 .dev.vars(本地)设置。客户端脚本设置见 .env.example。
| 变量 | 必填 | 示例 / 默认值 | 说明 |
|---|---|---|---|
ENVIRONMENT | 是 | development / testnet / production | 仅当环境为 production 时才接受主网网络;production 仅接受主网 |
X402_NETWORK | 是 | eip155:84532(Base Sepolia)/ eip155:8453(Base) | CAIP-2 |
X402_PAY_TO | 是 | 0x… | 公开收款地址。不接受零地址占位符 |
X402_FACILITATOR_URL | 是 | https://x402.org/facilitator(测试网)/ https://api.cdp.coinbase.com/platform/v2/x402 | 主网不接受 x402.org |
CDP_API_KEY_ID、CDP_API_KEY_SECRET | 机密信息,生产环境 | 使用 CDP facilitator 时必填 | |
PRICE_EXTRACT_USD | 是 | 0.003 | 美元十进制金额,最多 6 位小数,0 < 价格 ≤ 1 |
PRICE_EXTRACT_STRUCTURED_USD | 是 | 0.005 | |
DNS_CHECK | 否 | doh | doh = 对每一跳进行 DNS-over-HTTPS 预检查;off = 禁用 |
EXTRACTION_ENGINE | 否 | wrangler.jsonc 中为 lite(代码默认值为 readability) | lite = Workers AI toMarkdown(适用于免费计划);readability = linkedom + Readability(质量最佳,需要 Workers Paid)。需要 AI 绑定;如果没有该绑定,服务会回退到 readability,并记录 config.lite_engine_without_ai。GET /health 会报告当前使用的引擎 |
DOH_URL | 否 | https://cloudflare-dns.com/dns-query | |
FETCH_TIMEOUT_MS | 否 | 10000 | 总截止时间,包含重定向和响应正文 |
MAX_RESPONSE_BYTES | 否 | 3000000 | 内容提取的下载上限 |
INSPECT_MAX_BYTES | 否 | 65536 | 检查的下载上限 |
MAX_REDIRECTS | 否 | 5 | |
MAX_URL_LENGTH | 否 | 2048 | |
MAX_REQUEST_BODY_BYTES | 否 | 8192 | /mcp 允许的值为此值的 8 倍 |
DEFAULT_MAX_CHARACTERS / MAX_CHARACTERS_LIMIT | 否 | 20000 / 100000 | |
MAX_LINKS / MAX_HEADINGS | 否 | 200 / 100 |
支付配置无效时会采取默认拒绝策略。付费端点会返回 503,并记录原因(payments.misconfigured);免费端点仍可正常工作。速率限制使用 Workers Rate Limiting 绑定(每个 Cloudflare 位置按客户端 IP 计算:INSPECT_RATE_LIMITER 为 30 次/分钟,PAID_RATE_LIMITER 为 120 次/分钟)。请在 wrangler.jsonc 中调整这些值。
测试
npm test
所有测试都通过 @cloudflare/vitest-plugin 在 workerd(Workers 运行时)中运行,不会访问互联网、区块链或真实的 facilitator。
| 套件 | 覆盖范围 |
|---|---|
tests/unit/security.test.ts | URL 验证/规范化、约 50 种 SSRF 拒绝情形(环回地址、私有地址、链路本地地址、元数据、IPv6、映射 IPv4、十进制/十六进制/八进制 IP、通配符 DNS 主机、端口、凭据、协议)、IP 分类、DoH 防护 |
tests/unit/fetch.test.ts | 手动重定向、每一跳都重新验证、每一跳的 DNS 防护、重定向次数限制、超时、Content-Length 和流式大小限制、标头卫生 |
tests/unit/extraction.test.ts | Readability 提取、移除模板内容、Markdown(列表、表格、代码、链接)、元数据、标题/链接、截断、格式错误的 HTML、SPA 检测、字符集、真实场景结构回归 |
tests/unit/config.test.ts | 价格/网络/收款方/facilitator 验证、架构、错误规范化 |
tests/integration/payments.test.ts | 402 格式(金额、资产、网络、payTo)、402 前验证、伪造/垃圾/篡改/付款不足/收款方错误/重放的支付、完整的 @x402/fetch 流程、提取失败时不结算、结算失败时不提供内容、facilitator 故障 |
tests/integration/http.test.ts | 健康检查、发现、OpenAPI、inspect 变体、请求体/内容类型限制、速率限制、所有提取失败模式、截断、重定向、纯文本、旧版字符集 |
tests/integration/mcp.test.ts | 初始化、工具发现、免费工具、支付质询、付款前验证、伪造的 _meta、使用 @x402/mcp 自动支付、失败时不结算、结算失败 |
模拟内容与真实内容。 支付测试使用真实的 x402 SDK 进行端到端测试:
- 客户端使用
@x402/fetch和@x402/mcp,并通过一次性钱包生成真实的 EIP-712 签名。 - 服务端使用
@x402/hono和@x402/mcp。 - facilitator 使用真实的
x402Facilitator和@x402/evm的 exact 方案,因此签名、金额、收款方、到期时间和 nonce 检查均由 SDK 代码执行。
只有区块链是模拟的(tests/helpers/facilitator.ts),上游网页也一样(tests/helpers/upstream.ts)。npm test 不会覆盖真实网络上的实际结算。 请使用 npm run smoke:testnet 进行测试。
部署到 Cloudflare
先决条件:
- 一个 Cloudflare 账户。免费套餐即可使用,但需设置
EXTRACTION_ENGINE=lite,这是默认配置。只有在 Workers Paid(每月 $5)上才能切换到readability;请参阅提取引擎。 - 账户已启用 Workers AI(默认已启用)。文档说明 HTML→Markdown 转换免费;付费的图像描述功能已明确禁用。
npx wrangler login。
生产环境(Base 主网,真实 USDC)
# 1. Create a CDP API key: https://portal.cdp.coinbase.com (needed for the CDP facilitator)
npx wrangler secret put CDP_API_KEY_ID --env production
npx wrangler secret put CDP_API_KEY_SECRET --env production
# 2. Set your mainnet receiving address in wrangler.jsonc → env.production.vars.X402_PAY_TO
# (an address you control on Base; consider a dedicated wallet)
# 3. Deploy
npm run deploy:production # → https://agentread.<your-subdomain>.workers.dev
# 4. Verify health shows {"network":"eip155:8453"} and an unpaid request returns 402 with amount "3000"
日志:运行 npx wrangler tail --env production,或在控制面板中查看 Workers Observability(已在 wrangler.jsonc 中启用)。
自定义域名(稍后配置)
将域名添加到 Cloudflare 账户,然后在 env.production 下添加 "routes": [{ "pattern": "read.example.com", "custom_domain": true }] 并重新部署。支付要求会将请求 URL 用作 x402 resource,因此无需其他更改。
测试网支付演示
测试网可通过 npm run dev 在本地运行(Base Sepolia,x402.org facilitator)。已生成两个测试网钱包:收款方 0xDE20133E787A3cc64565b7AF1cF2bE3bEA3298Ea,以及付款方 0x7816FC23017c86aa917AdbD44367312696421322。两个私钥都保存在 .secrets/testnet-wallets.json 中;该文件已加入 gitignore,且仅文件所有者可访问。仅限测试网使用:切勿向这些地址发送主网资金。
- 在 https://faucet.circle.com 为付款方领取 Base Sepolia USDC(选择 Base Sepolia)。无需 ETH。
- 在本地运行付费流程:
echo "X402_PAY_TO=0xDE20133E787A3cc64565b7AF1cF2bE3bEA3298Ea" > .dev.vars npm run dev # in one terminal export EVM_PRIVATE_KEY=$(node -p 'require("./.secrets/testnet-wallets.json").payer.privateKey') npm run smoke:testnet && npm run smoke:mcp # in another (AGENTREAD_URL defaults to localhost:8787) - 在 https://sepolia.basescan.org/address/0xDE20133E787A3cc64565b7AF1cF2bE3bEA3298Ea 查看转账记录
可观测性
结构化 JSON 日志(console.log),不包含机密、付款载荷、签名或页面内容:
| 事件 | 字段 |
|---|---|
http.request | method、route、status、durationMs、paid(已结算) |
extract.success / extract.failure | op、host(仅主机名)、method 或错误代码、ms、chars |
mcp.extract.success / mcp.extract.failure | tool、host、method / code |
payment.settled / payment.settle_rejected | network、amount、公开交易哈希、payer(加盐 SHA-256 前缀,不是地址) |
payment.verify_rejected、payment.verify_error、payment.settle_error | reason / error class |
payments.misconfigured、config.invalid、http.internal_error | reason |
这些日志可用于了解请求量、成功率、失败原因、延迟、不同付款方和重复付款方数量(payer 哈希),以及验证 / 结算失败情况。可在 Workers Observability 中查询。
提取引擎
两种引擎使用相同的 API、架构和付款流程(EXTRACTION_ENGINE):
lite(默认) | readability | |
|---|---|---|
| 工作方式 | 通过子字符串检查选取内容根节点(#mw-content-text、.markdown-body、单个 <article>、<main>、[role=main])。将页面交给 Workers AI toMarkdown(转换在我们的 JS 之外运行)。使用少量正则表达式读取 <head> 中的元数据。逐行移除已知噪声 | linkedom DOM、移除冗余内容、Mozilla Readability、Turndown(GFM 表格) |
| 每个请求的 Edge CPU(实测真实页面) | p50 ≈ 10–13 ms,p90 ≈ 35–47 ms | p50 ≈ 70–80 ms,p75 ≈ 120–285 ms |
| Workers Free 计划下 | 约 180 个请求中 CPU 限制导致的终止次数为 0(5 次基准测试) | 33–84% 的请求被终止(错误 1102) |
| 质量 | 适用于文档、文章、Wikipedia 和 GitHub README。对于没有明确内容根节点的页面效果较差;某些 GitHub 代码块会丢失围栏;索引页噪声较多 | 整体效果最佳;内容评分可处理任意布局 |
| 额外依赖 | Workers AI toMarkdown(文档说明 HTML 转换免费) | 无 |
这些测量数据来自临时的 src/bench.ts Worker,由 .github/workflows/bench.yml 驱动;CPU 百分位数据来自 Cloudflare 的 GraphQL 分析(2026 年 10 月)。Free 计划的 CPU 限制并非简单地按每个请求 10 ms 截断:lite 的实测 CPU 用量超过 10 ms,但从未被终止。限制机制不透明,且可能发生变化;如果看到 1102 错误,请重新运行基准测试。当收入足以支撑每月 $5 的费用时,切换到 Workers Paid 和 EXTRACTION_ENGINE=readability。
定价和成本说明
默认价格:$0.003(提取)和 $0.005(结构化)。更改价格前,请考虑以下成本(2026 年 10 月价格,请核实当前价格):
- Cloudflare Workers:
- 使用
lite引擎时为 Free:每天 100k 个请求,$0。 - Paid(每月至少 $5,
readability所需)包含 10M 个请求和 30M CPU-ms;超出后每百万个请求收费 $0.30,每百万 CPU-ms 收费 $0.02。每次 Readability 提取约消耗 80 CPU-ms,因此额度用完后,每个请求的计算成本约为 $0.000002。
- 使用
- Workers AI
toMarkdown: 文档说明 HTML 转换免费。AgentRead 从不启用图片转换;该功能会使用付费 AI 模型。 - CDP facilitator(主网): 每月前 1,000 次结算免费,此后每次结算收费 $0.001。按 $0.003 的价格计算,这占收入的 33%;按 $0.005 计算则占 20%。这是主要的边际成本。SDK 支持批量结算,可降低这项成本(见后续步骤)。
- Gas: 在
exactEIP-3009 流程中,付款方只需签署授权。结算交易由 facilitator 提交,因此付款方不需要 ETH。CDP 声明,Gas 在链上支付,与其费用分开。请查看 facilitator 当前条款,确认是否会将 Gas 成本转嫁给用户。 - 浏览器渲染(暂缓):会增加按秒计费的浏览器成本;如需添加,应单独定价。
限制
- Lite 引擎质量(免费计划默认使用)在内容根节点不明确的页面上不如 Readability。部分 GitHub 代码块会丢失围栏。它依赖 Workers AI 的
toMarkdown对 HTML 免费开放。 - 不执行 JavaScript。 由客户端渲染的页面会返回
javascript_required(422,不收费)。inspect_url会通过mayRequireJavaScript标记可能需要 JavaScript 的情况。 - 支持的内容: 仅支持 HTML/XHTML、
text/plain和text/markdown。PDF、图片和 JSON 会被拒绝,并返回unsupported_content_type。 - 会遵守访问控制:绝不会转发 Cookie、身份验证标头或客户端凭据,也不会绕过付费墙和 CAPTCHA。
- SSRF: 具备强大的应用层控制和 Workers 网络隔离,但无法在应用层彻底消除 DNS 重绑定风险。请参阅 docs/security.md。此项目尚未经过独立安全审查或渗透测试。
- 支付:
- 不提供退款;除支付促进方和 EIP-3009 nonce 所提供的保证外,不保证恰好执行一次。
- 如果提取完成后结算失败,内容将不会提供(402)。
- 在少数情况下,如果支付促进方在广播后超时,转账仍可能上链,但内容不会送达。此情况已记录在 docs/payments.md 中。
- 速率限制按 Cloudflare 位置计算,且为近似值(Workers Rate Limiting API)。
- 仅配置了 Base 和 Base Sepolia,并通过
exact方案使用 USDC。 - 尚未完成真实资金支付。 真实支付促进方流程已验证至余额检查。请为生成的付款方钱包充值并运行
npm run smoke:testnet(见上文)。 - Bazaar 发现仅针对 MCP 工具声明,不适用于 HTTP 路由(SDK 验证使用 Workers 禁止的运行时代码生成)。
故障排除
| 症状 | 解决方法 |
|---|---|
npm install 失败,并显示 Cannot read properties of null (reading 'edgesOut') | 这是 npm 10.9 中与 vitest 对等依赖相关的已知 arborist 错误。请使用 npm 11:npx -y npm@11 install |
npm 11 警告 install-scripts … workerd | workerd 二进制通过可选平台包提供,无需运行脚本即可使用。如果 npx wrangler --version 失败,请运行 npx npm@11 install-scripts approve workerd |
付费端点返回 503 payments_unavailable | 支付配置无效。检查日志中的 payments.misconfigured(例如,X402_PAY_TO 为零地址、缺少 CDP 密钥,或生产环境使用了测试网) |
npx wrangler deploy 成功,但 workers.dev URL 超时 | 某些网络和 ISP 会屏蔽 Cloudflare 的 workers.dev 地址段(188.114.96.0/20)。请尝试其他网络、使用自定义域名,或运行 CI 冒烟测试任务(gh workflow run CI -f agentread_url=…) |
| 付款后仍返回 402 | 付款方可能未在正确的网络上持有 USDC,授权可能已过期(maxTimeoutSeconds: 120),金额可能对应另一个端点,或者载荷已被重放。payment.verify_rejected 日志会记录原因 |
javascript_required | 页面由客户端渲染。AgentRead 不会运行 JavaScript |
公共站点返回 blocked_destination | 该站点解析到非公共 IP,使用了非默认端口,或 URL 使用了 IP 字面量 |
提取失败并出现 1102 / CPU 限制错误 | 在免费计划上,请确保 EXTRACTION_ENGINE=lite,并且该环境中存在 AI 绑定(GET /health → "extractionEngine":"lite")。Wrangler 不会将绑定继承到 env.* 块中 |
wrangler dev 报告兼容日期有问题 | 请确保 compatibility_date 不晚于捆绑的 workerd 版本(npx wrangler --version) |
项目结构
src/
index.ts Worker entry
api/ Hono app, per-request context, discovery, OpenAPI
mcp/server.ts Stateless MCP server + x402 payment wrapper
payments/x402.ts Facilitator client, resource server, route pricing, logging hooks
extraction/ Bounded fetch, HTML cleaning, Readability, Markdown, operations
security/ URL policy, IP classification, DoH guard
schemas/ Versioned zod schemas (shared by HTTP, MCP, OpenAPI, tests)
config/env.ts Config loading/validation (fails closed)
utils/ Errors, structured logging
tests/{unit,integration,helpers,fixtures}
scripts/ Smoke tests and example clients (Node)
docs/ Architecture, payments, security, demo script
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。