mcpField
X
x402 Merchant Check

x402 Merchant Check

認証済みおすすめリモート
mcp.gradientdecisions.com金融データ05 週間前に更新
リポジトリ公式サイト

x402 Merchant Check について

x402 Merchant Checkは、Agentが支払い前に不慣れなx402加盟店を評価するためのMCPサービスです。観測可能なオンチェーンの行動に基づく機械可読な情報を返しますが、安全を保証するものではありません。

主な機能

  1. 01オンチェーンの加盟店情報確認
  2. 02信頼性と価格の参考情報
  3. 03BaseとSolanaに対応
  4. 04Base x402による有料チェック

活用シーン

  1. 01Agentの支払い前に加盟店を確認
  2. 02利用可能なデータで価格を比較

MCP 対応

`https://mcp.gradientdecisions.com/mcp`で、有料ツール`check_merchant`をMCPクライアントに提供します。チェック料金はBase上のx402で$0.01です。

設定

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

エンドポイントhttps://mcp.gradientdecisions.com/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 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。

ツール

1
パラメーター型必須説明
assetstringいいえ任意。402 レスポンスの accepts[].asset にある決済アセット/トークンです。スコア評価には不要です。
pricenumberいいえ妥当性を確認する場合の、米ドル相当の提示価格
amountstringいいえ任意。`price` の代わりに使用する場合は、402 レスポンスの accepts[].amount にある最小単位の決済額を指定します。スコア評価には不要です。
networkstringいいえ任意。402 レスポンスの accepts[].network にある CAIP-2 ネットワーク ID(例: "eip155:8453")です。すでに分かっている場合に指定してください。スコア評価には不要です。
service_urlstringいいえ任意。エージェントがアクセスしようとしていたリソース/サービスの URL です。スコア評価には不要です。
resource_typestringいいえ非推奨ですが、受け付けられます。使用されません。価格の妥当性は、商店自身のオンチェーンデータから自動的に判定したカテゴリと比較されるため、この項目を指定する必要はありません。
merchant_wallet_addressstringはい販売者の受取ウォレットアドレスです。Base(0x...)または Solana(base58)のアドレスを指定します。通常は 402 Payment Required レスポンスの accepts[].payTo から直接抽出できます。

詳細

merchant-check-mcp

Gradient Decisions は、自律型コマース向けの加盟店インテリジェンスを提供します。 x402 Merchant Check は、オンチェーンで観測できる決済行動を評価し、エージェントが不慣れな x402 加盟店への支払い前に、より多くの情報に基づいて判断できるようにします。エージェントネイティブ、機械可読、x402 ネイティブです。x402 で支払う 1 回あたり 0.01 ドルです。これは支払い前の判断を支援する基本要素であり、認定、安全性の保証、またはエージェント独自の決済ポリシーの代わりとなるものではありません。発見 → チェック → 判断 → 支払いの全体的な流れ、402 レスポンスから入力へのマッピング、正確なレスポンス形式については、INTEGRATION.md を参照してください。データの取り扱いについては、プライバシーポリシーを参照してください。このファイルでは、実際に実装済みの機能、スタブのままの機能、今後進めるうえでユーザーの対応が必要な事項を記録しています。

プライバシーポリシー

gradientdecisions.com/privacy(src/privacy.ts)で公開されており、ホームページのフッターとこのファイルからリンクされています。2026-08-13 に、直接の依頼を受けた Claude が、このシステムの実際のデータ運用(query_log と merchant_signals に実際に保存される内容。「内部呼び出し元トラッキングダッシュボード」の項を参照してください)に基づいて作成したもので、汎用的な定型文ではありません。弁護士によるレビューは受けていません。ページ自体にもその旨が記載されており、レビューが行われるまではこの注意書きを残す必要があります。連絡先メールアドレスは [email protected] です(開発者の個人アドレスから 2026-08-19 に変更。「データアクセス方針」の項で理由と方法を説明しています)。再度変更する必要がある場合、src/privacy.ts の CONTACT_EMAIL 定数を正とします。

DISCOVER → IDENTIFY PAYMENT DESTINATION → GRADIENT MERCHANT CHECK → AGENT PAYMENT POLICY → X402 PAYMENT

ステータス:2 つの公開窓口、1 つのプロダクト

  • エージェント向け:https://mcp.gradientdecisions.com/mcp — 有料の check_merchant MCP ツールです。実際の USDC を使って Base メインネットで稼働中です(Base Sepolia テストネットから切り替え済み。「メインネットへの移行」を参照してください。query_log で決済の実際の確定を確認済みで、Base 上に実際のトランザクションハッシュがあります)。加盟店の対象範囲は Base と Solana(PayAI/Helius)の両方です。「Solana データソース」を参照してください。決済フローはエンドツーエンドで検証済みです。無料の tools/list による検出 → tools/call が正しく 402 を返す → エージェントが x402 決済を作成して署名し、送信する → ファシリテーターが決済を確定する → 実際のティアが返されます。詳しくは、以下の「自分で試す」を参照してください。
  • 人間向け:https://gradientdecisions.com — 2026-08-19 時点では、最小限の静的プレースホルダー(src/homePlaceholder.ts)であり、稼働中のダッシュボードではありません。以前はスコアリング済みのすべての加盟店について、推奨事項/シグナル/料金を直接表示し、同じデータセットを /api/wallets で未加工の JSON として提供していました。これをロックダウンした理由については、以下の「データアクセス方針」を参照してください。再設計後の本格的なホームページ/ダッシュボードは、Figma のモックアップを基に構築する別途後続のタスクであり、このプレースホルダーとは別のものです。

注:上記の 2 つのチェーンにまたがる加盟店シグナルのデータは、このエンドポイント自体の決済レールとは別です。check_merchant 自体は引き続き Base x402 経由でのみ有料です(上記の「決済フロー」を参照)。この点に変更はありません。

データアクセス方針(2026-08-19)

構造化されたマーチャントデータ — recommendation、signals、pricing、 reasons、platforms、check_merchant が返すあらゆるデータ — は、 x402 によるクエリごとの従量課金で利用する有料の check_merchant MCP ツールを通じてのみ利用できます。以前はそうではなかったため、この変更を明確に記録しておきます。

  • / と /dashboard は以前、D1 から直接取得した、スコアリング済みのすべての マーチャントと、それぞれのレコメンデーション、シグナル、理由、価格を含む 完全なライブダッシュボードを表示していました。現在は、D1 へのクエリを一切 行わない静的なプレースホルダー(src/homePlaceholder.ts)になっています。
  • /api/wallets は以前、同じデータセットを未加工の JSON として提供していました。 レンダリングもスロットリングもなく、簡単にスクリプトから利用できました。 現在は、すべてのメソッドとサブパス(/api/wallets/、?query 文字列、HEAD、 OPTIONS、POST、……)に対して 404 を返し、Cache-Control: no-store を設定し、 CORS ヘッダーを付与しません。
  • /merchant/<address>(2026-08-18 に追加されたマーチャントごとのプロフィールページ。 Git の履歴を参照)は、同じ理由で同様に廃止しました。このページは特定の ウォレットの同じフィールドを返していましたが、check_merchant を呼び出す エージェントは、そのページを無料で読む代わりに料金を支払うために必要な アドレスをすでに持っているためです。将来、有料版またはキュレーション版で 再利用する可能性に備え、コードは src/merchantProfile.ts に未使用のまま残し、 削除していません。

