Skip to main content
Layers exposes two Model Context Protocol (MCP) servers that let AI assistants interact directly with your stores. Use them to manage merchandising, search tuning, catalog operations, and storefront queries through any MCP-compatible client.

Browser-local WebMCP controls

The Layers dashboard also exposes browser-local WebMCP controls when you are signed in and using a browser that supports navigator.modelContext. These tools act on the active dashboard page through its existing UI handlers. They are separate from the remote Layers MCP server and Storefront MCP server, and do not call their endpoints. Open the relevant conditions builder or picker in the dashboard before listing controls. The tools only expose builders and pickers that are currently mounted and visible. For conditions builders:
  • Use layers_list_condition_builders to find open builders, then layers_conditions_get to read the current query and the fields, operators, value types, and choices allowed in that builder.
  • Use layers_conditions_set with the builder id, a complete replacement query, and expected_query from the latest read. Nested and, or, and not conditions are supported where the builder allows them. Values must be literal; field-sourced values are not supported.
  • Setting conditions uses the existing UI handler and may sync the dashboard draft, but never submits or publishes it. If the builder is stale or locked, re-read it or unlock it before trying again.
For nested pickers:
  • Use layers_list_nested_comboboxes to find open pickers and layers_combobox_get_options to read their choices. Option lookup supports search, group, offset, and limit; the default limit is 50 and the maximum is 100.
  • Use layers_combobox_select with the picker id and an exact option value returned by the options tool, not its label. Disabled options are rejected.
  • If a picker id is stale or the picker is closed, open the relevant control and list the pickers again. Selection does not save or publish the dashboard.

Layers MCP server

The main MCP server exposes tools for managing your Layers stores. It uses OAuth authentication and requires the mcp:use scope.

Connecting

The official Layers connector is available in the Claude directory. Add it from Settings > Connectors in Claude, then authorize Layers when prompted. If you are using a different MCP client, configure it with the following endpoint:
When you connect for the first time, you’re redirected to the Layers dashboard to authorize the connection. After authorization, your MCP client stores the OAuth tokens and reconnects automatically. MCP connections are scoped to specific stores and specific permissions. On the authorization screen, you select which stores the connection can access and which of your own permissions it may use. A connection can never exceed your own access. If your access is later reduced, the connection’s access is reduced immediately to match. Because connections are store-scoped, store-list returns only the stores granted to the connection. Each store in store-list and store-details includes the permissions the connection was granted, so an assistant can check what it may do before calling a tool. If a tool is called for a store that wasn’t granted the permission it needs, the tool returns a permission error. If a tool is called with a store identifier that doesn’t exist, the tool returns a not-found error with up to three near matches drawn from the connection’s accessible stores. This distinguishes a typo (fix the slug and retry) from a store that exists but wasn’t granted to the connection (ask the connection owner to grant it). The store argument accepts a store’s UUID, slug, or primary domain (exact match), and also matches on the display name after ignoring case and punctuation, so Acme Outfitters, acme-outfitters, and acmeoutfitters all resolve to the same store. When a name matches more than one accessible store, the tool returns a 422 that names the conflicting slugs so the caller can retry with an exact identifier.

Granting access to future stores

By default, a connection only covers the stores you select on the authorization screen. Turn on Include future stores under the store picker to automatically grant the connection access to stores you gain access to after consenting. Use it for a personal assistant connection you want to keep in sync with your account instead of re-authorizing every time you’re added to a new store. When the switch is on, pick a permission preset for future stores: Behavior to know:
  • “Future” means stores you gain access to after consenting. Stores you could already access but left unselected stay excluded and are never granted automatically.
  • Grants still never exceed your own access. When you gain a new store, the connection receives the preset’s permissions intersected with the permissions you actually hold on that store.
  • New grants take effect on the connection’s next MCP request and appear in the store’s App Connections table like any other grant.
  • Re-authorizing the same app replaces the future-store setting along with the store grants. The most recent consent wins.
  • Revoking the connection for a store permanently excludes that store. Layers won’t re-grant it automatically, even if you still have access.

Available tools

Most resources use a *-manage tool for actions such as list, create, update, and delete. Some resources use separate list and collaborative editing tools instead.

Diagnosing collection and search overlap

collection-overlap is available on the admin Layers MCP server, not the Storefront MCP server. It summarizes page-one searches from the trailing 7-day window whose query text names a collection. Each row pairs a query and collection with a relation: exact means their names match, collection_broader means the collection name includes extra terms, and query_narrower means the query includes extra terms. Pass store as a required store slug or domain. Filter results with relation, linked, min_searches, or max_members_served_share (a fraction from 0 to 1). page defaults to 1. per_page defaults to 15 and accepts up to 100 results. The response includes total matching rows and total_pages. The tool orders rows by estimated coverage left on the table, calculated as searches multiplied by eligible collection members that search results did not serve. It then sorts by search count descending and query ascending. Each row includes search and collection counts such as searches, eligible_members, members_served, results_served, and results_in_collection, along with the corresponding shares. median_result_count and max_result_count report the median and maximum total results returned per search. min_members_missing_per_search gives a lower bound on how many eligible collection members each search left out, based on the largest result count. It does not identify the exact missing members. Collection labels come from a daily crawl of the storefront header navigation. The crawl excludes footer links and /collections/all, and runs again after collection data finishes syncing during onboarding. The tool requires an authenticated admin MCP connection and admin access. It has no tool-specific request rate limit. Calls without admin access return HTTP 403:
Example call and successful response:

Submitting feedback about MCP tooling

Admin MCP connections can call mcp-feedback to report friction with the MCP itself. Use it when an assistant hits a rough edge you want the platform to fix. Examples include a missing capability, a schema field that isn’t exposed, an action that takes too many calls, or a tool description that led an agent astray. The tool accepts a short set of fields: Example call:
The response confirms the submission:
delivered is false when feedback routing isn’t configured for the environment. The submission is still recorded.

Usage pattern

1

List your stores

Call store-list to see all stores you have access to. Note the store slug for each store you want to work with.
2

Pass the store slug

Include the store slug in all subsequent tool calls. For example, if your store is my-store.myshopify.com, the slug is my-store.
3

Use manage tools to make changes

Tools ending in -manage accept an action parameter, commonly list, create, update, or delete, plus tool-specific actions such as toggle_status or bulk actions like bulk_publish. Read-only actions like list are safe to call freely.

Write payload shapes

*-manage tools declare their writable fields inside a nested data object, but they accept the same fields at the top level of the arguments. Both of these calls apply the same update:
Write calls fail loudly instead of silently succeeding with no effect:
  • Arguments the tool doesn’t recognize return a validation error that names each unsupported argument and lists the fields the tool accepts. A misspelled field can’t be dropped silently.
  • A create or update call whose payload resolves to nothing returns a validation error rather than reporting success: true with an unchanged record.
  • Invalid field values return a readable Validation failed: ... error naming the field.
If an assistant reports a successful update but nothing changed, check the tool response. A genuine write returns the refreshed record. A rejected write returns success: false with the validation message.

Smaller MCP responses

