Skip to main content
Glama
catena-oss

x402-mcp-demo

by catena-oss

x402-mcp-demo

x402 上でツール呼び出しが計量・課金され、加えて標準 MCP クライアントが x402 の存在を知らずに有料ツールを使用できる paying-proxy リファレンスクライアントを備えた MCP サーバー。決済は Base Sepolia 上のテストネット USDC で行われ、Catena サンドボックスアカウントに入金されます。

flowchart LR
  CL["Standard MCP client<br/>Claude Code, Inspector"] -->|stdio JSON-RPC| PX["Paying proxy<br/>holds the wallet, spend cap"]
  PX -->|Streamable HTTP + x402| SV["Paid MCP server<br/>gate in front of the handler"]
  SV -->|verify then settle| F[Facilitator]
  F -->|USDC| CA[(Catena sandbox account)]

  classDef pay stroke-width:2px
  class PX,SV pay

仕組み

x402 チャレンジは MCP Streamable HTTP トランスポートの HTTP 層に存在し、JSON-RPC フレーミングの下に位置するため、MCP プロトコル自体は変更されず、標準クライアントとの互換性が維持されます。

  • initialize、tools/list、および無料ツール pricing は無料です。

  • premium_market_signal に対する tools/call は、x402 v2 チャレンジ (正確なスキーム) を伴う 402 を引き出します。プロキシがこれを支払い、ファシリテーターが設定された payTo に入金し、その後にのみ成功したツール結果が返されます。ミドルウェアの順序は不変条件です: 未払いの呼び出しはツールハンドラーに到達しません。MCP HTTP 4xx は決済をキャンセルします。

  • プロキシは、累計が PROXY_SPEND_CAP_USD を超える場合、支払い前に有料呼び出しを拒否します。キャップは設定であり、ツール引数から導出されることはないため、プロンプトインジェクションされたツール呼び出しでキャップが引き上げられることはありません。

呼び出しごとのシーケンス (決済がキャンセルされる箇所を含む) は docs/architecture.md にあります。

Related MCP server: x402 MCP Proxy

セットアップ

Node >= 22.13 (.nvmrc を参照) と pnpm が必要です。

corepack enable
pnpm install
cp .env.example .env
# SELLER_PAY_TO_ADDRESS: your Catena sandbox account's base-sepolia USDC
#   deposit address, from app.catena.com
# BUYER_EVM_PRIVATE_KEY: a testnet wallet the proxy pays from. Fund it with
#   Base Sepolia USDC at https://faucet.circle.com (select Base Sepolia).
#   USDC only; no ETH is needed, transfers are gasless EIP-3009.

両方のエントリポイントは、設定が不足しているか無効な場合に終了コード 2 を返し、必要な依存関係 (サーバーの場合はファシリテーター、プロキシの場合は上流 MCP サーバー) に到達できない場合に終了コード 1 を返します。

デモ: 1 つのコマンドで全ループを実行

pnpm demo

公開 x402 ファシリテーターに対して有料サーバーを起動し、標準 MCP クライアントを支払いプロキシ経由で駆動し、以下を出力します: 無料のディスカバリー、次に Catena 入金アドレスに 0.001 ドルのテストネット USDC を決済する有料ツール呼び出し。

自分で 402 を確認する

1 つのターミナルで pnpm server を実行し、支払いなしで有料ツールを要求します。サーバーは /healthz で価格と有料ツール名を応答します。これはプロキシが起動時にプローブするものと同じです:

curl -s http://localhost:4040/healthz
{"status":"ok","paidTool":"premium_market_signal","price":"$0.001"}

チャレンジ自体はボディ (ボディは {}) ではなく、PAYMENT-REQUIRED レスポンスヘッダーに含まれているため、ヘッダーをデコードして読み取ります:

