はじめに
この記事で作るもの
対象読者
前提知識
目次
Part 1: SVM Exact Schemeを理解する
EVMとSolanaの決済アプローチの違い
SVM Exact Schemeの14ステップ
PaymentRequirementsの構造(Solana)
PaymentPayloadの構造(Solana)
トランザクション構造の検証ルール
Part 2: SDKのアーキテクチャ
パッケージ構成
クライアント側の処理フロー
Facilitator側の処理フロー
Part 3: 支払い対応MCPサーバーを構築する
プロジェクトのセットアップ
ディレクトリ構成
環境変数と支払い設定
Import Pathの注意点
MCPサーバーの構築
処理の流れ
Part 4: エージェントクライアントの実装
クライアントの作成
実行
結果の読み方
Part 5: MCPクライアントのx402対応状況
現状: ネイティブ対応はまだない
MCPプラグインによる拡張
今後の展望
Part 6: 本番デプロイに向けて
Devnetからメインネットへ
複数ネットワーク対応
Facilitatorの選択肢
Hooksによる拡張
Part 7: MCPエコシステムとx402
主要な実装
@civic/x402-mcp のProxy機能
SEP-2007: MCPネイティブの決済サポート
実装時のハマりどころ
まとめ
参考リンク
この記事が参考になったら
https://s3.ap-northeast-1.amazonaws.com/hanzochang.com/_v2/1772109507960_sxaqhulrdn9.png

x402 × Solana実装ガイド | 支払い対応MCPサーバーをTypeScriptで構築する

x402 V2プロトコルのSolana実装を徹底解説。SVM exact schemeの仕組みから、@x402/mcpパッケージを使った支払い対応MCPサーバーの構築まで、実際のコードとともにステップバイステップで解説します。

公開日2026.02.26

更新日2026.02.26

CryptoAI

はじめに

前回の記事でx402プロトコルの全体像を、V2解説記事でマルチチェーン対応やExtensions機構を紹介しました。

この記事では、実際に手を動かします。x402 V2のSolana実装(SVM exact scheme)の仕組みを理解し、支払い対応のMCPサーバーをTypeScriptで構築するところまでを、コードとともに解説します。

この記事で作るもの

Solana USDC で課金できるMCPサーバーとクライアントを、TypeScript + @x402/mcp で構築します。

コンポーネント内容
MCPサーバー
無料ツール(ping)と有料ツール(premium_weather / $0.001 USDC)を提供。StreamableHTTPトランスポートで動作
MCPクライアント
サーバーに接続し、有料ツール呼び出し時にSolana Devnet上のUSDCで自動決済を実行
決済フロー
クライアントが402レスポンスを受信 → トランザクションを構築・部分署名 → Coinbase Facilitatorが検証・最終署名・送信

完成すると、以下のように動作します。

x402未対応のクライアントでは、有料ツールは402 Payment Requiredで止まります。

x402対応クライアント(@x402/mcp)を使うと、同じ呼び出しに対して自動決済 → 結果取得が裏で実行されます。

ソースコードはデモリポジトリ(GitHub)で公開しています。

対象読者

  • x402の概要を理解済みで、Solana上での実装に進みたいエンジニア
  • MCPサーバーにマネタイズ機能を追加したい方
  • AIエージェントの自律的な決済フローをプロダクションで試したい方

前提知識

  • x402プロトコルの基本概念(入門記事参照)
  • Solanaの基礎(トランザクション、SPLトークン)
  • TypeScript / Node.js の開発経験

目次

Part 1: SVM Exact Schemeを理解する

x402 V2では、チェーンごとに「スキーム(scheme)」と呼ばれる決済メカニズムが定義されています。Solanaで使われるのがSVM exact schemeです。EVMのEIP-3009 transferWithAuthorizationとは根本的にアプローチが異なります。

EVMとSolanaの決済アプローチの違い

項目EVM(Base等)Solana
署名対象
EIP-3009 authorization(オフチェーン署名)
VersionedTransaction(部分署名)
ガス代負担
FacilitatorがtransferWithAuthorizationを実行
FacilitatorがfeePayerとして最終署名
トークン送金命令
transferWithAuthorization
TransferChecked(SPL Token / Token-2022)
ペイロード形式
signature + authorization JSON
Base64エンコードされた部分署名済みトランザクション