List actions on updated manage tools accept fields to keep only selected keys on each item. Identity and structure keys such as id, code, and children remain in the response. Omit fields to return the full item. If you request a key that an item does not contain, the response identifies it in unknown_fields. Mutation actions return a compact confirmation with success, action, an identifier such as id, changed, and an optional message. Toggle and status actions also include the resulting state. Pass verbose: true or include=resource to include the full stored record in the response.

Discovering targetable fields

store-dimensions is the source of truth for what a store’s contextual conditions can target. It returns two lists in one response, both scoped to the store you pass:
  • dimensions — every dimension Layers records for the store, with source (built_in, analytics, or custom), value_type, coverage_percentage, unique_values, and last_seen_at. These are the codes accepted after SEGMENT BY in LayersQL.
  • contextual_fields — the context.* field names conditions accept in blocks-manage, request-transforms-manage, sort orders, product sequences, and merchandising rules edited through the collaborative editor. Each entry carries a label, group, type, allowed operators, and the same coverage stats. Store-specific labels from the Dimensions page flow through as label and description.
Filter with search (matches name, label, or description) or group (for example, Geography, Marketing, Custom), and paginate with page and per_page. Pass dimension to fetch the values observed for a single dimension or field instead of the listing; the tool returns up to values_limit samples (default 25, max 100) drawn from real traffic.
To sample values for one field before writing a condition:
Custom fields your storefront sends under custom on the contextual payload appear here automatically once Layers has seen them in traffic. There is no registration step. See Custom context fields for the discovery rules. layersql-schema accepts the same store argument. When present, each dimension in the response gains a store block with the matching store dimension code, its coverage stats, and the context.* field name conditions can target. Use it to move from an analytics query to a targeting condition without a second lookup.

Write validation

Every write tool that accepts a conditions payload validates leaves against the same registry. Layers rejects any condition that names an unknown field, an unknown operator, or an operator that doesn’t fit the field’s type. The error includes the nearest valid field name and points back to store-dimensions:
Store-specific fields (custom parameters, merchant-renamed dimensions) are not enumerated in tools/list, so calling store-dimensions for the target store is the reliable way to see the current vocabulary before a write.

Managing query dimensions

Use query-dimensions-manage to label queries with values from a product attribute and refine their rules. Read actions require ranking-rules.view; mutations require ranking-rules.edit.
  • list lists definitions. get requires code and returns the available labels, keyword rules, check status, agreement, grouped top-search preview, and saved corrections.
  • create takes data with code, name, attribute_code, and instructions; it can also take included_values. An attribute may have more than 150 distinct values, but a dimension can include at most 150 selected labels. Omit included_values to include all attribute values. update takes code and data; the code is immutable. An omitted included_values retains the current selection unless attribute_code changes, in which case the selection resets to all values.
  • Creating a dimension or changing its instructions, attribute, or included values starts background rule generation and checking. A name-only update does not. Use get to follow progress. Rechecking compares rules without replacing corrections.
  • update_rules takes code and the complete rules list, replacing the entire list, including corrections. Preserve every source: "correction" entry when sending an update, or those corrections will be lost. Each rule has match, kind (phrase or word_pattern), labels, and defer. Corrections use kind: "phrase" with the exact search words and must not defer. Empty labels explicitly mean no signal. Inspect dropped in the response for rejected rules and their reasons. Valid rules save and are checked without regeneration.
  • recheck compares current rules again. Set regenerate: true to regenerate generated and manually edited rules; this replaces those rules but preserves corrections. A normal recheck does not overwrite corrections.
  • delete requires code and is blocked while a ranking rule references the dimension, including unpublished rules.
Ranking-rule conditions can target request.query_dimension.<code>. A missing verdict does not count as an empty label or satisfy a no-signal condition. For a boost or bury action using the query’s labels, set filter.value_source to query_dimension, filter.value to the dimension code, filter.operator to in, and filter.property to that dimension’s attribute_code. Empty or unresolved labels skip that action. Use the collaborative editor to edit an existing ranking rule. See query dimension setup and previews for the merchant workflow.

Managing store settings

Use store-settings-manage for operational settings, and store-search-configuration for search behavior, prompts, and ranking weights. Both require the connection’s store.configure permission.
  • get returns all sections by default. Pass sections to request only sync_settings, preferences, sales_channel, or markets.
  • update accepts data keyed by those section names. Only supplied fields change; omitted fields keep their saved values. Read the current settings before making a change and check the returned sections afterward.
  • sync_settings manages crawler signature headers. Their values are write-only: reads report whether the headers are set, never their secret contents. Enabling signatures requires both headers, which can already be saved.
  • preferences controls Shopify resource syncing and inventory location scope. Sync overrides accept true to force on, false to force off, or null for auto-detection. Reads include detection state and available inventory locations. Missing Shopify permissions can prevent a sync preference from being changed.
  • sales_channel selects the Shopify channel treated as the app storefront. Read this section to discover available channels before selecting one.
  • markets selects strict for market catalog eligibility and localized pricing, pricing_only for localized pricing without catalog restrictions, or off to ignore markets. Reads include active markets.
These are direct saved changes, not collaborative drafts. They use the same validation and follow-up processing as the dashboard. Unsupported sections or fields are rejected rather than silently ignored. Staff-only settings are not available through this tool.

Managing banner types

Use banner-types-manage to manage reusable banner definitions. This is separate from placing a banner in a merchandising rule: edit those placements through set_presence and apply_patch on /banners.
  1. Call taxonomy to discover the supported field types, list support, and validation options.
  2. Use list or get to inspect existing types. get, update, and delete require banner_type_id.
  3. To create a type, provide data.name, data.allowed_modes, and data.field_schema. Placement modes are inject for banners between products and overtake for replacing the grid. The name determines a permanent slug.
  4. To update a type, send only the fields you want to change. If you send field_schema, it replaces the entire schema, not just individual fields.
Banner type writes save immediately. Removing a field or changing its type can drop stored values from banners that use it. Inspect existing uses before updating, and read any returned warning for affected rule banners.
System types are read-only. You cannot delete a type while a rule banner still uses it; remove those banners or switch their type first. Invalid schemas are rejected using the same validation as the dashboard.

Example: edit a merchandising rule

Ask your AI assistant:
“Pin the product ‘Classic White Tee’ to position 1 in the Summer Sale collection, and boost all products where brand is ‘Nike’ using a soft boost expression.”
The assistant uses merchandising-rules-manage to find an existing rule, then calls set_presence and apply_patch to edit its draft. To create a rule, use the tool’s create action. Newly created merchandising rules start disabled and do not affect live results until published.

Bulk actions

blocks-manage can duplicate one block with action: "duplicate" and block_id. The response includes the source block_id, the new duplicated_id, a message, and next_steps for continuing with the draft.
The tool also accepts bulk variants so an assistant can apply the same change to many blocks in one call:
  • blocks-manage — bulk_publish, bulk_unpublish, bulk_duplicate, bulk_delete (pass block_ids)
bulk_duplicate creates one copy for each entry in block_ids, in input order; repeating an id creates another copy. Unknown ids are returned in not_found_ids. Use bulk actions to update many blocks at once, such as unpublishing a batch of drafts after a seasonal campaign or deleting duplicate blocks.
The response includes an affected_count so the assistant can confirm how many records changed.

