Skip to main content
Glama

Shopify MCP デモ

Shopify の実際のストアから ChatGPT や Claude 内で買い物をし、Cashfree で支払いを完了できます — カタログ、カート、OTP ログイン、保存済み住所、支払いを、会話から離れることなく行えます。

"show me shirts from the store"
      ↓  SearchProducts
  product grid  ⇄  product detail
        └────────┬───────┘
                 ↓
  cart  →  phone  →  OTP  →  address  →  payment
                                            ↓
                            Cashfree  →  order summary

すべての支払い経路は同じ画面で終了します: 購入された商品、数量と価格、注文 ID とステータスが表示されます。 Cashfree は実際に資金が移動したことを確認しますが、Shopify のカートは見えないため、何が含まれていたかはわかりません。

支払い後に再度検索すると、ウィジェットは新しいショッピングセッションを開始します — 新しいカート、残ったレシートはありません。1 回の会話で 2 回購入することもできます。

仕組み

サーバーは 3 つの役割を同時に果たします:

  • AI ホストに対する MCP サーバー として、モデル向けのツール 1 つ (SearchProducts) とウィジェットリソースを公開します

  • Shopify に対する MCP クライアント として、https://{SHOP_DOMAIN}/api/ucp/mcp に対して JSON-RPC で通信します — 認証は不要で、ショップドメインがすべての設定です

  • Cashfree に対する REST クライアント として、注文作成、OTP ログイン、保存済み住所、注文ステータスを処理します

React ウィジェットが全体の流れを管理します。モデルに到達するのは商品検索のみで、それ以降はすべてウィジェット対サーバーであり、フローは決定論的に保たれます。

閲覧は 2 画面です。グリッドには各商品の価格帯とオプション数がカードで表示され、タップすると説明、バリアント選択、カート追加ボタンがある詳細画面が開きます。単一バリアントの商品はカードから直接追加でき、すでにカートにある商品には数量ステッパーが表示されます — つまり、一般的なケースは 1 タップで済み、実際に選択が必要な場合のみ画面が表示されます。両方の画面で現在のカート内容がバッジ表示され、2 つのカウントは同じ関数から得られます。

Related MCP server: Shopify Agentic MCP Gateway

セットアップ

npm install
cp .env.example .env      # set SHOP_DOMAIN and the Cashfree keys
npm run build
npm start

公開します (ngrok http 8787)、.env の SERVER_URL を公開オリジンに設定し、再起動してから、ホストのコネクターとして <公開オリジン>/mcp を追加します。

再起動だけで十分です — 再ビルドは不要です。オリジンは起動時に読み込まれ、リソースが提供されるたびにウィジェット HTML に注入されます。

変数

目的

SHOP_DOMAIN

ストア。この行を編集して再起動するとストアを切り替えられます。

UCP_AGENT_PROFILE

すべての UCP 呼び出しで必須。Shopify の公開サンプルプロファイルでデモには十分です。

CASHFREE_ENV

sandbox(デフォルト)または production。

CASHFREE_CLIENT_ID / CASHFREE_CLIENT_SECRET

ダッシュボード → 開発者 → API キー。

CASHFREE_RETURN_URL

Cashfree が購入者を戻す URL。デフォルトは安定したホストページ。

SERVER_URL

トンネリング時の公開オリジン。

PORT

デフォルトは 8787。

PAYMENT_ANNOTATIONS

honest(デフォルト)または readonly。.env で切り替えて再起動すると両方のディスパッチ経路をテストできます。readonly にすると支払いツールが読み取り専用であると主張するため、ホストがそれらをディスパッチします — 診断のみ、上記参照。シェル変数はファイルを上書きします。なぜなら --env-file は既に設定された環境変数を上書きしないからです。

実行時に期待されること