これら3つの機能が存在していたのは、check_merchant がクエリあたり $0.01 を 課金する同じデータを、人間やエージェントが無料で読み取れるようにすると、 プロダクトの価値を損なうためです。check_merchant 自体(src/tool.ts)には 一切変更を加えていません。これまでも常に、上記のいずれかのルート経由ではなく、 src/db/queries.ts(getMerchantSignals、getComparablePrices、getOwnPrices)を 通じて D1 から直接データを取得していました。そのため、今回の変更で有料ツールの ロジックやスキーマに手を加える必要はありませんでした。

連絡先メールアドレス。 プライバシーポリシーの CONTACT_EMAIL (src/privacy.ts)は [email protected] です。以前このファイルや プライバシーポリシーに記載されていた開発者個人の Gmail アドレスに代わり、 こちらを設定しました。これは、同じ個人用受信トレイに転送する Cloudflare Email Routing の転送先として設定されています。info@ 宛ての メールは既存の Gmail に届きますが、個人用アドレス自体は、公開される場所にも コミットされる場所にも表示されなくなりました。Email Routing は受信と転送のみ に対応しており、[email protected] としてメールを送信することは できません。送信者アドレスとして使う必要が生じた場合は、別途手作業での設定 (Google Workspace またはカスタム SMTP)が必要です。Email Routing では対応できません。

有効にする方法(Cloudflare API トークンがなく API 経由で設定できないため、 Cloudflare ダッシュボードを使用します。このセクションを含む PR/コミットの 「Manual steps」注記を参照してください):

  1. Cloudflare ダッシュボード → gradientdecisions.com ゾーン → Email → Email Routing。
  2. ゾーンで Email Routing を有効にします。Cloudflare が必要な MX レコード (Cloudflare 自身のメールサーバーにルーティングするもの)と TXT レコード (v=spf1 include:_spf.mx.cloudflare.net ~all。既存の SPF レコードがある場合は そこに統合)を自動的に追加するため、手動で DNS レコードを追加する必要は ありません。Cloudflare がすでに DNS を管理しているゾーンではこの方法が使えます (このゾーンでは wrangler.toml の custom_domain = true により、すでに Cloudflare DNS が設定されています)。
  3. ルーティングルールを追加します:[email protected] → Destination address → 既存の個人用 Gmail アドレス。
  4. 送信先として初めて Gmail アドレスを追加すると、Cloudflare がそのアドレスに 確認メールを送信します。リンクをクリックするには人の操作が必要で、 プログラムからは実行できません。クリックするまでは、info@ 宛てのメールは 実際には転送されません。

追加の決済プロトコル(2026-08-19 に調査)

Cloudflare の Agent Readiness「Commerce」チェックリスト(ACP、AP2、 MPP、UCP、x402)をきっかけに調査しました。ユーザーは、どのプロトコルが 使われるかは気にせず、エージェントが実際に支払いできることだけを重視すると 明言していたため、各プロトコルの仕様を実際に確認してから判断しました。

実装済み:GET /check(src/httpCheckEndpoint.ts)— check_merchant の プレーン HTTP 版 x402 です。/mcp とまったく同じファシリテーター/決済確定の 経路を再利用します。判断の詳しい理由は、このファイルのモジュールコメントを 参照してください。要点として、MCP 経由の 402 チャレンジは JSON-RPC レスポンス 本文の中にあるため、Cloudflare 自身のスキャナーを含む、単純な GET だけを送る プレーン HTTP クライアントには見えません。

調査済み、未実装:

  • ACP(Agentic Commerce Protocol — OpenAI/Stripe)と UCP (Universal Commerce Protocol — Google/Shopify/Etsy/Walmart):どちらも カートの作成、委任決済トークン、税金/配送、Google Pay などの認証情報 プロバイダーを扱う、総合的な小売決済の標準です。従量課金 API 呼び出しへの 支払いではなく、カタログにある商品を購入するために作られています。実際の 仕様(agenticcommerce.dev、developers.openai.com/commerce、 orkes.io/blog/universal-commerce-protocol-ucp-explained)で確認したところ、 形状の不一致は構造上のものであり、実装作業を増やせば解決するものでは ありません。

  • AP2(Agent Payments Protocol — Google/PayPal):Cloudflare の スキャナーが示唆するように A2A だけでなく、実際に MCP をトランスポートとして サポートしています。中核となる仕組みは、暗号署名された「mandates」です。 これは、ユーザーがエージェントに委任した購入権限を検証可能な形で証明する ものです。実用的で興味深いプロトコルですが、解決するのは、ユーザーに代わって エージェントが買い物をする際の委任同意という別の課題です。このサービスの モデルは、ユーザーによる委任レイヤーを必要とせず、エージェントが直接支払う ものです。同じセッションで追加する範囲ではなく、独立したプロジェクトとして 取り組むべきです。

  • MPP(Machine Payments Protocol — Tempo/Stripe、mpp.dev):概念上は 最も適合しています。公式ドキュメントでも、「API 呼び出しへの支払い…… データへのアクセス権の購入」向けと説明されており、その usdc 決済方式では、 x402 の exact スキームがすでに使用しているものと同じ署名プリミティブ、 EIP-3009 transferWithAuthorization を使います。障壁となるのは、MPP の 仕様にファシリテーターモデルがないことです。Coinbase の CDP ファシリテーターが秘密鍵をこちらで扱うことなく送信と決済確定を行う x402 とは 異なり、MPP では加盟店自身のサーバーがオンチェーンでトランザクションを送信し、 ガス代を直接支払うことを想定しています。実際に実装するには、次の作業が 必要です。

    1. ガス代用の ETH を入金した専用 EVM ウォレットを用意し、新しい秘密情報として 保管します。x402 では署名鍵をこちらで保管しないのとは異なり、残高を維持する という実質的に新たな運用責任が生じます。
    2. サーバー側のリレイヤーコードを用意します。Authorization: Payment 認証情報を解析し、RPC プロバイダー経由で transferWithAuthorization 呼び出しを送信し、確認をポーリングし、再試行や失敗に対応します。
    3. リリース前に、正式なセキュリティレビューを実施します。これは直接保管で 資金を動かすコードであり、ファシリテーターを介する x402 のモデルとは 根本的に異なります。
    4. 既存の x402 ヘッダーに加え、WWW-Authenticate: Payment / Authorization: Payment ヘッダーを処理します(RFC draft-ryan-httpauth-payment)。

    これはウォレットへの入金、リレイヤー、監視、セキュリティレビューを伴う複数日の インフラプロジェクトであり、設定変更ではありません。推奨事項: GET /check または /mcp のトラフィックから実際の自然発生的な需要が確認 できた段階で再検討してください。現時点で query_log に記録されている自然な 有料利用はほぼゼロであり(呼び出し元の分析を参照)、今のところ決済手段を もう一つ追加する根拠はありません。これは取り下げた案ではなく、正式な ロードマップ項目として記録しています。