Collaborative editor

Assistants edit merchandising rules, sort orders, recommendation blocks, facet values, ranking rules, and audiences through a collaborative editing flow instead of direct writes. An assistant announces presence on a resource, reads the current shared document and its revision, then applies small RFC 6902 JSON Patch operations. This lets multiple humans and agents work on the same resource without overwriting each other. It also powers the same real-time collaboration experience that shows other editors in the dashboard. When an agent calls set_presence or apply_patch, humans on the same page see the agent live in the dashboard. The agent gets its own presence avatar with a distinct color, and its focused field or selected rows are tinted and locked like any other collaborator’s. On the facet-values editor, a compact agent activity popover also surfaces the intent and message your agent supplies. Provide a short, human-readable activity.intent and activity.message on set_presence, and a concise metadata.intent on apply_patch. This lets a human co-editor tell at a glance what your agent is doing. Use the collaborative editor tools when your assistant needs to change:
  • Merchandising rule fields, pins, or banners
  • Sort order fields (referenced by id or handle)
  • Recommendation block fields (referenced by block id)
  • Facet value sorting, grouping, and hidden values (referenced by attribute id or code)
  • Ranking rule fields, including targeting, actions, visual pins, and enabled status (referenced by rule id)
  • Audience names, conditions, and optional SegmentQL source (referenced by audience id)
The supported resource_type values are merchandising_rule, sort_order, block, facet_values, ranking_rule, and audience. Use merchandising-rules-manage and sort-orders-manage to list or create those resources. Use blocks-manage with action: "list" or blocks-catalog, facet-values-manage with action: "list", ranking-rules-manage with action: "list", and audiences-manage with action: "list" or action: "get" to inspect other resources.
Use the collaborative editor to change existing merchandising rules, sort orders, blocks, or facet values. ranking-rules-manage supports creating and deleting ranking rules; edit existing rules through the shared editor rather than direct update or toggle_status calls. New merchandising and ranking rules start disabled. Publishing a new draft turns it on when both its saved and draft states are off; if you explicitly turn an already-live resource off in the draft, publishing keeps it off. Newly created blocks remain drafts until published. A new sort order is not added to the storefront’s available sort menu, but it can still affect a request when a collection, rule, or block explicitly references it.
When a management tool creates a draft, follow the returned next_steps: call set_presence with the indicated resource type and id, use validate_document and apply_patch if you need to edit its content, then call request_publish and check the result with check_publish_request. The admin tool sort-orders-manage does not replace the storefront sort-orders-list tool, which remains available to list sort orders for storefront integrations.

Managing audiences

Use audiences-manage to discover and create saved audiences. Reads require merchandising-rules.view; creation, deletion, and publishing edits require merchandising-rules.edit. Your MCP connection must have access to the store.
  • Discover: list supports name search and pagination. get takes audience_id and returns the audience and its references, so you can check where a change will apply.
  • Choose fields: attributes lists supported condition fields, operators, and SegmentQL codes. Use store-dimensions for store-specific custom context fields.
  • Create: Pass data.name and data.conditions. Names are unique within the store, ignoring case. An audience cannot reference another audience.
  • Estimate: Pass audience_id for a saved audience, or data.conditions for a proposed definition. Estimates use recent recorded traffic and report conditions they cannot measure; they are not forecasts. See traffic estimate limitations.
  • Edit: There is no direct update action. Call set_presence with resource_type: "audience" and the audience id as resource_id, then validate and patch /fields/name, /fields/conditions, or /fields/segmentql. Finish with request_publish and check_publish_request.
  • Delete: Pass audience_id. Deletion is refused while rules reference the audience, a person has it open, or a publish review is pending. Remove references or settle the review first; a refused deletion leaves the audience unchanged.
conditions is the authoritative audience definition. The optional segmentql text is retained for display, not compiled by audiences-manage or the shared-editor publish path. Do not patch only the query text and expect targeting to change. When changing conditions without matching source text, clear /fields/segmentql to null so the editor opens the builder instead of a stale query. Audience drafts do not affect live targeting until saved. Approval or immediate publishing saves the definition used by every referencing rule; audiences do not have a separate enabled toggle. Audience publish responses use config_version_id: null.

Editing flow

The safe change-and-review sequence is set_presence -> validate_document -> apply_patch -> request_publish -> check_publish_request.
1

Announce presence and read the document

Call set_presence with the store, resource_type, and resource_id. If you do not pass a presence_id, the tool creates an active session or reuses your existing one for that store and resource. The response includes the presence_id, current document, revision, active_editors, and an expires_at timestamp.
2

Plan and (optionally) validate patches

Build small RFC 6902 patches against the public roots /fields, /pins, and /banners. Prefer granular list operations like add /pins/-, replace /pins/5, remove /pins/3, and move over replacing the entire /pins or /banners collection. Call validate_document first when it would reduce risk, for example before a large or unusual patch.
3

Apply the patch

Call apply_patch with the same presence_id, the observed revision, and the RFC 6902 patch list. Patches are applied atomically: if any operation fails, no changes are committed and the revision is unchanged. On success, the response returns the new revision and document.
4

Refresh presence and retry on conflict

Presence expires at expires_at unless refreshed. Calling set_presence again with the same presence_id refreshes it; calling without an id reuses your active session for the same store and resource. Successful apply_patch calls also refresh presence. Use release: true to end the session when you are done. If a patch fails with a stale revision or a failed test operation, refresh presence to re-read the document and re-plan.

set_presence

Declares an agent’s activity state and returns the shared document. Supported activity_state values are viewing, editing, thinking, and validating. An agent has one active presence per store and resource: calling without presence_id reuses that session and updates its activity state and expiry. Supply release: true with the presence_id or the resource locator to release it; activity_state is not required for a release. A successful release returns released: true; releasing an already missing or expired session is idempotent and returns released: false.
Example response:
Pass a sort order handle or id as resource_id when resource_type is sort_order. Keep the returned presence_id for later editor calls, or pass it to set_presence to refresh or release the session.

validate_document

Validates the current document or a proposed set of patches without applying changes. Whole-collection replacements return warnings so agents prefer granular list operations. An active presence_id can identify the resource without resource_type and resource_id; if you pass both forms, they must identify the same resource.

apply_patch

