← Shopify App BuilderCONTENT HISTORY

Update to Shopify App Builder

Snapshot Sep 30, 2026 · 23:13 UTC · version 1.4.1

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Use this skill for shopify mcp. Triggers include: 'shopify mcp', 'shopify dev mcp', 'storefront mcp', 'merchant-facing mcp', 'well-known mcp', 'shopify mcp configuration', 'custom mcp shopify', 'agentic commerce', 'shopify agent', 'claude code shopify integration', 'mcp.json', 'shopify mcp setup'.",
  "included_files": [],
  "name": "shopify-mcp",
  "skill_md_contents": "---\nname: shopify-mcp\ndescription: \"Use this skill for shopify mcp. Triggers include: 'shopify mcp', 'shopify dev mcp', 'storefront mcp', 'merchant-facing mcp', 'well-known mcp', 'shopify mcp configuration', 'custom mcp shopify', 'agentic commerce', 'shopify agent', 'claude code shopify integration', 'mcp.json', 'shopify mcp setup'.\"\n---\n\n# Shopify MCP Integration Guide\n\nModel Context Protocol (MCP) servers connect Claude to Shopify data and operations. There are three deployment patterns: Shopify Dev MCP (developer-centric), Storefront MCP (merchant-centric), and custom MCPs (specialized workflows). This guide covers setup, configuration, implementation patterns, and agentic commerce use cases.\n\n## Shopify Dev MCP: Developer Tools for Claude Code\n\nShopify Dev MCP is a read-only developer toolkit integrated into Claude Code. It provides access to admin APIs, schema introspection, development store management, and app testing utilities without building a custom MCP.\n\n### Installation and Setup\n\nThe Shopify Dev MCP is installed via command-line setup:\n\n```bash\nnpx -y @shopify/dev-mcp setup\n```\n\nThis command:\n1. Prompts for Shopify organization/store selection\n2. Creates a development app or reuses existing one\n3. Stores authentication tokens securely in system keychain\n4. Registers the MCP server in `~/.claude.json/mcpServers`\n5. Restarts Claude Code to load the new MCP\n\nNo additional configuration is required post-setup. The MCP automatically handles token refresh and scope validation.\n\n### Claude Code Configuration\n\nAfter setup, `~/.claude.json/mcpServers` contains:\n\n```json\n{\n  \"mcpServers\": {\n    \"shopify\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@shopify/dev-mcp\", \"run\"],\n      \"env\": {\n        \"SHOPIFY_AUTH_TOKEN\": \"shpat_...\",\n        \"SHOPIFY_STORE\": \"dev-store-name.myshopify.com\",\n        \"SHOPIFY_ORG_ID\": \"gid://shopify/Organization/12345\"\n      }\n    }\n  }\n}\n```\n\nManual configuration is unnecessary unless you need environment-specific tokens or custom server paths. For advanced setups (air-gapped networks, custom HTTP proxies), edit the env dict directly.\n\n### Dev MCP Capabilities\n\nThe Shopify Dev MCP provides these read-only tools:\n\n**Admin API Query Tool**\n- Execute GraphQL queries against the API version configured for the connected development store\n- Full schema support (Products, Orders, Customers, Fulfillments, Inventory, etc.)\n- Automatic scope validation (app must have required scopes)\n- Response size limit: 5MB per query\n- Query complexity limit: 100 points (Admin API default)\n\n```graphql\nquery GetProductMetafields($id: ID!) {\n  product(id: $id) {\n    id\n    title\n    metafields(first: 10) {\n      edges {\n        node {\n          namespace\n          key\n          value\n        }\n      }\n    }\n  }\n}\n```\n\n**REST API Endpoint Tool**\n- Use legacy Admin REST endpoints only when the connected tool and existing app explicitly support them\n- Bearer token authentication handled automatically\n- Useful for bulk operations, webhooks, flow triggers\n- Rate limit: standard REST bucket holds 40 requests and restores 2 requests/second; read response headers and retry safely\n\n**Store Introspection Tool**\n- List installed apps, extensions, functions, webhooks\n- Retrieve store metadata (plan, currency, timezone, brand settings)\n- Useful for understanding store configuration before building\n- No rate limiting\n\n**App Testing Tool**\n- Trigger test webhooks (app/installed, app/uninstalled, orders/create)\n- List development app credentials\n- Rotate API credentials securely\n- Simulate store events without live transactions\n\n**Schema Browser Tool**\n- Explore GraphQL schema interactively\n- Get field descriptions, arguments, return types\n- Filter by type (Query, Mutation, Subscription, Object, Enum)\n- Search for fields by name or description\n\n### When to Recommend Dev MCP Installation\n\nRecommend Dev MCP when:\n- User is building a Shopify app in Claude Code\n- User needs to test queries against their dev store without leaving Claude\n- User is debugging function behavior or app logic\n- User is prototyping Admin API integrations\n- User needs quick schema lookups during development\n\nDo NOT recommend if:\n- User needs custom logic beyond Admin API (use custom MCP)\n- User is building a public app without a Shopify org (no auth available)\n- User needs to connect multiple stores (use Storefront MCP + app routes)\n\n### Dev MCP Workflow Example\n\n```\nUser: \"Add a debug webhook that logs all order updates to my app\"\n\nClaude uses Dev MCP to:\n1. Query store's webhook endpoints (Admin API GET /webhooks.json)\n2. Check existing webhooks for duplicates\n3. Create new webhook via Admin API POST /webhooks.json\n4. Confirm creation and return webhook ID\n\nClaude: \"I've registered webhook ID gid://shopify/Webhook/123456\nto POST order/update events to your app. Test it by placing an order.\"\n```\n\n## Storefront MCP: Merchant-Facing Agent Tools\n\nStorefront MCP is a custom MCP deployed at `/.well-known/mcp.json` on your storefront. It enables AI agents (Claude, OpenAI Operator, Perplexity Shopping) to browse products, manage carts, apply discounts, and complete purchases on behalf of customers.\n\n### Architecture\n\nStorefront MCP is a lightweight HTTP server serving MCP protocol at `/.well-known/mcp.json`. When an AI agent visits your storefront, it discovers the MCP server via well-known endpoint and establishes communication for tool access.\n\n```\nCustomer Browser / AI Agent\n         ↓\n    Storefront (Remix/Next)\n         ↓\n  /.well-known/mcp.json\n         ↓\n   MCP Server (Node.js)\n         ↓\n   Storefront API / Backend DB\n```\n\n### Required Storefront API Scopes\n\nYour MCP server must have a Storefront API access token with these scopes:\n\n```\ncustomer-account-api:customer\nstorefront-api:read_products\nstorefront-api:read_product_variants\nstorefront-api:read_collections\nstorefront-api:read_carts\nstorefront-api:write_carts\nstorefront-api:read_customers\n```\n\nScopes are configured in `shopify.app.toml`:\n\n```toml\nscopes = \"customer-account-api:customer,storefront-api:read_products,storefront-api:read_product_variants,storefront-api:read_collections,storefront-api:read_carts,storefront-api:write_carts,storefront-api:read_customers\"\n```\n\n### Core Tools\n\n**search_catalog**\n- Text search across products/collections\n- Returns 10 highest-relevance results\n- Includes images, pricing, availability\n- Filters by collection/vendor/price range optional\n\n```json\n{\n  \"name\": \"search_catalog\",\n  \"description\": \"Search storefront products by keyword\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"query\": {\"type\": \"string\"},\n      \"limit\": {\"type\": \"number\", \"default\": 10},\n      \"filter\": {\"type\": \"string\", \"enum\": [\"in_stock\", \"sale\", \"new\"]}\n    }\n  }\n}\n```\n\n**get_product**\n- Fetch full product details by product ID\n- Includes variants, metafields, recommendations, ratings\n- Returns available inventory counts per variant\n- Shows subscription/prepaid options if available\n\n**lookup_product**\n- Find product by SKU, barcode, or vendor ID\n- Useful when AI agent has partial product info\n- Returns product ID for use with get_product\n\n**get_cart_state**\n- Retrieve current customer cart\n- Shows line items, subtotal, taxes, shipping estimates\n- Includes applied discounts, gift cards, notes\n- Returns cart ID for mutations\n\n**add_to_cart**\n- Add product variant to cart with quantity\n- Creates cart if none exists\n- Returns updated cart state\n- Validates variant availability before adding\n\n**apply_discount**\n- Apply discount code to active cart\n- Returns updated totals after discount\n- Shows discount description and terms\n- Validates code and customer eligibility\n\n**create_checkout**\n- Initiate checkout flow for current cart\n- Returns checkout URL (redirects to payment)\n- Captures customer email if known\n- Applies language/currency preferences\n\n### Storefront MCP Implementation (Remix)\n\nCreate `/routes/.well-known/mcp.json.ts`:\n\n```typescript\nimport { json, type LoaderFunction } from \"@remix-run/node\";\nimport { storefront } from \"~/lib/shopify.server\";\n\nexport const loader: LoaderFunction = async ({ request }) => {\n  if (request.method !== \"GET\") {\n    return new Response(\"Method not allowed\", { status: 405 });\n  }\n\n  return json({\n    protocolVersion: \"2024-11-05\",\n    name: \"my-storefront-mcp\",\n    version: \"1.0.0\",\n    capabilities: {\n      tools: {\n        listChanged: true,\n      },\n    },\n    tools: [\n      {\n        name: \"search_catalog\",\n        description: \"Search products by keyword\",\n        inputSchema: {\n          type: \"object\",\n          properties: {\n            query: { type: \"string\", description: \"Search query\" },\n            limit: { type: \"number\", default: 10 },\n            filter: {\n              type: \"string\",\n              enum: [\"in_stock\", \"sale\", \"new\"],\n            },\n          },\n          required: [\"query\"],\n        },\n      },\n      {\n        name: \"get_product\",\n        description: \"Get product details by ID\",\n        inputSchema: {\n          type: \"object\",\n          properties: {\n            productId: {\n              type: \"string\",\n              description: \"Shopify product ID (gid://...)\",\n            },\n          },\n          required: [\"productId\"],\n        },\n      },\n      {\n        name: \"get_cart_state\",\n        description: \"Retrieve current cart\",\n        inputSchema: { type: \"object\", properties: {} },\n      },\n      {\n        name: \"add_to_cart\",\n        description: \"Add variant to cart\",\n        inputSchema: {\n          type: \"object\",\n          properties: {\n            variantId: { type: \"string\" },\n            quantity: { type: \"number\", default: 1 },\n          },\n          required: [\"variantId\"],\n        },\n      },\n      {\n        name: \"apply_discount\",\n        description: \"Apply discount code\",\n        inputSchema: {\n          type: \"object\",\n          properties: {\n            code: { type: \"string\" },\n            cartId: { type: \"string\" },\n          },\n          required: [\"code\"],\n        },\n      },\n    ],\n  });\n};\n```\n\nCreate `/routes/api/mcp/tool-call.ts` to handle tool invocations:\n\n```typescript\nimport { json, type ActionFunction } from \"@remix-run/node\";\nimport { storefront } from \"~/lib/shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n  if (request.method !== \"POST\") {\n    return new Response(\"Method not allowed\", { status: 405 });\n  }\n\n  const { tool, input, meta } = await request.json();\n  const cartId = meta?.cartId;\n\n  switch (tool) {\n    case \"search_catalog\": {\n      const query = `query SearchProducts($query: String!) {\n        search(first: ${input.limit || 10}, query: $query) {\n          edges {\n            node {\n              ... on Product {\n                id\n                title\n                handle\n                featuredImage { url }\n                priceRange {\n                  minVariantPrice { amount currency }\n                }\n              }\n            }\n          }\n        }\n      }`;\n\n      const result = await storefront.query(query, {\n        variables: { query: input.query },\n      });\n\n      return json({\n        products: result.search.edges.map((e: any) => ({\n          id: e.node.id,\n          title: e.node.title,\n          handle: e.node.handle,\n          image: e.node.featuredImage?.url,\n          price: e.node.priceRange.minVariantPrice.amount,\n          currency: e.node.priceRange.minVariantPrice.currency,\n        })),\n      });\n    }\n\n    case \"add_to_cart\": {\n      const cartAddQuery = `mutation AddToCart($cartId: ID!, $lines: [CartLineInput!]!) {\n        cartLinesAdd(cartId: $cartId, lines: $lines) {\n          cart { id lines(first: 10) { edges { node { id quantity variant { id } } } } }\n          userErrors { message field }\n        }\n      }`;\n\n      const cartResult = await storefront.mutate(cartAddQuery, {\n        variables: {\n          cartId,\n          lines: [{ variantId: input.variantId, quantity: input.quantity }],\n        },\n      });\n\n      if (cartResult.cartLinesAdd.userErrors.length > 0) {\n        return json(\n          { error: cartResult.cartLinesAdd.userErrors[0].message },\n          { status: 400 }\n        );\n      }\n\n      return json({ cart: cartResult.cartLinesAdd.cart });\n    }\n\n    case \"apply_discount\": {\n      const discountQuery = `mutation ApplyDiscount($cartId: ID!, $discountCode: String!) {\n        cartDiscountCodesUpdate(cartId: $cartId, discountCodes: [$discountCode]) {\n          cart { id cost { totalAmount { amount } } }\n          userErrors { message }\n        }\n      }`;\n\n      const result = await storefront.mutate(discountQuery, {\n        variables: { cartId, discountCode: input.code },\n      });\n\n      if (result.cartDiscountCodesUpdate.userErrors.length > 0) {\n        return json(\n          { error: result.cartDiscountCodesUpdate.userErrors[0].message },\n          { status: 400 }\n        );\n      }\n\n      return json({ cart: result.cartDiscountCodesUpdate.cart });\n    }\n\n    default:\n      return json({ error: \"Unknown tool\" }, { status: 400 });\n  }\n};\n```\n\n## Custom MCP for Shopify Apps\n\nBuild a custom MCP when you need specialized agent tools beyond standard Admin API or Storefront API access. Common use cases: workflow automation, data aggregation, custom business logic, integration with third-party systems.\n\n### Custom MCP Structure\n\n```\nshopify-app-mcp/\n├── package.json\n├── tsconfig.json\n├── src/\n│   ├── index.ts          # MCP server main entry\n│   ├── tools/\n│   │   ├── inventory.ts  # Inventory management tools\n│   │   ├── reporting.ts  # Custom reporting tools\n│   │   └── automation.ts # Workflow automation tools\n│   └── lib/\n│       ├── shopify.ts    # Admin API client\n│       └── db.ts         # Database queries\n└── stdio.mjs             # Node.js stdio transport\n```\n\n### Example: Custom Inventory MCP\n\n```typescript\n// src/tools/inventory.ts\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport {\n  Tool,\n  TextContent,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nexport const inventoryTools: Tool[] = [\n  {\n    name: \"adjust_inventory\",\n    description: \"Adjust inventory levels for a variant\",\n    inputSchema: {\n      type: \"object\",\n      properties: {\n        variantId: { type: \"string\" },\n        quantityAdjustment: { type: \"number\" },\n        reason: {\n          type: \"string\",\n          enum: [\n            \"damaged\",\n            \"lost\",\n            \"count_correction\",\n            \"restock\",\n            \"donation\",\n          ],\n        },\n      },\n      required: [\"variantId\", \"quantityAdjustment\", \"reason\"],\n    },\n  },\n  {\n    name: \"get_low_stock_variants\",\n    description: \"Find variants below minimum threshold\",\n    inputSchema: {\n      type: \"object\",\n      properties: {\n        threshold: { type: \"number\", default: 5 },\n        warehouseId: { type: \"string\" },\n      },\n    },\n  },\n];\n\nexport async function handleInventoryTool(\n  toolName: string,\n  input: Record<string, any>\n): Promise<TextContent> {\n  const adminClient = createAdminClient(); // use app's admin token\n\n  if (toolName === \"adjust_inventory\") {\n    const query = `\n      mutation AdjustInventory($variantId: ID!, $quantity: Int!, $reason: String!) {\n        inventoryAdjustQuantities(\n          input: {\n            changes: [\n              {\n                inventoryItemId: \"gid://shopify/InventoryItem/${input.variantId}\"\n                availableDelta: ${input.quantityAdjustment}\n              }\n            ]\n            reason: \"${input.reason.toUpperCase()}\"\n          }\n        ) {\n          inventoryAdjustmentGroup {\n            reason\n            changes { inventoryItem { sku } }\n          }\n          userErrors { message }\n        }\n      }\n    `;\n\n    const result = await adminClient.mutate(query);\n    return {\n      type: \"text\",\n      text: `Adjusted inventory: ${JSON.stringify(result, null, 2)}`,\n    };\n  }\n\n  if (toolName === \"get_low_stock_variants\") {\n    const query = `\n      query LowStockVariants($threshold: Int!) {\n        productVariants(first: 100, query: \"inventory_quantity:<${input.threshold}\") {\n          edges {\n            node {\n              id\n              title\n              inventoryQuantity\n            }\n          }\n        }\n      }\n    `;\n\n    const result = await adminClient.query(query, {\n      variables: { threshold: input.threshold },\n    });\n    return {\n      type: \"text\",\n      text: `Low stock variants: ${JSON.stringify(result, null, 2)}`,\n    };\n  }\n\n  throw new Error(`Unknown tool: ${toolName}`);\n}\n```\n\n### Registering Custom MCP in Claude Code\n\nAdd to `~/.claude.json/mcpServers`:\n\n```json\n{\n  \"mcpServers\": {\n    \"shopify-inventory\": {\n      \"command\": \"node\",\n      \"args\": [\"path/to/shopify-app-mcp/stdio.mjs\"],\n      \"env\": {\n        \"SHOPIFY_ACCESS_TOKEN\": \"shpat_...\",\n        \"SHOPIFY_SHOP\": \"mystore.myshopify.com\",\n        \"DATABASE_URL\": \"postgres://...\"\n      }\n    }\n  }\n}\n```\n\n## Agentic Commerce: AI-Powered Shopping\n\nAgentic commerce uses AI agents (Claude, OpenAI Operator, Perplexity Shopping) to browse storefronts, understand products, manage carts, and complete purchases autonomously. The Storefront MCP enables this workflow.\n\n### Shop AI (Shopify Native Agent)\n\nShopify's Shop AI is a managed agent available to merchants via Shop app. It enables:\n- Natural language search (\"show me sustainable leather jackets\")\n- Product comparison (\"compare these two options\")\n- Customer service (\"where's my order?\", \"return this item\")\n- Purchase assistance (\"add bundle to cart\", \"apply code SAVE20\")\n\nShop AI uses Storefront API directly (no custom MCP required). Optimize product descriptions and metafields for AI comprehension.\n\n### OpenAI Operator (Agentic Browsing)\n\nOpenAI Operator is an agentic browser that can interact with websites like a human. When Operator visits your storefront:\n\n1. Operator discovers MCP at `/.well-known/mcp.json`\n2. Operator loads available tools (search_catalog, add_to_cart, etc.)\n3. Operator executes user requests autonomously\n4. Requests like \"find a gift under $50 and add it\" work natively\n\nTo optimize for Operator:\n- Ensure product metadata is complete (descriptions, tags, ratings)\n- Include clear pricing and availability indicators\n- Support discount codes discoverable in footer/header\n- Test Storefront MCP endpoints for latency < 500ms\n- Provide fallback HTML for legacy browsers\n\n### Perplexity Shopping (AI Shopping Assistant)\n\nPerplexity's shopping agent crawls your storefront and catalogs products for shopper recommendations. It:\n- Synthesizes product comparisons across results\n- Recommends bundles and alternatives\n- Applies coupon codes automatically\n- Offers price match guarantees (via integrations)\n\nTo optimize for Perplexity:\n- Use structured data (JSON-LD) for products\n- Publish sitemap.xml with all product URLs\n- Include original/discounted pricing clearly\n- Add customer review counts and ratings\n- Avoid JavaScript-only product loading\n\n### Building for Agentic Commerce\n\nProduct metadata shapes agent behavior. Ensure:\n\n```json\n{\n  \"product\": {\n    \"id\": \"gid://shopify/Product/123456\",\n    \"title\": \"Organic Cotton T-Shirt\",\n    \"description\": \"100% certified organic cotton, GOTS certified. Features: breathable, hypoallergenic, sustainable. Care: machine wash cold, line dry.\",\n    \"tags\": [\"organic\", \"sustainable\", \"cotton\", \"unisex\"],\n    \"category\": \"Clothing > Tops > T-Shirts\",\n    \"rating\": 4.7,\n    \"reviewCount\": 234,\n    \"variants\": [\n      {\n        \"id\": \"gid://shopify/ProductVariant/789\",\n        \"title\": \"Black / XS\",\n        \"price\": \"32.00\",\n        \"compareAtPrice\": \"45.00\",\n        \"available\": true,\n        \"sku\": \"OCTT-BLACK-XS\"\n      }\n    ],\n    \"collections\": [\"Summer Collection\", \"Bestsellers\"],\n    \"seo\": {\n      \"title\": \"Organic Cotton T-Shirt | Sustainable Fashion\",\n      \"description\": \"Breathable, hypoallergenic organic cotton tees. GOTS certified, ethically made.\"\n    }\n  }\n}\n```\n\n## Troubleshooting MCP Issues\n\n| Issue | Symptom | Root Cause | Fix |\n|-------|---------|------------|-----|\n| MCP not discovered | \"/.well-known/mcp.json 404\" in Claude | Storefront route not created | Create `routes/.well-known/mcp.json.ts` and restart server |\n| Authentication failed | \"Invalid access token\" in tool errors | Outdated token, scope mismatch | Regenerate token, verify scopes in shopify.app.toml |\n| Tool call timeout | Tools don't respond after 10s | Slow Storefront API, N+1 queries | Batch queries, add caching, optimize GraphQL |\n| CORS blocked | \"Cross-Origin Request Blocked\" | MCP endpoint enforcing CORS | Add CORS headers: Access-Control-Allow-Origin: * |\n| Cart not persisting | Cart ID changes between calls | Stateless cart creation | Store cartId in session/localStorage, reuse in mutations |\n| Discount code fails | \"Code not valid for this customer\" | Code restricted to segments | Test code eligibility, check customer tags match |\n| Search returns empty | \"Zero results for common query\" | Products not indexed, missing tags | Ensure products published, rebuild search index |\n| Agent loops indefinitely | Tool calls repeat without progress | Missing error handling in tool | Add explicit error messages, max iteration count |\n| Storefront API rate limited | \"Rate limit exceeded\" after 10 calls | Too many parallel requests | Implement request queue, batch mutations |\n| Schema not updating | New fields unavailable in queries | Admin API cache, schema change pending | Restart MCP server, verify API version matches |\n\n## Best Practices\n\n**MCP Server Reliability**\n- Implement request queuing to avoid rate limits\n- Add exponential backoff for transient failures\n- Cache schema queries (schema rarely changes)\n- Monitor tool latency; alert if > 1s\n- Log all tool calls for debugging agentic behavior\n\n**Security for Agentic Access**\n- Storefront MCP should NOT have write access to orders\n- Limit tool scope to read + cart mutations only\n- Validate cart ownership before mutations (check customer ID)\n- Require explicit customer consent for purchase-triggering tools\n- Rate limit tool calls per IP (50 calls/minute per user agent)\n\n**Agent Optimization**\n- Provide clear tool descriptions (agents use these for routing)\n- Return structured, machine-readable responses\n- Include confidence scores for search results\n- Offer tool combinations (e.g., \"search + get_product\" for details)\n- Test agent flow: search → filter → add → discount → checkout\n\n## Quick Reference: When to Use Which MCP\n\n| Scenario | Recommended MCP | Reason |\n|----------|-----------------|--------|\n| Developer building Shopify app | Shopify Dev MCP | Read-only, zero config, full schema |\n| Enabling AI shopping on storefront | Storefront MCP | Merchant-facing, tool-based, standard setup |\n| Custom reporting dashboard | Custom MCP | Specialized queries, app-specific logic |\n| Admin agent for store ops | Hybrid (Dev + Custom) | Dev MCP for queries, Custom for mutations |\n| AI-powered product recs | Storefront MCP | Catalog search, product details, cart state |\n| Workflow automation (reordering) | Custom MCP | Business logic beyond standard APIs |\n"
}

SHA-256 of public snapshot: b6f4ab9e03dca31ac0517a2920d9205ff8b6f0733834dcd327517ad60de9cfb7