MCPでAIエージェントをレシートローラーに接続する

MCP AIエージェント Claude OAuth PKCE クイックスタート

この記事では、AIエージェントをレシートローラーのMCPサーバーに接続し、売上・商品・予約などのデータを会話から扱えるようにします。前半は Claude をコネクタとして接続する手順、後半は自分で MCP クライアントを作る開発者向けに、メタデータの取得、動的クライアント登録、認可コード+PKCE によるトークン取得、curl での initialize / tools/list / tools/call の呼び出し、Node.js スクリプトでの自動化までを順に試します。

はじめに必要なもの

  • レシートローラーのアカウントと、そのアカウントが所属するビジネスアカウント。ツールが返すのは、そのビジネスアカウントにある実データです(テスト用の環境はまだありません)。
  • MCP 接続のために開発者ポータルでアプリを登録する必要はありません。クライアントは動的クライアント登録で自分を登録します。
  • 後半を試す場合は curl、openssl、Node.js 20 以降。

ツールは、接続したユーザー本人のアクセストークンでレシートローラーの API をそのまま呼び出します。そのため、API 側のスコープチェックと権限チェックがそのまま適用されます。承認されていないスコープが必要な操作や、ユーザーの役割で許可されていない操作(たとえば売上を見る権限のない店舗の売上)は実行できません。

接続情報

項目値
MCP エンドポイントhttps://mcp.receiptroller.io/mcp(/mcp まで必要です。ルートへの POST は 405 になります)
通信方式HTTP POST、JSON-RPC 2.0。プロトコルバージョン 2025-03-26
認可エンドポイントhttps://receiptroller.io/oauth/authorize
トークンエンドポイントhttps://receiptroller.io/api/v1/auth/token
クライアント登録POST https://mcp.receiptroller.io/register

要求されるスコープ

MCP クライアントが要求できるスコープは次の 14 個です。同意画面ではこのうちクライアントが要求したものが並びます。

スコープ主に使うツール
sales.readsales_get_kpis などの sales_*
store.products.read / store.products.writepim_list_products、pim_get_product / pim_create_product、pim_update_product
store.orders.read / store.orders.writeoms_*
store.inventory.read / store.inventory.writewms_*
store.reservations.read、store.memberships.read、store.passes.readreservation_*、membership_list_members、pass_list_holders(閲覧のみ)
sns.content.read、sns.content.write、sns.audience.read、analytics.readsns_*

各スコープの意味は OAuthスコープ一覧と利用可否 を参照してください。

パート1:Claude を接続する

  1. Claude(claude.ai または Claude Desktop)のコネクタ設定で、カスタムコネクタを追加します。
  2. URL に https://mcp.receiptroller.io/mcp を入力して保存します。
  3. 接続を始めるとブラウザでレシートローラーのログイン画面が開くので、ログインします。
  4. 同意画面を確認して「許可する」を押します。Claude の画面に戻ると接続済みになります。

同意画面には、接続するクライアントの名前と「あなたのレシートローラーアカウントへのアクセスを要求しています。ビジネスアカウントはリクエストごとに選択します。」という説明、そして「要求されるアクセス権限」としてスコープが分類ごとにチェックボックスで並びます。すべてチェック済みで表示され、外したスコープは許可されません。すべて外して許可すると接続は拒否扱いになります。ビジネスアカウントはこの画面では選ばず、ツールを呼ぶたびに organizationId で指定します。

試してみる

話しかける例呼ばれるツール
「私のビジネスアカウント全体の今月のKPIを、先月との比較つきで教えて」list_my_organizations → sales_get_kpis
「商品マスタから、名前かSKUに『コーヒー』を含む商品を一覧にして」pim_list_products(search)
「渋谷店の明日の予約を一覧にして」list_my_stores → reservation_list(from / to)

売上のツールは、ビジネスアカウント全体ならビジネスアカウントの管理者の役割、店舗単位ならその店舗の売上を見られる役割が必要です。権限が足りないと、ツールは Upstream 403 で始まるエラーを返します。

パート2:自分の MCP クライアントから接続する

ここからは、Claude が裏で行っている手順を curl で一つずつ実行します。

ステップ1:メタデータを取得する