カテゴリ分類

信頼度ティアのスコアリングに追加するもので、src/scoring.tsには触れません。すべての マーチャントに、固定された6つの値のいずれかからcategoryが割り当てられます (src/categorize/types.ts:data_api、compute、content_generation、 financial_data、storage、other)。このセットにない値が作られることはありません。 パイプラインで確信を持って分類できないものはotherに分類され、推測せず、 スポットチェック用にcategory_review_logに記録されます。

2段階で処理します。初回取り込み時にウォレットごとに1回実行されます(2時間ごとの 信頼シグナル更新とは別です。説明文が変わった可能性に備えて全件を再実行する 月次Cronが別途あり、詳細はwrangler.tomlを参照してください)。

  1. ルール(src/categorize/rules.ts) — Bazaarのリスティングテキストに対する キーワード照合です。カテゴリがちょうど1つ一致した場合のみ、確信を持って分類されたと みなします。一致が0件、または複数件(一意に決まらない)の場合は、第2段階に進みます。
  2. モデル(src/categorize/model.ts) — 固定カテゴリの一覧と説明文を Claude Haiku(claude-haiku-4-5-20251001)に渡し、値を1つだけ返すよう指示します。 応答は使用前に固定セットと照合して検証されるため、盲目的に信頼することはありません。 解析できないものはすべてotherに分類され、記録されます。

ANTHROPIC_API_KEY(wrangler secret put ANTHROPIC_API_KEY)は 第2段階で使用されます。これがない場合、ルールで一致しなかった説明文はすべて そのままotherに分類されます(黙って推測するのではなく、 other_model_unavailableとして記録されます)。2026-08-11に設定し、動作を確認しました。 POST /categorize(管理者トークンによる認証が必要で、/refreshと同じパターン)が 未処理分を処理します。?force=trueを指定すると全件を再分類し、 ?limit=Nを指定すると1回の呼び出しで処理する件数を制限します(デフォルトは200件)。 数百のウォレットすべてを強制再実行すると1回のWorker呼び出しの実行時間を超える可能性があるためです (手動で4回に分けて各100件処理し、確認済み)。

本番環境で実際に実行して初めて見つかった実際のバグが2件ありました。コードの確認だけでは見つかりませんでしたが、両方とも修正して再デプロイ済みです。

  • ルール照合で単純な部分文字列チェックを使っていたため、 "computer vision"の中の"compute"が誤って一致し、 単独の"search"が"research"の中でも一致する状態でした。実際に、 2件のリスティング(「Tavily Search」と「Exa /search endpoint」)は、本来ならルールで確実にdata_apiに分類されるはずが、 第2段階に進んだ結果、モデルによってcontent_generationに分類されていました。 .includes()ではなく単語境界の正規表現で照合するよう修正しました。
  • runCategorizationのremaining件数はforce=trueの場合に誤っていました。 このモードのWHERE句では処理済みの行を除外しないため、単純に再集計すると毎回合計件数が 報告されます。実際には異なるウォレットを処理している4回のバッチ後も同じ件数が 報告されるのを見て、問題に気づきました(category_updated_atの分布で確認済み)。 各実行前にタイムスタンプを記録し、それより古い行を数えるように修正しました。

現在の本番環境での分布(2026-08-11、実在するマーチャント375件):data_api 145件、 other 93件(現在は実際に分類が曖昧なもの。「モデルを利用できない」ためではありません)、 financial_data 59件、content_generation 45件、compute 21件、storage 4件、 正当に未分類のものが8件あります(前回の更新後にBazaarから削除された古いリスティング。 設計上、変更していないものであり、バグではありません)。

自分で試す

npm run demo

2026-08-11 時点で古くなっています — デプロイ済みサーバーが現在受け付けるのは Base mainnet のみです (X402_NETWORK = eip155:8453。「Going to mainnet」を参照してください)。このスクリプトは引き続き Base Sepolia で支払うため、本番環境のデプロイでは失敗します。ローカルでテストネット設定のブランチを試す人向けに、参考用として残しています。実際に動作する本番環境でのテストについては、以下の「Mainnet live payment test」を参照してください。

.env.demo にある使い捨てキーペアを使用します(Git で無視され、テストネット専用で、実際の価値はありません)。「insufficient balance」の段階を通過し、テストネットのデプロイで実際に支払い済みの trusted/avoid レスポンスを確認するには、次の手順に従ってください。

  1. 支払者のアドレスを確認します。.env.demo の DEMO_PAYER_ADDRESS にあります (0x9AaF5bB90307bacb9cB60f54c1be2B65B0771282)。
  2. Base Sepolia のテスト用 USDC を入金します。 faucet.circle.com(Base Sepolia を選択してください)。
  3. npm run demo を再実行します。このデモ用に D1 に直接挿入された 2 つのシード済みウォレット (0x1111...11d1/0x2222...22d2。「Demo data」を参照してください)は、それぞれ trusted と avoid として返され、実際の決済トランザクションハッシュが表示されるはずです。

Mainnet live payment test

実際のお金がかかります。 scripts/mainnet-live-test.ts は、本番環境の mainnet デプロイに対して実際に 0.01 ドルの x402 決済を 1 回行います。これは、ファシリテーターの設定が正しく解決されること(支出なしで別途すでに検証済み)だけでなく、決済がエンドツーエンドで本当に機能することを証明するものです。意図的に npm run demo やその他のデフォルトコマンドには組み込んでいません。明示的な npm run mainnet-test でのみ実行され、ローカルの .env.mainnet-test ファイルを通じて指定した秘密鍵が必要です。このファイルは Git で無視され、マシンから外部に出ることはありません(このプロジェクトの他のすべてのシークレットと同じ方式です。こちらで生成、保管、使用することはありません)。

cd "/Users/colincleven/Documents/merchant-check-mcp"

# 1. Generate a fresh throwaway keypair (runs locally, nothing sent anywhere)
PATH="/Users/colincleven/.nvm/versions/node/v24.15.0/bin:$PATH" node --input-type=module -e "
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
const k = generatePrivateKey();
const a = privateKeyToAccount(k);
console.log('address:', a.address);
console.log('private key:', k);
"

# 2. Save the private key locally (paste the value the command above printed)
cat > .env.mainnet-test << 'EOF'
MAINNET_PAYER_PRIVATE_KEY=paste_the_private_key_here
EOF

# 3. Send a small amount of real USDC on Base mainnet (e.g. $0.05) to the
#    "address:" printed in step 1, from your own wallet/exchange.

# 4. Run the real test
PATH="/Users/colincleven/.nvm/versions/node/v24.15.0/bin:$PATH" npm run mainnet-test