EVM vs Solana 決済アプローチの比較

EVMではオフチェーンの「認可署名」をFacilitatorが代行実行しますが、Solanaではクライアントが実際のトランザクションを構築・部分署名し、Facilitatorがガス代を負担して最終署名・送信するモデルです。

SVM Exact Schemeの14ステップ

SVM exact schemeの決済フローは、4つのアクター間で14のステップに分かれます。

SVM Exact Scheme 決済フロー
クリックで拡大

EVMとの最大の違いはステップ3〜5です。クライアントがSolanaのVersionedTransactionを自分で構築し、TransferChecked命令を含む形でトランザクションを組み立てます。

PaymentRequirementsの構造(Solana)

サーバーが402レスポンスで返す支払い条件は以下の形式です。

{
  "scheme": "exact",
  "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
  "amount": "1000",
  "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "payTo": "2wKupLR9q6wXYppw8Gr2NvWxKBUqm4PPJKkQfoxHDBg4",
  "maxTimeoutSeconds": 60,
  "extra": {
    "feePayer": "EwWqGE4ZFKLofuestmU4LDdK7XM1N4ALgdZccwYugwGd"
  }
}
フィールド説明
network
CAIP-2形式のSolanaネットワーク識別子
amount
支払い金額(トークンの最小単位。USDCなら6桁 = 1000 = $0.001)
asset
トークンのMintアドレス(USDC: EPjFWdd5...
payTo
リソースサーバーの受取ウォレット
extra.feePayer
ガス代を負担するFacilitatorのアドレス

EVM版との大きな違いはextra.feePayerフィールドです。Solanaではトランザクション手数料を別アカウントが支払えるため、クライアントはSOLを持っていなくても決済が可能です。

PaymentPayloadの構造(Solana)

クライアントが送信する決済ペイロードは以下の形式です。

{
  "x402Version": 2,
  "resource": {
    "url": "https://example.com/api/data",
    "description": "Access to protected content",
    "mimeType": "application/json"
  },
  "accepted": {
    "scheme": "exact",
    "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
    "amount": "1000",
    "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "payTo": "2wKupLR9q6wXYppw8Gr2NvWxKBUqm4PPJKkQfoxHDBg4",
    "maxTimeoutSeconds": 60,
    "extra": {
      "feePayer": "EwWqGE4ZFKLofuestmU4LDdK7XM1N4ALgdZccwYugwGd"
    }
  },
  "payload": {
    "transaction": "base64-encoded-partially-signed-versioned-transaction"
  }
}

payload.transaction部分署名済みのVersionedTransactionをBase64エンコードしたものです。EVMのsignature + authorization構造とは異なり、Solanaではトランザクション全体が格納されます。

トランザクション構造の検証ルール

Facilitatorがトランザクションを検証する際、以下の厳格なルールが適用されます。

命令の構成(3〜5命令):

順番命令必須説明
1
ComputeBudget SetLimit
必須
計算ユニット上限の設定
2
ComputeBudget SetPrice
必須
計算ユニット単価の設定
3
TransferChecked
必須
SPLトークン送金
4
Lighthouse
任意
Phantomウォレットの保護命令
5
Lighthouse
任意
Solflareウォレットの保護命令

セキュリティ検証:

  • feePayer保護: feePayerのアドレスが送金命令のaccountsに含まれてはならない。feePayerが自分の資金を移動させられるトランザクションを拒否
  • 計算ユニット単価の上限: 5 lamports/CU以下に制限し、ガス代の悪用を防止
  • 送金先の検証: TransferCheckedの送金先がAssociated Token Account PDA(owner = payTo, mint = asset)と一致すること
  • 金額の完全一致: TransferCheckedのamountがPaymentRequirements.amountと一致すること
  • シミュレーション: 決済前にトランザクションをシミュレーションし、実行可能性を確認

これらの検証はFacilitatorが自動で行うため、リソースサーバーの開発者がブロックチェーンの知識を持つ必要はありません。

Part 2: SDKのアーキテクチャ

実装に入る前に、x402 TypeScript SDKのSolana関連パッケージを整理します。

パッケージ構成

@x402/core          ← プロトコル共通型・ユーティリティ
@x402/svm           ← Solana決済メカニズム (mechanisms/svm)
  ├── ExactSvmScheme    ← クライアント/ファシリテーター実装
  ├── signer            ← トランザクション署名
  └── utils             ← RPC・トランザクション操作
@x402/mcp           ← MCPトランスポート
  ├── server/           ← createPaymentWrapper
  └── client/           ← createX402MCPClient
@x402/express       ← Express用ミドルウェア
@x402/hono          ← Hono用ミドルウェア
@x402/next          ← Next.js用ミドルウェア

クライアント側の処理フロー

ExactSvmSchemeクラスのcreatePaymentPayloadメソッドが、Solanaトランザクションの構築を担います。

ステップ1と5でSolana RPCへの通信が発生しますが、この一連の処理はすべて@x402/svmパッケージが内部で行うため、開発者が直接Solanaのトランザクションを組み立てる必要はありません。

Facilitator側の処理フロー

FacilitatorのExactSvmSchemeverifysettleの2つのメソッドを持ちます。

verify(検証): トランザクションをデコードし、前述の6つの検証ルールを順に適用。最後にシミュレーションで実行可能性を確認します。

settle(決済): feePayerとして最終署名を行い、Solanaネットワークに送信。30回までのリトライでトランザクション確認を待ちます。

Part 3: 支払い対応MCPサーバーを構築する

ここからが本題です。@x402/mcpパッケージを使って、AIエージェントがSolana USDCで支払いを行うMCPサーバーを構築します。

プロジェクトのセットアップ

mkdir x402-mcp-server && cd x402-mcp-server
pnpm init
pnpm add @x402/mcp @x402/core @x402/svm \
  @modelcontextprotocol/sdk express zod \
  @solana/kit @scure/base
pnpm add -D typescript tsx @types/node @types/express

package.json"type": "module"を追加します。

{
  "type": "module",
  "scripts": {
    "server": "tsx src/server.ts",
    "client": "tsx src/client.ts"
  }
}

ディレクトリ構成

x402-mcp-server/
├── src/
│   ├── server.ts   # MCPサーバー(x402 + StreamableHTTP)
│   └── client.ts   # MCPクライアント(x402決済対応)
├── .env            # SOLANA_WALLET_ADDRESS, SOLANA_PRIVATE_KEY
└── package.json

環境変数と支払い設定

.envファイルに以下を設定します。

SOLANA_WALLET_ADDRESS=your-solana-wallet-address
SOLANA_PRIVATE_KEY=your-base58-encoded-private-key
FACILITATOR_URL=https://x402.org/facilitator

x402 SDKは定数とビルダーを提供しているため、Mint アドレスやネットワークIDを手動で定義する必要はありません。

import { SOLANA_DEVNET_CAIP2, USDC_DEVNET_ADDRESS } from "@x402/svm";
// SOLANA_DEVNET_CAIP2  = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"
// USDC_DEVNET_ADDRESS  = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"

支払い条件はbuildPaymentRequirementsで動的に構築されます。price: "$0.001"と指定するだけで、USDC decimals(6桁)を考慮したamount: "1000"に自動変換されます。さらに、Facilitatorのextra.feePayerも自動設定されます。

Solana Devnetで試す場合、以下の2つのFaucetからテストトークンを取得してください。

  • SOL(ガス代): Solana Faucetsolana airdrop 2 コマンドでも取得可能
  • USDC(決済用): Circle Testnet Faucet — Solana Devnetを選択し、ウォレットアドレスを入力(2時間ごとに最大20 USDC)

Import Pathの注意点

x402のSVMパッケージにはサーバー用とクライアント用の2つのExactSvmSchemeが存在します。

// サーバー側: parsePrice, enhancePaymentRequirements 等を持つ
import { ExactSvmScheme } from "@x402/svm/exact/server";

// クライアント側: createPaymentPayload, sign 等を持つ
import { ExactSvmScheme } from "@x402/svm";

間違えるとparsePrice is not a function等の実行時エラーになります。これはx402実装で最も多い落とし穴です。

MCPサーバーの構築

@x402/mcpcreatePaymentWrapperを使って、ツールに決済を組み込みます。MCP SDK v1.27+ではStreamableHTTPトランスポートが推奨されており、セッション管理付きのステートフルサーバーを構築します。

Import pathに注意: サーバー側のExactSvmSchemeは@x402/svm/exact/serverからインポートします。デフォルトの@x402/svmはクライアント専用で、parsePrice等のサーバーメソッドがありません。

// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import { createPaymentWrapper, x402ResourceServer } from "@x402/mcp";
import { HTTPFacilitatorClient } from "@x402/core/server";
import { ExactSvmScheme } from "@x402/svm/exact/server";
import { SOLANA_DEVNET_CAIP2, USDC_DEVNET_ADDRESS } from "@x402/svm";
import { z } from "zod";
import { randomUUID } from "node:crypto";

const facilitatorUrl = process.env.FACILITATOR_URL
  ?? "https://x402.org/facilitator";
const solanaAddress = process.env.SOLANA_WALLET_ADDRESS!;

async function main() {
  // 1. x402リソースサーバーを初期化
  const facilitatorClient = new HTTPFacilitatorClient({
    url: facilitatorUrl,
  });
  const resourceServer = new x402ResourceServer(facilitatorClient);
  resourceServer.register(SOLANA_DEVNET_CAIP2, new ExactSvmScheme());
  await resourceServer.initialize();

  // 2. 支払い条件を構築($0.001 → amount: "1000"に自動変換)
  const paymentAccepts = await resourceServer.buildPaymentRequirements({
    scheme: "exact",
    network: SOLANA_DEVNET_CAIP2,
    payTo: solanaAddress,
    price: "$0.001",
  });

  // 3. セッション管理
  const transports: Record<string, StreamableHTTPServerTransport> = {};

  function createMcpServer() {
    const server = new McpServer({
      name: "x402-solana-mcp-server",
      version: "1.0.0",
    });

    const wrapWithPayment = createPaymentWrapper(resourceServer, {
      accepts: paymentAccepts,
    });

    // 無料ツール
    server.tool("ping", "Health check (free)", {}, async () => ({
      content: [{ type: "text", text: "pong" }],
    }));

    // 有料ツール(Payment Wrapperで包む)
    server.tool(
      "premium_weather",
      "Premium weather data ($0.001 USDC)",
      { city: z.string().describe("City name") },
      wrapWithPayment(async (args: { city: string }) => ({
        content: [{
          type: "text" as const,
          text: JSON.stringify({
            city: args.city,
            temperature: Math.round(Math.random() * 30 + 10),
            condition: ["sunny", "cloudy", "rainy"][
              Math.floor(Math.random() * 3)
            ],
          }, null, 2),
        }],
      })),
    );

    return server;
  }

  // 4. Express app(SDK helperを使用)
  const app = createMcpExpressApp();

  app.post("/mcp", async (req, res) => {
    const sessionId = req.headers["mcp-session-id"] as string | undefined;
    try {
      let transport: StreamableHTTPServerTransport;
      if (sessionId && transports[sessionId]) {
        transport = transports[sessionId];
      } else if (!sessionId && isInitializeRequest(req.body)) {
        transport = new StreamableHTTPServerTransport({
          sessionIdGenerator: () => randomUUID(),
          onsessioninitialized: (sid) => {
            transports[sid] = transport;
          },
        });
        transport.onclose = () => {
          const sid = transport.sessionId;
          if (sid && transports[sid]) delete transports[sid];
        };
        const server = createMcpServer();
        await server.connect(transport);
        await transport.handleRequest(req, res, req.body);
        return;
      } else {
        res.status(400).json({
          jsonrpc: "2.0",
          error: { code: -32000, message: "Bad Request" },
          id: null,
        });
        return;
      }
      await transport.handleRequest(req, res, req.body);
    } catch (error) {
      if (!res.headersSent) {
        res.status(500).json({
          jsonrpc: "2.0",
          error: { code: -32603, message: "Internal server error" },
          id: null,
        });
      }
    }
  });

  app.get("/mcp", async (req, res) => {
    const sessionId = req.headers["mcp-session-id"] as string;
    if (!sessionId || !transports[sessionId]) {
      res.status(400).send("Invalid or missing session ID");
      return;
    }
    await transports[sessionId].handleRequest(req, res);
  });

  app.delete("/mcp", async (req, res) => {
    const sessionId = req.headers["mcp-session-id"] as string;
    if (!sessionId || !transports[sessionId]) {
      res.status(400).send("Invalid or missing session ID");
      return;
    }
    await transports[sessionId].handleRequest(req, res);
  });

  const port = process.env.PORT ?? 4022;
  app.listen(port, () => {
    console.log(`x402 MCP Server running on http://localhost:${port}/mcp`);
  });
}

main().catch(console.error);

ポイントは以下の通りです。

  • createMcpExpressApp(): MCP SDKが提供するExpressアプリファクトリ。express.json()とDNS rebinding保護が設定済み
  • isInitializeRequest(): セッション開始リクエストの判定。新規クライアントか既存セッションかで処理を分岐
  • req.bodyを第3引数で渡す: handleRequest(req, res, req.body) — ミドルウェアでパース済みのボディを渡す必要がある
  • buildPaymentRequirementsprice: "$0.001"と指定すると、トークンのdecimalsに応じたamountが自動計算される(6桁 → "1000"
  • createPaymentWrapperがツールハンドラーをラップし、決済の検証・実行を透過的に処理

処理の流れ

Payment Wrapperが行う処理を整理します。

@x402/mcp Payment Wrapper 処理フロー
クリックで拡大

重要なポイントは、ツールハンドラー自体は決済ロジックを一切含まないことです。createPaymentWrapperがミドルウェアとして決済の検証・実行をすべて担い、ハンドラーはビジネスロジックだけに集中できます。

Part 4: エージェントクライアントの実装

Part 3で構築したサーバーに対して、x402対応のMCPクライアントを作成します。このクライアントがAIエージェントの「財布」として機能し、402レスポンスを受け取ったら自動的にSolana USDCで決済を行います。

クライアントの作成

src/client.tsを作成します。ポイントは3つです。

  1. Import pathの使い分け — クライアント側のExactSvmScheme@x402/svm(デフォルトexport)からインポート。サーバー側の@x402/svm/exact/serverとは別物
  2. createx402MCPClient(小文字のxに注意)— 標準MCPクライアントをラップし、402レスポンスの自動処理を追加
  3. autoPayment: true — 402を受け取ったら自動的にトランザクション構築・リトライ
// src/client.ts
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { createx402MCPClient } from "@x402/mcp";
import { ExactSvmScheme, SOLANA_DEVNET_CAIP2 } from "@x402/svm";
import { createKeyPairSignerFromBytes } from "@solana/kit";

async function main() {
  // 1. Solanaウォレットのセットアップ
  const { base58 } = await import("@scure/base");
  const signer = await createKeyPairSignerFromBytes(
    base58.decode(process.env.SOLANA_PRIVATE_KEY!),
  );
  console.log(`Address: ${signer.address}`);

  // 2. x402対応MCPクライアントを作成
  const client = createx402MCPClient({
    name: "my-ai-agent",
    version: "1.0.0",
    schemes: [
      {
        network: SOLANA_DEVNET_CAIP2,
        client: new ExactSvmScheme(signer),
      },
    ],
    autoPayment: true,
    onPaymentRequested: async (context) => {
      const price = context.paymentRequired.accepts?.[0];
      console.log(`Payment: ${price?.amount} on ${price?.network}`);
      return true;  // trueを返すと自動支払い
    },
  });

  // 3. MCPサーバーに接続(StreamableHTTPトランスポート)
  const transport = new StreamableHTTPClientTransport(
    new URL("http://localhost:4022/mcp"),
  );
  await client.connect(transport);

  // 4. ツール一覧の取得
  const tools = await client.listTools();
  console.log("Available tools:", tools.tools.map(t => t.name));

  // 5. 無料ツールの呼び出し
  const pingResult = await client.callTool("ping", {});
  console.log("Ping:", pingResult);
  // → { content: [{ text: "pong" }], paymentMade: false }

  // 6. 有料ツールの呼び出し(自動決済フロー)
  const weatherResult = await client.callTool(
    "premium_weather",
    { city: "Tokyo" },
  );
  console.log("Weather:", weatherResult);
  // → paymentMade: true(決済が試行された)

  await client.close();
}

main().catch(console.error);

実行

サーバーとクライアントを別ターミナルで起動し、決済フローを確認します。

export SOLANA_WALLET_ADDRESS="your-solana-wallet-address"
npx tsx src/server.ts
export SOLANA_PRIVATE_KEY="your-base58-encoded-private-key"
npx tsx src/client.ts

結果の読み方

無料ツールpingpaymentMade: falseでそのまま結果が返り、有料ツールpremium_weatherでは以下が自動実行されます。

  1. サーバーが402 Payment Requiredを返却
  2. クライアントがSolana USDCトランザクションを構築・署名
  3. Facilitatorが検証・決済完了
  4. リトライで結果取得 → paymentMade: true

DevnetではクライアントウォレットにSOL(ガス代)とUSDC(決済用)の両方が必要です。

USDC残高がない場合、Facilitatorのシミュレーションでtransaction_simulation_failedとなります。これは正常な動作で、決済フロー自体は正しく動作していることを示します。

Part 5: MCPクライアントのx402対応状況

Part 4では@x402/mcpcreatex402MCPClientを使って決済フローが動くことを確認しました。では、Claude CodeやCursorといった既存のMCPクライアントからはどうでしょうか。

現状: ネイティブ対応はまだない

2026年2月時点で、主要なMCPクライアント(Claude Code, Cursor, Gemini等)はx402の決済フローにネイティブ対応していません。MCPツールの発見と呼び出しはできますが、サーバーが402 Payment Requiredを返した時点で止まります。

これはMCPプロトコル自体に決済の概念がないためです。x402の決済処理 — 402レスポンスの解析、Solanaトランザクションの構築・署名、リトライ — はPart 4で実装したcreatex402MCPClientのようなx402対応レイヤーが必要です。

MCPプラグインによる拡張

ネイティブ対応を待たずとも、MCPプラグインやプロキシを経由して既存クライアントにx402決済機能を追加できます。

ツール方式概要
Payments MCP (Coinbase)
MCPサーバー
Claude Code, Cursor, Gemini等にウォレット・決済機能を付与。Solana対応。npx @coinbase/payments-mcp で導入可能
Proxy
402レスポンスを透過処理するプロキシ。現時点ではEVMのみ(Part 7で詳述)
MCPサーバー
70以上のx402対応APIをMCP経由で検出・決済・利用
ClawRouter (OpenClaw)
LLMルーター
OpenClaw向け。x402マイクロペイメントでLLM推論費用を支払い

各プラグインのセットアップ手順はそれぞれのドキュメントを参照してください。記事で個別に解説するとかなり長くなってしまうため、今回はスコープ外とします。本記事ではPart 4のcreatex402MCPClientによる動作確認をもってx402決済フローの検証としています。

今後の展望

MCPプロトコル自体にネイティブ決済サポートの提案(SEP-2007)が提出されています。payments/listメソッドで支払い方法を発見し、エラーコード-32803で支払い要求を返却する設計で、x402 V2が最初のサポート対象プロトコルとして想定されています。

これが採用されれば、Claude CodeやCursorといったメジャーなMCPクライアントがプロトコルレベルでx402決済をネイティブ処理できるようになります。そうなったとき、Part 3で構築したサーバーはコードを一切変更せずに、あらゆるAIエージェントから決済付きで利用可能になります。

Part 6: 本番デプロイに向けて

Devnetからメインネットへ

// config.ts - メインネット設定
export const paymentRequirements: PaymentRequirements[] = [
  {
    scheme: "exact",
    network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",  // メインネット
    amount: "10000",  // 0.01 USDC
    asset: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",  // USDC メインネット
    payTo: process.env.SOLANA_WALLET_ADDRESS!,
    maxTimeoutSeconds: 60,
    extra: {},
  },
];

複数ネットワーク対応

V2のResourceInfo分離により、同一ツールに複数の支払い方法を提示できます。

const paymentRequirements: PaymentRequirements[] = [
  // Solana USDC
  {
    scheme: "exact",
    network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
    amount: "10000",
    asset: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    payTo: "your-solana-wallet",
    maxTimeoutSeconds: 60,
    extra: {},
  },
  // Base USDC
  {
    scheme: "exact",
    network: "eip155:8453",
    amount: "10000",
    asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    payTo: "your-evm-wallet",
    maxTimeoutSeconds: 60,
    extra: { name: "USDC", version: "2" },
  },
];

Facilitatorの選択肢

Solanaをサポートするx402 Facilitatorは複数存在します。

FacilitatorURL特徴
Coinbase CDP
https://x402.org/facilitator
公式。月1,000tx無料。Devnet/Mainnet対応
Thirdweb
https://api.thirdweb.com/v1/payments/x402/
Solana Mainnet/Devnet対応。統一API
PayAI
https://facilitator.payai.network
Solana特化。ガスレス体験
Kora
Solana Foundation推奨

Hooksによる拡張

createPaymentWrapperはライフサイクルフックをサポートしています。

const wrapWithPayment = createPaymentWrapper(resourceServer, {
  accepts: paymentRequirements,
  hooks: {
    // 決済検証後、ツール実行前
    onBeforeExecution: async ({ payment, args }) => {
      console.log("Payment verified, executing tool...");
      // falseを返すとツール実行をキャンセル
      return true;
    },
    // ツール実行後、決済前
    onAfterExecution: async ({ payment, result }) => {
      console.log("Tool executed, settling payment...");
    },
    // 決済完了後
    onAfterSettlement: async ({ settlement }) => {
      console.log("Settlement:", settlement.transaction);
      // 売上レコードの保存、メトリクスの記録等
    },
  },
});

Part 7: MCPエコシステムとx402

@x402/mcp以外にも、x402 × MCPの実装は急速にエコシステムが広がっています。

主要な実装

パッケージ/プロジェクト言語特徴
TypeScript
Coinbase公式。サーバー/クライアント両方
TypeScript
Proxy対応。Claude Desktopとの統合
Go
mcp-go拡張。Solana/EVM両対応
TypeScript
MetaMask提供のMCPサーバー
TypeScript
MCP × x402インフラ

@civic/x402-mcp のProxy機能

@civic/x402-mcpは、x402に対応していないMCPクライアント(Claude Desktop等)でも支払い対応MCPサーバーを利用できるProxy機能を提供しています。

  • Client Proxy: クライアント側にProxyを立て、402レスポンスを透過的に処理
  • Server Proxy: 既存のMCPサーバー(APIキー認証)の前段にProxyを立て、x402決済でアクセス制御

SEP-2007: MCPネイティブの決済サポート

MCPプロトコルレベルでの決済標準化については、Part 5で詳しく解説しています。

実装時のハマりどころ

この記事のコードは実際に動作確認済みのデモリポジトリに基づいています。実装中に遭遇した落とし穴をまとめます。

問題原因解決策
parsePrice is not a function
サーバーで@x402/svmのクライアント用ExactSvmSchemeを使用
@x402/svm/exact/serverからインポート
getSupported is not a function
x402ResourceServerに生のURLを渡した
HTTPFacilitatorClientインスタンスを渡す
Cannot POST /mcp (404)
Express 5 + StreamableHTTPの組み合わせ
createMcpExpressApp() + isInitializeRequest() + req.bodyを第3引数で渡す
@solana/kitの鍵が抽出不可
generateKeyPairSigner()はnon-extractableなCryptoKeyを生成
@solana/web3.js v1のKeypair.generate()を使用

まとめ

この記事では、x402 V2のSolana実装を3つのレベルで解説しました。

  1. プロトコルレベル: SVM exact schemeの14ステップ、PaymentPayload構造、Facilitator検証ルール
  2. SDKレベル: @x402/svmのトランザクション構築、@x402/mcpのPayment Wrapper
  3. 実装レベル: 支払い対応MCPサーバーとクライアントの構築、実際のDevnet接続テスト

Solanaの低コスト・高速決済とx402のHTTPネイティブ設計が組み合わさることで、AIエージェントが1回のツール呼び出しに$0.001から課金できる世界が現実になっています。Facilitatorがガス代を負担するモデルにより、クライアントはSOLすら持つ必要がありません。

参考リンク

デモリポジトリ

公式ソース

Solana x402ガイド

MCP × x402

シリーズ記事

この記事が参考になったら

X (Twitter)Xをフォローする株式会社hanzochangお問い合わせフォーム
picture
hanzochang - 半澤勇大
慶應義塾大学卒業後、Webプランナーとして勤務。 ナショナルクライアントのキャンペーンサイトの企画・演出を担当。 その後開発会社に創業メンバーとして参加。 Fintech案件や大手企業のDXプロジェクトに関わり、その後個人事業主として独立し、 2023年にWeb3に特化した開発会社として法人化しました。 現在はWeb3アプリ開発を中心にAI開発フローの整備を行っています。
また、趣味で2017年ごろより匿名アカウントでCryptoの調査等を行い、 ブロックチェーンメディアやSNSでビットコイン論文等の図解等を発信していました。
X (Twitter)

最新の記事

x402 V2 解説 | Solana等マルチチェーン対応・OpenClawの自動売買にも利用されるHTTP決済プロトコル

x402 V2 解説 | Solana等マルチチェーン対応・OpenClawの自動売買にも利用されるHTTP決済プロトコル

累計$600M超のボリュームを記録するHTTP決済プロトコルx402がV2で大幅進化。Solana等のマルチチェーン対応、Extensions拡張機構、Discovery APIを解説。OpenClaw FoundryやClawRouterなどAIエージェントの自動売買での採用事例も紹介します。

x402とは? AIエージェント × MCP × 暗号資産が交差するHTTP自動決済プロトコル

x402とは? AIエージェント × MCP × 暗号資産が交差するHTTP自動決済プロトコル

HTTP 402を活用した決済プロトコル「x402」の入門ガイド。暗号資産(USDC)即時決済、AIエージェントによるMCP経由の自動購入、マルチチェーン対応まで、全体像を解説します。

ClaudeCode スマホでリモート接続 - OpenClawはもう不要?! RemoteControlを使おう

ClaudeCode スマホでリモート接続 - OpenClawはもう不要?! RemoteControlを使おう

Claude Code Remote Controlを使えば、PCで動作中のセッションをスマホやタブレットからそのまま操作できます。QRコード接続、セキュリティ設定、トラブルシューティングまで解説します。

OpenClaw × Solana 事例まとめ - 公式スキル・周辺プロダクト・使いどころ

OpenClaw × Solana 事例まとめ - 公式スキル・周辺プロダクト・使いどころ

OpenClawとSolanaが交わる実例(公式スキル、周辺プラグイン、取引・監視系プロジェクト、ハッカソン)を一次ソースのリンク付きで一覧にまとめます。

OpenClaw APIトークン節約 - Happy + Claude Codeで出先から開発・指示出し

OpenClaw APIトークン節約 - Happy + Claude Codeで出先から開発・指示出し

Happyのプロセス切断をOpenClawで復帰させ、出先からいつでもClaude Code / Codexにリモートアクセスする方法を解説。APIトークンの節約と、スマホ1台で完結するAI開発ワークフローを紹介します。

FDE(Forward Deployed Engineering)とは - 中小企業のAI導入で活きる場面

FDE(Forward Deployed Engineering)とは - 中小企業のAI導入で活きる場面

FDE(Forward Deployed Engineering)はPalantirが体系化した顧客現場型エンジニアリング手法です。中小企業のAI導入においてFDEが活きる具体的な場面と、従来のSIer・コンサル・SaaSとの違いを解説します。