Applies an atomic RFC 6902 patch. Requires an active presence_id from set_presence and the latest observed revision. Supported operations are add, remove, replace, move, copy, and test. Patch paths must be under /fields, /pins, or /banners.
Readable pin positions
set_presence, apply_patch, and validate_document responses include pin_positions alongside the shared document. Use this read-only summary to describe pin order without treating the document’s raw position values as shopper-facing slot numbers. For example, a sequential pin with a raw ordering value of 2048 might be second in the sequence, not result 2048. A pin fixed at absolute position 10 reports that value separately and does not count toward sequential numbering. Use the returned path to patch the correct item, and refresh it after edits that change the array. Do not patch pin_positions itself.
Pin consistency
Each pin must be internally consistent. A pin with pin_type: "variant" requires a non-null pinned_variant_id. Any other pin type requires a null pinned_variant_id. If you omit pin_type, it defaults to product. apply_patch rejects any patch that leaves a pin inconsistent and doesn’t change the document. validate_document reports the same problem without writing. To change a pin’s type, set both fields in the same patch. You can also remove the pin and add /pins/- a new, consistent one. apply_patch rejects a lone replace /pins/0/pin_type that leaves pinned_variant_id unchanged.
Use test before replace to catch concurrent edits:
Explaining edits with metadata.notes
metadata accepts an optional notes object that maps an exact patch path in the same call to a one-line reason for the change. Notes appear in the dashboard’s agent activity feed as the caption for that step, so a human co-editor can see why the agent made each change.
Rules for notes:
  • Each key must match a path present in the same patches list. Notes for unknown paths are dropped.
  • Values must be non-empty strings. Non-strings and empties are dropped.
  • Notes are trimmed and capped at 240 characters.
  • Write a short reason (why this helps the shopper), not a restatement of the operation.
notes is optional. Patches sent without notes apply the same way.

Error handling

apply_patch returns deterministic errors so agents can recover automatically:

Requesting publish approval

apply_patch only updates the shared draft. To save a change to the live store, use request_publish for any supported resource: merchandising rules, sort orders, recommendation blocks, facet values, ranking rules, or audiences. Human review is the default. For a new draft, the reviewer must explicitly choose Approve & save to activate it. A regular editor save can also settle a pending request as published, but it preserves the submitted live/enabled state, so a published status alone does not guarantee that the resource is enabled. After requesting review, poll check_publish_request until the request settles. Do not call apply_patch again on the same resource while a request is pending unless the request is rejected or the user explicitly asks for more edits. The Review changes dialog shows the requesting assistant and its summary. Review in editor closes the dialog so the reviewer can inspect the actual draft. Approve & save saves the current editor contents, so the reviewer should check the draft rather than relying only on the assistant’s summary. Review and rejection are supported for all six resource types. Selecting Reject opens a confirmation step with an optional note and two choices:
  • Discard draft, selected by default, restores the last saved version in the shared editor and removes unpublished changes. Confirm with Reject & discard draft.
  • Keep draft for further edits leaves the shared draft available for revision. Confirm with Reject & keep draft.
Neither rejection choice changes the published version. Continue polling check_publish_request to read the outcome and any note. For facet values, approval requires a full dashboard save that submits both facet_value_group_sorts and hidden_values. A partial facet save leaves the request pending and preserves the shared draft, while a save to unrelated attribute settings does not approve it. See Editing facet values for the facet-specific workflow.
1

Request approval

Call request_publish with the same presence_id and the latest observed revision. Include an optional message describing what changed so the reviewer has context. The response includes a publish_request_id, a dashboard review_url, and an expires_at timestamp.
2

Share the review URL and wait

Send the review_url to the user or reviewer. They can inspect the affected resource in the dashboard and choose Approve & save or Reject from the request review. A regular editor save can also settle a pending request as published while preserving the submitted live/enabled state. Do not make further edits in that shared room while the request is pending unless the request is rejected or the user explicitly asks for more changes.
3

Poll for the outcome

Call check_publish_request with the publish_request_id until settled is true. The final status is published, rejected, superseded, or expired. Publish requests expire after 72 hours.
4

Handle the result

On published, the resource was saved. A ranking rule’s enabled field still determines whether it runs on the storefront. Merchandising rules, sort orders, and ranking rules have a config_version_id; blocks, facet values, and audiences return null. For rejected or expired, review any resolution note before deciding what to do. On superseded, a newer request has replaced this one. Switch to the newer publish_request_id.
request_publish
Requests human approval for the shared document the agent is currently editing. Review is the default; use publish_immediately only when the user explicitly asks to publish without review. Requires an active presence_id from set_presence and the latest revision you observed. The revision must still match the current room revision, or the tool returns a RevisionMismatch error. Refresh presence and try again.
Example response:
Calling request_publish while another request is pending for the same shared editor room marks the older request as superseded and returns the new publish_request_id.
check_publish_request
Returns the current state of a publish request. Safe to call as often as needed.
Example response after approval:
status values:

Publishing immediately

Set publish_immediately: true on request_publish to apply the shared editor document straight to the saved merchandising rule, sort order, recommendation block, facet values, ranking rule, or audience, with no reviewer step. Use it only when the user has explicitly asked to publish without review. When you omit the flag or pass false, the tool creates a pending review request. Requirements and behavior:
  • Supported for resource_type: "merchandising_rule", sort_order, block, facet_values, ranking_rule, and audience.
  • The caller needs the resource’s edit permission on the store: merchandising-rules.edit for merchandising rules and audiences, sort-orders.edit, blocks.edit, attributes.edit for facet values, or ranking-rules.edit. Without it, the tool returns 403.
  • There is no force override for a stale revision or a human editing the room.
  • The provided revision must still match the current room revision, or the tool returns 409 RevisionMismatch. Call set_presence to refresh and retry.
  • If a human is actively co-editing the room, or connects during the publish, the tool returns 409 HumanEditingInProgress and nothing is written. Ask the user to save from the dashboard, or retry once the human leaves.
  • The document is validated with the same rules the dashboard save uses. Validation errors are returned as 422 with per-field messages.
  • On success, the response includes status: "published", confirming the resource was saved. config_version_id is populated for merchandising rules, sort orders, and ranking rules, and is null for blocks, facet values, and audiences. Pass the returned publish_request_id to check_publish_request to verify the saved result without waiting for review.
Layers uses a stable snapshot for an immediate publish. If a human is editing the same room, the publish is rejected rather than overwriting that work.
Example response:

collaborative-merchandising-editor prompt

The Layers MCP server exposes a collaborative-merchandising-editor prompt that describes the full safe-edit workflow, including the request_publish handshake. MCP clients that surface prompts (for example, Claude Desktop) can select it to prime an assistant with the correct presence-then-patch-then-request sequence before making changes.

Editing facet values

Facet value sorting, grouping, and hidden values live inside a filterable attribute’s configuration. Assistants co-edit them through the same collaborative editor tools by passing resource_type: "facet_values" and the attribute id or code as resource_id. The shared document exposes two keys under /fields:
  • facet_value_group_sorts — an ordered list of { facet_value, position, facet_value_group_filter } entries. position controls the manual storefront order, and facet_value_group_filter is { name } when the value belongs to a named group (all values sharing one facet_value_group_filter.name render as one group), or null when ungrouped.
  • hidden_values — a list of facet value strings that are hidden on the storefront.
