Noodle Seed for Shopify
Deploy one reusable Shopify search, focused conversational-commerce, store knowledge, and checkout solution for any Shopify store.
This guide deploys one reusable Noodle Seed solution for Shopify businesses. Buyers can clarify what they want, review up to three live matches, inspect one selected product, ask about published policies, pages, and articles in a public embedded assistant, and continue to Shopify's hosted checkout. Products and published store records come directly from the Storefront GraphQL API; natural-language policy and FAQ answers use Shopify's standard Storefront MCP server through a frozen Noodle connector, with published Storefront content as a deterministic fallback when that FAQ source has no answer. There is no product database, vector index, synchronization job, transparent MCP proxy, or tenant source fork.
The interface is deliberately chat-first. It never renders an entire storefront inside the conversation.
An ambiguous request gets one short natural-language question with no widget; a search gets at most three recommendation
cards; details replace those cards for the selected product; and a one-item checkout summary appears only
after an explicit choice. The final Continue to Shopify action calls one app-only cartCreate mutation.
Shopify returns the final checkoutUrl, and the host opens that exact allowlisted URL.
What ships out of the box
Shopify-native natural-language relevance with partial-prefix matching, one bounded zero-result rewrite,
availability controls, price sorting, natural conversational clarification, three-result recommendations, selected-product detail, live policy/page/article
knowledge, one composed ask_store path with a Noodle-owned answer widget, a public shopping assistant, variants
and cursor pagination, one-item review, one confirmed
cartCreate, Shopify-authoritative totals and availability, and exact-origin checkout handoff.
Architecture
This boundary matters. The widget never receives the Storefront access token, never calls Shopify directly, and never receives Shopify's cart ID. Noodle resolves the token server-side through the credential broker.
Before you start
You need:
- A Shopify store where you can install sales channels and manage Storefront API permissions.
- At least one active product with a purchasable variant published to the Headless sales channel.
- Node.js 24 LTS or newer.
- The Noodle Seed CLI and a Noodle account for hosted deployment. Local development does not require an account.
- The store's canonical
https://<shop>.myshopify.comorigin. Do not use the admin URL.
The finished implementation is the Shopify Storefront example. Use it as the source of truth while following this guide.
1. Create a Shopify Headless storefront
In Shopify Admin:
- Open Apps and sales channels and install the Headless sales channel.
- Open Headless, then select Add storefront or Create storefront.
- Give the storefront a name that identifies this MCP App and environment, such as
MCP checkout - dev. - Open Storefront API permissions and grant only
unauthenticated_read_product_listings,unauthenticated_read_content,unauthenticated_read_checkouts, andunauthenticated_write_checkoutsfor this example. - Publish the products you want buyers to see to the Headless sales channel.
Shopify creates public and private Storefront access tokens for the storefront. Copy the private Storefront access token for this server-side integration.
Keep the private token server-side
The example sends Shopify-Storefront-Private-Token, Shopify's server-side authentication header. Never put
this token in browser JavaScript, widget props, tool results, logs, or source control. Enter it only through
Noodle's managed-secret prompt.
For buyer-initiated server traffic, Shopify also recommends forwarding Shopify-Storefront-Buyer-IP so its
bot protection and throttling can distinguish buyers. This reusable baseline does not forward network
identifiers into merchant connectors. Load-test against expected traffic before opening a high-volume store;
add buyer-IP forwarding only through a dedicated, privacy-reviewed trusted-proxy boundary.
Official Shopify setup references:
- Getting started with the Storefront API
- Storefront API authentication
- Shopify API access scopes
- Storefront search
- Product detail query
- Shop profile and policies
2. Start the Noodle project
Create a widget project and install its dependencies:
noodle init shopify-checkout --template widget
cd shopify-checkout
npm installCopy the files from the complete example into the matching project paths:
src/
helpers.ts
server.ts
shopify-assistant.ts
shopify-config.ts
shopify-content-responses.ts
shopify-mcp-responses.ts
shopify-queries.ts
shopify-recommendation-responses.ts
shopify-responses.ts
views/
product-detail.tsx
product-recommendations.tsx
store-answer.tsx
shopify-mini.css
shopify-widget-model.ts
test/
server.test.ts
shopify-mini-widgets.test.tsxThe example uses idiomatic TypeScript authoring. Do not copy or hand-edit compiled manifest or connector artifact files.
3. Bind one merchant environment
The reusable source names an operator-managed variable:
import { variable } from '@noodleseed/one';
export const SHOPIFY_STORE_ORIGIN = variable('SHOPIFY_STORE_ORIGIN');
export const SHOPIFY_STOREFRONT_MCP_ENDPOINT = variable('SHOPIFY_STOREFRONT_MCP_ENDPOINT');
export const SHOPIFY_STOREFRONT_API_VERSION = '2026-07';Do not edit that file per store. Link a merchant environment and bind its exact origin:
noodle link --org <org> --app shopify --env dev
noodle variables set SHOPIFY_STORE_ORIGIN --scope env \
--value https://your-shop.myshopify.com
noodle variables set SHOPIFY_STOREFRONT_MCP_ENDPOINT --scope env \
--value https://your-shop.myshopify.com/api/mcpUse one canonical bare HTTPS origin with no path, trailing slash, credentials, or wildcard. The same managed value drives:
- The connector
baseUrl. - The connector SSRF
allowedOriginslist. - The separately bound Storefront MCP endpoint must resolve inside that exact origin.
- The app's checkout
handoff.allowedDomainslist. - The embedded assistant's browser-origin allowlist.
- The widget's final checkout URL validation.
Noodle binds that value across every runtime authority and widget projection at deployment. Missing or malformed values fail closed. This is what lets one application serve many Shopify businesses without weakening the SSRF, embed, or external-link boundary.
The example pins Shopify Storefront API 2026-07 so a deploy does not change behavior unexpectedly. Shopify
releases stable API versions quarterly. Review the Storefront changelog and update the pinned version and
tests deliberately rather than switching to an unversioned URL.
If your product images come from another origin, add only that exact origin to the widget's
csp.resourceDomains. The default example allows https://cdn.shopify.com. Its
csp.connectDomains remains empty because the widget makes no direct network requests.
4. Understand the Shopify connector
The server declares one HTTP connector with brokered API-key authentication:
const shopifyStorefront = connector('shopify_storefront')
.version('1.0.0')
.http({
baseUrl: SHOPIFY_STORE_ORIGIN,
allowedOrigins: [SHOPIFY_STORE_ORIGIN],
auth: {
kind: 'apiKey',
header: 'Shopify-Storefront-Private-Token',
secret: secret('SHOPIFY_STOREFRONT_PRIVATE_TOKEN'),
},
operations: {
search_products: { /* POST Shopify storefront search */ },
get_recommendations: { /* POST nodes(ids:) for the final one to three products */ },
get_product: { /* POST one product(handle:) detail query */ },
get_shop_information: { /* POST live shop and policy fields */ },
search_store_content: { /* POST live page and article search */ },
create_cart: { /* POST one cartCreate mutation */ },
},
});All operations POST to /api/2026-07/graphql.json. They return the raw GraphQL envelope to a sandboxed
compute connector, which emits only the typed fields the tools need.
The same source also declares a headless MCP connector for Shopify's standard Storefront server:
const shopifyStorefrontMcp = connector('shopify_storefront_mcp')
.version('1.0.0')
.mcp({
endpoint: SHOPIFY_STOREFRONT_MCP_ENDPOINT,
allowedOrigins: [SHOPIFY_STORE_ORIGIN],
operations: {
store_answer: {
type: 'read',
tool: 'search_shop_policies_and_faqs',
result: 'text',
input: z.object({ query: z.string(), context: z.string().optional() }),
},
},
});Noodle never runs tools/list during a shopper request and never forwards Shopify tool metadata, _meta,
resources, prompts, or UI. The published ask_store tool calls the frozen operation, bounds and normalizes its
text, and attaches a Noodle-owned React view. If the normalized result is not_found, the recorded server flow
searches published pages and articles and returns at most three sources through the same tool and view. An
explicit guide request selects that content path directly. Text-only MCP clients receive the same bounded
result without the view; source fields keep FAQ answers and published content distinct.
Product search, detail, and policy knowledge
search_products sends Shopify's bounded Storefront search query with types: [PRODUCT] and prefix: LAST.
It exposes Shopify's native relevance or price ordering, ascending/descending direction, and unavailable-item
policy. Shopify returns totalCount, merchant-configured productFilters, at most 20 products, and at most
20 variants per result. Each product also reports whether its returned variants are complete, so the assistant
cannot mistake a high-variant product preview for the full option set. Each variant includes the merchandise
ID required by cartCreate, availability, selected options, current price, and compare-at price.
get_product fetches up to 100 variants plus completeness metadata for one returned handle.
For a named comparison, the assistant fetches each product and answers in concise prose organized by the
requested criteria, with links. It does not render recommendation cards: those cards help select an item,
but do not communicate differences well enough to constitute a comparison.
get_recommendations re-fetches only the final one to three Shopify product IDs after all headless ranking
work finishes, so intermediate pages never create transcript widgets and stale model-provided display data
never enters the view.
For a top-N request that Shopify's selected order proves, the assistant requests exactly N products and
stops after that page. It does not call get_product for a routine recommendation list because
get_recommendations already re-fetches the finalists. Full detail and pagination are reserved for named
comparisons, selected products, or client-side constraints that unseen matches could change.
get_store_information remains a lower-level direct-MCP read for the live shop profile, shipping countries,
contact information, and published privacy, refund, shipping, and terms policies. The embedded assistant
receives one knowledge tool: ask_store. Its store_information source reads the shop profile; its policy
source selects exactly one canonical policy kind (contact, privacy, refund, shipping, or terms);
its answer source checks the curated Storefront MCP FAQ operation first and conditionally
calls the internal search_store_content operation only after not_found; its published_guides source searches
published Shopify pages and blog articles directly for explicit guide, sizing, care, brand, and other
written-material requests. The separate outward search_published_guides tool remains available to direct MCP
clients that need the lower-level paginated result. Both content normalizers strip HTML and bound text before
model exposure.
The cursor belongs inside the GraphQL variables object:
variables: {
query: '${args.query}',
first: '${args.first}',
after: '${args.after}',
sortKey: '${args.sortKey}',
reverse: '${args.reverse}',
unavailableProducts: '${args.unavailableProducts}',
}Do not enable automatic HTTP query-string pagination for this operation. Shopify's endCursor must be
passed back as the GraphQL after variable in the next request body.
Checkout mutation
create_cart receives only the explicitly reviewed merchandise line and quantity (the connector contract
also permits an optional bounded note for extensions):
mutation CreateCheckout($input: CartInput!) {
cartCreate(input: $input) {
cart {
checkoutUrl
cost {
subtotalAmount { amount currencyCode }
totalAmount { amount currencyCode }
}
}
userErrors { code field message }
warnings { code message target }
}
}Notice that the selection does not request cart.id. A Shopify cart ID contains a key that must be treated
as a secret. This app does not need it because it creates the cart once and immediately hands the buyer to
Shopify checkout.
Shopify's cartCreate reference
defines the input, checkoutUrl, userErrors, and warnings used here.
5. Understand the tool surface
The raw MCP server exposes seven reads and one confirmed widget action. The embedded assistant deliberately
receives five reads: every merchant-knowledge question uses the composed ask_store path instead of choosing
between adjacent tools. Three reads have focused views; the knowledge view preserves canonical-policy, FAQ,
and published-content source boundaries while lower-level content search remains headless:
| Tool | Visibility | Responsibility |
|---|---|---|
search_products | Model and app | Search and paginate headlessly with Shopify-native ordering. |
show_product_recommendations | Model and app | Re-fetch one to three final product IDs and render exactly one recommendation view. |
get_product | Model and app | Verify one product's bounded detail headlessly. |
show_product | Model and app | Re-fetch and render one named or shopper-selected product. |
ask_store | MCP model and embedded assistant | Read the shop profile, select one canonical policy, check FAQ then conditionally fall back to published content, or directly search guides; render the bounded source-specific result. |
get_store_information | Direct MCP model | Read the shop profile and published policies as a lower-level primitive; omitted from the embedded assistant. |
search_published_guides | Direct MCP model | Search and paginate published pages and articles as a lower-level primitive; omitted from the embedded assistant. |
create_checkout | App only | After explicit confirmation, create one Shopify cart and return the checkout URL. |
create_checkout uses visibility: ['app'] so the selected-product view owns its one-line checkout request.
It still crosses the same runtime policy and connector boundary and declares exact-action confirmation before
external mutation. The five assistant reads form its closed capability allowlist; adding another server tool
does not expose it automatically.
Every list is bounded, every tool has a human-readable title and annotations, and tool output is normalized
instead of forwarding raw Shopify payloads. See Designing tools for agents for
the rules enforced by noodle check.
6. Understand progressive checkout
The product-detail view stores only the selected variant and quantity with useViewState:
const [variantId, setVariantId] = useViewState('shopify_selected_variant', initialVariantId);
const [quantity, setQuantity] = useViewState('shopify_quantity', 1);This state is a buyer-interface convenience, not a cart session. Choose this item first reveals a compact
one-item summary. Only Continue to Shopify calls create_checkout; the host then presents the tool's
exact-action confirmation. Do not put access tokens, checkout URLs, Shopify cart IDs, customer credentials,
or payment data in view state. The displayed multiplication is an estimate. Shopify revalidates merchandise,
quantity, stock, contextual price, discounts, tax, shipping, and the final total.
7. Configure Shopify and the embedded assistant
For local development, put the merchant bindings in a project-root .env excluded from version control:
SHOPIFY_STORE_ORIGIN=https://your-shop.myshopify.com
SHOPIFY_STOREFRONT_MCP_ENDPOINT=https://your-shop.myshopify.com/api/mcp
SHOPIFY_STOREFRONT_PRIVATE_TOKEN=replace-with-your-private-storefront-tokenConfirm .env and .env.noodle are ignored before saving the value. Never put the token in server.ts, a
test fixture, a widget property, a tool result, or a command-line argument.
For the hosted target, link the project and enter the token through the managed-secret prompt:
noodle login
noodle link --org <org> --app shopify --env prod
noodle variables set SHOPIFY_STORE_ORIGIN --scope env \
--value https://your-shop.myshopify.com
noodle variables set SHOPIFY_STOREFRONT_MCP_ENDPOINT --scope env \
--value https://your-shop.myshopify.com/api/mcp
noodle secrets set SHOPIFY_STOREFRONT_PRIVATE_TOKEN --scope envNoodle resolves these names for the linked organization, app, and environment. The reusable source and
compiled artifact contain references, never credential values. The source uses noodleManaged(), so the
merchant does not configure a provider endpoint, model identifier, or model secret. Hosted inference fails
closed until a Noodle operator enrolls the exact org/app/environment; enrollment is operator state and does
not fork or modify the reusable source.
8. Test locally
Run the deterministic tests and static checks first:
pnpm test
noodle validate
noodle check --min-severity warn
noodle check --target chatgptThen start the local runtime and Devtools:
noodle devOpen the Devtools URL printed by the command. Call search_products with a Shopify search string:
{
"query": "boots",
"first": 12,
"unavailableProducts": "HIDE"
}Verify all of the following before deploying:
- “What are the best items you can find me?” asks one natural-language question, calls no tool, and shows no widget.
- “Find the best in-stock options under my budget” asks for the product type and numeric maximum budget in one question; “my budget” is never treated as an amount.
- A short “well?” follow-up restates only the missing information and never starts a generic priority loop.
- The recommendation view shows at most three products and contains no search form, sort/filter controls, catalog navigation, cart, or checkout.
- Any number of headless search/pagination calls produces exactly one final recommendation widget.
- A Shopify-sorted top-three request fetches three results, does not paginate, and does not make redundant finalist detail calls.
- A named comparison fetches each product and explains the requested differences in prose; it never substitutes ordinary recommendation cards for the comparison.
- Clarification stays in prose. Recommendation and single-product views have only their documented one-sentence action cue, never a summary, tool narration, or repeated view content.
- Products and variants match the live Shopify catalog.
- Shopify-native result counts, natural-language relevance, partial-prefix matching, low/high price sorting, and in-stock filtering match the storefront.
- Product and content discovery make at most two search calls: the original query and, only after no relevant result, one materially different rewrite. They never repeat a query or relax a hard constraint.
- An exact product name is never broadened; no result is preferable to an irrelevant substitute or a recommendation widget.
- A cheapest/most-expensive answer remains correct when more products exist than one page; the assistant follows the cursor whenever ordering does not already prove the answer.
- Details replaces the recommendation list with the one live selected product rather than appending a storefront.
- A high-variant product is never described as complete when the returned variant page is partial.
- Published shipping, refund, privacy, and terms answers call
ask_storeonce withsource: "policy"plus the exact policy kind, match Shopify, and report missing policies as missing without consulting FAQ or content search. - Shopify FAQ sentinels such as
[],{},null, or whitespace normalize tonot_found, trigger the one conditional published-content fallback, and never appear as a successful answer or raw sentinel. - Natural-language FAQs and explicit guide, page, and article searches both use
ask_storein the embedded assistant. The former usessource: "answer"; the latter usessource: "published_guides". Missing evidence is reported as missing. - A harmless general educational question uses no tool and clearly distinguishes general knowledge from facts about this merchant.
- The embedded assistant is accepted only from the exact configured storefront origin.
- An unpublished product does not appear.
- The assistant follows the next GraphQL cursor internally when ranking completeness requires it; pagination controls never clutter the view.
- Unavailable variants cannot be chosen.
- Product search, detail, selection, and quantity changes do not make a Shopify cart request.
- Choose this item reveals only the one-item review; Continue to Shopify makes exactly one
create_checkouttool call. - The entire widget background follows the host in light and dark mode with no light iframe band around a dark card.
- The inline view remains readable and operable at 280px without nested vertical scrolling.
- Shopify errors prevent handoff and remain understandable.
- A successful call opens an HTTPS URL on the exact configured Shopify origin.
- Tool results, logs, view state, and widget payloads contain no Shopify cart ID or token.
Answer-quality golden prompts
Run these against the embedded assistant, then verify every product, amount, option, availability statement, and policy/content claim against the live Shopify source:
| Shape | Prompt | Pass condition |
|---|---|---|
| Ambiguous discovery | “What are the best items you can find me?” | Asks one concise natural-language question, calls no tool, shows no widget or checkout UI, and waits. |
| Missing budget | “Find the best in-stock options under my budget.” | Asks once for what the shopper is buying and the numeric maximum budget; after the answer, searches without another clarification. |
| Recovery | “well?” after the missing-budget question | Restates the one missing detail naturally; never emits a priority menu, internal directive, or repeated widget. |
| Direct ranking | “What are the three cheapest products in stock?” | Uses ascending Shopify price order, excludes unavailable items, and states the ranking basis. |
| Fuzzy constraints | “I need a blue gift under $75.” | Applies hard constraints first, labels near misses, and does not infer color from a title or tag. |
| Semantic discovery | “I am new to snowboarding and want something easy to learn on.” | Starts with one concise concept-preserving query; after no relevant match it may try one materially different rewrite, never more than two searches total, and renders at most three relevant finalists once. |
| Comparison | “Compare A and B, including every available option.” | Fetches both details and discloses any partial variant list. |
| Exact-name negative | “Do you carry the exact Orbital Noodle Seed Quantum Teapot?” | Searches the exact name once, says no when it is absent, never broadens the name, and renders no substitute or widget. |
| Policy edge | “Can I return a used item after 60 days?” | Quotes or paraphrases only a published policy and says when the store has not published an answer. |
| Canonical policy | “What is your published shipping policy?” | Calls ask_store once with the shipping policy source and does not call FAQ or content search when the canonical field answers it. |
| Missing FAQ | “Do you offer repairs?” | Calls ask_store once; an empty FAQ sentinel triggers the published-content fallback, and an empty fallback becomes a neutral unpublished-answer state rather than ok. |
| Store knowledge | “What does your size guide recommend?” | Calls ask_store once with the guide source, names returned pages/articles, and distinguishes them from a formal policy. |
| General education | “In general, what does water-resistant mean?” | Answers without tools and labels the answer as general, not a claim about this merchant or its products. |
| Follow-up | “Only show the available ones, cheapest first.” | Preserves the subject, switches to HIDE plus ascending PRICE, and does not restart with an unrelated query. |
Also score visual restraint: no answer may render more than three recommendation cards; no recommendation view may contain global search, sort, filter, cart, or checkout controls; product detail must replace the list; and checkout review must appear only after an explicit selection. Test both host themes. A factually correct answer still fails if its view becomes a miniature storefront, if intermediate search pages create extra widgets, or if the prose repeats information already visible in the widget.
Score each answer on factual grounding, constraint satisfaction, ranking completeness, uncertainty discipline, useful comparison, source discipline, and a clear next step. A fluent answer with one unsupported product fact fails. A correct answer that hides a hard-constraint miss also fails.
After the manual matrix, run the repeatable live subset. It creates and revokes a temporary assistant client using the current CLI login and emits only a bounded JSON summary:
pnpm smoke:shopify:semantic -- \
--service <deployed-noodle-service-url> \
--origin https://your-shop.myshopify.com \
--org <org> --app shopify --env devPass --expectations <ignored-private-json> to layer exact live product-title expectations onto the committed
generic cases without committing merchant data to the repository. The expectations file itself contains live
merchant product titles, so keep it private. Pass --client-credentials-file <0600-json> when an operator
has already provisioned a target-bound assistant client and the smoke must not create one.
{
"version": 1,
"cases": [
{
"id": "semantic_product_discovery",
"expect": { "expectedProductTitles": ["Your known live product title"] }
}
]
}9. Deploy safely
Start with owner-only access while you run the live-store smoke test:
noodle deploy --access owner-only
noodle openThe deploy output prints a public assistant embed ID. Install its one-line loader in the active Shopify theme so it appears on every storefront page:
<script src="https://cloud.noodleseed.dev/v1/assistant/embed.js"
data-embed-id="pub_replace_with_deploy_output" async></script>In Shopify Admin, open Online Store → Themes, open the active theme's action menu, choose Edit code,
open layout/theme.liquid, and paste the exact deploy-generated snippet immediately before </body>.
Save the file, reload the storefront, and verify the launcher opens Noodle Seed for Shopify. The embed ID
is a public surface identifier, not a credential; the private Storefront token remains only in Noodle
managed secrets. Repeat this small installation step whenever the merchant publishes a
different theme.
Repeat the local verification against the deployed endpoint. Confirm the hosted target has the correct environment-scoped secret and that Shopify attributes the checkout to the intended Headless channel.
Before changing the deployment to public consumer access, review expected traffic, Storefront API rate behavior, abuse controls, product visibility, fulfillment rules, returns, privacy disclosures, and the host's app-directory requirements. Checkout still happens on Shopify, so the MCP App must never collect card or payment details.
Error handling you must keep
Shopify GraphQL can return HTTP 200 with a top-level errors array. A successful HTTP status is not enough.
The example normalizer also checks:
- Missing or malformed product data.
cartCreate.userErrorssuch as an invalid or unavailable merchandise line.cartCreate.warningssuch as insufficient stock or a reduced quantity.- A missing or invalid
checkoutUrl. - A checkout URL whose origin differs from the configured store.
Keep these checks if you customize the output. Do not render identifier-dependent actions from a pending, errored, or malformed initial tool result.
Troubleshooting
| Symptom | Check |
|---|---|
| Shopify returns 401, 423, or an authentication error | Confirm you copied the private token from the Headless storefront and use Shopify-Storefront-Private-Token. Re-enter the managed secret instead of logging it. Development stores remain password-protected in the browser, but private server authentication is separate from the storefront password. |
| Product list is empty | Publish the products and variants to the Headless sales channel, confirm they are active, and review Shopify Search & Discovery configuration. |
| GraphQL says access is denied | Review the storefront's Storefront API permissions and make sure the query has not been expanded beyond them. |
| Search works but images are blocked | Add the image CDN's exact HTTPS origin to csp.resourceDomains; do not add a wildcard. |
| Hosted assistant works but no launcher appears in Shopify | Confirm the exact deploy-generated embed.js snippet is saved immediately before </body> in the active theme's layout/theme.liquid. Reinstall it after publishing a different theme. |
| Checkout returns a user error | Show the normalized code/message, keep the buyer in the one-item review, and let them change the selected variant or quantity. |
| Checkout URL is rejected | Confirm SHOPIFY_STORE_ORIGIN is the canonical myshopify.com origin returned by checkout. |
| Totals differ from the widget estimate | Use Shopify's mutation/checkout total. The one-item multiplication is only a display estimate. |
| A shopper wants several products | Keep the chat view focused; hand off the selected item to Shopify and let Shopify own multi-item cart management. |
Migrating from a synchronized storefront app
When replacing an older app that synchronized products into its own database, remove the synchronization assumptions instead of rebuilding them in Noodle:
| Older pattern | This guide |
|---|---|
| Product records copied into an app database | Live Storefront search and product(handle:) queries on demand. |
| Database product ID | Shopify product GID for display identity. |
| Database variant/SKU record | Shopify variant GID as merchandiseId. |
| Search index/filter state | Shopify GraphQL endCursor, totalCount, and merchant productFilters. |
| Copied FAQ/policy index | Live published Shopify policies, pages, and articles, normalized to bounded plain text. |
| Server-side session cart created while browsing | No cart while browsing; only selected variant and quantity live in widget state. |
| Incremental cart mutations | One cartCreate when checkout begins. |
| Stored cart ID used to resume | No cart ID requested, returned, or persisted. |
Reconcile old filters and search behavior with Shopify Search & Discovery and Storefront search syntax. Add semantic indexing only after merchant evidence shows Shopify's native catalog search is insufficient; keep Shopify identity, publication, price, and availability authoritative either way.
When you need cart resume later
Do not move Shopify's cart ID through the model, widget, view state, ordinary Noodle state handles, or tool results. The ID includes a secret key. A resumable design needs an encrypted server-side cart vault keyed by an opaque, non-secret handle, plus tenant/user ownership checks, expiry, redaction, and lifecycle cleanup.
Until that dedicated boundary exists, keep this app on the selected-item plus one-cartCreate design.
Next steps
Shopify example
Read and copy the complete tested implementation.
Connectors
Learn request mappings, credentials, allowlists, and compute normalization.
Apps & widgets
Learn widget result handling, app-only tools, view state, and handoff.
Deploy & operate
Manage environments, secrets, deployment access, logs, and rollback.
Apps & widgets
Render React interfaces inside an MCP host and connect them to your tools. Learn widget authoring, resource delivery and the current host limitations.
Embed an assistant in your SaaS
Deploy a Noodle Seed assistant, exchange your existing signed-in user on the backend, and mount the published browser SDK without exposing credentials.