AgentRead について
AgentReadは、公開ウェブページをLLMやエージェント向けの構造化Markdownとメタデータに変換するCloudflare Workerです。無料のURL検査と成功リクエストごとの有料抽出を提供し、Base上のUSDCによるx402決済に対応します。アカウントやAPIキーは不要です。
主な機能
- 01無料のURL到達性検査
- 02ページのMarkdown抽出
- 03メタデータとリンクの構造化抽出
- 04HTTP APIとリモートMCPアクセス
- 05x402によるリクエスト単位の決済
- 06URLと接続先の検証
活用シーン
- 01抽出前にページの到達性を確認
- 02エージェントに整形済みMarkdownを提供
- 03ページのメタデータ、見出し、リンクを抽出
- 04MCPクライアントからページ抽出を実行
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 エージェント向けのクリーンなウェブコンテンツを、リクエストごとに課金して提供します。
AgentRead は、公開ウェブページを LLM や自律型エージェント向けのクリーンで構造化された Markdown に変換する Cloudflare Worker です。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 Registry では 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/mcp など、x402 に対応したクライアントが必要です):
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を参照してください。エージェント向けドキュメント:/llms.txt、/openapi.json、/.well-known/x402。
ステータス:MVP、稼働中です。
- 本番環境(Base メインネット、実際の USDC): https://agentread.omar-cv.workers.dev。支払いは CDP ファシリテーターを介して
0xb866dD8C8401438Efb29b538ec4df11b6b407Ce6に決済されます。- テストネット: ホスト型のテストネットはありません。
npm run devは、Base Sepolia と無料の x402.org ファシリテーターを使ってローカルで実行されます。検証済みの項目:
- 自動テスト: すべて合格しています(237 件のテストを Workers ランタイム内で実行)。
- ディレクトリのチェック: どちらの有料エンドポイントも CDP Bazaar バリデーター(すべての事前確認)と AgentCash/x402scan の検出監査に合格し、x402scan に登録されています。
- ローカルのスモークテスト(9/9): ライブの x402.org ファシリテーターを使い、
wrangler devに対して実行しました。資金のないウォレットから実際に署名された支払いは、署名と要件の確認を通過したうえでinsufficient_balanceにより拒否されるため、送金そのものを除く実際の通信経路が証明されています。- 本番環境へのデプロイ: GitHub Actions(
gh workflow run CI)からすべてのスモークチェックに合格しています。ライブの CDP ファシリテーターは AgentRead を認証し、署名済みの支払いを検証します。開発用ネットワークがworkers.devの IP 範囲をブロックするため、CI の実行環境から確認しています。- Workers Free プランで稼働します。 デプロイ済みの
liteエンジンは、Cloudflare のエッジ(Free アカウント)で約 180 件のベンチマークリクエストを実行した際、CPU 制限による強制終了はありませんでした。一方、readabilityエンジンは最大 84% のリクエストで強制終了しました。抽出エンジンを参照してください。- デプロイ済みのエッジでは、まだ実施していません: プロダクトの Worker を介した有料の抽出。同じ抽出コードをエッジでベンチマークしましたが、資金のある支払いはまだ行われていません。
資金のある支払いはまだ行われていません(テストネットの支払いデモを参照)。利用する前に制限事項をお読みください。
エージェントが料金を支払う理由
生の HTML の大部分はノイズです。ナビゲーション、Cookie バナー、広告、スクリプト、スタイルなどです。ページを自分で取得するエージェントはトークンを無駄にし、HTML のクリーニング、SSRF 保護、文字コード検出、リダイレクト処理、サイズ制限を維持しなければなりません。AgentRead はこれらを一度に処理し、予測可能なバージョン管理された JSON を返します。
x402 を使うと、エージェントは自分のウォレットから、呼び出しごとに 1 セント未満の金額を支払えます。サインアップは不要で、認証情報も保存されません。抽出に失敗した場合、料金は請求されません。
これは検証すべき仮説であり、実証済みの市場ではありません。このサービスは検証に必要な数値を記録します(可観測性を参照)。
アーキテクチャ
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 は 1 つだけで、データベース、キュー、コンテナはありません。
- 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(サービスが検証に使用する zod スキーマから生成された OpenAPI 3.1)。
すべてのエラーは同じ形式です。
{ "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:無料
支払いの前に、到達可能性と抽出可能性を確認します。ダウンロードするのは最大 64 KB で、DOM は構築しません。タイムアウト、404、HTML 以外などの上流の問題は、200 のレスポンス本文で reachable: false または limitation として報告されます。無効な URL またはブロックされた 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 本文には人間またはエージェントが読める概要が含まれます。
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 は小数点以下 6 桁の USDC で $0.003 です。
有効な 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) のみ)。
ステータスコード(有料エンドポイント)
| ステータス | 意味 | 課金されるか |
|---|---|---|
| 200 | 抽出に成功し、支払いが確定しました | はい |
| 400 | 無効な入力/ブロック対象の宛先(402 が返される前に返されます) | いいえ |
| 402 | payment_required(未払い)、payment_invalid(拒否:details.reason にファシリテーターの理由が示されます。例: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 エラー、ファシリテーターのエラー/上流のタイムアウト | いいえ |
| 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 での支払いには、HTTP ヘッダーではなく @x402/mcp のトランスポート規約を使用します。
- 支払いなしで有料ツールを呼び出すと、
isError: trueのツール結果が返されます。そのstructuredContentは x402 のPaymentRequiredオブジェクトであり、content[0].textにも JSON としてエンコードされます。 - クライアントは署名し、
params._meta["x402/payment"]に支払い情報を含めて再試行します。 - サーバーは検証してツールを実行し、その後、支払いを確定します。レシートは
_meta["x402/payment-response"]に格納されます。
無効な入力とブロック対象の URL は、支払いチャレンジの前に拒否されます。失敗したツール結果(isError)は決済されません。
互換性に関する注意:有料ツールでは、意図的に outputSchema を宣言していません。MCP SDK クライアントは、エラー結果であっても structuredContent を outputSchema に照らして検証します。その場合、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 に対応したファシリテーターがカタログに登録できます。HTTP ルートには含まれません。@x402/hono 2.28 は ajv で Bazaar ルートのメタデータを検証しますが、ajv は実行時にコードをコンパイルします。Workers ではこれが禁止されているため、リクエストのたびにエラーがログに記録されます。HTTP の検出には GET / と /openapi.json を使用します。
汎用 MCP クライアント(IDE、デスクトップアプリ)は /mcp に接続して inspect_url を使用できます。有料ツールを利用できるのは、@x402/mcp など、x402 MCP 支払いフローを実装したクライアントのみです。それ以外のクライアントには、支払いチャレンジがツールエラーとして表示されます。あらゆるエージェントフレームワークとの互換性は保証していません。
ローカル開発
必要条件: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(strict) |
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 に対する無料のスモークテスト。コストなしのファシリテーター実地確認を含みます(空のウォレットからの署名済み支払いは、残高不足として拒否される必要があります) |
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 ファシリテーターを使用する場合に必要です | |
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(Free プランで動作)。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 を介して Workers ランタイムである workerd 内で実行されます。インターネット、ブロックチェーン、実際のファシリテーターには一切アクセスしません。
| スイート | 対象 |
|---|---|
tests/unit/security.test.ts | URL の検証/正規化、SSRF を拒否する約 50 件のケース(ループバック、プライベート、リンクローカル、メタデータ、IPv6、マッピングされた IPv4、10 進数/16 進数/8 進数の 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 | 価格/ネットワーク/受取先/ファシリテーターの検証、スキーマ、エラーの正規化 |
tests/integration/payments.test.ts | 402 の形式(amount、asset、network、payTo)、402 より前の検証、偽造/不正形式/改ざん/支払い不足/送金先違い/再利用された支払い、完全な @x402/fetch フロー、抽出失敗時に決済しないこと、決済失敗時にコンテンツを提供しないこと、ファシリテーターの停止 |
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を使用します。 - ファシリテーターには、
@x402/evmの exact スキームを使った実際のx402Facilitatorを使用するため、署名、金額、受取先、有効期限、ノンスのチェックには SDK のコードが使われます。
シミュレートされるのはブロックチェーン(tests/helpers/facilitator.ts)と上流のウェブページ(tests/helpers/upstream.ts)のみです。実際のネットワークでの決済は npm test では対象外です。 その場合は npm run smoke:testnet を使用してください。
Cloudflare へのデプロイ
前提条件:
- Cloudflare アカウント。デフォルトで設定されている
EXTRACTION_ENGINE=liteであれば、Free プランで動作します。readabilityに切り替えるのは Workers Paid($5/month)の場合のみです。抽出エンジンを参照してください。 - アカウントで Workers AI が有効になっていること(デフォルトで有効です)。HTML→Markdown 変換は無料と記載されています。有料の画像説明機能は明示的に無効化されています。
npx wrangler login。
本番環境(Base mainnet、実際の 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 ファシリテーター)。テストネット用のウォレットを 2 つ生成しました。受取先は 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、公開 tx hash、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 では無料と記載されています) | なし |
測定値は、.github/workflows/bench.yml から実行した一時的な src/bench.ts Worker によるものです。CPU パーセンタイルは Cloudflare の GraphQL 分析(2026 年 10 月)に基づいています。Free プランの CPU 制限は、リクエストごとの単純な 10 ms のしきい値ではありません。lite の測定 CPU 時間は 10 ms を超えていますが、強制終了されたことはありません。制限の仕組みは不透明で、変更される可能性があるため、1102 エラーが発生した場合はベンチマークを再実行してください。収益が月額 $5 の費用に見合うようになったら、Workers Paid と EXTRACTION_ENGINE=readability に切り替えてください。
料金とコストに関する注意事項
デフォルト料金:$0.003(extract)、$0.005(structured)。料金を変更する前に、以下のコストを考慮してください(2026 年 10 月時点。現在の料金を確認してください)。
- Cloudflare Workers:
liteエンジンでは Free:1 日あたり 100k リクエスト、$0。- Paid(最低月額 $5。
readabilityに必要)には 10M リクエストと 30M CPU-ms が含まれ、その後は 100 万リクエストあたり $0.30、100 万 CPU-ms あたり $0.02 です。Readability の抽出 1 回あたり約 80 CPU-ms の場合、無料枠を超えた後のコンピューティングコストは 1 リクエストあたり約 $0.000002 です。
- Workers AI
toMarkdown: HTML では無料と記載されています。AgentRead では、有料 AI モデルを使用する画像変換を有効にすることはありません。 - CDP facilitator(mainnet): 月あたり最初の 1,000 件の決済は無料で、それ以降は 決済 1 件あたり $0.001 です。料金が $0.003 の場合、これは 収益の 33%、$0.005 の場合は 20% です。これは限界費用の大部分を占めます。SDK がサポートする一括決済を使えば、この費用を削減できます(次のステップを参照)。
- Gas:
exactEIP-3009 フローでは、支払者は承認に署名するだけです。決済トランザクションはファシリテーターが送信するため、支払者に ETH は必要ありません。CDP によると、Gas は手数料とは別にオンチェーンで支払われます。Gas の費用をファシリテーターが転嫁するかどうかは、現在の利用規約を確認してください。 - ブラウザーでのレンダリング(保留中)を追加すると、概ね秒単位のブラウザー料金が発生するため、追加する場合は別途料金を設定してください。
制限事項
- Lite エンジンの品質(無料プランのデフォルト)は、コンテンツのルートが明確でないページでは Readability より劣ります。一部の GitHub のコードブロックではフェンスが失われます。HTML 用の Workers AI
toMarkdownが無料のままであることに依存しています。 - JavaScript は実行されません。 クライアント側でレンダリングされるページは
javascript_required(422、課金対象外)を返します。inspect_urlはmayRequireJavaScriptで該当する可能性のあるケースを示します。 - 対応コンテンツ: 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)。
exactスキームによる USDC を使う Base と Base Sepolia のみが設定されています。- 資金を使ったライブ決済はまだ行われていません。 ライブのファシリテーターパスは残高確認まで検証済みです。生成された支払い側ウォレットに資金を入れ、
npm run smoke:testnetを実行してください(上記を参照)。 - Bazaar による検出は MCP ツールのみを対象として宣言されており、HTTP ルートは対象外です(SDK の検証ではランタイムコード生成を使用しますが、Workers では禁止されています)。
トラブルシューティング
| 症状 | 対処方法 |
|---|---|
npm install が Cannot read properties of null (reading 'edgesOut') で失敗する | vitest のピア依存関係に関する、既知の npm 10.9 の 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 に解決される、デフォルト以外のポートを使っている、または IP リテラル形式の URL です |
抽出が 1102/CPU 制限エラーで失敗する | Free プランでは、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 サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。