curl https://mcp.receiptroller.io/.well-known/oauth-protected-resource
{
  "resource": "https://mcp.receiptroller.io/mcp",
  "authorization_servers": ["https://mcp.receiptroller.io"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["sns.content.read", "sns.content.write", "...", "store.passes.read"]
}

同じ内容は /.well-known/oauth-protected-resource/mcp でも返ります。次に認可サーバーのメタデータを取得します。

curl https://mcp.receiptroller.io/.well-known/oauth-authorization-server
{
  "issuer": "https://mcp.receiptroller.io",
  "authorization_endpoint": "https://receiptroller.io/oauth/authorize",
  "token_endpoint": "https://receiptroller.io/api/v1/auth/token",
  "registration_endpoint": "https://mcp.receiptroller.io/register",
  "scopes_supported": ["sns.content.read", "...", "store.passes.read"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["client_secret_post", "none"],
  "code_challenge_methods_supported": ["S256", "plain"]
}

認可とトークンのエンドポイントは mcp.receiptroller.io ではなく receiptroller.io にあります。

ステップ2:クライアントを登録する

curl -X POST https://mcp.receiptroller.io/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My MCP Client",
    "redirect_uris": ["http://localhost:8765/callback"]
  }'

201 で次のレスポンスが返ります。

{
  "client_id": "mcp_xxxxxxxxxxxxxxxxxxxx",
  "client_secret": "{client_secret}",
  "client_id_issued_at": 1793000000,
  "client_secret_expires_at": 0,
  "redirect_uris": ["http://localhost:8765/callback"],
  "client_name": "My MCP Client",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "client_secret_post"
}
  • client_secret はこのレスポンスでしか確認できません。保存しておいてください。
  • 保存されるリダイレクトURLは redirect_uris の最初の 1 件だけです。認可リクエストとトークン交換では、その値を完全一致で使います。
  • client_name は同意画面に表示されます。省略すると Unnamed MCP client になります。
  • redirect_uris が空だと 400 invalid_redirect_uri になります。

ステップ3:PKCE の値を作る

code_verifier=$(openssl rand -base64 48 | tr -d '=+/\n' | cut -c1-64)
code_challenge=$(printf '%s' "$code_verifier" | openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '=\n')
echo "$code_verifier"; echo "$code_challenge"

ステップ4:ブラウザで認可する

次のURLをブラウザで開きます(改行は読みやすさのためです。実際は 1 行にし、値は URL エンコードしてください)。

https://receiptroller.io/oauth/authorize
  ?response_type=code
  &client_id={client_id}
  &redirect_uri=http%3A%2F%2Flocalhost%3A8765%2Fcallback
  &scope=sales.read%20store.products.read%20store.reservations.read
  &state={state}
  &code_challenge={code_challenge}
  &code_challenge_method=S256

ログインして「許可する」を押すと、http://localhost:8765/callback?code={code}&state={state} にリダイレクトされます。「拒否する」を押した場合は ?error=access_denied が付きます。認可コードの有効期限は 10 分で、1 回しか使えません。

エラー(400)原因と対処
unsupported_response_typeresponse_type=code を指定してください。
invalid_clientclient_id が登録されていません。ステップ2の値を確認してください。
invalid_redirect_uri登録した最初の redirect_uris と完全一致していません。

ステップ5:コードをトークンに交換する

curl -X POST https://receiptroller.io/api/v1/auth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=authorization_code \
  -d code={code} \
  -d redirect_uri=http://localhost:8765/callback \
  -d client_id={client_id} \
  -d client_secret={client_secret} \
  -d code_verifier={code_verifier}
{
  "access_token": "{access_token}",
  "refresh_token": "{refresh_token}",
  "token_type": "Bearer",
  "expires_in": 28800,
  "scope": "sales.read store.products.read store.reservations.read"
}

レスポンスには互換用に accessToken などのキャメルケースのキーも含まれます。アクセストークンの有効期限は 8 時間です。メタデータには none も載っていますが、動的登録したクライアントはシークレットを持つクライアントとして扱われるため、client_secret を必ず送ってください。

エラー(401)原因と対処
invalid_clientclient_id または client_secret が違います。
invalid_grantコードが期限切れ・使用済み、redirect_uri が認可時と違う、code_verifier がない、または PKCE の検証に失敗しました。error_description で理由を確認し、ステップ4からやり直してください。

このエンドポイントは invalid_grant でも 400 ではなく 401 を返します。期限が切れたら grant_type=refresh_token と refresh_token、client_id、client_secret で更新できます。詳しくは はじめてのAPI呼び出し:アプリ登録から取引一覧の取得まで を参照してください。

ステップ6:initialize

curl -X POST https://mcp.receiptroller.io/mcp \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-03-26",
    "capabilities": { "tools": {} },
    "serverInfo": { "name": "Receipt Roller MCP", "version": "0.1.0" }
  }
}