支払いは、保存済みカードを除いて機能します。 ホストは MCP アノテーションに基づいて支払いツールをゲートします。cashfree-here は正直なアノテーションを提供します — カードに課金するツールに対しては { readOnlyHint: false, destructiveHint: true } — これらに対してホストはディスパッチを拒否します: モデルが意図を形成し、ホストがツールのウィジェットテンプレートをプリフェッチし、tools/call は決して届きません。

PAYMENT_ANNOTATIONS=readonly に設定すると、それらを { readOnlyHint: true, destructiveHint: false } に上書きし、5 つのツールのうち 4 つがディスパッチされます: UPI、ネットバンキング、ホスト型チェックアウト、新しいカード — そして、ハンドオフが行われれば保存済みカードも。

このフラグは計測のためのものであり、修正ではありません。資金を移動させるツールが何もしないと主張することになり、それを捕捉するために存在する正確な制御に対する嘘です。デフォルトではオフになっており、最初の使用時に警告を表示し、出荷してはいけません。

ツールがブロックされた場合、ウィジェットはその旨を表示し、Cashfree のリンクを提供します。これは機能します。タブで支払い、戻ってくると、ウィジェットが注文を確認します。

ハンドオフは遅延するが失われるわけではない — そして「ブロックされた」が誤りである可能性もあります。 このファイルには以前、ウィジェットからモデルへのハンドオフが約半分の確率で失敗すると書かれていました。あるキャプチャされたセッションは別のことを示しています: CheckoutTool はウィジェットの 2 回の試行 (2 × 4 秒) 後にブロックされたと宣言され、購入者は Cashfree リンクを取り、その後 tools/call がとにかく到着しました — クリックから約 2-4 秒後、つまり全行程で約 12-20 秒です。サーバーは 37ms で処理しました。その遅延のすべてのミリ秒は上流にありました。

sendFollowUpMessage はホストにツールの実行を依頼するものではありません。ユーザーターンを投稿し、そのメッセージが 配信 された時点で解決するため、ウィジェットの確認ウィンドウはエンキュー時に開始され、その後ホストのインフラストラクチャ上でのモデル推論ターン全体を測定します — 4 秒のタイムアウトはそれに対して調整されていませんでした。

結果は遅い画面よりも悪いものです: 購入者は外部リンクで支払いをしている間に、同じ payment_session_id の Cashfree ウィジェットが背後にレンダリングされていました。1 つの注文に対して 2 つのアクティブな支払い画面です。DISPATCH_ATTEMPTS = 2 はフォローアップを 2 回送信するため、ウィジェットも 2 つになる可能性があります。

「半分の確率で失敗する」という読み取りは、私たち自身のバグでした。 MCP Apps トランスポートが提供する 1 つの postMessage チャンネル上で 2 つの App インスタンスが開かれていました: useMcpApp がレンダリング用に 1 つを構築して接続し、getClientPlatform() が支払いハンドオフ用に別の 1 つを構築して接続していました。ハンドシェイクが競合し、負けた方がその後すべてに対して 「接続されていません」 と応答しました — 購入者が支払い方法を選択しても、tools/call がサーバーに届くことはありませんでした。コインフリップのハンドオフは、2 方向の競合のように見えるものであり、信頼性の低いホストではありません。フックは共有クライアントをサブスクライブするようになり、connect() は冪等になり、アンマウント時に React ツリーよりも長生きするシングルトンに対して close() を呼び出さなくなりました。

この修正以降、すべてのディスパッチは初回で成功しています。attemptsFor() は MCP Apps ホストでは既に 1 を返すため、リトライは LegacyOpenAiClient を使用する ChatGPT にのみ適用され、この競合は発生しませんでした — その削除を正当化する測定値はないため、そのまま残しています。

UPI は ₹1,00,000 を超えると失敗します。 「この注文では支払い方法が利用できません」というエラー — UPI の 1 取引あたりの制限で、₹99,600 (OK) / ₹100,800 (失敗) で確認済みです。ネットバンキングとホスト型チェックアウトはより高い制限があります。カートに上限はないため、+ を数回タップすると警告なしにそれを超える可能性があります。