Rows in facet_value_group_sorts carry no ids. The server keys sorts by facet_value and groups by name (both distinct per attribute) and owns the underlying UUIDs. Never send an id on a sort entry or on facet_value_group_filter. Layers rejects it. Facet value sorting, grouping, and hidden values are edited in the shared document. Use request_publish to request human review, or publish_immediately only when the user explicitly asks to publish without review. attributes-manage create and update reject facet_value_group_sorts and hidden_values with a 422 that points back to the shared editor. Don’t try to set them alongside other attribute fields. Facets don’t have /pins or /banners, so patches must target /fields/facet_value_group_sorts or /fields/hidden_values. The attributes.view permission is required to read a facet’s document and attributes.edit is required to apply patches or call request_publish. Reorder a value:
Group two values under a shared display name by giving both entries the same facet_value_group_filter.name:
Hide a value from the storefront:
Sampling and grouping facet values
facet-values-manage exposes two actions that operate on facet values directly, without drafting RFC 6902 patches by hand. samples shows what a facet value actually resolves to on the storefront. For each requested value it returns the shopper-visible product count (computed on the same filter path the storefront uses, so counts match what a shopper sees), the display name, the swatch (metaobject or option), and a few sample products and variants:
Each entry in values reports { facet_value, display_name, product_count, swatch, samples }, where each sample is { product_title, product_handle, variant_title, image_url }. For a facet that isn’t filterable on the storefront (for example, one not visible on the storefront), product_count is null and the entry carries an error note instead of failing the call. upsert places individual values into groups inside the shared collaborative draft. Use it when you want to group a handful of values without rewriting the whole facet_value_group_sorts list with apply_patch. Call set_presence first to get a presence_id; the draft’s current revision is resolved for you. Each row in rows is { facet_value, group, after? }:
  • facet_value — the value to place. All existing memberships of that value are removed before the new row is inserted.
  • group — the group name to place the value in. A new name creates the group; null ungroups the value. Required on every row.
  • after — optional. Place the value directly after this value inside the target group; when omitted or when after isn’t in that group, the row lands at the end of the group. A moved value keeps its position only when staying in its current group.
The response returns the new draft revision and the changed_rows that were applied. upsert requires attributes.edit and an authenticated user, and writes into the same shared draft apply_patch edits. Request review of the finished draft with request_publish.
Requesting approval for the facet-values draft
apply_patch and facet-values-manage upsert only update the shared draft. By default, call request_publish with the active presence_id, the latest revision, and an optional message to request human review. See Requesting publish approval for the review and polling workflow, and Publishing immediately only when the user explicitly asks for no review. The reviewer approves by saving from the dashboard review URL. A facet-values approval requires a full save that submits both facet_value_group_sorts and hidden_values. Partial saves leave the request pending and preserve the shared draft; saves to unrelated attribute settings do not approve it. check_publish_request reports config_version_id: null for facet values.
The response follows the request_publish shape described in Requesting publish approval. For facet values, config_version_id is null. The removed facet-values-manage publish action is not supported: cached clients that call it receive an error directing them to request_publish with the presence_id and latest revision from set_presence. The list, samples, and upsert actions are unchanged.

collaborative-facet-editor prompt

The Layers MCP server also exposes a collaborative-facet-editor prompt covering the presence-patch-review flow, /fields patch boundaries, and the human-in-the-loop rule for publishing. Select it before asking an assistant to reorder, group, or hide facet values.

Working with grouped attributes

Some attributes are containers that hold other attributes rather than a single filterable value, for example a “dimensions” attribute that wraps length, width, and height. MCP tools treat these differently:
  • Groups are listed but not selectable. attributes-manage with action: "list" returns grouped attributes as read-only entries with their child attributes nested under children. Only the scalar children are marked selectable.
  • Search, browse, facets, and sort orders reject group codes. Passing a group, object, or otherwise non-selectable attribute code to filter_group, facets, or a sort order returns a validation error. When the parent has scalar children, the error lists them so your assistant can retry with a valid code.
Use attributes-manage with action: "list" to discover which attributes are selectable before building filters or sort orders. Pass usable_for: "attributes" to get exactly the codes accepted as result attributes, or usable_for: "facets" for the codes accepted in facets and filter_group.

Checking the keyword weight budget

The list response includes a top-level keyword_weight_holders summary of the store’s keyword search weight budget. Check it before you set keyword_search_weight so your assistant knows how much budget is left. The summary lists every attribute holding a weight, including group or structured metafield attributes that never appear as individual entries in the list:
  • total and remaining are the combined weight in use and the weight still available, out of 10.
  • is_searchable is false for attributes that hold a weight but aren’t searchable. Their weight still counts toward the budget.
  • hidden is true for attributes that don’t appear as selectable entries in the list output.
attributes-manage rejects a keyword_search_weight on an attribute that isn’t searchable. To add a weight, set is_searchable: true in the same update request.

Fetching attribute values

Call attributes-manage with action: "values" to enumerate the values available for a single scalar attribute. Use it before building filter_group conditions when your assistant needs to know what values a facet actually contains. For example, list every vendor or size before choosing one to filter on. The response depends on the attribute’s type:
  • Categorical attributes return a cursor-paginated values array. Each entry is { "value": "<string>", "kind": "group" | "value" }. Pass the returned next_cursor back in a follow-up call to fetch the next page.
  • Numeric attributes return a range object with the minimum and maximum values in the catalog.
Both responses can be scoped to a collection with collection_handle so the values reflect only products in that collection. When the attribute has facet value groups, the first page lists each group with kind: "group" before any raw values, and raw members of a group are hidden from the results. Pass the group name as the filter value and Layers expands it to the group’s members at match time. Ungrouped raw values are returned with kind: "value".

Selecting product and variant fields

The attributes argument on search-semantic, browse, and related tools accepts three kinds of selectors, all discoverable through attributes-manage with action: "list":
  • Attribute codes — any storefront-visible attribute code (vendor, color, size).
  • Core product fields — top-level product columns like title, handle, featured_image, body_html, product_type, tags. Dotted sub-paths (images.src) are only accepted under jsonb product columns.
  • Qualified variant selectors — variant fields must be qualified with a container: variants.<field> returns the value for every variant, and first_or_matched_variant.<field> returns the single variant that matches the current filter (or the first variant when no variant filter is active). Hidden variant columns such as cost are rejected.
Bare variant field names like price are rejected with a message that names the qualified forms (variants.price, first_or_matched_variant.price). Unknown sub-paths on a core product field return a 422 that suggests the parent field, for example title.foo → Did you mean ‘title’.
attributes-manage with action: "list" lists the qualified variant selectors alongside attribute codes so an assistant can discover the full vocabulary in one call. When a query matches nothing, the response includes a suggestions list of the top-5 nearest codes.

Classifying attributes for query understanding

attributes-manage create and update accept a data.classes array that controls how the query-understanding pipeline uses an attribute. Set it separately from is_searchable, keyword_search_weight, and enable_as_storefront_facet. Each switch controls a different behavior. Pass one or more classes; values are lowercased and deduped server-side. To leave the existing classes unchanged on update, omit the classes key. Passing null or any non-array value returns a validation error rather than clearing the field. Adding identifier to a searchable attribute, a non-metafield attribute, or a store that already has five identifier attributes also returns a validation error.
Toggling is_searchable triggers a catalog reindex, so batch those changes. The attribute-setup prompt walks an assistant through the full workflow: discovering the attribute, sampling values with query_catalog, picking classes, and verifying with product-search-diagnostics. Select it from an MCP client that surfaces prompts (for example, Claude Desktop) before asking an agent to classify attributes.

Creating and publishing computed attributes