デフォルトでは、ライブデータセットにある実際の trusted-tier の加盟店(0xffc458db291b4abce020fe3de4f91f2770e537b1)を確認します。TEST_MERCHANT_WALLET=0x... npm run mainnet-test で変更できます。成功すると実際のトランザクションハッシュと BaseScan のリンクが表示されます。

Demo data

merchant_signals には、wrangler d1 execute --remote を使って直接挿入した合成データの行が 2 つあります。アドレスは明らかな偽物で(0x11111111111111111111111111111111111111d1、 0x22222222222222222222222222222222222222d2)、実際の加盟店ではありません。実際の avoid の例がまだないため、残してあります(以下の「Still needed」を参照してください)。これらには is_demo = 1 と network = 'eip155:84532'(Base Sepolia — デモ/テストの決済フローが実際に使うネットワーク。実際のすべての BazaarDataSource の行で使われる Base mainnet の eip155:8453 とは異なります)が設定されています。2 つの列を分けているのは意図的です。is_demo は偽の行を示し、network は実際の行がどのチェーンに由来するかを示します。防止すべき問題(合成データと、実際のテストネットデータが mainnet のデータセットに混入すること)が異なるため、1 つのフラグにまとめず、両方を明示しています。現在は両方のフィルター(WHERE is_demo = 0 AND network = 'eip155:8453')で同じ結果が返されますが、実際のデータソースでテストネット上のアクティビティを観測できるようになった時点で変わります。これらは公開ダッシュボードと /api/wallets(src/dashboard.ts)から除外されています。gradientdecisions.com に表示されるのは実際の mainnet データのみです。ただし、check_merchant 自体は引き続きこれらを参照するため、npm run demo では 3 つすべてのティアを引き続きテストできます。(以前のバージョンでは、この 2 つのアドレスは 40 文字ではなく 38 文字の 16 進数でした。isValidWalletAddress 自身の正規表現で拒否されたため、決済は正常に完了していても、デモ呼び出しはすべて「not a valid EVM address」を返していました。アドレスの長さは目視ではなくプログラムで確認してください。git の履歴を参照してください。)

デプロイ中に見つかり、修正された既知の問題

src/index.ts の createPaymentWrapper 設定にある resource.serviceName は、印字可能な ASCII 文字のみ(em ダッシュ不可)で、32 文字以下で なければなりません。@x402/core の ResourceInfoSchema はそれ以外を ZodError で拒否します。その結果、クライアント側で支払いが必要な状態の 検出がひそかに機能しなくなっていました(不正なレスポンスは、自動支払いの 対象として検討する価値のあるものではなく、単なる何も起きないエラー結果に 見えていました)。ドキュメントではなく、本番デプロイに対するテストで 見つかりました。ほかの場所でリソースのメタデータを追加する場合は、 知っておくとよいでしょう。

残っていた一度限りのアカウント設定(完了)

古い見出しです。履歴のために残しています。 このセッション中、すべての デプロイで失敗していた workers.dev サブドメインのブロッカー (cron トリガーをアタッチする前にアカウントでサブドメインが必要だと wrangler が報告)は、2026-08-18 時点で解決しています。Cloudflare ダッシュボードの Workers セクションを一度開くと、サブドメインが プロビジョニングされました。実際の wrangler deploy で確認済みです。

schedule: 0 */4 * * *
schedule: 0 0 1 * *

両方の cron トリガーが、問題なくアタッチされるようになりました。上記の デプロイ出力時点では、更新ワーカーは 4 時間ごとに実行されていました。 2026-08-19 に 2 時間ごとに変更されています(現在は schedule: 0 */2 * * *。 wrangler.toml を参照してください)。月次の強制再分類は毎月 1 日に 実行されます。これに気づいて修正するまで、データは 6 日以上古い状態に なっていました。cron が動作していても、ほかの定期ジョブと同様に、 ホームページの lastRefreshedAt を定期的に確認するとよいでしょう (/api/wallets は廃止済みです。「データアクセスポリシー」を 参照してください)。

手動更新トリガー

cron がアタッチされたため、もう必須ではありません。ただし、2 時間ごとの 実行間隔外にオンデマンドで更新する場合には、引き続き便利です (共有シークレットヘッダー X-Admin-Token):

curl -X POST https://mcp.gradientdecisions.com/refresh \
  -H "X-Admin-Token: <your ADMIN_TOKEN>"

内部呼び出し元追跡ダッシュボード

2026-08-13 に追加されました。公開されている加盟店スコアリング ダッシュボードとは別のものです。GET /admin/callers(/refresh/ /metrics と同じ X-Admin-Token によるアクセス制限。生の JSON を 取得するには ?format=json を、ヘッドライン統計の対象期間を変更するには ?window=<seconds> を追加します。デフォルトは 7 日間)は、どの加盟店の スコアが高いかではなく、check_merchant 自体の利用状況、つまり誰が、 どのくらいの頻度で、何をチェックしているかを追跡します。公開サイトからは リンクされていません。

目的(この機能を追加したリクエストからの原文):「この呼び出し元データは、 購入意思が確認できる正確なデータです(加盟店をチェックするために支払う ウォレットは、実際の購入を積極的に検討しています)。加盟店のスコアリング だけでなく、将来的に買い手側の行動セグメンテーションに活用することを 目的としています。」 このセグメンテーションはここでは実装されていません。 ここで提供するのは、追跡/集計の基盤(期間ごとのユニークな呼び出し元数、 単発スクリプトと繰り返し利用する/アクティブなエージェントを区別するための ウォレットごとのクエリ頻度、チェック対象のカテゴリ分布、30 日間の保持)のみ です。セグメンテーションのロジックは、明示的に今後の作業としています。

このプロジェクトのほかの部分と同じく、本人確認を行わない原則に従います。 この画面のすべてのビューは payer_address(支払いを行ったオンチェーンの ウォレット)をキーとしており、オフチェーンの本人情報との照合は行いません。 設計に関するコメント全文は src/callerDashboard.ts を参照してください。

実装メモ:これは新しいテーブルを作るのではなく、主に既存の query_log (/metrics 用にすでに存在)を拡張しています。追加したのは新しい列 2 つで、 並行するログ記録経路ではありません。queried_category(レスポンスに含まれる チェック対象の加盟店カテゴリ)と、caller_supplied_price_atomic (実際に呼び出し元が指定した値。input.price から取得)を追加しました。

フェーズ 0 の解決(スタックの互換性)

Cloudflare 公式ドキュメントの確認に加え、実際のパッケージをインストールして型チェックを行い、次の点を確認しました(説明を読んだだけではありません)。

  • MCP トランスポート:@modelcontextprotocol/sdk の WebStandardStreamableHTTPServerTransport — fetch()/Request/Response ベースのトランスポートで、JSDoc 自体に Cloudflare Workers の使用例が含まれています。Workers 上でネイティブに動作し、ポリフィルは不要です。
  • 決済:@x402/core + @x402/evm + @x402/mcp — 公式の x402-foundation/Coinbase パッケージです(プロトコル仕様と同じ公開元です)。 x402-hono は使用せず、Cloudflare 独自の agents パッケージも使用しません — 詳細は下記の「x402-hono を使用しない理由」と「Cloudflare の agents パッケージを使用しない理由」を参照してください。
  • データストア:D1(要件に基づく) — 支払い元ウォレットと価格観測値のリレーショナル結合には、KV のキー・バリューモデルでは不十分です。

