mcpField
A
AgentRead

AgentRead

リモート
agentread.omar-cv.workers.devブラウザ自動化04 日前に更新
リポジトリ公式サイト

AgentRead について

AgentReadは、公開ウェブページをLLMやエージェント向けの構造化Markdownとメタデータに変換するCloudflare Workerです。無料のURL検査と成功リクエストごとの有料抽出を提供し、Base上のUSDCによるx402決済に対応します。アカウントやAPIキーは不要です。

主な機能

  1. 01無料のURL到達性検査
  2. 02ページのMarkdown抽出
  3. 03メタデータとリンクの構造化抽出
  4. 04HTTP APIとリモートMCPアクセス
  5. 05x402によるリクエスト単位の決済
  6. 06URLと接続先の検証

活用シーン

  1. 01抽出前にページの到達性を確認
  2. 02エージェントに整形済みMarkdownを提供
  3. 03ページのメタデータ、見出し、リンクを抽出
  4. 04MCPクライアントからページ抽出を実行

MCP 対応

ステートレスなStreamable HTTP MCPエンドポイントで、`inspect_url`、`extract_url`、`extract_url_structured`を提供します。有料ツールはx402 MCPの規約に従い、クライアントが支払いメタデータを送信すると決済レシートを返します。

設定

リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。

エンドポイントhttps://agentread.omar-cv.workers.dev/mcp
インストールコマンドをコピー
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。

ツール

詳細

AgentRead

AI エージェント向けのクリーンなウェブコンテンツを、リクエストごとに課金して提供します。

AgentRead は、公開ウェブページを LLM や自律型エージェント向けのクリーンで構造化された Markdown に変換する Cloudflare Worker です。HTTP API とリモート MCP サーバーの両方として公開されています。有料の操作は、成功したリクエストごとに x402 マイクロペイメント(Base 上の USDC)で課金されます。アカウント、API キー、サブスクリプションは不要です。

操作HTTPMCP ツール価格(デフォルト)
URL の検査POST /v1/inspectinspect_url無料
Markdown のクリーンな抽出POST /v1/extractextract_url$0.003
構造化抽出POST /v1/extract/structuredextract_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、exact EVM スキームには @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。不明なフィールドは拒否されます):

フィールド型デフォルト備考
urlstring必須絶対形式の公開 http(s) URL。デフォルトポートを使用し、認証情報と IP リテラルは不可。最大 2048 文字
maxCharactersinteger20000100 ~ 100000。Markdown は段落の区切りで切り詰められます
includeLinksbooleantruefalse の場合、リンクテキストは保持されますが、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 が返される前に返されます)いいえ
402payment_required(未払い)、payment_invalid(拒否:details.reason にファシリテーターの理由が示されます。例:invalid_exact_evm_insufficient_balance)、または payment_settlement_failedいいえ
413 / 415リクエストボディが大きすぎる/JSON ではないいいえ
422unsupported_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 のトランスポート規約を使用します。

  1. 支払いなしで有料ツールを呼び出すと、isError: true のツール結果が返されます。その structuredContent は x402 の PaymentRequired オブジェクトであり、content[0].text にも JSON としてエンコードされます。
  2. クライアントは署名し、params._meta["x402/payment"] に支払い情報を含めて再試行します。
  3. サーバーは検証してツールを実行し、その後、支払いを確定します。レシートは _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 devwrangler dev でローカル Worker を実行(実際の workerd、実際の外部ネットワークを使用)
npm run typechecktsc --noEmit(strict)
npm test@cloudflare/vitest-plugin を使用して workerd 内で Vitest の全テストを実行
npm run buildwrangler deploy --dry-run --outdir dist で本番用バンドルを作成
npm run checktypecheck + test + build
npm run smoke:localAGENTREAD_URL に対する無料のスモークテスト。コストなしのファシリテーター実地確認を含みます(空のウォレットからの署名済み支払いは、残高不足として拒否される必要があります)
npm run smoke:testnet実際の有料テストネットテスト(EVM_PRIVATE_KEY が必要)
npm run smoke:mcpMCP の検出と無料ツール。有料の呼び出しは EVM_PRIVATE_KEY が設定されている場合に実行
npm run cf-typegenwrangler.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いいえdohdoh = 各ホップを 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.tsURL の検証/正規化、SSRF を拒否する約 50 件のケース(ループバック、プライベート、リンクローカル、メタデータ、IPv6、マッピングされた IPv4、10 進数/16 進数/8 進数の IP、ワイルドカード DNS ホスト、ポート、認証情報、スキーム)、IP 分類、DoH ガード
tests/unit/fetch.test.tsリダイレクトの手動処理、すべてのホップでの再検証、各ホップでの DNS ガード、リダイレクト上限、タイムアウト、Content-Length とストリーミングのサイズ上限、ヘッダーの衛生管理
tests/unit/extraction.test.tsReadability による抽出、ボイラープレートの除去、Markdown(リスト、表、コード、リンク)、メタデータ、見出し/リンク、切り詰め、不正な HTML、SPA の検出、文字セット、実際の構造に関するリグレッション
tests/unit/config.test.ts価格/ネットワーク/受取先/ファシリテーターの検証、スキーマ、エラーの正規化
tests/integration/payments.test.ts402 の形式(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 対象で、所有者のみがアクセスできます。テストネット専用です。これらのアドレスにメインネットの資金を絶対に送らないでください。

  1. https://faucet.circle.com で支払者に Base Sepolia USDC を入金します(Base Sepolia を選択)。ETH は不要です。
  2. 有料フローをローカルで実行します。
    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)
    
  3. https://sepolia.basescan.org/address/0xDE20133E787A3cc64565b7AF1cF2bE3bEA3298Ea で送金を確認します。

オブザーバビリティ

秘密情報、決済ペイロード、署名、ページの内容を含まない、構造化された JSON ログ(console.log)です。

イベントフィールド
http.requestmethod、route、status、durationMs、paid(決済完了)
extract.success / extract.failureop、host(ホスト名のみ)、method またはエラーコード、ms、chars
mcp.extract.success / mcp.extract.failuretool、host、method / code
payment.settled / payment.settle_rejectednetwork、amount、公開 tx hash、payer(ソルト付き SHA-256 のプレフィックス。アドレスではありません)
payment.verify_rejected、payment.verify_error、payment.settle_errorreason / error class
payments.misconfigured、config.invalid、http.internal_errorreason

これらのログから、リクエスト数、成功率、失敗理由、レイテンシ、一意の支払者とリピーター(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 msp50 ≈ 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: exact EIP-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

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。