关于 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 服务
Agent 可以读取和修改 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
两阶段模式(预览 → 令牌 → 执行)是核心规范。每个写入工具都会:
- 预览 — 读取当前状态并计算将发生的更改,不执行任何修改调用
- 令牌 — 通过 SHA-256 指纹生成与精确预览清单绑定的计划令牌
- 审批 — 超过
approvalRequiredAboveItems(默认值为 25)或包含始终需要审批的操作时,等待人工通过服务器启动时打印的、包含令牌的 URL 批准计划(例如http://127.0.0.1:4319/?token=<token>) - 执行 — 重新读取当前值;若值与预览不一致(
STATE_CHANGED),则拒绝执行,然后逐项应用修改,并完整记录成功与失败情况
清单超过 hardMaxItems(默认值为 250)的计划会被直接拒绝——不会生成令牌,也没有审批途径。
不可逆操作(cancel_order、refund_order)无论项目数量多少都始终需要审批,且无法回滚。可逆操作(价格更改、库存调整)支持在可配置的时间窗口内回滚(默认值为 24 小时)。
威胁模型
风险不在于恶意 Agent——Agent 受信任,能够编写正确的 GraphQL。风险来自受信任但可能出错的 Agent:操作在语法上完美无误、格式正确,问题却出在其作用范围上。
致命场景——语法完全正确的批量调价操作中,小数点位置错了:
update_prices([...], newPrice: 1.5) ← meant 15.00, typed 1.5
如果没有预览和审批就执行涉及 500 个产品的批量更新,或者 Agent 的价格计算中有拼写错误,就会大规模地产生完全错误的结果。审批可以发现问题:人工看到“将 500 个价格从 $X 改为 $1.50”就会察觉不对劲。没有审批,或没有能在损害发生之前让其显现的预览,错误就会悄无声息地写入 Shopify。
三种机制共同保障安全:
1. 先预览,再计算差异。 每个写入工具都会读取当前状态并计算清单({ref, before, after} 对),且不会调用任何修改操作。在执行时重新读取并发现 STATE_CHANGED,就会拒绝写入。任何更改发生之前,其影响范围就已清晰可见。
2. 超过阈值时要求审批。 涉及 >= approvalRequiredAboveItems 个项目(默认值为 25)的计划需要人工审批。该阈值用于判断“规模是否大到需要人工检查?”——对批量价值更改而言很合适;对单项操作则不适用(不可逆操作则始终需要审批)。
3. 计划令牌绑定精确清单。 令牌是精确预览清单的 SHA-256 指纹,而非不透明 ID。若执行时替换为更大范围的项目集或不同价格,指纹就会改变,并以 STATEMENT_MISMATCH 为由拒绝执行。
回滚可在回滚时间窗口内恢复可逆操作中的错误(错误价格、错误库存水平)。但对于不可逆操作,回滚无法恢复:已取消的订单仍为取消状态,已退款的付款仍为已退款状态。
快速开始
npm install
npm test
npm run build
设置必需的环境变量,并将 Claude Desktop 指向该服务(参见下文的配置)。node dist/index.js 会在 MCP stdio 服务旁启动本地主机审批界面。
**演示:**分步演练脚本(拒绝全店调价 → 需要审批的调价 → 单次调用回滚 → 哈希链审计)位于 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 | 计划令牌的有效时长(毫秒)。可通过 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 | 回滚时限(毫秒,默认 24 小时)。可通过 SHOPIFY_ROLLBACK_TTL_MS 覆盖 |
approvalServer.enabled | boolean | true | 与 MCP 服务一同启动本地主机审批 UI。可通过 SHOPIFY_APPROVAL_SERVER_ENABLED 覆盖 |
approvalServer.port | positive int | 4319 | 本地主机审批 UI 的端口(仅限 127.0.0.1)。可通过 SHOPIFY_APPROVAL_SERVER_PORT 覆盖 |
approvalServer.requireAuth | boolean | true | 每个审批服务路由都必须提供会话专属的 bearer 令牌。设为 false 可回退到 0.4.0 之前的行为(不建议)。可通过 SHOPIFY_APPROVAL_SERVER_REQUIRE_AUTH 覆盖 |
approvalServer.authToken | string? | (仅环境变量) | 审批服务的显式 bearer 令牌 — 绝不可写入配置文件,只能通过 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) |
返回:products[],包含 id、title、vendor、tags、variants(每个变体包含 id、sku、price、inventoryItemId、inventoryLevels),以及用于指示商品是否带有受保护标签的 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) |
返回: orders[],包含 id、name、financialStatus、fulfillmentStatus、totalPrice、lineItems[]。
安全属性: 纯读取操作 — 零修改调用。
写入工具(两阶段)
所有写入工具均遵循 预览 → 令牌 →(批准)→ 执行 流程。
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
本地主机审批界面
一个纯 HTML 页面,供人工审批或拒绝超出阈值的计划。该页面由独立的本地专用 HTTP 服务运行,并与 MCP 服务同时启动。
- **访问方式:**在运行服务的计算机上,使用服务启动时打印的含令牌 URL(例如
http://127.0.0.1:4319/?token=<token>)。其他计算机无法访问。 - **身份验证:**每个路由(包括只读 GET 路由)都需要每个会话的 bearer 令牌(通过
Authorization: Bearer <token>请求头传递,或使用打印的 URL 所用的?token=查询参数回退方式)。除非设置了SHOPIFY_APPROVAL_SERVER_AUTH_TOKEN,否则每次启动都会生成一个令牌;脚本调用方应通过请求头传递令牌(curl -H "Authorization: Bearer $TOKEN" ...)。对于脚本化或长期使用,优先使用环境变量令牌,而不是从启动日志中提取令牌——标准错误输出通常会被捕获到日志文件中,凭据也会因此留存。回环绑定以及 Host/Origin 检查可阻止恶意浏览器页面,但只有 bearer 令牌才能阻止知道计划令牌的其他本地进程。设置approvalServer.requireAuth: false可回退到 0.4.0 之前的行为(不推荐)。 - API:
GET /api/plans以 JSON 格式返回待处理计划;POST /api/plans/:token/approve和POST /api/plans/:token/reject用于处理审批。 - 安全边界:审批或拒绝操作绝不会作为 MCP 工具公开——Agent 无法审批自己的计划。
审计日志
每次预览、审批、执行、拒绝和驳回都会向审计文件写入一个 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 的数据填充器类似。所有数据都由带种子的伪随机数生成器(mulberry32)生成,因此使用相同种子运行两次会产生完全相同的数据和计数。
运行:
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>— 伪随机数生成器种子;默认值为42。使用相同种子的任意两次运行结果都相同。--dry-run— 输出计划的计数并验证规模不变量,不会调用任何 API(无需凭据)。--order-delay-ms <ms>— 创建订单之间的等待时间。开发商店将orderCreate限制为每分钟五次,因此 120 个订单的计划时间约为 24 分钟;默认不等待。
数据结构(种子 42):
| 资源 | 数量 | 说明 |
|---|---|---|
| 产品 | 300 | 标题为 Seeded Product 1…300,分布在 8 个供应商和 8 种产品类型中 |
| 变体 | 768 | 每个产品有 1–4 个变体,SKU 为 SEED-<product>-<variant>,价格为 $5–$300 |
| 地点 | 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'范围内的商品调价需要请求批准,但不会被拒绝。
规模结构不受种子影响:每第 5 个产品(300 个中的 60 个)带有 sale 标签,而每个产品有 1–4 个变体,因此 sale 标签始终涵盖 60–240 个变体。
幂等性:重新运行时,会先清除所有先前带有 seeded-store 标签的订单、客户和产品(先删除订单,因为只有在订单删除后才能删除客户),然后重新生成。删除产品时,其变体和库存项目也会一并删除;两个地点会重复使用,不会删除。脚本会在最后输出计数,以便比较两次运行的结果。
实时集成测试套件
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 上运行。
覆盖范围(每个文件都会由 describe.skip 控制跳过,除非同时设置了 SHOPIFY_STORE_DOMAIN 和 SHOPIFY_ADMIN_TOKEN):
| 文件 | 验证内容 |
|---|---|
readTools.test.ts | 按供应商、标签、SKU、标题调用 search_products;验证第一页分页和受保护标签标记(所有预置产品均不受保护);验证 list_orders 的财务和履约筛选。产品数量必须与 seed-42 常量完全一致;订单数量允许在一定范围内浮动(本套件自身的破坏性测试会消耗这些订单) |
updatePrices.test.ts | 针对真实变体进行 update_prices 两阶段测试:预览 → 执行小幅更改 → 验证 → 通过 rollback_plan 回滚 → 验证已恢复 |
updateInventory.test.ts | 针对真实库存项和位置进行 update_inventory 两阶段测试:预览 → 执行 +1 更改 → 验证 → 回滚 → 验证已恢复 |
createDiscount.test.ts | create_discount 两阶段测试:预览 → 创建 → 验证已启用 → 通过回滚停用 |
cancelRefund.test.ts | 有破坏性——对运行时找到的、已付款且未履约的预置订单,分别且仅调用一次真实的 cancel_order 和 refund_order,执行预览 → 批准 → 执行,并断言成功记录与审计信息一致 |
throttle.test.ts | 基于成本的限流:超过 API 成本预算的并行突发请求由默认客户端处理(退避);不重试的客户端会返回 ShopifyApiError SHOPIFY_THROTTLED,而不是未知错误 |
文件会串行运行(fileParallelism: false),因为整个套件共用一个可变商店——破坏性写入绝不能与读取数量的操作并发。配置:vitest.integration.config.ts。
限制
明确说明,不作隐瞒:
-
订单取消/退款不可逆。 审批可以保护这些操作,但无法实现回滚。订单取消后无法撤销取消;退款后无法撤回款项。RollbackPlan 会拒绝
cancel_order和refund_order令牌,并返回ROLLBACK_UNSUPPORTED。 -
回滚是尽力而为的快照恢复。 快照会在预览时捕获操作前的状态;只有在外部状态未发生变化时,才能将其用作逆向变更的目标。超过
rollbackTtlMs(默认 24h)后,回滚会被拒绝,并返回ROLLBACK_WINDOW_EXPIRED。回滚会恢复数值,不会撤销外部副作用(例如,已发送的退款通知)。 -
部分失败会留下逐项记录,而不是抛出异常。 批量变更中,部分项目失败、其他项目成功时,执行器会如实记录每项结果。各项目之间不会进行全有或全无的回滚;回滚时只会对成功的项目逐项执行逆向变更。
-
计划状态驻留在内存中,并受进程范围限制。 PlanStore 和 SnapshotStore 会将所有待处理和已执行的计划状态保存在进程内存中。服务器重启后,所有待处理计划都会丢失(需要重新预览),所有回滚窗口也会丢失。审计日志是操作记录的持久化依据。
-
仅支持单一存储,不支持多租户。 一个服务器进程只连接一个 Shopify 存储。若要服务多个存储,需运行多个服务器实例,并为每个实例配置独立的凭据和审计文件。
-
不提供按用户划分的身份验证。
callerId用于标识部署(默认值为"unknown"),而非个人。v1 不支持按 MCP 会话或用户进行身份验证。任何能够访问服务器 stdio 传输的人员,都可以使用已配置的存储凭据;任何持有审批服务器 bearer token 的人员(或在停用身份验证时访问 UI 的人员)都可以审批计划。 -
STATE_CHANGED是写入前的状态漂移检查,并非通用的比较并交换机制。 重新读取可以检测发送变更前已存在的状态漂移,但无法消除重新读取与写入之间的时间窗口。Shopify 为部分操作提供了提供方级别的比较并交换机制(例如,库存操作中的changeFromQuantity),但并非所有操作都支持(普通价格更新采用最后写入者胜出策略)。
许可证
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。