結果として、Vercel/Railway に切り替える必要はありませんでした。Cloudflare Workers で両方の処理に対応できます。

x402-hono を使用しない理由

x402-hono(および一般にフレームワークのミドルウェア群)は、HTTP の ルート単位でアクセスを制限します。MCP では、initialize、tools/list、 tools/call など、すべての JSON-RPC メソッドが単一の POST エンドポイントに配置されます。 ルート全体を制限すると、tools/list までペイウォールの対象となり、エージェントによる検出が機能しなくなります(要件に記載された GTM 計画は、エージェントが支払いを決める前にツールの説明を読めることを前提としています)。一方、@x402/mcp の createPaymentWrapper は 特定のツールハンドラーをラップするため、検出は無料のままで、 check_merchant の実行時にのみ使用量が計測されます。

Cloudflare の agents パッケージを使用しない理由

当初の計画では、確認前に Cloudflare の agents パッケージ (@modelcontextprotocol/server をベースにした createMcpHandler)を使用する予定でした。 実際にインストールしてみると、@x402/mcp は @modelcontextprotocol/sdk(関連はあるものの別のパッケージ)に直接依存していることが分かりました。また、@x402/mcp と agents の MCP サーバーラッパーとの連携について、ドキュメント化された方法はありません。誰も文書化していない方法で決済関連の SDK を 2 つ組み合わせるのではなく、@x402/mcp 自身の README の例と同じように、@modelcontextprotocol/sdk を直接使用します。このプロジェクトでは agents は依存関係に含まれていません。

プラットフォーム/ウェブサイト情報

2026-08-12 に追加。check_merchant とダッシュボードでは、加盟店のウォレットが紐づく実際のリソース URL(複数の場合あり)と、検出フィードから取得できる場合はサービス名も表示するようになりました。たとえば https://api.example.com/v1/weather のような URL で、ウォレットアドレスだけを表示するわけではありません。これは新たなデータ収集ではありません。Bazaar と PayAI はどちらも、各リスティングについて resource URL と serviceName をすでに返していましたが、以前はカテゴリ分類用に bazaar_description の自由記述テキストに混ぜ込まれ、それ以外では破棄されていました。現在は MerchantSignalRow. platforms_json({url, serviceName} の JSON 配列。1 つのウォレットが複数のリソースに紐づく場合があります)に構造化して保存されます。CheckMerchantOutput.platforms で呼び出し元に公開され、ダッシュボードの Platform 列から直接リンクされます。ウォレットが少なくとも一度取り込まれるまでは NULL/空です。これは category と同じ扱いで、スコアリングには使用されません。

実装済みの内容

  • src/index.ts — Worker のエントリーポイントで、パス名(ホスト名ではないため、workers.dev のフォールバック URL でも動作します)に基づいてルーティングします。/mcp は WebStandardStreamableHTTPServerTransport を介して check_merchant を x402ResourceServer、ExactEvmScheme、createPaymentWrapper でラップします。/ と /dashboard は人間向けのダッシュボードを配信し、/api/wallets は同じデータを JSON で返し、/refresh は管理者認証が必要な手動更新トリガーです。
  • src/dashboard.ts — 公開ダッシュボード(gradientdecisions.com で配信され、MCP エンドポイントの mcp.gradientdecisions.com と同じ Worker を使用します)。自己完結型の HTML/CSS/JS で、外部依存関係はありません。ライト/ダークモードに対応し、クライアント側の検索とティアフィルターを備えています。is_demo = 1 の行は除外します。「デモデータ」を参照してください。
  • src/tool.ts — check_merchant のロジックです。D1(merchant_signals、price_observations)からのみ読み取り、ブリーフで求められている事前計算済みストアの要件に従い、有料リクエストの処理経路ではチェーンにアクセスしません。
  • src/scoring.ts — ルールベースの v1 複合スコアです。各 reasons エントリーは、名前付きのシグナルに対応します。しきい値はすべてファイルの先頭に定数として定義されています。
  • src/refresh/index.ts — raw アクティビティを集約して merchant_signals の行にする、スケジュール実行される Worker です(cron:2 時間ごと。wrangler.toml を参照してください)。
  • src/refresh/indexer.ts — ChainDataSource インターフェースと、公開 x402 Bazaar に対する実際の(スタブではない)実装である BazaarDataSource です。下記の「データソース」を参照してください。
  • src/refresh/solana-indexer.ts — BazaarDataSource に対応する Solana 側の PayAIDataSource です。PayAI による検出に、任意で Helius による支払者多様性の補強を加えます。下記の「Solana データソース」を参照してください。
  • src/chains.ts — ウォレットアドレスを扱うすべてのモジュールで共有される、チェーンの検出とアドレスの正規化です。現在、ストアの同じテーブルに Base(0x 16 進数、大文字と小文字を区別しない)と Solana(base58、大文字と小文字を区別する)の両方のウォレットが格納されるようになりました。
  • db/schema.sql — D1 のスキーマです。稼働中のリモート merchant-signals D1 データベースに適用済みです(wrangler d1 execute --remote)。
  • scripts/backtest.ts と scripts/labeled-wallets.json — 実際のクエリに課金する前に行う、ブリーフで必須とされている検証ステップです。

意図的にスタブ化している内容

  • シグナル 6(速度/ハーネス破損の異常):2026-08-10 のご判断に従い、購入者側のウォレットハーネスのパイプラインはまだ存在しません。そのため、src/refresh/index.ts の detectVelocityAnomalyStub() は、ティアのロジックを気付かれないまま誤調整するような推測上のヒューリスティックをでっち上げるのではなく、常に「異常なし」を返します。パイプラインができたら、関数本体を置き換えてください。
  • 支払者のクラスタリング(シグナル 2 のクラスターフラグ):同じファイル内で、値は 0 にハードコードされています。実際に実装するには、支払者のウォレットが資金源を共有しているか、短期間にまとめて作成されたかを確認する必要があります。照合対象となる実際のデータソースができるまでは対象外です。
  • ウォレットの経過日数(シグナル 1)と返金(シグナル 4):BazaarDataSource ではこれらを確認できません(下記の「データソース」を参照してください)。そのため、Bazaar 由来の行ではすべて wallet_age_days = null、refund_count = 0 となり、現時点では scoreMerchant() が実データに対してどちらのフラグも立てることはありません。「問題なし」とでっち上げているのではなく、測定されていないだけです。
  • 価格の分散(シグナル 5):同じ理由によります。BazaarDataSource は price_observations にデータを格納しないため、実データに対して computePriceVarianceFlag() は常に 0 を返します。ロジック自体は実装済みで、すでに接続されています。このデータを格納できるソースがあれば、追加費用なしですぐに有効になります。

データソース

