shopify-operations-mcp について
shopify-operations-mcpは、Shopifyストアの読み取りと変更を行うMCPサーバーで、書き込み操作に安全策を設けています。変更を事前にプレビューし、対象プランには人の承認を求め、状態変化を検査して、操作をハッシュチェーン監査ファイルに記録します。
主な機能
- 01商品検索と注文一覧の取得
- 02書き込み実行前のプレビュー
- 03対象プランへの承認要求
- 04プレビュー後の状態変化を検出
- 05可逆プランのロールバック
- 06ハッシュチェーン監査記録への操作記録
活用シーン
- 01ストアの商品と注文を確認
- 02承認付きで在庫を一括調整
- 03確定前に価格変更を確認
- 04ロールバック期間内に可逆変更を復元
MCP 対応
MCPクライアントにShopifyの読み書き操作を公開します。書き込みツールはプレビュー、トークン、実行の手順に従い、承認対象または不可逆の操作では人の承認を求めます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
npx -y shopify-operations-mcpSHOPIFY_STORE_DOMAINThe myshopify.com store domain, e.g. my-store.myshopify.com. Overrides shopify.storeDomain from the config file.SHOPIFY_ADMIN_TOKENシークレットShopify Admin API access token. Required and only ever read from the environment — never from the config file.SHOPIFY_CONFIGPath to a config.json with shopify/plans/approvalServer/protectedTags settings. Defaults to ./config.json in the working directory.SHOPIFY_PLAN_TTL_MSHow long a plan token stays valid before it must be executed or expires, in milliseconds. Default 60000.SHOPIFY_APPROVAL_SERVER_PORTPort the localhost human-approval HTTP server binds to (127.0.0.1 only). Default 4319.SHOPIFY_PROTECTED_TAGSComma-separated tags that plans may never modify; any plan touching an item carrying one is refused. Default do-not-touch.SHOPIFY_CALLER_IDIdentity recorded as the caller on every audit log row. Default unknown.SHOPIFY_AUDIT_PATHFile path for the tamper-evident JSONL audit log. Default shopify-operations-audit.jsonl in the working directory.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
Shopify Admin API 操作用の安全な書き込み MCP サーバー
エージェントは、取り返しのつかない事故を起こすことなく、Shopify ストアのデータを読み取り、変更できます。この安全機能が差別化要因です。すべての書き込み操作は確定前にプレビューされ、大規模または不可逆な変更には別経路での人間による承認が必要で、すべてのアクションは改ざんを検知できるハッシュチェーン監査ファイルに記録されます。
アーキテクチャ
flowchart TB
subgraph agent["Agent"]
A[Claude]
end
subgraph mcp["MCP stdio transport"]
T[tools/call]
end
subgraph core["safe-write-mcp-core"]
PS[PlanStore]
AS[ApprovalServer]
end
subgraph shopify["Shopify Admin API"]
GQL[GraphQL endpoint<br/>/admin/api/2026-04/graphql.json]
end
subgraph audit["Audit"]
LF[JSONL audit file<br/>hash-chained]
end
A -->|"MCP stdio"| T
T -->|create plan| PS
T -->|preview| PS
T -->|execute plan| PS
PS -->|awaiting_approval| AS
AS -->|approve/reject| PS
PS -->|execute| GQL
PS -->|record| LF
style PS fill:#e1f5fe
style AS fill:#fff3e0
style LF fill:#f3e5f5
style GQL fill:#e8f5e9
2 フェーズのパターン(プレビュー → トークン → 実行)が中核となる規律です。すべての書き込みツールは次のように動作します。
- プレビュー — 現在の状態を読み取り、変更内容を計算します。変更呼び出しは一切行いません
- トークン — SHA-256 フィンガープリントを使い、プレビュー済みの正確なマニフェストに紐付けたプラン・トークンを発行します
- 承認 —
approvalRequiredAboveItems(デフォルトは 25)を超えるプラン、または常に承認が必要な操作を含むプランは、サーバー起動時に表示されるトークン付き URL(例:http://127.0.0.1:4319/?token=<token>)で人間の承認を待ちます - 実行 — 現在の値を再読み込みし、プレビューから変わっていた場合は拒否(
STATE_CHANGED)してから、項目ごとに変更を適用し、成功・失敗の全件記録を作成します
マニフェストが hardMaxItems(デフォルトは 250)を超えるプランは、即座に拒否されます。トークンも承認経路もありません。
不可逆な操作(cancel_order、refund_order)は、項目数にかかわらず常に承認が必要で、ロールバックできません。可逆な操作(価格変更、在庫調整)は、設定可能な期間内(デフォルトは 24 時間)でロールバックできます。
脅威モデル
リスクは、悪意あるエージェントではありません。エージェントは正しい GraphQL を作成するものとして信頼されています。リスクとなるのは、信頼されているが間違いを犯す可能性のあるエージェント、つまり構文的には完璧で形式も正しいものの、問題は操作の対象範囲にある場合です。
致命的なシナリオ — 小数点の位置を間違えた、構文的には完璧な一括価格変更:
update_prices([...], newPrice: 1.5) ← meant 15.00, typed 1.5
プレビューと承認なしで実行される 500 商品の一括更新や、エージェントの価格計算に誤記がある場合、規模を伴ってまさに誤った結果が生じます。承認があれば誤りを見つけられます。人間が「500 件の価格を $X から $1.50 に変更」と確認して、違和感に気づけるからです。承認がない場合や、実行前に被害を可視化するプレビューがない場合、誤りは誰にも気づかれないまま Shopify に反映されます。
安全性を保証するのは、次の 3 つの仕組みです。
1. プレビューを先に行い、差分を計算する。 すべての書き込みツールは現在の状態を読み取り、変更を行うことなく、マニフェスト({ref, before, after} の組)を計算します。実行時に再読み込みした結果、プレビュー以降に状態が変わっていれば、STATE_CHANGED として書き込みを拒否します。何かが変わる前に影響範囲を確認できます。
2. しきい値以上で承認を必須にする。 >= approvalRequiredAboveItems(デフォルトは 25)件に影響するプランには、人間による承認が必要です。このしきい値は「人間が目を通す必要があるほど大規模か?」を判断するために設定されています。一括での値変更では意味がありますが、1 項目の操作では関係ありません(不可逆な操作は、項目数にかかわらず別途必ず承認が必要です)。
3. プラン・トークンを正確なマニフェストに紐付ける。 トークンは不透明な ID ではなく、プレビュー済みの正確なマニフェストの SHA-256 フィンガープリントです。実行時に対象項目を増やしたり、価格を変えたりするとフィンガープリントが変わり、STATEMENT_MISMATCH として拒否されます。
ロールバックにより、ロールバック期間内であれば、誤った価格や在庫数といった可逆的なミスから復旧できます。不可逆な操作からは復旧できません。キャンセルした注文はキャンセルされたままで、返金した支払いは返金されたままです。
クイックスタート
npm install
npm test
npm run build
必要な環境変数を設定し、Claude Desktop からサーバーを指定します(「設定」を参照)。node dist/index.js を実行すると、MCP stdio サーバーとともに localhost 承認 UI が起動します。
デモ: ステップごとのウォークスルー・スクリプト(ストア全体の価格変更を拒否 → 承認が必要な価格変更 → 1 回の呼び出しでロールバック → ハッシュチェーン監査)は、docs/demo-runbook.md にあります。
設定
設定ファイル(デフォルトでは作業ディレクトリ内の config.json。または SHOPIFY_CONFIG で指定したパス):
{
"shopify": {
"storeDomain": "my-store.myshopify.com",
"apiVersion": "2026-04"
},
"plans": {
"planTtlMs": 60000,
"approvalRequiredAboveItems": 25,
"hardMaxItems": 250,
"maxPriceChangePct": 30,
"rollbackTtlMs": 86400000
},
"approvalServer": {
"enabled": true,
"port": 4319,
"requireAuth": true
},
"protectedTags": ["do-not-touch"],
"callerId": "shopify-operations-mcp"
}
設定リファレンス
| フィールド | 型 | デフォルト | 説明 |
|---|---|---|---|
shopify.storeDomain | string | (必須) | MyShopify ドメイン。例:"my-store.myshopify.com" |
shopify.apiVersion | string | "2026-04" | 固定された四半期ごとの Admin API バージョン |
shopify.adminToken | string | (環境変数のみ) | Admin API トークン — 設定ファイルには絶対に記載せず、SHOPIFY_ADMIN_TOKEN 環境変数からのみ読み込みます |
plans.planTtlMs | positive int | 60000 | プランのトークンが有効な期間(ms)。SHOPIFY_PLAN_TTL_MS で上書きできます |
plans.approvalRequiredAboveItems | positive int | 25 | この数以上のアイテムに変更を加えるプランには、人間による承認が必要です。SHOPIFY_APPROVAL_REQUIRED_ABOVE_ITEMS で上書きできます |
plans.hardMaxItems | positive int | 250 | このアイテム数を超えるプランは拒否されます。SHOPIFY_HARD_MAX_ITEMS で上書きできます |
plans.maxPriceChangePct | positive int | 30 | この割合を超える価格変更には承認が必要です。SHOPIFY_MAX_PRICE_CHANGE_PCT で上書きできます |
plans.rollbackTtlMs | positive int | 86400000 | ロールバック可能な期間(ms、デフォルトは24時間)。SHOPIFY_ROLLBACK_TTL_MS で上書きできます |
approvalServer.enabled | boolean | true | MCP サーバーとともに localhost の承認 UI を起動します。SHOPIFY_APPROVAL_SERVER_ENABLED で上書きできます |
approvalServer.port | positive int | 4319 | localhost の承認 UI 用ポート(127.0.0.1 のみ)。SHOPIFY_APPROVAL_SERVER_PORT で上書きできます |
approvalServer.requireAuth | boolean | true | 承認サーバーのすべてのルートで、セッションごとのベアラートークンを必須にします。false に設定すると、0.4.0 より前の動作に戻ります(推奨されません)。SHOPIFY_APPROVAL_SERVER_REQUIRE_AUTH で上書きできます |
approvalServer.authToken | string? | (環境変数のみ) | 承認サーバー用の明示的なベアラートークン — 設定ファイルには絶対に記載せず、SHOPIFY_APPROVAL_SERVER_AUTH_TOKEN 環境変数からのみ読み込みます。未設定の場合、起動ごとにランダムなトークンが生成され、stderr に一度だけ出力されます |
protectedTags | string[] | ["do-not-touch"] | プランで変更できないタグ。SHOPIFY_PROTECTED_TAGS(カンマ区切り)で上書きできます |
callerId | string | "unknown" | すべての監査行に記録される識別子。SHOPIFY_CALLER_ID で上書きできます |
不変条件: plans.hardMaxItems は plans.approvalRequiredAboveItems 以上である必要があります。満たされない場合、ローダーはエラーをスローします。
環境変数
すべての設定フィールドは環境変数で上書きできます(優先順位:環境変数 > 設定ファイル > デフォルト)。SHOPIFY_ADMIN_TOKEN は必須で、環境変数からのみ読み込まれます。
ツール
読み取りツール
search_products
タイトル、SKU、ベンダー、タグで商品を検索します。バリエーション、現在の価格、ロケーションごとの在庫レベルを含む商品を返します。
引数:
| フィールド | 型 | 説明 |
|---|---|---|
title | string? | タイトルに指定した語句を含む商品に一致します(Shopify のあいまい検索) |
sku | string? | バリエーションの SKU が指定した語句と一致する商品に一致します |
vendor | string? | 指定したベンダーの商品に一致します |
tag | string? | 指定したタグが付いた商品に一致します |
first | positive int? | Admin API に渡すページサイズ(デフォルトは50) |
戻り値: id、title、vendor、tags、variants(各要素に id、sku、price、inventoryItemId、inventoryLevels を含む)を持つ products[]。さらに、商品に保護対象タグが付いているかどうかを示す flags.protected / flags.protectedTags も含まれます。
安全性: 読み取り専用で、変更操作の呼び出しは一切ありません。保護対象タグが付いた商品も返され(除外されません)、後続の書き込みプランがそれらに変更を加えようとした場合は拒否されます。
list_orders
財務状況、フルフィルメント状況、日付範囲で絞り込んで注文を一覧表示します。
引数:
| フィールド | 型 | 説明 |
|---|---|---|
financialStatus | FinancialStatus? | "pending" | "authorized" | "partially_paid" | "paid" | "partially_refunded" | "refunded" | "voided" |
fulfillmentStatus | FulfillmentStatus? | "fulfilled" | "partial" | "unfulfilled" |
createdAfter | ISO-8601 string? | この日時以降に作成された注文 |
createdBefore | ISO-8601 string? | この日時以前に作成された注文 |
first | positive int? | ページサイズ(デフォルトは250) |
戻り値: id、name、financialStatus、fulfillmentStatus、totalPrice、lineItems[]を含むorders[]。
安全性の特性: 読み取り専用です。変更を行う呼び出しはありません。
書き込みツール(二段階)
すべての書き込みツールは、プレビュー → トークン →(承認)→ 実行の手順で動作します。
update_inventory
名前付きロケーションにある複数の在庫アイテムについて、在庫数量を絶対値で設定します。プレビューでは現在の在庫数を読み取り、実行時にはinventorySetQuantitiesを呼び出します。
引数:
| フィールド | 型 | 説明 |
|---|---|---|
locationId | string | gid://shopify/Location/… ID |
adjustments | InventoryAdjustment[] | 各{inventoryItemId, quantity}は、locationIdにおける利用可能数量を設定します |
安全性の特性:
- しきい値による制御:
adjustments.length >= approvalRequiredAboveItems(デフォルトは25)の場合、承認が必要です。> hardMaxItems(デフォルトは250)の場合は、処理が拒否されます - 保護タグの適用:保護タグが付いた商品に影響するプランは、トークン発行前に
PROTECTED_RESOURCEをスローします。承認による実行経路はありません - アイテムごとの台帳:一部失敗は記録され、隠されることはありません
- ロールバック:対応しています。スナップショットを使って
before.availableの数量を復元します
cancel_order
Shopifyの注文をキャンセルします。アイテム数にかかわらず、常に承認が必要です。ロールバックできません。
引数:
| フィールド | 型 | 説明 |
|---|---|---|
orderId | string | gid://shopify/Order/… ID |
reason | string | "customer" | "inventory" | "fraud" | "other" |
restock | boolean | アイテムを在庫に戻します |
notifyCustomer | boolean | キャンセルメールを送信します |
安全性の特性:
- 常に承認が必要:
alwaysRequireApproval: trueはツールにハードコードされており、承認しきい値は一切参照されません - スナップショットなし:
snapshotStore.capture()の呼び出しはありません。この操作ではロールバックが開始されません - ロールバック:
ROLLBACK_UNSUPPORTEDで拒否されます。キャンセルは値の変更ではなく、状態遷移です
refund_order
Shopifyの注文を返金します。アイテム数にかかわらず、常に承認が必要です。ロールバックできません。
引数:
| フィールド | 型 | 説明 |
|---|---|---|
orderId | string | gid://shopify/Order/… ID |
refundLineItems | RefundLineItem[]? | 返金対象のラインアイテムと数量。省略した場合、フルフィルメント済みのすべてのアイテムが全額返金されます |
reason | string | 監査に記録される、人が読める理由 |
各RefundLineItem:{lineItemId, quantity, restockType?}。restockTypeは"RETURN" \| "NO_RESTOCK" \| "CANCEL"です。
安全性の特性:
- 常に承認が必要:
alwaysRequireApproval: trueがツールにハードコードされています refundCalculateによるプレビュー: 書き込みを行わないGraphQL呼び出しで、承認画面に表示する正確な推奨返金額を返します- スナップショットなし: ロールバックには対応していません
- PIIを含まない監査記録: 注文IDと返金額のみを記録します。顧客名やメールアドレスが監査証跡に含まれることはありません
rollback_plan
ロールバック期間内(デフォルトは24時間)に、実行済みのロールバック可能なプランを取り消します。
引数:
| フィールド | 型 | 説明 |
|---|---|---|
planToken | string | ロールバックする実行済みプランのトークン |
安全性の特性:
- 承認は不要です。以前の状態に戻すことが安全な方向だからです
- 期間ガード:スナップショットの有効期限が切れている場合、またはプランが一度もプレビューされていない場合は
ROLLBACK_WINDOW_EXPIREDを返します - 種別ガード:プラン種別が
cancel_orderまたはrefund_orderの場合はROLLBACK_UNSUPPORTEDを返します - 実行時に成功した参照に対してのみ逆変更を行います。失敗した参照には変更を加えません
- 項目ごとの記録:ロールバックの一部が失敗した場合、その内容を正確に記録します
プランのライフサイクル
Agent calls preview tool
│
▼
Manifest built
(pure reads, zero writes)
│
▼
Item count checked
│
├─── <= hardMaxItems ──► token issued
│ │
│ >= approvalRequiredAboveItems
│ │ or alwaysRequireApproval
│ ▼
│ status: "awaiting_approval"
│ │
│ human approves
│ │
▼ │
HARD_MAX_ITEMS_EXCEEDED │
(no token, refused) ▼
execute_plan
│
STATE_CHANGED check
(re-read, compare digest)
│
┌────┴────┐
success failure
│ │
per-item per-item
ledger ledger
│
snapshot stored
for rollback
ローカルホストの承認 UI
しきい値を超えるプランを人が承認または拒否するためのプレーンな HTML ページです。MCP サーバーと同時に起動する、ローカル専用の HTTP サーバーとして動作します。
- アクセス: サーバーの起動時に表示されるトークン付き URL(例:
http://127.0.0.1:4319/?token=<token>)を、サーバーを実行しているマシンで使用します。他のマシンからはアクセスできません。 - 認証: 読み取り専用の GET も含め、すべてのルートでセッションごとのベアラートークンが必要です(
Authorization: Bearer <token>ヘッダー、または表示された URL で使われる?token=クエリのフォールバック)。トークンは、SHOPIFY_APPROVAL_SERVER_AUTH_TOKENで固定のトークンを設定しない限り、起動ごとに生成されます。スクリプトから呼び出す場合は、ヘッダーを渡します(curl -H "Authorization: Bearer $TOKEN" ...)。起動ログを取得するよりも、スクリプトからの使用や長期間の使用には環境変数のトークンを推奨します。標準エラー出力は通常ログファイルに記録されるため、起動ログから取得すると認証情報が残り続ける可能性があります。ループバックへのバインドと Host/Origin のチェックにより悪意のあるブラウザページを防げますが、プランのトークンを知っている別のローカルプロセスを防ぐのはベアラートークンのみです。以前の動作(0.4.0 より前)に戻すにはapprovalServer.requireAuth: falseを設定します(非推奨)。 - API:
GET /api/plansは保留中のプランを JSON で返します。POST /api/plans/:token/approveとPOST /api/plans/:token/rejectは承認を処理します。 - セキュリティ境界: 承認/拒否は、MCP ツールとして公開されることはありません。エージェントは自身のプランを承認できません。
監査ログ
プレビュー、承認、実行、拒否、および承認拒否のたびに、次のような JSON オブジェクトが監査ファイルに書き込まれます。
{"seq":1,"prev_hash":"0000...","hash":"ab12...","ts":1734567890000,"tool":"update_inventory","reason":"adjusting stock","planToken":"abc123","status":"executed","previewCount":10,"callerId":"shopify-ops","durationMs":234,"detail":"all 10 item(s) executed"}
seq— ファイルごとに単調増加するシーケンス番号prev_hash— 前の行の SHA-256(ジェネシスは64個のゼロ文字)hash— この行の SHA-256(hashフィールド自体を除く)- 改ざんを検知できますが、改ざんを防止するものではありません: 終端以外の行を編集、並べ替え、または削除すると、その行でチェーンが破損します。末尾の切り詰めや、ファイル全体を有効なチェーンに置き換える行為は、ファイルだけでは検知できません。
- PII に対する多層防御:
/customerEmail|customerName/iに一致するトップレベルのキーは、ハッシュ計算前に取り除かれます。自由記述のreason/detail文字列はスキャンされないため、ホスト側でrecord()を呼び出す前にサニタイズする必要があります。 - 再起動時の安全性: オープン時に既存のチェーンを検証し、最後の行の
seq/prev_hashから処理を再開します。
scripts/verify-audit.ts でチェーンを検証します。
開発用ストアのシードデータ生成
scripts/seed-store.ts は、サーバーの接続先となる現実的なストアを生成します。sw-postgres-mcp のシードデータ生成と同様に決定論的で、すべてのデータはシード値を指定した PRNG(mulberry32)から生成されるため、同じシード値で実行すれば、2 回の実行で同一のデータと件数になります。
実行方法:
SHOPIFY_STORE_DOMAIN=my-dev.myshopify.com SHOPIFY_ADMIN_TOKEN=shpat_... npm run seed -- --seed 42
シードデータ生成スクリプトではサーバーの loadConfig を再利用するため、SHOPIFY_STORE_DOMAIN/SHOPIFY_ADMIN_TOKEN/SHOPIFY_API_VERSION(および任意の設定ファイル)は、サーバーと同じようにそのまま適用されます。--seed のデフォルト値は 42 です。フラグは以下のとおりです。
--seed <number>— PRNG のシード値です。デフォルトは42です。同じシード値で実行すれば、必ず同一の結果になります。--dry-run— API を呼び出さずに、計画された件数を表示し、データ量の不変条件を確認します(認証情報は不要です)。--order-delay-ms <ms>— 注文を作成する間隔です。開発ストアではorderCreateが 1 分あたり 5 回に制限されるため、120 件の注文の作成には約 24 分かかる見込みです。デフォルトでは間隔を設けません。
データ構成(シード値 42):
| リソース | 件数 | 備考 |
|---|---|---|
| 商品 | 300 | タイトルは Seeded Product 1…300 で、8 つのベンダーと 8 つの商品タイプに分散しています |
| バリアント | 768 | 商品あたり 1~4 件。SKU は SEED-<product>-<variant>、価格は $5~$300 です |
| ロケーション | 2 | ストア内の最初の 2 つのロケーションです(ロケーションは物理的なものであり、作成されません) |
| 顧客 | 20 | [email protected] |
| 注文 | 120 | Bogus-Gateway のテスト注文(test: true、在庫チェックはスキップされます) |
注文の状態の内訳は、支払い済みが 116 件、保留中が 4 件、フルフィルメント済みが 40 件、未フルフィルメントが 80 件です。12 件には固定額の割引コードが付いています。すべての商品、顧客、注文には seeded-store タグが付いています。
データ量の不変条件(API を呼び出す前に読み込んだ設定に対して検証されます。不変条件を満たさない場合、シードデータ生成スクリプトは即座に失敗します):
- バリアントの全件数(768)は
hardMaxItems(デフォルトは 250)を超えるため、ストア全体の価格変更は拒否されます(HARD_MAX_ITEMS_EXCEEDED)。 saleタグが付いたバリアントは 156 件で、approvalRequiredAboveItems(デフォルトは 25)からhardMaxItems(250)までの範囲内であるため、tag:'sale'を対象とする価格変更は承認を要求しますが、拒否はされません。
データ構成の規模はシード値に依存しません。300 商品のうち 5 件ごとに 1 件(60 件)に sale が付き、商品ごとのバリアント数は 1~4 件なので、sale タグが付くバリアント数は常に 60~240 件です。
冪等性: 再実行時には、seeded-store タグが付いた以前の注文、顧客、商品をすべて削除してから再生成します(顧客は注文がなくなってからでないと削除できないため、注文を先に削除します)。商品を削除すると、そのバリアントと在庫アイテムも削除されます。2 つのロケーションは再利用され、削除されることはありません。スクリプトは最後に件数を表示するので、2 回の実行結果を比較できます。
ライブ統合テストスイート
tests/integration/ は、実際の Admin API とシード済みの開発ストアに対してサーバーを検証する、手動実行専用かつ環境変数でゲートされたテストスイートです。実際のストア認証情報が必要(シークレットを CI に渡してはいけません)であり、レート制限のある呼び出しによって不安定になる可能性があるため、意図的に CI の対象外としています。
実行方法:
SHOPIFY_STORE_DOMAIN=my-dev.myshopify.com SHOPIFY_ADMIN_TOKEN=shpat_... npm run seed -- --seed 42
SHOPIFY_STORE_DOMAIN=my-dev.myshopify.com SHOPIFY_ADMIN_TOKEN=shpat_... npm run test:integration
破壊的テストで新しい候補注文を検出できるよう、実行のたびに再シードしてください。認証情報がない場合、スイート全体がコンソールにメッセージを表示してスキップされ、終了コード 0 で終了します。npm run test:integration とデフォルトの npm test はどちらも何も実行せずに成功し、CI では実行されません。
テスト対象(各ファイルは、SHOPIFY_STORE_DOMAIN と SHOPIFY_ADMIN_TOKEN が設定されていない場合、describe.skip でスキップされます):
| ファイル | 検証内容 |
|---|---|
readTools.test.ts | ベンダー/タグ/SKU/タイトルによる search_products、最初のページのページネーション、保護タグのフラグ(シード済みの商品はすべて保護されていません)、list_orders の決済/フルフィルメントのフィルター。商品数は seed-42 の正確な定数と一致します。注文数は許容範囲で判定します(このスイートの破壊的テストで注文が消費されるためです) |
updatePrices.test.ts | 実際のバリアントに対する update_prices の 2 フェーズ処理:プレビュー → 小さな変更を実行 → 検証 → rollback_plan でロールバック → 復元を検証 |
updateInventory.test.ts | 実際の在庫アイテムとロケーションに対する update_inventory の 2 フェーズ処理:プレビュー → +1 の変更を実行 → 検証 → ロールバック → 復元を検証 |
createDiscount.test.ts | create_discount の 2 フェーズ処理:プレビュー → 作成 → 有効状態を検証 → ロールバックで無効化 |
cancelRefund.test.ts | 破壊的 — 実行時に検出した、支払い済みかつ未フルフィルメントのシード済み注文に対して、実際の cancel_order を 1 件、実際の refund_order を 1 件、プレビュー → 承認 → 実行の手順で行い、監査記録と整合する成功台帳を検証します |
throttle.test.ts | コストを考慮したスロットリング:API のコスト予算を超える並列バーストをデフォルトクライアントが吸収します(バックオフ)。リトライなしのクライアントでは、不明なエラーではなく ShopifyApiError の SHOPIFY_THROTTLED が発生します |
このスイートは 1 つの可変ストアを共有するため、ファイルは直列で実行されます(fileParallelism: false)。破壊的な書き込みと読み取り件数の取得が競合してはいけません。設定ファイル:vitest.integration.config.ts。
制限事項
明確に記載されており、隠されていません。
-
注文のキャンセル/返金は取り消せません。 承認はこれらを保護しますが、ロールバックを可能にするものではありません。キャンセルした注文を未キャンセルの状態に戻すことはできず、返金した支払いを取り消すこともできません。RollbackPlan は
cancel_orderとrefund_orderのトークンをROLLBACK_UNSUPPORTEDで拒否します。 -
ロールバックはベストエフォートのスナップショット復元です。 スナップショットはプレビュー時点の状態を記録するもので、世界の状態が変わっていない間のみ、逆変更の対象として使用できます。
rollbackTtlMs(デフォルトは 24h)を過ぎると、ロールバックはROLLBACK_WINDOW_EXPIREDで拒否されます。ロールバックで復元されるのは値であり、外部への副作用(すでに送信された返金通知など)は復元されません。 -
一部の失敗は例外ではなく、項目ごとの台帳に記録されます。 バッチ変更で一部の項目が失敗し、残りが成功した場合、実行器はそれぞれの結果を正確に記録します。項目全体を対象とした全件成功または全件失敗のロールバックはありません。ロールバック時に逆変更を適用できるのは、成功した項目ごとに限られます。
-
プランの状態はメモリ内にあり、プロセス単位です。 PlanStore と SnapshotStore は、保留中および実行済みのすべてのプラン状態をプロセスのメモリ内に保持します。サーバーを再起動すると、保留中のプラン(再プレビューが必要)とロールバック可能期間がすべて失われます。監査ログは、発生した内容を記録する永続的な記録です。
-
ストアは単一で、マルチテナントには対応していません。 1 つのサーバープロセスが 1 つの Shopify ストアと通信します。複数のストアで実行するには、認証情報と監査ファイルを分けて、サーバーインスタンスを複数実行する必要があります。
-
ユーザーごとの認証はありません。
callerIdは個人ではなく、デプロイ環境を識別します(デフォルトは"unknown")。v1 では、MCP セッション単位またはユーザー単位の認証はありません。サーバーの stdio トランスポートに接続できる人は誰でも、設定済みのストア認証情報を使って操作できます。承認サーバーのベアラートークンを持つ人(または認証が無効な状態で UI にアクセスできる人)は、プランを承認できます。 -
STATE_CHANGEDは書き込み前のドリフトチェックであり、あらゆる状況に対応する比較交換ではありません。 再読み込みで検出できるのは、変更が送信される前に発生したドリフトです。再読み込みから書き込みまでの間に生じる差異を防ぐものではありません。Shopify は一部の操作(在庫のchangeFromQuantityなど)ではプロバイダーレベルの比較交換を提供しますが、すべての操作に対応しているわけではありません(通常の価格更新では、最後に書き込まれた値が優先されます)。
ライセンス
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。