レスポンスは毎回 1 つの JSON で返ります。セッションIDのヘッダーやストリーミングはなく、各リクエストは独立しています。サーバーが扱うメソッドは initialize、tools/list、tools/call、ping で、それ以外(notifications/initialized を含む)には -32601 のエラーが返ります。この応答は無視してかまいません。

ステップ7:tools/list

curl -X POST https://mcp.receiptroller.io/mcp \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "list_my_organizations",
        "description": "List every ReceiptRoller organization the authorized user belongs to. ...",
        "inputSchema": { "type": "object", "properties": {}, "additionalProperties": false },
        "annotations": { "readOnlyHint": true, "idempotentHint": true }
      },
      { "name": "list_my_stores", "...": "..." },
      { "name": "sales_get_kpis", "...": "..." }
    ]
  }
}

一覧には、トークンのスコープに関係なくすべてのツールが含まれます。スコープが足りないツールは、呼んだときにエラーになります。

ステップ8:tools/call

curl -X POST https://mcp.receiptroller.io/mcp \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list_my_organizations","arguments":{}}}'
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [ { "type": "text", "text": "1 organization(s)" } ],
    "structuredContent": {
      "organizations": [
        { "id": "{organizationId}", "name": "サンプル商店", "role": "SuperUser", "roles": ["SuperUser"] }
      ],
      "page": 1,
      "pageSize": 1000,
      "totalCount": 1,
      "totalPages": 1
    }
  }
}

データは structuredContent に入ります。ここで得た id を、ほかのツールの organizationId に渡します。たとえば KPI は次のように取得します。

curl -X POST https://mcp.receiptroller.io/mcp \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"sales_get_kpis","arguments":{"organizationId":"{organizationId}"}}}'

ステップ9:Node.js から呼び出す

アクセストークンを環境変数で渡すと、ツール一覧を表示し、list_my_organizations を呼び出すスクリプトです。追加のパッケージは不要です。

// mcp-check.mjs  —  node mcp-check.mjs
const MCP_URL = "https://mcp.receiptroller.io/mcp";
const accessToken = process.env.RR_ACCESS_TOKEN;
if (!accessToken) {
  console.error("Set RR_ACCESS_TOKEN first.");
  process.exit(1);
}

let nextId = 1;

async function rpc(method, params = {}) {
  const response = await fetch(MCP_URL, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${accessToken}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ jsonrpc: "2.0", id: nextId++, method, params })
  });

  if (response.status === 401) {
    throw new Error(`401: ${response.headers.get("www-authenticate")}`);
  }
  const body = await response.json();
  if (body.error) {
    throw new Error(`JSON-RPC ${body.error.code}: ${body.error.message}`);
  }
  return body.result;
}

const { tools } = await rpc("tools/list");
console.log(`${tools.length} tools:`);
for (const tool of tools) console.log(`  ${tool.name}`);

const result = await rpc("tools/call", { name: "list_my_organizations", arguments: {} });
if (result.isError) {
  console.error("Tool error:", result.content[0].text);
  process.exit(1);
}
for (const org of result.structuredContent.organizations) {
  console.log(`${org.id}  ${org.name}  (${org.roles.join(", ")})`);
}
RR_ACCESS_TOKEN={access_token} node mcp-check.mjs

よくあるエラー

401 と WWW-Authenticate

Authorization ヘッダーがないと、本文なしの 401 に次のヘッダーが付きます。

WWW-Authenticate: Bearer resource_metadata="https://mcp.receiptroller.io/.well-known/oauth-protected-resource/mcp"

MCP クライアントはこのURLからメタデータをたどって認可を始めます。トークンが期限切れ・取り消し済み・不正な場合は error="invalid_token" が加わります。リフレッシュトークンで更新するか、認可をやり直してください。

JSON-RPC のエラーとツールのエラー

返り方意味と対処
HTTP 400、error.code が -32700本文が JSON として読めません。
error.code が -32601サーバーが扱わないメソッドです。
result.isError が true、organizationId is required...先に list_my_organizations で ID を調べて渡してください。予約系は storeId も必要です(list_my_stores)。
result.isError が true、Upstream 403トークンに必要なスコープがないか、ユーザーがそのビジネスアカウント・店舗でその操作を許されていません。詳しくは 403/401が返る(権限・スコープ) を参照してください。
result.isError が true、Unknown tool: ...ツール名が違います。tools/list の名前を使ってください。

次のステップ

公開日: 2026-10-26 更新日: 2026-10-26