MODULE 06 / 08
AI Integration: Wire the DDS Sovereign AGI Suite to Your Storefront
This is the module that justifies migrating to Hydrogen in 2026. The Storefront MCP turns your headless storefront into an addressable surface for AI agents. We will use the DDS Sovereign AGI Suite as the worked example, but the same pattern applies to any agent stack you operate.
You will leave this module having built:
- A working Storefront MCP integration on Hydrogen 2026.4
- Four agent-callable tools wired to the Sovereign Orchestrator Pro
- An AI-powered product recommendation route using the suite
6.1 What Storefront MCP actually is
Model Context Protocol is the open standard Anthropic introduced for agent-to-tool communication. Shopify added a Storefront MCP server in the Winter '26 Edition. Hydrogen 2026 automatically proxies requests to /api/mcp to that server, exposing live commerce data (products, collections, cart, checkout) to any MCP-compatible agent.
Practically: an AI agent connected to https://your-storefront.com/api/mcp can query products, manipulate carts, and assist customers without any custom backend you have to write.
DDS AGI Suite
Sovereign Orchestrator Pro V5.0 + Synthetic Employees
→
Hydrogen Edge
/api/mcp proxy on Oxygen
→
Shopify MCP
Storefront API + Cart + Checkout
6.2 The Sovereign AGI Suite, reframed for headless
For the worked example, the four flagship synthetic employees from the DDS Sovereign Orchestrator map to four storefront integration points. Each employee's role is reframed below.
SOVEREIGN ORCHESTRATOR PRO V5.0
The conductor. Receives customer-intent prompts, decides which sub-agent should respond, dispatches the call to MCP, and returns a structured response.
Storefront role: /api/agent route handler
AGI-CORE-PRO V1.0
The reasoning layer. Handles ambiguous customer queries that require multi-turn clarification or product comparison synthesis.
Storefront role: search-by-intent endpoint
VIBETUBE AI V1.1.1
The content engine. Generates product-specific video scripts, descriptions, and structured marketing copy from the product graph.
Storefront role: PDP description enrichment
NICHE-FORGE-CORE V3.0
The targeting engine. Maps a visitor session to a niche persona and surfaces the right collection or product subset.
Storefront role: collection page personalization
6.3 Step 1: confirm Storefront MCP is enabled
In Hydrogen 2026.4, MCP proxying happens automatically as long as your server.ts uses createRequestHandler with a valid storefront in context. No additional configuration is required.
// server.ts (verify your handler looks like this)
import { createRequestHandler } from "@shopify/hydrogen/oxygen";
import { createAppLoadContext } from "~/lib/context";
export default {
async fetch(request, env, executionContext) {
const handler = createRequestHandler({
build: await import("./build/server"),
getLoadContext: () => createAppLoadContext(request, env, executionContext),
// proxyStandardRoutes is removed in 2026.4 — proxy is always enabled
});
return handler(request);
},
};
Verify it works locally: curl http://localhost:3000/api/mcp/health should return a 200 with the MCP server identity. If you get a 404, your context is missing the storefront property.
6.4 Step 2: build the AGI gateway route
The Sovereign Orchestrator needs a single endpoint on the storefront that accepts a customer-intent prompt and routes it. This is the /api/agent route.
// app/routes/api.agent.tsx
import type { Route } from "./+types/api.agent";
interface AgentRequest {
prompt: string;
sessionId: string;
cartToken?: string;
agent?: "orchestrator" | "agi-core" | "vibetube" | "niche-forge";
}
export async function action({ request, context }: Route.ActionArgs) {
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
const { env } = context;
const body: AgentRequest = await request.json();
const agent = body.agent ?? "orchestrator";
// Forward to your Sovereign Orchestrator endpoint.
// The orchestrator decides which synthetic employee handles this.
const orchestratorResponse = await fetch(env.SOVEREIGN_ORCHESTRATOR_URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${'$'}{env.SOVEREIGN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt: body.prompt,
session_id: body.sessionId,
// MCP endpoint the orchestrator will call back to:
mcp_endpoint: `${'$'}{new URL(request.url).origin}/api/mcp`,
mcp_auth: env.MCP_PROXY_AUTH_TOKEN, // for storefront-issued auth
target_agent: agent,
cart_context: body.cartToken
? await context.cart.get()
: null,
}),
});
if (!orchestratorResponse.ok) {
return Response.json(
{ error: "Orchestrator unavailable" },
{ status: 502 }
);
}
const result = await orchestratorResponse.json();
return Response.json(result, {
headers: { "Cache-Control": "private, no-store" },
});
}
6.5 Step 3: register the four MCP tools the orchestrator can call
Storefront MCP exposes a default tool set. You can extend it with your own tools by adding routes under /api/mcp/tools/. The four below match the four AGI Suite employees.
// app/routes/api.mcp.tools.search-by-intent.tsx (AGI-CORE-PRO target)
import type { Route } from "./+types/api.mcp.tools.search-by-intent";
export async function action({ request, context }: Route.ActionArgs) {
const { intent, niche, priceMax } = await request.json();
const { products } = await context.storefront.query(SEARCH_QUERY, {
variables: {
query: `${'$'}{intent} ${'$'}{niche ? `tag:${'$'}{niche}` : ""} ${'$'}{priceMax ? `price:<${'$'}{priceMax}` : ""}`,
first: 10,
},
cache: context.storefront.CacheShort(),
});
return Response.json({
tool: "search-by-intent",
results: products.nodes.map((p: any) => ({
handle: p.handle,
title: p.title,
price: p.priceRange.minVariantPrice,
url: `/products/${'$'}{p.handle}`,
})),
});
}
// app/routes/api.mcp.tools.enrich-pdp.tsx (VIBETUBE AI target)
import type { Route } from "./+types/api.mcp.tools.enrich-pdp";
export async function action({ request, context }: Route.ActionArgs) {
const { handle } = await request.json();
const { product } = await context.storefront.query(PRODUCT_QUERY, {
variables: { handle },
cache: context.storefront.CacheLong(),
});
// Forward to VibeTube AI for content generation.
const enriched = await fetch(context.env.VIBETUBE_AI_URL, {
method: "POST",
headers: { "Authorization": `Bearer ${'$'}{context.env.SOVEREIGN_API_KEY}` },
body: JSON.stringify({
product_data: product,
output_types: ["video_script", "long_description", "social_caption"],
}),
});
return Response.json({
tool: "enrich-pdp",
base_product: product,
enriched_content: await enriched.json(),
});
}
// app/routes/api.mcp.tools.personalize-collection.tsx (NICHE-FORGE-CORE target)
import type { Route } from "./+types/api.mcp.tools.personalize-collection";
export async function action({ request, context }: Route.ActionArgs) {
const { sessionId, collectionHandle } = await request.json();
// 1. Ask Niche-Forge for the visitor's persona vector
const persona = await fetch(`${'$'}{context.env.NICHE_FORGE_URL}/persona`, {
method: "POST",
headers: { "Authorization": `Bearer ${'$'}{context.env.SOVEREIGN_API_KEY}` },
body: JSON.stringify({ session_id: sessionId }),
}).then((r) => r.json());
// 2. Pull the collection
const { collection } = await context.storefront.query(COLLECTION_QUERY, {
variables: { handle: collectionHandle },
cache: context.storefront.CacheShort(),
});
// 3. Re-rank server-side using Niche-Forge weights
const reranked = collection.products.nodes.sort(
(a: any, b: any) => scoreFor(b, persona) - scoreFor(a, persona)
);
return Response.json({
tool: "personalize-collection",
persona_tag: persona.dominant_niche,
products: reranked.slice(0, 12),
});
}
function scoreFor(product: any, persona: any) {
let score = 0;
for (const tag of product.tags ?? []) {
if (persona.weights?.[tag]) score += persona.weights[tag];
}
return score;
}
// app/routes/api.mcp.tools.cart-assist.tsx (Orchestrator target)
import type { Route } from "./+types/api.mcp.tools.cart-assist";
export async function action({ request, context }: Route.ActionArgs) {
const { intent, variantId, quantity } = await request.json();
const { cart } = context;
switch (intent) {
case "add":
return Response.json(await cart.addLines([{ merchandiseId: variantId, quantity: quantity ?? 1 }]));
case "remove":
return Response.json(await cart.removeLines([variantId]));
case "summary":
return Response.json({ cart: await cart.get() });
default:
return Response.json({ error: "Unknown cart intent" }, { status: 400 });
}
}
6.6 Step 4: client-side hook that talks to the agent
// app/components/AgentChat.tsx
import { useState } from "react";
export function AgentChat() {
const [messages, setMessages] = useState<Array<{ role: string; text: string }>>([]);
const [input, setInput] = useState("");
const [isThinking, setIsThinking] = useState(false);
async function send() {
if (!input.trim()) return;
const userMsg = { role: "user", text: input };
setMessages((m) => [...m, userMsg]);
setInput("");
setIsThinking(true);
const res = await fetch("/api/agent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
prompt: input,
sessionId: getOrCreateSessionId(),
}),
});
const data = await res.json();
setMessages((m) => [...m, { role: "agent", text: data.response }]);
setIsThinking(false);
}
return (
<div className="agent-chat">
<ul>
{messages.map((m, i) => (
<li key={i} data-role={m.role}>{m.text}</li>
))}
</ul>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && send()}
placeholder="Ask the AGI Suite anything..."
disabled={isThinking}
/>
<button onClick={send} disabled={isThinking}>Send</button>
</div>
);
}
function getOrCreateSessionId() {
let id = sessionStorage.getItem("agi-session");
if (!id) {
id = crypto.randomUUID();
sessionStorage.setItem("agi-session", id);
}
return id;
}
6.7 Step 5: orchestrator-side configuration
On the Sovereign Orchestrator side (running on your local Ollama instance, a hosted endpoint, or wherever your suite lives), register the storefront's MCP endpoint as a tool source. The configuration shape varies by orchestrator framework. The minimum the orchestrator needs:
{
"mcp_servers": [
{
"name": "dds_storefront",
"url": "https://your-hydrogen-storefront.com/api/mcp",
"auth": {
"type": "bearer",
"token_env": "MCP_PROXY_AUTH_TOKEN"
},
"tool_filter": [
"search-by-intent",
"enrich-pdp",
"personalize-collection",
"cart-assist",
"products.list",
"products.get",
"cart.get"
]
}
]
}
Production Note
This wiring is a worked example, not a turnkey integration with a public Sovereign AGI Suite endpoint. The DDS Sovereign AGI Suite is internal infrastructure. The pattern shown here is the same pattern any team would use to wire their own agent stack to a Hydrogen storefront via Storefront MCP. Treat the URLs and bearer tokens as placeholders for your equivalents.
6.8 Failure modes to plan for
- MCP rate limiting: Storefront MCP shares the Storefront API rate budget. Cache aggressively on the agent side. Do not let an agent loop hammer
products.list.
- Stale cart context: Agents that operate on stale cart state will produce confusing UX. Always pass the live cart token to
/api/agent and let the orchestrator re-fetch.
- Tool ambiguity: Agents will sometimes pick
search-by-intent when they should call products.get. Resolve this by giving each tool a sharper description in your tool registry, not by adding more tools.
- Auth token rotation: The bearer token between orchestrator and storefront should rotate on a fixed schedule. Store it in Oxygen environment variables and version it.