attributes-manage create, update, publish, and delete operate on computed attributes. Shopify options, metafields, and other catalog fields sync automatically and can’t be created or removed through MCP. What each action does: Rules create enforces:
  • data.type must be calculated or derived.
  • data.code is auto-prefixed with calculated. when it has no reserved namespace, so style_number becomes calculated.style_number. A code that already targets a synced namespace (contains a dot, for example options.size or metafields.custom.x) is rejected. A code that matches an existing attribute case-insensitively (options.Size when options.size already exists, or a bare vendor) is rejected too. Call list to confirm what’s already there.
  • type: "calculated" requires data.source_code. type: "derived" requires data.calculated_logic.
  • Exactly one implementation may be set per attribute. create, update, and publish all reject a request whose effective source_code and calculated_logic are both non-blank. Pass null on the field you’re clearing in the same request.
  • data.output_schema is a list of { "name": "<string>", "type": "<string>", "nullable": <bool> } objects, never a bare object.
  • A multi-field output_schema (more than one entry) can’t set enable_as_storefront_facet: true on the parent. Enable the facet on the specific virtual child code instead, for example calculated.dimensions.width.
create a JavaScript computed attribute:
Source validation at write time. create, update, and publish validate data.source_code synchronously before persisting. Source that can’t compile is rejected with a 422 carrying the parser’s rejection, instead of being accepted and surfacing later as an asynchronous compile_error. The same applies to data.input_query, which must be a bare selection set (a query { ... } document is rejected). publish validates the effective payload, so a bad draft saved earlier is also rejected at publish time. Input query validation. data.input_query is checked against your store’s schema at every level. Unknown fields are rejected with the valid sibling fields and a closest-match suggestion, as are sub-selections on fields that have none and calculated, which always resolved to null inside an input query. Metafield names and reference sub-selections are checked against your store’s metafield definitions. draft_update applies the same input_query and output_schema rules as create, update, and publish, and an unknown output type lists the allowed ones. Previewing before publishing. Use preview to confirm an input query returns the data your function expects before you publish:
Each entry in the response’s products array carries the resolved input and, with run_function: true, the function’s output (or an output_error for a product where it failed). Values over 16 KB per product come back truncated. run_function needs an attribute whose compile_status is ready, and it always runs the live function on the live input query. Draft source never runs server-side, so publish it and wait for ready first. When the previewed query differs from the live one, the response includes a note saying the output came from the live query. After publish reaches ready, Layers recomputes only that attribute’s values on each product. A full catalog rebuild runs only when the attribute feeds search, sequences, families, merchandising rules, or variant breakout conditions. Drafts and publishing. draft_update writes to draft_source_code, draft_input_query, draft_output_schema, and draft_test_state on the live attribute. A draft is an editor autosave buffer only. Nothing compiles until publish promotes it into the live fields. A partial draft_update only touches the keys you send. Omitted draft fields keep their saved value, and an explicit null clears just that one field. publish promotes the draft. Compilation runs asynchronously, so poll get (or list) afterward to read compile_status and compile_error:
Each of source_code, input_query, and output_schema resolves independently on publish. An explicit data.* value beats a saved draft, which beats the current live value. Publishing a single-field override, for example just data.output_schema, doesn’t drop a saved draft for the other fields. calculated_logic is only touched when you pass it explicitly. Deleting a computed attribute. delete runs the same usage guard the dashboard does, extended to cover reports and every virtual output-schema child. A blocked delete names the context, for example Cannot delete attribute that is used in a sort order. When the blocking usage sits on a virtual child, the message adds (referenced via output field '<child code>') so you know which field to detach first. Catalog attributes always fail this action. Use the dashboard or Shopify to remove those sources.

Diagnosing search coverage