BazaarDataSource(src/refresh/indexer.ts)— スタブではなく、実際のデータソースです。Coinbase 独自のファシリテーター発見カタログであるx402 Bazaar(GET https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources)からデータを取得します。公開されており、アカウントや API キーは不要です。稼働中であることを確認済みです。2026-08-10 時点で、約 14,500 件のリソースが登録されています。ある更新処理では、最初の 2,000 件から Base メインネットの加盟店ウォレットを 365 件特定し、Coinbase 独自の加盟店ごとの 30 日間の呼び出し量とユニークな支払者の指標に基づく、実際の total_tx_count / unique_payer_count をインデックス化しました。

実際の対象範囲の制限について(影響を受けるシグナルの詳細は、上記の「意図的にスタブ化しているもの」を参照してください):Bazaar にリソースを登録した加盟店のみが対象で、x402 決済を一度でも受け取ったすべてのウォレットが対象になるわけではありません。初回アクティビティのタイムスタンプはなく、ディレクトリの一覧からは決済の完了や返金も確認できません。また、Bazaar の「リソース」(API エンドポイント)は、check_merchant の価格妥当性チェックで使われる商品・サービスの resource_type 区分には対応していません。

これらの不足を補うには、Coinbase の CDP ウォレット履歴 API(無料の CDP アカウントと API キーが必要です。portal.cdp.coinbase.com で取得できます。アカウントの作成は、私ではなくあなた自身が行う必要があります)、または独自のチェーンインデクサーが必要です。どちらも今後の作業であり、現在稼働中のものを妨げることはありません。ネットワークに接続せずにローカルテストを行う場合は、FixtureDataSource も利用できます。

Solana データソース

2026-08-12 に追加(要件:Solana では現在、テストネット上の活動だけでなく、実際の x402 取引量が発生しているため、Base と同じ信頼度ティアのスコアリングに反映されます。実装の詳細は src/refresh/solana-indexer.ts を参照してください)。構成要素は 2 つです。

