Shopify MCP Demo
Shopify MCP Demo
Durchstöbere einen echten Shopify-Shop direkt aus ChatGPT oder Claude und bezahle mit Cashfree – Katalog, Warenkorb, OTP-Anmeldung, gespeicherte Adressen und Zahlung, ohne die Unterhaltung verlassen zu müssen.
"show me shirts from the store"
↓ SearchProducts
product grid ⇄ product detail
└────────┬───────┘
↓
cart → phone → OTP → address → payment
↓
Cashfree → order summaryJeder Zahlungsweg endet auf demselben Bildschirm: was gekauft wurde, mit Mengen und Preisen, plus Bestell-ID und Status. Cashfree bestätigt, dass Geld geflossen ist, hat aber nie den Shopify-Warenkorb gesehen und kann daher nicht sagen, was darin war.
Nach dem Bezahlen erneut suchen – das Widget startet eine neue Shopping-Sitzung: neuer Warenkorb, keine Quittung bleibt übrig. Zweimaliges Kaufen in einer Unterhaltung funktioniert.
Funktionsweise
Der Server ist drei Dinge zugleich:
ein MCP-Server für den KI-Host, der ein modellseitiges Tool (
SearchProducts) sowie eine Widget-Ressource bereitstelltein MCP-Client für Shopify, der über JSON-RPC mit
https://{SHOP_DOMAIN}/api/ucp/mcpkommuniziert – keine Authentifizierung, die Shop-Domain ist die gesamte Konfigurationein REST-Client für Cashfree, zur Erstellung von Bestellungen, OTP-Anmeldung, gespeicherten Adressen und Bestellstatus
Das React-Widget steuert die gesamte Reise. Nur die Produktsuche erreicht das Modell; alles danach läuft Widget-zu-Server, was den Ablauf deterministisch hält.
Das Stöbern umfasst zwei Bildschirme. Das Raster zeigt eine Karte pro Produkt mit dessen Preisspanne und Anzahl der Optionen; ein Tippen öffnet einen Detailbildschirm mit Beschreibung, Variantenauswahl und „In den Warenkorb". Ein Produkt mit einer einzigen Variante kann direkt von seiner Karte hinzugefügt werden, und ein bereits im Warenkorb befindliches Produkt erhält dort einen Mengenwähler – so kosten die häufigsten Fälle einen Tipp und nur eine echte Wahl einen Bildschirm. Beide Bildschirme zeigen an, was bereits im Warenkorb ist, und die beiden Zählungen stammen aus derselben Funktion.
Related MCP server: Shopify Agentic MCP Gateway
Einrichtung
npm install
cp .env.example .env # set SHOP_DOMAIN and the Cashfree keys
npm run build
npm startMachen Sie es zugänglich (ngrok http 8787), setzen Sie SERVER_URL in .env auf die öffentliche Herkunft, starten Sie neu, und fügen Sie dann <öffentliche-Herkunft>/mcp als Connector in Ihrem Host hinzu.
Ein Neustart reicht – kein Neubau nötig. Die Herkunft wird beim Start gelesen und bei jeder Bereitstellung der Ressource in das Widget-HTML eingefügt.
Variable | Zweck |
| Der Shop. Wechseln Sie Shops, indem Sie diese Zeile bearbeiten und neu starten. |
| Erforderlich bei jedem UCP-Aufruf. Das öffentliche Beispielprofil von Shopify ist für eine Demo geeignet. |
|
|
| Dashboard → Entwickler → API-Schlüssel. |
| Wo Cashfree den Käufer zurückleitet. Standardmäßig eine stabile gehostete Seite. |
| Öffentliche Herkunft bei Tunnelbetrieb. |
| Standardmäßig 8787. |
|
|
Was Sie beim Ausführen erwartet
Zahlung funktioniert, außer gespeicherte Karten. Der Host steuert Zahlungstools basierend auf deren MCP-Annotationen. cashfree-here liefert die ehrlichen – { readOnlyHint: false, destructiveHint: true } für ein Tool, das eine Karte belastet – und dagegen lehnt der Host den Dispatch ab: das Modell formuliert die Absicht, der Host ruft die Widget-Vorlage des Tools vorab ab, und es kommt nie ein tools/call an.
Durch Setzen von PAYMENT_ANNOTATIONS=readonly werden sie zu { readOnlyHint: true, destructiveHint: false } überschrieben, und vier der fünf Tools werden dispatchen: UPI, Online-Banking, gehosteter Checkout, neue Karte – und sobald die Übergabe erfolgt, auch gespeicherte Karte.
Dieses Flag ist eine Messung, keine Lösung. Es lässt ein Tool, das Geld bewegt, behaupten, es tue nichts – eine Lüge gegenüber genau der Kontrolle, die existiert, um dies zu verhindern. Es ist standardmäßig ausgeschaltet, gibt bei erster Verwendung eine Warnung aus und darf nicht ausgeliefert werden.
Wenn ein Tool blockiert ist, zeigt das Widget dies an und bietet einen Cashfree-Link an, der funktioniert. Im Tab bezahlen, zurückkommen, und das Widget bestätigt die Bestellung.
Die Übergabe ist spät, nicht verloren – und „blockiert" kann falsch sein. Diese Datei sagte früher, dass die Widget-zu-Modell-Übergabe etwa zur Hälfte der Fälle fehlschlug. Eine aufgezeichnete Sitzung sagt etwas anderes: CheckoutTool wurde nach zwei Versuchen des Widgets (2 × 4s) als blockiert gemeldet, der Käufer nutzte den Cashfree-Link, und das tools/call kam dann trotzdem an – etwa 2–4s nach dem Klick, also etwa 12–20s Ende-zu-Ende. Der Server bearbeitete es in 37ms. Jede Millisekunde dieser Verzögerung lag upstream.
sendFollowUpMessage bittet den Host nicht, ein Tool auszuführen. Es sendet eine Benutzeraktion und löst sich auf, sobald diese Nachricht zugestellt ist – daher beginnt das Bestätigungsfenster des Widgets zum Zeitpunkt der Einreihung und misst dann eine vollständige Modell-Inferenzrunde auf der Host-Infrastruktur – gegen die das 4s-Timeout nie kalibriert wurde.
Die Folge ist schlimmer als ein langsamer Bildschirm: Der Käufer bezahlte auf dem externen Link, während im Hintergrund ein Cashfree-Widget für dieselbe payment_session_id gerendert wurde. Zwei aktive Zahlungsoberflächen für eine Bestellung. DISPATCH_ATTEMPTS = 2 sendet die Folgenachricht zweimal, also sind auch zwei Widgets möglich.
Die Lesart „scheitert zur Hälfte der Fälle" war unser eigener Fehler. Zwei App-Instanzen wurden auf dem einen postMessage-Kanal geöffnet, den der MCP-Apps-Transport bereitstellt: useMcpApp erstellte und verband eine für das Rendering, getClientPlatform() erstellte und verband eine andere für die Zahlungsübergabe. Die Handshakes liefen um die Wette, und der Verlierer antwortete auf alles Folgende mit „Nicht verbunden" – ein Käufer wählte eine Zahlungsmethode und es erreichte nie ein tools/call den Server. Eine Münzwurf-Übergabe sieht aus wie ein Zwei-Wege-Rennen, nicht wie ein unzuverlässiger Host. Der Hook abonniert nun den gemeinsamen Client, connect() ist idempotent, und das Aushängen ruft nicht mehr close() auf einem Singleton auf, der den React-Baum überlebt.
Seit dieser Korrektur ist jeder Dispatch beim ersten Versuch gelandet. attemptsFor() gibt bereits 1 bei MCP-Apps-Hosts zurück, daher gilt die Wiederholung nur für ChatGPT, das LegacyOpenAiClient verwendet und dieses Rennen nie hatte – es gibt keine Messung, die seine Entfernung rechtfertigt, also bleibt es.
UPI scheitert über ₹1.00.000 mit „Zahlungsmethode ist für diese Bestellung nicht geeignet" – das Transaktionslimit von UPI, bestätigt durch Aufteilung bei ₹99.600 (ok) / ₹1.00.800 (fehlgeschlagen). Online-Banking und gehosteter Checkout haben höhere Limits. Der Warenkorb hat keine Obergrenze, daher kann man mit ein paar Tippen auf + ohne Warnung darüber hinausgehen.
Das Neuladen des Host-Fensters ist sicher, bei beiden Hosts. Warenkorb, Checkout-Schritt, gespeicherte Adressen und der Zahlungsbildschirm kommen alle zurück; der Warenkorb-Inhalt wird aus dem persistenten Zustand wiederhergestellt, nicht erneut abgerufen, daher kostet ein Neuladen in Claude keine Aufrufe an Shopify. ChatGPT liefert das Tool-Ergebnis nicht erneut aus, daher kostet es einen search_catalog-Aufruf, um das Produktraster neu aufzubauen, und nichts weiter. Gemessen über vier Abläufe mit Neuladungen bei mehreren Schritten: 18 upstream-Aufrufe, keiner davon eine Wiederholung.
Die Build-ID wird auf dem Zahlungsbildschirm angezeigt. Hosts cachen Widget-Instanzen, und eine gecachte ist von einer aktuellen nicht zu unterscheiden – mehrere Debugging-Runden gingen einst in Code, der bereits gelöscht war. Wenn die Build-ID nicht mit dem laufenden Server übereinstimmt, sehen Sie ein veraltetes Widget.
Ein Neubau erreicht den Browser nicht, und ein neuer Chat auch nicht. Die Widget-URI trägt die Build-ID, sodass jeder Build eine eigene Ressource ist – und das reicht immer noch nicht: Claude lieferte ein gecachtes Widget über Neubauten und neue Unterhaltungen hinweg aus, ohne ein einziges resources/read im Log. Zwei Debugging-Runden gingen in Instrumentierung, die nie ausgeführt wurde. Der einzig zuverlässige Weg, ein erneutes Lesen zu erzwingen, ist, den Connector zu trennen und wieder zu verbinden. Überprüfen Sie das Log auf POST /mcp (resources/read ui://widget/shopify-store-<build>.html), bevor Sie irgendetwas Gesehenem vertrauen.
Das erneute Verbinden ist immer noch nicht die ganze Geschichte: Die gecachten Tool-Metadaten des Hosts können eine URI aus einem früheren Build nennen, sodass selbst eine neue Unterhaltung nach einer Build-ID fragen kann, die dieser Server nicht mehr hat. Er antwortet für jede davon – siehe „Versionierung der Widget-URI führt zu ihrer Ablösung" weiter unten – aber die ID in dieser resources/read-Zeile ist die des Hosts, nicht unbedingt die des laufenden Servers. window.__BUILD__ auf dem Zahlungsbildschirm sagt Ihnen, welches Bundle ausgeführt wird.
Bekannte Einschränkungen
Karteneingabe kann in Claude nicht gerendert werden und wird upstream nicht behoben. Cashfree Elements mountet seine PCI-Felder als verschachtelte Cross-Origin-Iframes. Claude erzwingt
frame-src 'self' blob: data:und ignoriert dieframeDomains, die eine UI-Ressource deklariert, sodass die Felder als leere, nicht anklickbare Boxen geladen werden. Dies ist eine Richtlinie, kein Fehler im Transit: Ein Anthropic-Ingenieur erklärte am 09.04.2026 in claude-ai-mcp#40, dass verschachtelte Iframes aus Sicherheitsgründen nicht erlaubt sind, und zwei spätere Fragen nach „dauerhaft oder vorübergehend?“ blieben unbeantwortet.connectDomainsundresourceDomainswurden im April behoben und funktionieren – nur das Framing ist blockiert, daher istfetchvom Widget zu diesem Server in Ordnung.Stripe traf auf die gleiche Hürde: Die MCP-Apps-Dokumentation öffnet eine gehostete Checkout-Seite mit
app.openLink()anstatt Karteneingabefelder einzubetten. Das ist die gleiche Form wieCheckoutToolhier, das heute funktioniert und der empfohlene Weg für Karten in Claude ist.Die Karteneingabe im Gespräch zu behalten ist möglich – einfache
<input>-Felder (kein Iframe), die direkt an diesen Server senden, wascashfree-herevor Commit55da139auslieferte, als es durch Elements ersetzt wurde. Es leitet Roh-PANs durch unseren Server (SAQ-D-Territorium) und 3DS leitet immer noch aus, daher erkauft es die Form, nicht den Ablauf. Nicht gebaut; eine Produktentscheidung, keine technische.Zahlung mit gespeicherter Karte schlägt innerhalb von Cashfree fehl.
CardPaymentToolsendet und listet gespeicherte Karten korrekt, aber die Zahlung mit einer Karte gibtHTTP 500 {"message":"Internal Server Error"}von/pg/orders/sessions/jszurück. Auf eine Bestellung isoliert: UPI und Netbanking geben beide 200 mit derselbenpayment_session_idund denselben Headern zurück; nur der Zweigpayment_method.card.instrument_idliefert 500, mit oder ohne CVV. Ein generischer 500 ist ein Fehler seitens Cashfree – deren Validierungsfehler sind 400 mit spezifischen Nachrichten. Benötigt Cashfree.UPI wird oberhalb seines Limits von ₹1.00.000 angeboten und schlägt bei Cashfree fehl, anstatt im Auswahlmenü deaktiviert zu sein.
cashfree-herewird direkt patched. Zwei Korrekturen leben im Schwester-Checkout, nicht in diesem Repository:useReconciliation.start()löscht jetzt den vorherigen Poll-Timer, und die Benachrichtigung über erfolgreiche Zahlung feuert einmal pro Bestellung. Ohne sie hat eine bezahlte Bestellung „Payment completed successfully“ an den Chat gesendet, alle paar Sekunden, endlos.Die ausgewählte Adresse ist nicht an die Bestellung gebunden. Der Käufer wählt eine aus und Cashfree wird nicht informiert. Ungelöst; benötigt eine Antwort vom OCC-Team.
Es wird keine Shopify-Bestellung erstellt. Die Bestellung lebt nur in Cashfree. Das Erstellen einer benötigt Shopify-Admin-API-Anmeldeinformationen, die dieses Projekt bewusst vermeidet.
Der Session-Speicher ist im Arbeitsspeicher. Ein Server-Neustart verliert einen laufenden Checkout.
Angebote und Gutscheine werden zurückgestellt. Beide APIs sind nachgewiesen und dokumentiert in
docs/cashfree-occ-api.md; nur die UI fehlt.Nur INR, passend zu den Demoshops.
Hinweise für alle, die dieses Projekt erweitern
Erkenntnisse, die viel Zeit gekostet haben. Jede ist gemessen, nicht angenommen.
Shopify
Ziele nur
/api/ucp/mcpan. Die ältere/api/mcpist nach dem 31.08.2026 veraltet und sagt dies bei jeder Antwort.Die veröffentlichte Dokumentation ist an drei Stellen falsch: Sie vertauscht die Aufteilung der Endpunkte für Katalog/Warenkorb und dokumentiert Warenkorbzeilen als
merchandise_id, wenn UCPline_items[].item.iderfordert. Typen hier stammen aus erfassten Payloads insrc/lib/ucp/__fixtures__/, nicht aus der Dokumentation.update_cartist deklarativ – sende jedes Mal den vollständigen gewünschten Zeilensatz; das Entfernen wird durch Weglassen einer Zeile ausgedrückt.Geld ist in Untereinheiten mit der Währung einmal auf Warenkorbebene gehalten.
formatMoneynimmt die Dezimalstellenanzahl vonIntl, daher werden Währungen ohne Dezimalstellen nicht geteilt.Ein passwortgeschützter Shop stellt weiterhin Katalog-, Warenkorb- und Checkout-Links bereit. Nur das Durchsuchen des Shops stößt auf die Passwortseite.
search_cataloggibt Produktbeschreibungen als HTML zurück, und Varianten tragen ihre Achsen alsoptions: [{ name, label }]. Die Beschreibung wird innormalise.tsauf Nur-Text reduziert, bevor sie React erreicht: Es handelt sich um vom Shop kontrollierte Inhalte, die auf demselben Bildschirm gerendert werden, der eine OTP sammelt, und keine Formatierung darin ist eine Injection-Oberfläche wert.Ein Produkt mit einer einzigen Variante hat dennoch eine Option – Shopifys Platzhalter
{ name: "Titel", label: "Standardtitel" }. Das Rendern zeigt „1 Titel“ unter einem Produkt, das keine Auswahlmöglichkeiten bietet.
Cashfree
x-chxs-idist diepayment_session_idaus „Bestellung erstellen“. Das erzwingt, dass die Bestellung vor dem Login existiert. Eine erfundene gibtpayment_session_id_invalidzurück.Die OCC-Aufrufe benötigen genau drei Header. Keine der Browser-Fingerprinting-Daten in erfassten Anfragen – Geräte-IDs, Forter-Token, Cookies, Origin – wird erzwungen.
Adressen erfordern eine kombinierte Adresszeile von 10–185 Zeichen. Kürzere geben einen 400 ohne Hinweis in der UI, es sei denn, man prüft darauf.
Die Antwort auf Adresserstellung ist
{ shipping_address, billing_address }, keine Liste. Das Parsen als Liste gibt bei Erfolg ein leeres Array zurück./api/orders/:idgibt den rohen Body von Cashfree weiter, weil die Abgleichslogik voncashfree-heredieses Format parst.
Das Widget
Eine Karte ist ein Produkt; eine Warenkorbzeile ist eine Variante, und sie passen nicht zusammen. Drei Farben eines T-Shirts in eine Karte zusammenzufassen ist richtig zum Durchsuchen und mehrdeutig für einen Step-Stepper – ein Minus unter einer Karte, die ein Rot und ein Blau enthält, muss raten, welche weggenommen werden soll. Die Karte zählt, wie viele Varianten des Produkts im Warenkorb sind, und bietet einen Stepper nur an, wenn die Antwort genau eins ist; andernfalls zeigt sie die Gesamtzahl als Badge und schickt den Käufer zum Detailbildschirm. Verweigern ist billiger, als etwas zu entfernen, das sie nicht ausgewählt haben.
Der Detailbildschirm hält keinen eigenen Zustand. Das ausgewählte Produkt und die Variante leben im Widget-Zustand, weil das Widget neu gemountet wird, wenn der Käufer scrollt (siehe unten) und ein lokales
useStatewürde die Auswahl damit verlieren. Sie werden bei einer neuensearchIdgelöscht, sonst würde das Produkt der vorherigen Suche über den neuen Ergebnissen wieder aufgehen.
Dieser Host
Prüfen Sie
Access-Control-Allow-Headers, bevor Sie die Plattform beschuldigen.
In dieser Datei hieß es früher, dass GET-Anfragen aus dem Widget-Iframe den Server nie erreichen. Das tun sie doch.cashfree-heresendet bei seinem Abgleich-GETngrok-skip-browser-warning, was die Anfrage preflight-pflichtig macht; unsere Erlaubnisliste enthielt diesen Header nicht, also lehnte der Browser den Preflight ab und der GET wurde nie gesendet. Der Abgleich meldete dann „Zahlungsstatus kann nicht überprüft werden“ und zeigte Zahlung fehlgeschlagen bei Bestellungen, die bereits BEZAHLT waren.Zwei Dinge ließen es wie eine Plattform-Barriere aussehen: Es erschien nie ein GET im Log, und Preflights wurden als Rauschen aus dem Log gefiltert – so waren eine abgelehnte Anfrage und eine nie gesendete Anfrage nicht zu unterscheiden. Preflights auf
/api/*werden jetzt protokolliert.Die Nur-POST-Endpunkte (
/api/pay/addresses/list,/api/orders/status) wurden auf dieser falschen Diagnose aufgebaut. Sie funktionieren, sind aber nicht notwendig.Nur ein modellaufgerufener Tool-Aufruf bewirkt, dass der Host die
outputTemplatedieses Tools rendert.callToolführt den Handler aus und rendert nichts.window.openwird im Widget-Iframe blockiert, und der externe Öffnungsmechanismus des Hosts navigierte weg und tötete den MCP-Connector mitten in der Zahlung. Ein einfaches<a target="_blank">ist das Einzige, was funktioniert.Der MCP-Transport ist zustandslos (
sessionIdGenerator: undefined). Das Ausstellen von Session-IDs beim Erstellen eines neuen Servers pro Anfrage führt dazu, dass alles nachinitializemit „Server nicht initialisiert“ fehlschlägt.Der Widget-Zustand überlebt das Widget, daher muss jedes Tool-Ergebnis datiert sein.
Der Host behält den Zustand für die gesamte Konversation und rehydriert jedes neue Widget daraus. Eine Suche nach einer Zahlung wachte daher mitscreen: "checkout"auf und antwortete auf „zeige mir Hemden“ mit der vorherigen Quittung – und der nächste hinzugefügte Artikel landete in einem Warenkorb, den Shopify bereits abgeschlossen hatte.SearchProductsstempelt jetzt pro Aufruf einesearchIdund das Widget setzt zurück, wenn es eine noch nicht gezeigte ID sieht.Was auch immer den Zustand besitzt, ist das, was ein Zurücksetzen löschen muss.
useCartunduseCheckoutFlowinitialisieren sich beim Mounten und lesen nie wieder die übergebenen Werte, daher bewirkte das Löschen des Widget-Zustands allein nichts und sie schrieben ihre veralteten Werte einen Render später direkt zurück. Die Session ist aufsearchIdgeschlüsselt, sodass React sie stattdessen verwirft.Leiten Sie ein Zurücksetzen während des Renderings ab, nicht in einem Effekt. Ein Effekt malt zuerst den alten Bildschirm; ein Käufer, der nach Hosen fragt, sah die „Zahlung erhalten“-Meldung der vorherigen Bestellung erscheinen und dann ersetzt werden.
Nichts ordnet Schreibvorgänge zwischen aktiven Widgets. Jedes frühere Widget in einer Konversation läuft weiter und schreibt in einen einzigen ursprungsweiten
localStorage-Schlüssel. Einrevision-Zähler verhindert, dass ein veralteter Snapshot einen neueren innerhalb einer Instanz ersetzt; er ordnet keine Schreibvorgänge zwischen Instanzen, da jede ihren eigenen Zähler hochzählt. Die Schlüsselung des Zustands pro Konversation würde das Problem richtig beheben.Ein Widget wird viel häufiger neu gemountet, als es aussieht, und der CORS-Preflight verrät es. Claude zerstört und erstellt das Widget-Iframe neu, während der Käufer scrollt, und bedient das HTML aus seinem eigenen Cache – daher erscheint kein
resources/readund das Neumontieren ist im Log unsichtbar. Was es verrät, istOPTIONS /api/shop/cart: Ein Preflight wird pro Dokument zwischengespeichert, also bedeutet ein neuer Preflight ein neues Dokument. Gemessen um 22:48:29 und 22:52:25, während nichts außer Scrollen passierte. Jeder Latch, Ref und Observer innerhalb des Widgets stirbt damit, daher ist „einmal beim Mounten laden“ keine Ratenbegrenzung – es ist eine Rate pro Scrollen, pro Widget.IntersectionObserversagt Ihnen nicht, ob das Widget auf dem Bildschirm ist. Die naheliegende Lösung oben – nur bei Sichtbarkeit abrufen – funktioniert nicht: Mit einem Null-Root in einem verschachtelten Browsing-Kontext misst der Observer gegen den Viewport dieses Iframes, nicht den der Host-Seite. Jedes weit aus dem Blickfeld gescrollte Widget meldet sich als vollständig sichtbar. Gebaut, getestet, gemessen, gelöscht – drei Widgets holten bei jedem Neuladen immer noch Daten.Zwischenspeichern, was der Host nicht zurückgibt. Bei einer Routine des häufigen Neumontierens statt seltener Ereignisse wird alles, was beim Mounten neu abgerufen wird, ständig neu abgerufen: Drei aktive Widgets bedeuteten drei neu geladene Warenkörbe pro Host-Neuladen, und Shopify antwortete irgendwann mit
429 Rate limit exceeded. Der Warenkorb-Inhalt wird jetzt mit der Warenkorb-ID und einem Zeitstempel persistiert und nur nach Ablauf einer TTL neu abgerufen. Eine Drei-Fluss-Sitzung ging von 19–20 Upstream-Aufrufen auf 13 zurück, und zwei Neuladevorgänge, die früher sechs Aufrufe kosteten, kosten jetzt keinen.Die TTL betrug zunächst 30s, was nichts verhinderte: Gemessen über drei Flüsse betrug die Lücke zwischen dem letzten Abruf eines Warenkorbs und seinem nächsten Mounten 32s, 41s, 41s, 42s, 53s, 80s und 143s – jeder einzelne überschritt das Fenster. Jetzt beträgt sie 10 Minuten. Der Inhalt ist nur zur Anzeige; eine Mengenänderung bezieht die Daten neu vom Server, und die Zahlung wird über den Shopify-Warenkorb bepreist, sodass ein veralteter Wert nicht bezahlt werden kann.
Die Versionierung der Widget-URI macht sie ungültig, also bedienen Sie jeden Build. Die URI trägt eine Build-ID (siehe unten), um das Host-Caching zu umgehen. Der Preis ist, dass ein Neubau die ID ungültig macht, mit der jedes bereits in einer Konversation befindliche Widget erstellt wurde: Der Host liest die gespeicherte URI erneut, der Server antwortet mit
-32602 Resource not found, und diese Widgets rendern „Store konnte nicht geladen werden“. EineResourceTemplatefürui://widget/shopify-store-{build}.htmlbedient jetzt das aktuelle Bundle für ungültige IDs, was sie aktualisiert statt sie zu zerstören. Beachten Sie, dass dies auch in einer neuen Konversation zuschlägt – die zwischengespeicherten Tool-Metadaten des Hosts nennen noch die alte URI.Eine CSP-Warnung kann sich auf etwas beziehen, das Sie nie ausliefern wollten. MCPJam meldete
https://cdn.openai.comals blockiert bei jedem Tool. Die zwanzig Referenzen stammten aus@font-face-Regeln inkatex.min.css, die über das./css-Barrel des Apps SDK eingebunden wurden, für Mathematik, die dieses Widget nicht rendert. Die Deklaration der Domain hätte ein Shopify- und Cashfree-Widget von OpenAIs CDN innerhalb von Claude abhängig gemacht; das Importieren der anderen sechs Stylesheets über den Pfad entfernte stattdessen die Referenz und 21KB CSS. Beachten Sie auch, dass das CSP-Modell der MCP Apps keinscriptSrchat – nurconnectDomains,resourceDomainsundframeDomains– daher kann eine Skriptquellen-Beschwerde nicht vom Server beantwortet werden.Lehnen Sie das GET-Bein von streamable HTTP mit 405 ab, nicht mit 404. Der Transport ist zustandslos, daher gibt es keinen Server-zu-Client-Stream zum Öffnen. MCPJam öffnete dieses Bein 97 Mal in einer Sitzung und nahm die 404 hin, ohne abzubrechen, also ist dies nicht das, was es dort zum Scheitern bringt; strengere Clients sollen vor
initializeaufgeben. 404 wird als „kein solcher Endpunkt“ gelesen, was eine andere und falsche Antwort ist.Die Neuladekosten unterscheiden sich je nach Host, und es ist ChatGPT, das zahlt. In Claude kostet ein Neuladen jetzt nichts: Zustand und Warenkorb-Inhalt kommen beide aus dem Speicher zurück. In ChatGPT wird der Katalog nicht erneut ausgeliefert, daher fragt
useProductsdiesen Server danach – einsearch_catalogpro Neuladen und sonst nichts.
Endpunkte
Pfad | Zweck |
| MCP über HTTP für den KI-Host |
| Warenkorb erstellen/aktualisieren gegen Shopify |
| Katalogwiederherstellung für einen Host, der neu geladen hat, ohne das Tool-Ergebnis erneut auszuliefern |
| Cashfree-Bestellung erstellen, bepreist über den Shopify-Warenkorb |
| OTP-Anmeldung |
| Gespeicherte Adressen: lesen und erstellen |
| Wurde ein Zahlungs-Tool-Handler tatsächlich ausgeführt? |
| Bestellstatus für unseren eigenen Verifikationsbildschirm |
| Roher Bestelltext für den Abgleich von |
Logs
Jede Anfrage protokolliert Methode, Pfad, Status und Dauer; MCP-Aufrufe nennen die Methode und das Tool, und Ressourcen-Lesevorgänge nennen die URI – POST /mcp allein ist unlesbar, wenn jeder Host-Aufruf identisch aussieht.
13:59:48.201 → POST /mcp (tools/call SearchProducts) 200 328ms
13:59:52.884 → POST /api/shop/cart 200 904ms
14:00:03.117 → POST /api/pay/order 200 1026ms
14:00:09.640 ✗ POST /api/pay/addresses 502 121msDer Zeitstempel ist vorhanden, weil Dauern allein die Lücke zwischen zwei Anfragen nicht messen können, was die einzige relevante Frage ist, wenn eine Zahlungsauslieferung zu spät eintrifft.
Tests
npm test # watch
npm run test:run
npm run type-checkTests liegen neben dem Code, den sie abdecken. Fixtures unter src/lib/ucp/__fixtures__/ sind echte aufgezeichnete Shopify-Antworten, sodass eine Formänderung einen Test und nicht eine Demo zum Scheitern bringt. Cashfree-Fixtures sind handgeschrieben und geschwärzt – deren Session-Token dürfen nicht eingecheckt werden.
Dokumente
docs/cashfree-occ-api.md– der OCC-Vertrag, live verifiziert. Nicht in Cashfrees veröffentlichten Dokumenten.docs/spikes/2026-08-12-occ-spike.md– was der Spike gemessen hat.docs/superpowers/specs/– Designspezifikationen für jeden Meilenstein.docs/superpowers/plans/– die Aufgaben-für-Aufgaben-Umsetzungspläne, zu denen sie wurden.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Shopify store data (products, customers, orders) via GraphQL, providing comprehensive tools for store management through Claude.873MIT
- AlicenseAqualityFmaintenanceEnables AI agents to autonomously browse inventory, negotiate terms, manage carts, and execute secure payments on Shopify stores using standardized protocols. It provides a bridge for LLMs to handle the entire commerce lifecycle from discovery to order tracking through a verifiable mandate chain.52MIT
- FlicenseNot gradedqualityFmaintenanceEnables AI assistants to query and interact with Shopify store data via the Storefront API, including products, collections, carts, and customer information.9
- FlicenseNot gradedqualityDmaintenanceAI-powered Shopify Admin via Claude + MCP, enabling full store management through conversation including products, collections, analytics, and bulk operations.
Related MCP Connectors
Manage your Savanto store from your AI: catalog, content, prompts, and analytics, by chat.
Manage your NanoCart store from any AI agent: products, orders, coupons, subscribers, reports.
Amazon brand, seller, niche & buy-box intelligence inside your own Claude or ChatGPT.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/droiddevgeeks/shopify-mcp-demo'
If you have feedback or need assistance with the MCP directory API, please join our Discord server