Use product-search-diagnostics when a product is missing from search-semantic results, to verify what content reached its search document, or to confirm an attribute change flowed through. Resolve a product by numeric id, Shopify gid (gid://shopify/Product/...), or handle. Pass include_variant_documents: true on per-variant-indexed products to include the rendered document text for each variant. The response has three sections:
On dev stores that skip LLM content selection, the payload’s content_selection field is "skipped", and the store indexes the full unfiltered catalog instead.

Diagnosing missing facets

product-search-diagnostics also explains facet availability. Pass facet_codes (and optionally a collection handle) to check why a facet you requested from the Facets API is missing from the response:
The response’s facet_diagnostics section lists the requested codes, which were included, and which were dropped with a reason: attribute_not_found, not_enabled_as_facet, attribute_not_filterable, facet_still_building, facet_build_failed, or facet_cardinality_exceeded (Layers turned the facet off automatically because it has too many distinct values). These are the same readiness checks the Facets API applies when serving requests, so a dropped code here explains a missing facet in storefront responses. See When a facet goes live for how facet builds work. For a store-wide rollup, call store-operational-status and read search_documents. It returns aggregate ready, pending, and needs_attention counts across the catalog. The same response includes a facets section with each facet’s build phase and last refresh time under states, plus a withheld list naming any enabled facets the API is currently holding back and why.

Batch search testing

Use search-batch to test search relevance across many queries at once. For example, check a regression list after tuning ranking weights, or compare how a configuration change affects a set of important queries. Runs are deterministic and unjudged: the tool records what each search returned, with no LLM grading. For judged, admin-managed evaluation runs over stored query sets, use store-evaluations instead. The tool is action-based, like the publish-request flow: start kicks off an asynchronous run and returns a run_id, get polls progress and reads results, list shows recent runs, and cancel stops a run. Limits per run:

Starting a run

Each entry in queries is a plain string or an object with query, an optional expect (a product id, handle, or title the query is expected to return), per_page (1-25, default 10), and an optional filter_group. Attributes and every filter_group are validated synchronously when you call start, so an invalid code fails the start call instead of failing jobs mid-run. Each entry in variants accepts a label, bypass_cache, an excludeRuleIds list of ranking rule ids to hold out (see Testing ranking rule impact), and the same tuning, searchStrategy, and pipeline bypass options as search-semantic (bypassQueryExpansion, bypassIntentModifiers, bypassAICuration, bypassAIDiversity, bypassRankingRules). Layers validates rule ids in excludeRuleIds when you call start, so an unknown id fails the start call before any searches run. When variants is omitted, the run uses a single baseline variant. Batch searches bypass the results cache by default so comparisons reflect live behavior; set bypass_cache: false to opt out.
The response returns the run_id, the run status, total_searches, and the store’s current config_version_id:
If dispatch fails, the response carries status: "failed" and an error explaining why.

Reading results

Poll with action: "get" and the run_id. Partial results are readable while the run is in progress; settled turns true once the run reaches completed, failed, or cancelled. Results paginate with page and per_page (max 25). Every get response also reports config_changed_during_run. When true, the store’s search configuration changed after the run started, so discard the comparison and start a new run. include: "diff" tracks the baseline variant’s top product across the other variants. For each query, every variant reports the rank of that product in its own results (compared_product_rank) and the rank_delta_from_baseline. If the product dropped out of a variant’s results entirely, the rank is null with the reason baseline_top_product_not_in_variant_results. Queries with an expect product report whether it was found and at what rank:
A search job that fails is recorded as a failed row with its error message, so completed + failed always accounts for every search in the run. Runs and their results are pruned after 24 hours.

Testing ranking rule impact

Use excludeRuleIds on search-semantic to hold specific ranking rules out of a single search. This answers “is this rule still earning its place?” with one read-only call, instead of disabling the rule in live config, testing, and re-enabling it. Pass an array of ranking rule ids (up to 50). Get ids from ranking-rules-manage with action: "list", or from debug.appliedRankingRules on a previous debug search. The debug payload reports each applied rule as an {id, name, type} object. The tool also accepts exclude_rule_ids as an alias.
The response echoes an excluded_ranking_rules block naming each held-out rule:
Use the echo to distinguish “the rule was excluded and results didn’t change” from “the exclusion didn’t take”. Behavior to know:
  • Unknown rule ids fail with a validation error rather than being silently ignored, so an inert exclusion never reads as an inert rule.
  • Disabled rules are accepted. The echo reports enabled: false so you can tell the rule wasn’t running anyway.
  • bypassRankingRules: true takes precedence: no rules run at all and the exclusion list has no effect.
  • Layers treats searches with a non-empty exclusion list as diagnostic, so holdout sweeps stay out of search analytics and zero-result reporting.
To compare baseline results against results without a rule in one run, pass excludeRuleIds on a search-batch variant and read the diff:
Pin actions share a single pin budget across all ranking rules. Holding out a pin-heavy rule can let later rules’ pins land, so result movement can exceed the excluded rule’s own pins. This matches what deleting the rule would do.

Comparing sort orders

sort-orders-preview accepts sort_order_codes (up to 3) to compare how the same products rank under different sort orders. The response aligns each product’s rank across every requested code and includes rank_deltas relative to the first code:
Ranks are computed over a bounded comparison window per sort order (reported in the response as comparison_window), not just the requested page. The window catches cases where a product sits on page 1 of one sort order and page 3 of another. A product outside the window reports a null rank with the reason outside_comparison_window. Use sort_order_code (singular) for a plain paginated preview of one sort order.

Explaining sort order rankings

sort-orders-preview returns a compact product payload with a 1-based position for each product. Image URLs and variant details are omitted to keep responses small. Pass explain: true to attach the full ranking explanation to every product on the page. The explanation contains the same sort effects the dashboard preview renders, including matched soft boosts, position movement, lift, and any boost-mode switch. To explain one product, pass explain_product with a product ID, GID, handle, or SKU. This requires a singular sort_order_code. The tool scans a bounded window of results (up to 500 products) for the product and returns its position and full explanation:

Browsing without a sort order

browse picks a sort order automatically when you omit sort_order_code. It uses the collection’s default when one is set. If the collection has no default, it falls back to the store’s storefront-visible relevance sort order, and then to the first storefront-visible sort order (ordered by display order, then creation time). The call only fails with a 422 when the store has no storefront-visible sort orders at all. The response echoes the resolved sort order so an assistant can confirm what actually ran: Pass sort_order_code explicitly to override both the collection default and the fallback.

Publishing product sequences

Publishing a product sequence is two-phase. The publish action records intent immediately by setting is_published, but the sequence only starts serving after a background evaluation confirms it is eligible. A sequence needs at least two searchable products and must satisfy any configured conditions. The evaluation runs immediately after publish, create, update, and single-group reevaluate calls and normally settles within seconds. Read activation_status in the response instead of the raw is_published and is_active flags: For example, publishing a sequence returns its current state:
A pending response usually resolves within seconds; follow up with get to see the settled state. If a sequence was previously blocked, publishing preserves the blocking inactive_reason so an assistant can see why the sequence won’t activate and fix the cause instead of retrying. publish and unpublish also accept a group_ids array to change many sequences in one call. Prefer this over one call per group when flipping publish intent across a batch:
The response returns a per-group results array plus rollup counts:
Each write is its own transaction, so partial results are accurate. Check results for per-group errors instead of assuming success from the rollup. evaluation_queued reports whether the background eligibility job was dispatched; a false value with a warning means the status was saved but the evaluation didn’t queue and you can retry with reevaluate. unpublish settles is_active synchronously and omits evaluation_queued. Call reevaluate with a group_id to re-run the eligibility check for one sequence; the response includes evaluation_queued: true plus the group’s current payload. Omit group_id to queue a re-evaluation across every sequence in the store. The list action returns a slim projection for each group (ids, names, flags, activation_status, and products_count) without per-group products or contextual_conditions. Call get with a group_id when you need the full sequence payload. The product-sequence-builder prompt walks an assistant through creating groups in one batch call, setting is_published on the create/update payload (which already is the publish step), and verifying with activation_status. Select it from an MCP client that surfaces prompts (for example, Claude Desktop) before asking an agent to build sequences.

Building blocks with the catalog

Recommendation blocks combine anchor-dependent strategy types, strategy-specific configuration keys, and rollup-driven readiness. Call blocks-catalog before creating or updating a block so an assistant works from the real vocabulary instead of guessing:
  • Anchors — for each anchor type, the allowed strategy_types and every strategy key with its title, description, requirements, beta flag, relationship (complement, substitute, or sequence), readiness (ready and data_computed_at), and config_schema. The config_schema lists each accepted strategy_config key with its type, whether it’s required, and any allowed values or limits. See Strategy configuration keys for the current schema.
  • Rules — the block rule vocabulary: condition fields, their operators and allowed values, combinators, and the four action types.
  • Fallback modes — one-line definitions of replace and fill (see Fallback chains).
Pass anchor_type to scope the response to one anchor. A strategy key with ready: false is still computing its first data rollup; a block using it serves its fallback chain until data_computed_at is set. The default detail: "summary" response keeps payloads bounded. Without a strategy filter, each anchor includes only strategy type, key, requirements, and readiness; a filtered strategy also includes its title, description, and configuration field names. The summary lists only rule operators, combinators, and action names. Use detail: "full" for the complete configuration schemas and targeting rule vocabulary, or filter by anchor_type, strategy_type, or strategy_key to keep the response focused. blocks-manage validates writes against the same catalog. strategy_type, strategy_key, and rule action types are enums, and unknown strategy_config keys are rejected with a validation error that names the accepted keys and their allowed values. Cross-field errors list the strategy types allowed for the block’s anchor, so an assistant can correct a payload in one retry.

Fallback trees and block validation

A block’s fallback_tree has the shape [{ id, conditions?, chain: [{ block_id, mode }] }]. Each branch has an id, optional conditions, and an ordered chain of fallback blocks:
Use existing block IDs in chain. replace serves the fallback instead of the primary results; fill tops up the primary results with fallback products. For recommendation blocks, validate_document applies the same validation rules as publishing, including when it checks the current document without proposed patches. apply_patch validates the simulated result before writing and rejects a patch with 422 InvalidField when a top-level field touched by that patch is invalid. An unrelated pre-existing invalid field does not prevent a patch to another field. When a manual block uses collection mode, a non-collection anchor requires a non-empty strategy_config.collection_handle. A collection-anchored block uses its anchor collection and does not need this handle.

Anchor-relative filter values

In a block rule’s apply_filter, promote, or demote action, a filter_group expression can take its values from the anchor product instead of a fixed values list. Set value_source: "context" and leave out values: When more than one anchor value resolves (several matching tags, or several cart items), Layers ORs them: eq becomes in and neq becomes notIn. If no value resolves, the expression is dropped for that request. value_source: "context" requires anchor_type product or cart. blocks-manage rejects it for any other anchor, and rejects value_property or value_prefix unless value_source is "context".
On a unisex anchor tagged collection::women and collection::men, the second expression matches products tagged with either, so the block returns women’s, men’s, and unisex products from the anchor’s vendor. If you save with an incompatible anchor, blocks-manage returns a validation error keyed to the expression, for example rules.0.action.filter_group.expressions.0.value_source, and the message names the block’s anchor type: Anchor-relative filter values (value_source "context") read the anchor product, so they need a product or cart anchor; this block's anchor type is 'collection'. To fix it, change anchor_type to product or cart, or replace the expression with static values.

Previewing blocks

blocks-preview runs a block’s configuration against the live catalog and returns a compact product list without touching stored config. Provide exactly one of:
  • block_id — preview a saved block. This runs the full recommendation pipeline, including the block’s fallback chain.
  • data — preview an inline block definition before saving it. Inline previews run only the primary configuration; fallback_block_ids are not executed.
Optional parameters: Both saved and inline previews apply filter_group and exclude_product_ids. The shopper filter is combined with the block’s rule filters rather than replacing them.
The response returns each product as { id, handle, title, price, position } plus the resolved configuration after rules ran (strategy, strategy_config, safeguards, and whether a rule hid the block). processing: true means the strategy’s data rollup is still computing. With explain: true, the trace lists pipeline events in order: block selection, each rule evaluation and applied action, strategy resolution and readiness, strategy product counts, applied safeguards, exclusions, and fallback traversal with its result. Use the trace to answer “why did this block return these products”, for example to spot a rule that unexpectedly matched or a fallback that replaced the primary strategy.

Reading block performance

blocks-manage with action: "list" returns rolling 7-day performance counters for each block: impressions_7d, clicks_7d, ctr_7d, purchases_7d, and metrics_computed_at. Pass sort (ctr_7d, impressions_7d, or the default priority) to rank blocks by performance, and anchor_type to filter the list. A published block with zero impressions usually means the block isn’t wired into the theme. Rendering is theme-side, so Layers can’t detect a missing Liquid binding. See Rendering blocks in Liquid. For deeper analysis, query the blocks LayersQL dataset through layersql-query.

Example: run an analytics query

Analytics is exposed through LayersQL. Ask your assistant to inspect the schema first, then run a query:
Call layersql-schema to discover available datasets, metrics, and dimensions. Omit dataset to get a compact syntax cheat-sheet with clause order, operators, and two example queries. Pass include_reference: true for the full syntax reference. Call layersql-validate-query with the target store and the query to check it before you run it. Validation confirms every metric and dimension exists on the dataset, and errors list the available names so an assistant can correct the query without a retry loop. To render the result as a chart, include a VISUALIZE clause in the query. layersql-query returns a visualization chart config alongside the rows, so an assistant can pick the chart type, x-axis, and series without a second call. layersql-query returns 50 rows by default and accepts up to 500 rows per call. Use columns to project the result to selected columns. When the row cap is hit, the response sets "truncated": true and includes next_offset. Pass that value back as offset on the next call to page through the remaining rows, or narrow start_date and end_date to shrink the result set:
The search-health-triage prompt walks an assistant through triaging zero-result or unhealthy search traffic: discovering metrics with layersql-schema, ranking terms by the canonical zero_results measure, and splitting catalog-coverage misses (identifier_lane_misses) from relevance failures. Select it before asking an agent to investigate search health. The request-transform-authoring prompt covers the shape of a request transform: request_types, the action types accepted by request-transforms-manage (replace_query, modify_query, add_filter_group, set_default_selected_options, modify_tuning_parameters, override_sort_order), and the targeting condition vocabulary (context.* for session context, request.* for the request itself). Select it before asking an agent to author or edit a transform.

Managing saved reports and dashboards

analytics-manage reads and writes the same saved reports and dashboards that appear in the Layers dashboard under Analytics. Use it when an assistant should turn an ad-hoc LayersQL query into something a merchant can revisit, or assemble a set of reports into a dashboard.
  • resource — either report (a saved LayersQL query) or dashboard (an ordered set of cards).
  • action — list, get, create, update, or delete.
Preview a query with layersql-query before saving it. Once the rows and visualization look right, save the same query string as a report:
The response includes the report’s columns, rows, visualization, and a url deeplink that opens the report in the Layers dashboard. A dashboard is an ordered list of cards. Each card either references an existing report by report_id or creates one inline with query and name. Inline creation runs in a single transaction, so a bad later card leaves no orphan reports. Set each card’s width to full, half, or third. Layers computes each card’s position and size on a 12-column grid from those widths, so do not send raw coordinates:
get, create, and update responses return every card’s rows, visualization, position, size, and per-card truncation flags, plus a url deeplink to the dashboard. list returns lightweight metadata for reports and dashboards. Setting is_default: true on a dashboard unsets the previous default for that store. create, update, and delete require the analytics edit permission on the store. list and get require the analytics read permission. Deleting a report that is referenced by a dashboard card returns an error. Remove or replace the card first.

MCP audit log

Every write action your assistant performs through the Layers MCP server is recorded alongside dashboard configuration changes in the store’s Activity Log. Each MCP entry captures the tool, action, user, store, and timestamp. Review activity in the dashboard under Settings → Access & Security → Activity Log. MCP actions appear in the same feed as configuration edits, so you can see everything that changed in a store in one place. Filter by event, action, or free-text search, and open any entry to inspect its details. The operational context attached to an MCP call is only visible to users with API key management permissions. Lower-privilege users still see that an MCP action happened, but not its raw context.

Storefront MCP server

The Storefront MCP server exposes read-only storefront operations as MCP tools. It uses the same access token as the Storefront REST API, so you can integrate AI assistants into customer-facing experiences.

Connecting

Configure your MCP client with the storefront endpoint and your API token:
Authentication uses the same storefront access token you use for the REST API. Pass it as a bearer token in the authorization header.

Available tools

Common parameters

Most search and browse tools accept these optional parameters:
By default, search-text and browse strip the _meta payload from responses to keep results compact. Pass debug: true when you need scoring diagnostics, for example when debugging why a product ranked where it did.
filter_group, facets, and sort orders only accept scalar attribute codes. Group or container attribute codes are rejected with a validation error that lists the selectable child attributes when they’re available.

Example: text search with debug

When to use each server

  • Use the Layers MCP server when you want an AI assistant to help manage your store: creating merchandising rules, tuning search behavior, building sort orders, or exploring analytics.
  • Use the Storefront MCP server when you want to integrate AI-powered product discovery into a customer-facing experience. It’s also useful when building AI agents that need to search and browse your catalog.
If you only need quick read-only answers inside Shopify (zero-result searches, how a product is indexed, a collection’s rules or default sort), Shopify Sidekick can use the Layers tools bundled with the Shopify app without any MCP setup. See Shopify admin extensions.

Next steps