curl -si -X POST http://localhost:4040/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"premium_market_signal","arguments":{"topic":"usdc"}}}' \
  | grep -i '^payment-required:' | tr -d '\r' | cut -d' ' -f2 | base64 -d
{"x402Version":2,"error":"Payment required","resource":{"url":"http://localhost:4040/mcp","description":"One invocation of the premium_market_signal MCP tool","mimeType":""},"accepts":[{"scheme":"exact","network":"eip155:84532","amount":"1000","asset":"0x036CbD53842c5426634e7929541eC2318f3dCF7e","payTo":"0x000000000000000000000000000000000000dEaD","maxTimeoutSeconds":300,"extra":{"name":"USDC","version":"2"}}]}

| grep ... を削除するとステータス行 HTTP/1.1 402 Payment Required が表示されます。ツールは実行されていないため、決済は行われません。

Claude Code から使用する (標準クライアント)

1 つのターミナルで有料サーバーを実行し (pnpm server)、プロキシを通常の stdio MCP サーバーとして .mcp.json に登録します:

{
  "mcpServers": {
    "paid-market-signal": {
      "command": "pnpm",
      "args": ["--dir", "/path/to/x402-mcp-demo", "proxy"]
    }
  }
}

プロキシはこのリポジトリの .env から BUYER_EVM_PRIVATE_KEY と UPSTREAM_MCP_URL を読み取るため、秘密情報が .mcp.json に漏れることはありません (.mcp.json はここでは gitignore されています。この設定をコピーする場合はそのままにしておいてください)。

Claude Code は両方のツールをリストし、通常通り呼び出します。プロキシはバックグラウンドで 402 を支払います。MCP Inspector も同様に動作します: npx @modelcontextprotocol/inspector pnpm proxy。

テスト

pnpm test は、記録用の偽ファシリテーターを備えたプロセス内サーバーに対してサーバーとプロキシのスイートを実行します。ネットワークも金銭も不要です。各マネーパス不変条件には、それが破られた場合に失敗するテストがあります。

不変条件

テスト

ディスカバリーと無料ツールは無料

支払いなしで initialize、tools/list、無料ツールを提供する

未払いの有料ツール呼び出しは実行前に 402 になる

ツール実行前に、未払いの有料ツール呼び出しを 402 チャレンジで拒否する

有料呼び出しは正確に 1 回決済される

クライアントが支払うと有料ツールを実行し、その後もディスカバリーは無料のまま

ディスカバリーはプロキシ経由でも無料のまま

プロキシ経由でも無料のサーフェスを無料のままにする

標準クライアントは x402 の存在を知らずに支払う

透過的に有料ツールに支払い、結果を返す

JSON-RPC バッチは拒否され、アイテムごとにゲートされない

JSON-RPC バッチリクエストを完全に拒否する (フェイルクローズ)

MCP HTTP 4xx は決済をキャンセルする

有料呼び出しが MCP HTTP 4xx を返した場合に決済しない

通知 (id なし) は決して課金されない

通知形式の有料 tools/call (id なし) に課金しない

解析不能なボディは拒否され、価格設定されない

text/plain で送信された有料 tools/call を解析せず、課金もせずに拒否する

有料呼び出しごとに 1 回の上流実行

有料呼び出しを 2 回 (402 の後、有料で再試行) ポストし、1 回だけ決済する

固定ネットワーク上の USDC のみが署名される

ポリシー外のチャレンジ (間違ったネットワーク、間違った資産) を署名せずに拒否する

支出上限は支払い前に適用される

支払い前に、支出上限を超える呼び出しを拒否する

同時呼び出しが両方とも上限を下回ることはできない

同時有料呼び出しを制限する: 1 回の呼び出し上限の下で、2 つのうち 1 つだけが決済される

スコープ

公開サーフェスのみ: MCP TypeScript SDK、公開 x402 パッケージとファシリテーター、および受信側としての Catena サンドボックスアカウント。バージョンと制限: docs/architecture.md。

ライセンス

MIT

Related MCP Connectors

Related MCP Servers