ホストウィンドウのリロードは安全です、両方のホストで。 カート、チェックアウト手順、保存済み住所、支払い画面はすべて復元されます。カート本体は再取得ではなく永続化された状態から復元されるため、Claude ではリロード時に Shopify への呼び出しは発生しません。ChatGPT はツール結果を再配信しないため、search_catalog を 1 回呼び出して商品グリッドを再構築するだけで、それ以上はありません。いくつかのステップでリロードを含む 4 つのフローで測定: 上流呼び出し 18 回、重複なし。

ビルド ID は支払い画面に表示されます。 ホストはウィジェットインスタンスをキャッシュし、キャッシュされたものは最新のものと区別がつきません — デバッグの数ラウンドが、既に削除されたコードに費やされました。ビルド ID が実行中のサーバーと一致しない場合、古いウィジェットを見ています。

再ビルドはブラウザに到達せず、新しいチャットも同様です。 ウィジェット URI にはビルド ID が含まれているため、すべてのビルドは別個のリソースですが、それでも十分ではありません: Claude は再ビルド および 新しい会話の間、キャッシュされたウィジェットを提供し、ログに resources/read がまったくありませんでした。デバッグの 2 ラウンドが、決して実行されなかった計装に費やされました。再読み取りを強制する唯一の信頼できる方法は、コネクターを切断して再接続することです。ログに POST /mcp (resources/read ui://widget/shopify-store-<build>.html) があることを確認してから、見たものを信頼してください。

再接続しても完全ではありません: ホストのキャッシュされたツールメタデータは以前のビルドの URI を参照する可能性があるため、新しい会話でもこのサーバーにないビルド ID を要求する場合があります。サーバーはいずれのビルド ID にも応答します — 以下の「ウィジェット URI のバージョニングによる退避」を参照 — しかし、その resources/read 行の ID はホストのものであり、必ずしも実行中のサーバーのものではありません。支払い画面の window.__BUILD__ が、どのバンドルが実行されているかを示します。

  • カード入力はClaudeではレンダリングできず、上流で修正されることもありません。 Cashfree Elementsは、そのPCIフィールドをネストされたクロスオリジンiframeとしてマウントします。 Claudeはframe-src 'self' blob: data:を強制し、UIリソースが宣言するframeDomainsを無視するため、フィールドは空でクリックできないボックスとして読み込まれます。これはポリシーであり、転送中のバグではありません。Anthropicのエンジニアは2026-04-09にclaude-ai-mcp#40で、セキュリティ上の理由からネストされたiframeは許可されていないと述べ、その後の「恒久的か一時的か」という2つの質問には回答がありませんでした。connectDomainsとresourceDomainsは4月に修正され、実際に機能します。ブロックされているのはフレーミングのみであるため、ウィジェットからこのサーバーへのfetchは問題ありません。

    Stripeも同じ壁に直面しました。StripeのMCP Appsドキュメントでは、カードフィールドを埋め込む代わりに、ホストされたCheckoutページをapp.openLink()で開くことを推奨しています。これは、ここでのCheckoutToolと同じ形状であり、現在も機能しており、Claude上でカードを扱う推奨パスです。

    会話内でカード入力を維持することは可能です。プレーンな<input>フィールド(iframeなし)をこのサーバーに直接POSTする方法で、これはcashfree-hereがコミット55da139でElementsに置き換えられる前に出荷されていたものです。これにより、生のPANがサーバー(SAQ-D領域)を通過し、3DSは依然として外部にリダイレクトされるため、フォームは手に入りますが、フローは手に入りません。構築されていません。技術的な決定ではなく、製品上の決定です。

  • 保存済みカード支払いがCashfree内で失敗します。 CardPaymentToolは保存済みカードを正しく表示および一覧表示しますが、それらで支払いを行うと、/pg/orders/sessions/jsからHTTP 500 {"message":"Internal Server Error"}が返されます。1つの注文で切り分けました。UPIとネットバンキングは、同じpayment_session_idとヘッダーに対して200を返します。payment_method.card.instrument_idブランチのみが、CVVの有無にかかわらず500を返します。汎用の500はCashfree側の障害です。Cashfreeのバリデーションエラーは、特定のメッセージとともに400を返します。Cashfree側の対応が必要です。

  • UPIは₹1,00,000の制限を超えて提供されており、ピッカーで無効化される代わりにCashfreeで失敗します。

  • cashfree-hereはその場でパッチが適用されています。 2つの修正が、このリポジトリではなく、兄弟のチェックアウトに存在します。useReconciliation.start()は以前のポーリングタイマーをクリアするようになり、支払い成功通知は注文ごとに1回だけ発火するようになりました。これらがないと、支払済みの注文が「Payment completed successfully」を数秒ごとに、永遠にチャットに投稿し続けていました。

  • 選択された住所が注文にバインドされていません。 購入者が住所を選択しても、Cashfreeには通知されません。未解決です。OCCチームからの回答が必要です。

  • Shopifyの注文は作成されません。 注文はCashfree内にのみ存在します。注文を作成するにはShopify Admin APIの認証情報が必要ですが、このプロジェクトは意図的にそれを避けています。

  • セッションストアはインメモリです。 サーバーを再起動すると、処理中のチェックアウトが失われます。

  • オファーとクーポンは延期されています。 両方のAPIはdocs/cashfree-occ-api.mdで実証され、文書化されています。不足しているのはUIのみです。

  • INRのみ、デモストアに合わせています。

これを拡張する人への注意点

実際に時間をかけて確立された知見です。それぞれが推測ではなく、測定に基づいています。

Shopify

  • /api/ucp/mcpのみをターゲットにしてください。古い/api/mcpは2026-08-31以降非推奨であり、すべてのレスポンスでそのように表示されます。

  • 公開されているドキュメントには3箇所の誤りがあります。カタログ/カートのエンドポイント分割が逆であり、UCPがline_items[].item.idを必要とする場合に、カート明細をmerchandise_idとして文書化しています。ここでの型は、ドキュメントではなく、src/lib/ucp/__fixtures__/内のキャプチャされたペイロードに基づいています。

  • update_cartは宣言型です。毎回、完全な希望明細セットを送信します。削除は明細を省略することで表現されます。

  • 金額は最小単位で、通貨はカートレベルで1回保持されます。formatMoneyはIntlから小数点以下の桁数を取得するため、ゼロ小数点通貨は分割されません。

  • パスワードで保護されたストアでも、カタログ、カート、チェックアウトのリンクは提供されます。ストアフロントの閲覧のみがパスワードページにヒットします。

  • search_catalogは商品説明をHTMLとして返し、バリアントはその軸をoptions: [{ name, label }]として保持します。説明は、Reactに到達する前にnormalise.tsでプレーンテキストに変換されます。これはストアが管理するコンテンツであり、OTPを収集する同じ画面でレンダリングされ、その中のフォーマットはインジェクションサーフェスとして価値がありません。

  • 単一バリアントの商品でも、1つのオプション(Shopifyの{ name: "Title", label: "Default Title" }プレースホルダー)があります。これをレンダリングすると、選択肢がない商品の下に「1 titles」と表示されます。

Cashfree

  • x-chxs-idは、Create Orderからのpayment_session_idです。そのため、ログイン前に注文が存在している必要があります。偽造されたものはpayment_session_id_invalidを返します。

  • OCC呼び出しには、正確に3つのヘッダーが必要です。キャプチャされたリクエスト内のブラウザフィンガープリンティング(デバイスID、Forterトークン、Cookie、オリジン)は、どれも強制されません。

  • 住所には、10~185文字の結合された住所行が必要です。それより短いと、UIで手がかりなしに400エラーが返されます(確認しない限り)。

  • 住所作成のレスポンスは{ shipping_address, billing_address }であり、リストではありません。リストとして解析すると、成功時に空の配列が返されます。

  • /api/orders/:idはCashfreeの生のボディをプロキシします。これは、cashfree-hereの調整がその形状を解析するためです。

ウィジェット

  • カードは商品であり、カート明細はバリアントであり、それらは一致しません。 1つのTシャツの3色を1枚のカードにまとめるのは、ブラウジングには適切ですが、ステッパーには曖昧です。RedとBlueを1つずつ保持するカードの下にあるマイナスボタンは、どちらを取り除くかを推測する必要があります。カードは、その商品のバリアントがカートにいくつあるかをカウントし、その数が正確に1つである場合にのみステッパーを提供します。それ以外の場合は、合計数をバッジ表示し、購入者を詳細画面に送ります。拒否する方が、購入者が選んでいないものを削除するよりは安上がりです。

  • 詳細画面はそれ自体の状態を保持しません。 選択された商品とバリアントはウィジェットの状態に保持されます。なぜなら、ウィジェットは購入者がスクロールするたびに再マウントされ(下記参照)、ローカルのuseStateでは選択が失われるからです。これらは新しいsearchIdでクリアされます。そうしないと、前回の検索の商品が新しい結果の上に再び開いてしまいます。

このホスト

  • プラットフォームを非難する前に Access-Control-Allow-Headers を確認すること。
    このファイルは以前、ウィジェット iframe からの GET リクエストがサーバーに到達しないと主張していました。実際には到達します。cashfree-here はその調整 GET で ngrok-skip-browser-warning を送信し、リクエストをプリフライトさせます。私たちの許可リストにはそのヘッダーが含まれていなかったため、ブラウザはプリフライトを拒否し、GET は送信されませんでした。その結果、調整は「支払いステータスを確認できません」と報告し、すでに PAID だった注文に対して「支払い失敗」 を表示しました。

    プラットフォームの壁のように見えた理由は2つあります。ログに GET が一度も現れなかったこと、そしてプリフライトがノイズとしてログから除外されていたことです。そのため、拒否されたリクエストと送信されなかったリクエストの区別がつきませんでした。現在は /api/* のプリフライトがログに記録されています。

    POST 専用のエンドポイント(/api/pay/addresses/list、/api/orders/status)は、この誤った診断に基づいて構築されました。これらは動作しますが、必須ではありません。

  • モデルが呼び出したツールコールのみが、ホストにそのツールの outputTemplate をレンダリングさせます。callTool はハンドラーを実行しますが、何もレンダリングしません。

  • window.open はウィジェット iframe 内でブロックされ、ホストの外部オープンはナビゲーションを引き起こし、支払い処理中に MCP コネクタを強制終了しました。プレーンな <a target="_blank"> だけが機能します。

  • MCP トランスポートはステートレスです(sessionIdGenerator: undefined)。リクエストごとに新しいサーバーを構築しながらセッション ID を発行すると、initialize 以降のすべてが「サーバーが初期化されていません」で失敗します。

  • ウィジェットの状態はウィジェットよりも長く存続するため、すべてのツール結果には日付を付ける必要があります。
    ホストは会話全体の状態を保持し、そこから新しいウィジェットごとに再水和します。そのため、支払い後の検索は screen: "checkout" を保持したまま起動し、「シャツを見せて」という要求に対して以前の領収書を返しました。そして次に追加されたアイテムは、Shopify がすでに完了したカートに入ってしまいました。SearchProducts は呼び出しごとに searchId をスタンプし、ウィジェットは表示していない ID でリセットされるようになりました。

  • 状態を所有しているものが、リセットがクリアすべきものです。 useCart と useCheckoutFlow はマウント時に自身をシードし、渡された値を再読み取りしないため、ウィジェットの状態だけをクリアしても効果はなく、1レンダリング後に古い値をそのまま書き戻してしまいます。セッションは searchId でキー付けされているため、React はそれらを破棄します。

  • リセットはエフェクトではなく、レンダリング中に導出すること。 エフェクトは最初に古い画面を描画します。買い手がズボンを要求すると、以前の注文の「支払い受領」が表示され、その後置き換えられます。

  • ライブウィジェット間での書き込みの順序は保証されません。 会話内の以前のすべてのウィジェットは実行を続け、1つのオリジン全体の localStorage キーに書き込みます。revision カウンターは、インスタンス内で古いスナップショットが新しいものに置き換わるのを防ぎますが、インスタンス間の書き込みを順序付けはしません。各インスタンスが独自のカウンターを増加させるためです。会話ごとに状態をキー付けすれば、適切に修正できます。

  • ウィジェットは見た目よりもはるかに頻繁に再マウントされ、CORS プリフライトがその兆候です。 Claude は買い手がスクロールするたびにウィジェット iframe を破棄して再作成し、HTML を自身のキャッシュから提供します。そのため resources/read は表示されず、再マウントはログに現れません。それを示すのは OPTIONS /api/shop/cart です。プリフライトはドキュメントごとにキャッシュされるため、新しいプリフライトは新しいドキュメントを意味します。22:48:29 と 22:52:25 に測定されましたが、その間はスクロール以外何も起こっていません。ウィジェット内のすべてのラッチ、ref、オブザーバーはそれとともに消滅するため、「マウント時に一度だけ読み込む」はレート制限ではなく、スクロールごと、ウィジェットごとのレートです。

  • IntersectionObserver はウィジェットが画面内にあるかどうかを教えてくれません。 上記の明白な修正は、表示されている場合にのみフェッチすることです。しかし機能しません。ネストされたブラウジングコンテキスト内でルートが null の場合、オブザーバーはホストページのビューポートではなく、その iframe のビューポートに対して測定を行います。画面外に大きくスクロールされたすべてのウィジェットが、完全に表示されていると報告します。構築、テスト、測定、削除 — 3つのウィジェットが依然としてリロードごとにフェッチされていました。

  • ホストが返さないものをキャッシュすること。 再マウントが稀ではなく日常的になると、マウント時に再フェッチされるものは常に再フェッチされます。3つのウィジェットが生きていると、ホストリロードごとに3つのカートが再読み込みされ、Shopify は最終的に 429 Rate limit exceeded を返しました。カートのボディはカート ID とタイムスタンプとともに永続化され、TTL を過ぎた場合のみ再フェッチされるようになりました。3フローのセッションでは、アップストリーム呼び出しが19〜20回から13回に減少し、以前は6回の呼び出しが必要だった2回のリロードは、現在は0回で済みます。

    TTL は最初30秒でしたが、効果はありませんでした。3フローにわたって測定したところ、カートの最後のフェッチから次のマウントまでの間隔は、32秒、41秒、41秒、42秒、53秒、80秒、143秒で、すべてウィンドウを超えていました。現在は10分です。ボディは表示専用であり、数量変更はサーバーから再シードされ、支払いは Shopify のカートから価格設定されるため、古い数値で支払われることはありません。

  • ウィジェット URI をバージョン管理すると古いものが無効になるため、すべてのビルドを提供すること。 URI にはビルド ID(後述)が含まれており、ホストのキャッシュを無効化します。その代償として、再ビルドにより、会話内のすべての既存ウィジェットが作成時に使用した ID が無効になります。ホストは記憶している URI を再読み取りし、サーバーは -32602 Resource not found を返し、それらのウィジェットは「ストアを読み込めませんでした」と表示します。ui://widget/shopify-store-{build}.html の ResourceTemplate が、廃止された ID に対して現在のバンドルを提供するようになり、ウィジェットを壊す代わりにアップグレードします。これは新しい会話でも問題になります。ホストのキャッシュされたツールメタデータが依然として古い URI を参照しているためです。

  • CSP 警告は、意図して配信していないものに関する場合があります。 MCPJam はすべてのツールで https://cdn.openai.com がブロックされていると報告しました。20件の参照は、Apps SDK の ./css バレルによって取り込まれた katex.min.css 内の @font-face ルールによるもので、このウィジェットでは数式をレンダリングしません。そのドメインを宣言すると、Shopify と Cashfree のウィジェットが Claude 内で OpenAI の CDN に依存することになります。代わりに他の6つのシートをパスでインポートすることで、参照と21KBの CSS を削除しました。また、MCP Apps の CSP モデルには scriptSrc がなく、connectDomains、resourceDomains、frameDomains のみであるため、スクリプトソースに関する苦情はサーバーが対応できるものではありません。

  • ストリーミング可能 HTTP の GET レッグは 404 ではなく 405 で拒否すること。 トランスポートはステートレスであるため、開くべきサーバーからクライアントへのストリームはありません。MCPJam は1セッションでそのレッグを97回開き、404 を受け取っても中断しませんでした。そのため、これがそこで問題を引き起こすわけではありません。より厳格なクライアントは initialize の前に諦めると報告されています。404 は「そのようなエンドポイントは存在しない」と読め、これは異なる誤った回答です。

  • リロードのコストはホストによって異なり、支払うのは ChatGPT です。 Claude ではリロードのコストはゼロです。状態とカートボディの両方がストレージから復元されます。ChatGPT ではカタログが再配信されないため、useProducts がこのサーバーにカタログを要求します。リロードごとに1回の search_catalog で、他には何もありません。

エンドポイント

パス

目的

POST /mcp

AI ホスト向けの HTTP 上の MCP

POST /api/shop/cart

Shopify に対するカートの作成/更新

POST /api/shop/search

ツール結果を再配信せずにリロードしたホストのためのカタログ復元

POST /api/pay/order

Shopify カートから価格設定された Cashfree 注文の作成

POST /api/pay/otp、/otp/verify

OTP ログイン

POST /api/pay/addresses/list、/addresses

保存済み住所:読み取りと作成

POST /api/pay/dispatched

支払いツールハンドラーが実際に実行されたか?

POST /api/orders/status

独自の確認画面のための注文ステータス

GET /api/orders/:id

cashfree-here の調整用の生の注文ボディ

ログ

すべてのリクエストはメソッド、パス、ステータス、所要時間を記録します。MCP 呼び出しはメソッドとツールを指定し、リソース読み取りは URI を指定します。POST /mcp だけでは、すべてのホスト呼び出しが同一に見えるため読み取れません。

13:59:48.201 → POST /mcp (tools/call SearchProducts) 200 328ms
13:59:52.884 → POST /api/shop/cart 200 904ms
14:00:03.117 → POST /api/pay/order 200 1026ms
14:00:09.640 ✗ POST /api/pay/addresses 502 121ms

タイムスタンプがあるのは、所要時間だけでは2つのリクエスト間のギャップを測定できないからです。支払いディスパッチが遅れて到着した場合に重要なのはそのギャップだけです。

テスト

npm test          # watch
npm run test:run
npm run type-check

テストはカバーするコードの隣に配置されています。src/lib/ucp/__fixtures__/ のフィクスチャは実際にキャプチャされた Shopify のレスポンスであり、形状の変更はデモではなくテストで失敗します。Cashfree のフィクスチャは手書きで編集済みです。そのセッショントークンはコミットしてはいけません。

ドキュメント

  • docs/cashfree-occ-api.md — OCC 契約、ライブで検証済み。Cashfree の公開ドキュメントにはありません。

  • docs/spikes/2026-08-12-occ-spike.md — スパイクで測定した内容。

  • docs/superpowers/specs/ — 各マイルストーンの設計仕様。

  • docs/superpowers/plans/ — それらから作成されたタスクごとの実装計画。

Related MCP Connectors

Related MCP Servers