ディスカバリー — PayAIDataSource。 当初の計画では「Solana の x402 Agent Registry」としていましたが、この名称に該当する実在の加盟店カタログはありませんでした。solana.com/agent-registry は加盟店ディスカバリーではなく、購入者側のエージェント ID 製品だと判明しました。実際に機能上同等のものは、PayAI Network のファシリテーター・ディスカバリーフィード (https://facilitator.payai.network/discovery/resources)です。調査中に直接 curl でライブ稼働を確認しました。全 25,928 件の項目があり、x402 Bazaar と同じディスカバリーリスト JSON 形式の Base と Solana の混在カタログです。無料で公開されており、キーは不要です。Bazaar とは異なり、サンプルした項目には quality フィールドがなく、リスト自体には呼び出し量やユニークな支払者数は含まれません。

支払者の多様性の補完 — Helius(任意、HELIUS_API_KEY)。 PayAI のディスカバリーフィードに不足している情報を補います。PayAI が提示する Solana の加盟店ウォレットごとに、Helius の Enhanced Transactions API(mainnet.helius-rpc.com。api.helius.xyz は 401 を返します。Helius はこのエンドポイントを RPC ホストに移しました。実際の更新が失敗して初めて気づき、現行のドキュメントで確認しました)を使い、そのウォレット宛ての実際の USDC(SPL)送金数と、その送金元のユニークなアドレス数を集計します。上限は、更新実行 1 回あたり最大 8 ウォレット、各ウォレット 1 ページ(100 件)です。この上限は Helius の無料プランの制限ではありません(月 100 万クレジットあれば、これよりはるかに多く処理できます)。Cloudflare アカウントの Worker サブリクエスト予算を実際に確認した結果に基づくものです。1 回の呼び出しあたりの外部フェッチは約 50 回で、BazaarDataSource(最大 20 ページ)、PayAIDataSource 自体のディスカバリーページ(最大 5 ページ)、Helius の補完処理、Anthropic への初回ウォレット分類リクエスト(src/refresh/index.ts で実行ごとに別途最大 8 件)で共有されます。runRefresh はすべてのソースを連続して実行するため、これらはすべて 同じ 呼び出し内で行われます。この機能の初回デプロイでは、こうした上限が設けられる前に、実際の本番リクエストで wrangler tail を使って確認したとおり(シミュレーションではありません)、この上限に正確に達しました(Too many subrequests by single Worker invocation)。Cloudflare Workers のプランを上げれば(Bundled/Paid では上限が 1000 に引き上げられます)、ここまで厳しい予算にする必要はなくなります。これは現実的な選択肢ですが、ここでは実施せず、課金に関する判断として残しています。上限を超えるウォレットや、HELIUS_API_KEY が設定されていない場合は、PayAI の基本リスト情報(通常、呼び出し数 0/支払者数 0)のままです。scoreMerchant() はこれを信頼度を示す情報とは見なさず、いずれの方向にも推測せず、データ不足として扱います。

x402scan の有料 API ではなく Helius を使う理由。 x402scan(x402scan.com)は、実際に呼び出しごとの料金が発生する(x402 経由で 1 回あたり 0.01~0.02 ドル、自身の OpenAPI 仕様で確認済み)チェーン非依存の x402 エクスプローラーです。生の Helius による送金数の集計よりも、加盟店やトランザクションに関する豊富な x402 固有のデータが得られます。v1 では、単に「有料データを避ける」という方針からではなく、具体的な理由から採用を見送りました。利用すると、このバックエンドが資金を入れたウォレットを自律的に保有し、定期的にそこから支出することになり、「月 5~15 ドルは妥当か」という判断よりも大きなカストディ/アーキテクチャ上の責任が生じるためです。Helius の無料プランなら、その責任を負わずに、実際の Solana の支払者多様性データを今すぐ取り込めます。これは選択肢を閉ざすものではありません。Helius ベースのデータが稼働したら、実際に得られる情報と x402scan の有料 API が上乗せする情報を、推測ではなく実数で比較し、その結果を踏まえて再検討する予定です。詳しい理由は 2026-08-12 の会話履歴を参照してください(ビジネスモデルの対称性に関する論点:データアグリゲーター自身がデータに課金していても、より優れた上流データのために少額の料金を支払うのが本質的に間違っているわけではありません。未解決なのは、原則として支出が許容されるかではなく、すでに無料で得られるデータに対して、追加の費用でどれだけの限界価値が得られるかです)。

価格の公平性に関する注意点

src/scoring.ts の scorePriceFairness のコメントから引用しています。2026-08-13 に確認された実際の知見が2つあります。どちらも推測ではなく、対応前に本番環境の実データと照合しました。

  • 当初の「中央値の±25%」という「適正」範囲は誤りであることが、実データで確認されました。 価格が設定された392の加盟サービスについて、6つすべてのカテゴリにわたる本番環境の価格分布を確認したところ、「適正」に該当したのはわずか19%で、「高い」と「安い」はそれぞれ約2倍多く見られました。これは、ほとんどの加盟サービスの価格が実際に不適切だからではなく、実際のカテゴリ内の価格幅が±25%よりはるかに広いためです。data_api だけでも価格は $0.001 から $5.12 まで(5000倍超の幅)に及び、各カテゴリの実際の四分位範囲(p25~p75)は、中央値に対して両方向におよそ2~5倍の幅があります。観測された四分位範囲に基づいて HIGH_PRICE_RATIO/LOW_PRICE_RATIO を3.0倍/0.35倍に再調整しました。これは推測ではありません。同じ本番データに適用すると、「適正」は52%となり、「高い」と「安い」もおおむね均衡しました(99/91)。上記の支払者多様性に関する修正と同様、実データに基づくものであり、最適であると証明されたわけではありません。
  • 根本原因であるカテゴリの大まかさは、これだけでは完全には解消されません。 6つのカテゴリ(data_api、compute、content_generation、financial_data、storage、other)には、実際には種類が異なり、自然な価格帯も異なるリソースがまとめられています(たとえば、単純な検索 API と複雑なリアルタイム分析 API は、どちらも data_api になりえます)。比率の範囲を広げると「高い」「安い」の誤判定は減りますが、「実際のサービス内容にかかわらず、他のすべての data_api 加盟サービスと比較する」ことが正確な比較になるわけではありません。より細分化されたリソース分類が整備され、カテゴリ内で比較できるようになるまでは(整備されるとしても)、price_fairness/pricing.fairness_vs_category は正確な価格評価ではなく、大まかな目安として扱ってください。

Solana のシグナルに関する注意点

db/schema.sql の velocity_anomaly_flag コメントと、src/scoring.ts のシグナルごとのクロスチェーン読み取りで参照される内容を、ここにまとめています。

  • Helius は、x402 の支払いに限らず、すべての USDC 送金をカウントします。 別の経路(直接送金や、無関係な決済アプリなど)で USDC を受け取った加盟店は、x402 の取引量が水増しされているように見えます。Base では、Bazaar に逆方向の欠落(過少カウント)があり、登録済みのリスティングしか確認できません。どちらのソースも真実を示すものではなく、文書化された近似値です。
  • firstSeenAt は期間の制約を受けており、ウォレットの本当の経過期間ではありません。 Solana の行では、90 日間のルックバックと、ウォレットごとに最大 3 ページという Helius の制限内で最も古い送金のみを反映します。このシグナルについて、Bazaar 由来の Base の行がすでに抱えている、null と近似値のどちらを取るかというトレードオフと同じです(Bazaar はアクティビティ開始時刻をまったく提供しません)。
  • 手数料支払者によるスポンサーは、誤帰属のリスクではありません。 Solana の x402 の「exact」スキームでは、手数料支払者が送金元、権限者、送金先になることが暗号学的に除外されています(x402-foundation/x402 の specs/schemes/exact/scheme_exact.md を参照してください)。カウントされた送金の送信元アドレスは、常に実際の支払者のものであり、スポンサーであるリレイヤーのものではありません。これは推測ではなく、仕様を直接確認して検証済みです。
  • シグナル 6(速度/ハーネス破損)は、Base でのように現在スタブであるだけでなく、実装後もそのままでは機能しないと分かっている唯一のシグナルです。 Solana の決済はおよそ 4 倍速く(約 0.5 秒対 Base の約 2 秒)、Base のトラフィックに合わせて調整したトランザクション頻度の閾値では、まったく正常な Solana のアクティビティまで異常として過剰にフラグ付けしてしまいます。このシグナルを実装する人は、Base の閾値をそのまま流用せず、Solana 固有の閾値を設定する必要があります。
  • シグナル 2 の多様性比率の閾値(src/scoring.ts の LOW_PAYER_DIVERSITY_RATIO = 0.3)は Base に合わせて調整されたものであり、クロスチェーンで検証されていません。 比率そのもの(unique_payers / total_tx)は無次元なので、原理的には他チェーンにも適用できるはずですが、この具体的な閾値は、実際の Base Bazaar 加盟店 365 件から導き出されたものです。実際の Solana の支払者分布と照合できるだけの、Helius で補完された Solana の行が十分に集まるまでは、仮定として扱ってください。
  • USDC の小数桁数は両チェーンで一致しています(どちらも 6 桁)。 x402scan 自体のファシリテーター定数と、Solana の公式 USDC ミントレジストリを照合して確認済みです。そのため、db/queries.ts getComparablePrices における最小単位の価格比較は、変換処理なしで Base と Solana の同一カテゴリ間で有効です。これが、クロスチェーン比較を意図的にまとめており、分けていない唯一の箇所です。その理由については、この関数自体のコメントを参照してください。

完了済みと、今後必要な項目

2026-08-12 に修正 — このリストの大部分は古くなっていました。 デプロイがテストネットのみだった頃に書かれたもので、以下の「今後必要な項目」のいくつかはすでに完了していましたが、リストが更新されていませんでした。記憶に頼って編集したのではなく、書き直す前にライブデプロイで確認しました。 完了:

  • ✅ Cloudflare アカウントの認証が完了しました(wrangler login、開発者本人のアカウント)。
  • ✅ D1 データベース(merchant-signals)を作成し、スキーマをリモートに適用しました。
  • ✅ Base メインネットで稼働中。実際の PAYOUT_ADDRESS を使用し、実際に確定したメインネットのトランザクションと、その実際のトランザクションハッシュが query_log に記録されていることを確認しました(このプロジェクトで一貫して実践しているセキュリティ方針に従い、実際のアドレス値を目にしたり、扱ったりしたことはありません)。
  • ✅ mcp.gradientdecisions.com にデプロイしました(wrangler.toml の custom_domain = true により、この Cloudflare アカウントにすでにゾーンがあったため、DNS と SSL が自動でプロビジョニングされました)。
  • ✅ merchant_signals に合成デモ行を 2 件投入しました(「デモデータ」を参照)。
  • ✅ 実際のデータソースを接続し、実行しました。BazaarDataSource は公開されている x402 Bazaar から、Base メインネット上の実際の加盟店ウォレットをインデックス化します(アカウントは不要です)。
  • ✅ 実際のデータを使用したバックテストに合格しました。2 件中 2 件(trusted と caution。どちらも実際の Bazaar 加盟店です。scripts/labeled-wallets.json を参照)。avoid は引き続き明示的に未検証です。実際の悪意あるアクターのデータソースはまだ存在しません(履歴が乏しいウォレットを代用として使えない理由は、そのファイルの _avoid_bucket を参照)。これは現時点でも未解決であり、ここでは修正していません。
  • ✅ 2 つ目のデータソースとして Solana を追加しました(2026-08-12)。chain カラムをリモート D1 に移行し、PayAIDataSource と Helius を runRefresh() 内で BazaarDataSource と併用するよう接続しました。check_merchant の出力とダッシュボードの両方で chain を表示し、両方のチェーンに言及するようツールの説明を意味的意図のマッチング向けに書き直しました。また、platforms(加盟店のウェブサイト/API の URL)を出力とダッシュボードに表示します。上記の「Solana データソース」と「プラットフォーム/ウェブサイト情報」を参照してください。
  • ✅ HELIUS_API_KEY を設定し、正常に動作することを確認しました。実際の Solana の支払い元多様性データが流れています(「Solana データソース」を参照)。
  • ✅ ANTHROPIC_API_KEY を 2026-08-11 に設定し、正常に動作することを確認しました。分類のバックログを処理済みです。
  • ✅ レジストリへの登録を申請しました。公式 MCP レジストリ(com.gradientdecisions/merchant-check)に掲載され、第三者ディレクトリ(例:mcp.so)にもインデックスされています。定期的に確認する価値があります。これらのディレクトリは説明をキャッシュしている場合があり、ここで行った README/ツールの変更がすぐに反映されないことがあります。掲載情報が古く見える場合は、こちらのソースが古いとは限らず、先方のキャッシュ更新が遅れている可能性がありますが、両方を確認してください。
  • ✅ workers.dev サブドメインを 2026-08-18 に有効化しました。これにより、デプロイ時に cron トリガーが接続されるようになりました(本番環境で確認済み。上記の「残っている一度限りのアカウント設定」を参照)。これに気付くまで、データは 6 日以上にわたって気付かれないまま古い状態になっていました。自動更新(現在は 2 時間ごと)が再発を防ぐはずですが、lastRefreshedAt を時折確認する価値はあります。

引き続き実際に必要なこと:

  1. バックテスト用の実際の avoid の例。x402 に特化した実際の悪意あるアクターのデータソース(公開情報からは見つかっていません。技術がまだ新しすぎます)が必要か、長期間にわたって実際の利用データを十分に集め、自然にそのような例が見つかる必要があります。
  2. Robinhood Chain のキャリブレーション用データソース。2026-08-13 に明示的に後回しにしました。忘れているわけではありません。チェーン ID 4663(Arbitrum Orbit L2、2026-07-01 から稼働)と、その Virtuals Protocol/Agentic Accounts エコシステムは実在し、確認済みですが、構築に着手する前に、まだ 2 つの要件が不足しています。(a)エージェントのウォレットを特定するための、実在性が検証されたコントラクトアドレス。公開検索で見つかったのは、「Agentic」というブランドを利用した非公式の模倣トークンらしきものだけで、Robinhood/Virtuals の本物のインフラではありません。(b)「現在 Base で稼働しているのと同じロジック」に対するハーネス破損検出のキャリブレーションという目的には、実際のベースラインがまだありません。signal 6 は現在 Base 上で恒久的なスタブ(detectVelocityAnomalyStub、常に 0)です。検証済みアドレスを伴う実際のコンタクトを得るか、先に signal 6 を実装することで、これに着手できるようになります。

mainnet への移行(完了)

古い見出しです。履歴として残していますが、これはすでに完了しています。 wrangler.toml の X402_NETWORK は "eip155:8453"(Base mainnet)であり、このセクションで以前説明していた Sepolia testnet の値ではありません。PAYOUT_ADDRESS は実際の呼び出し元から実際の USDC を受け取ります。query_log には、実際の tx ハッシュを持つ Base mainnet の決済済みトランザクションが記録されており、リモート D1 に直接照合して確認済みです(上記の「実際に試す」を参照)。ローカル開発のために testnet に戻す必要が生じた場合、変更する値は、このセクションで以前説明していたものと同じ、X402_NETWORK の1行の編集です。変更の向きが逆になるだけです。

ローカル開発環境での制限(このマシン)

wrangler dev / wrangler d1 execute --local の実行には workerd が必要で、macOS 13.5.0 以降が必須ですが、このマシンは 12.6.0 のため、どちらも実行できませんでした。代わりに、スキーマ構文を sqlite3 で直接検証しました(D1 は SQLite と互換性があります)。3つのテーブルすべてが問題なく作成されました。npm run typecheck は最初から最後まで正常に通ります。実際のランタイムテスト(wrangler dev を起動し、実際の MCP クライアントで /mcp にアクセスする)には、新しいバージョンの macOS、Linux の devcontainer、または wrangler dev --remote やデプロイ済み Worker に直接接続してのテストが必要です。

既知の未解決事項

query_log(スコアリングの入力ではなく、利用状況と収益の可視化に使用)は、実際の tier の代わりに "settled" をプレースホルダーの tier_returned として記録します。@x402/mcp の onAfterSettlement フックは、ツールハンドラーの戻り値にはアクセスできず、決済情報だけを参照できるためです。v1 では問題ありません。実際の tier を記録する必要が生じた場合は、src/index.ts のコメントを参照してください。

category と price_fairness がエージェントに正しく返されるようになりました

2026-08-11 までは、category は D1 とダッシュボードにしか存在せず、check_merchant の応答には含まれていませんでした。また、実際の加盟店すべてについて price_fairness は恒久的に "unknown" のスタブでした。price_observations にデータが追加されたことが一度もなかったためです。どちらの問題も修正しました。現在はツールの出力に category が含まれ、price_fairness は、呼び出し元が指定するもののデータが一度も登録されていなかった旧来の resource_type ではなく、加盟店の価格を同じカテゴリ内の実在する同業他社の価格と比較します(互換性のため入力スキーマには残し、未使用であることを記載しています)。

本番環境に対してエンドツーエンドで実際に動作させる過程で、3つの実際のバグが見つかりました(いずれもコードの確認ではなく、実際に失敗したリクエストに対する wrangler tail で発見しました)。

  1. 価格データをウォレットごとに D1 に書き込んでいたため(リソースごとに最大1回の INSERT。一部のウォレットには65件以上あります)、1回の更新で約370のウォレットを処理すると、D1 の1回の呼び出しあたりの上限である1000クエリを超えていました。処理を一括操作に再構成して修正しました。370回のカテゴリ検索の代わりに、最初に一括でカテゴリを検索し、価格データをメモリに蓄積して、最後に約20件のチャンク化したステートメントで書き込みます。
  2. D1 の実際のバインドパラメーター上限はステートメントあたり約100で、SQLite の通常の上限である999とは異なり、D1 自身のエラーメッセージにも記載されていません。env.DB.batch() は呼び出し内の全ステートメントのパラメーター数を合算し、同じ上限と照合します。複数の大きな INSERT をまとめても上限は回避できず、各ステートメントのバインドパラメーターの合計を減らす必要があります。
  3. getComparablePrices は直近のデータを優先するために observed_at DESC でソートしていましたが、一括更新で追加されたすべての行がまったく同じタイムスタンプを共有していました。すべて同じ値のキーでソートすると、代表性のない任意のサブセットが返されます。ある実在の加盟店の価格は、カテゴリの実際の中央値の50~500分の1でしたが、LIMIT 200 で選ばれたサブセット自体が低価格側に偏っていたため、結果は "high" になっていました。ORDER BY RANDOM() に変更して修正しました。

実際の環境で検証済みです。実在する data_api 加盟店のカテゴリが check_merchant の出力に正しく表示され、そのカテゴリの実際の比較可能な観測値735件から算出した実際の中央値(約$0.01)を基準に、price_fairness が fair/low/high を正しく判定します。

コメント

MCP は初めてですか?

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

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