/**
* MCP Server Setup
* Registers all 5 agentic commerce tools with proper zod schemas.
* Uses getDeps() for all shared dependencies.
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { logger } from './utils/logger.js';
import { scoutInventory } from './tools/scout-inventory.js';
import { negotiateTerms } from './tools/negotiate-terms.js';
import { trackOrder } from './tools/track-order.js';
import { manageCart } from './tools/manage-cart.js';
import { executeCheckout } from './tools/execute-checkout.js';
import type { ExecuteCheckoutDeps } from './tools/execute-checkout.js';
import { getDeps } from './dynamo/factory.js';
/**
* Create and configure the MCP server with all tool registrations.
*/
export function createMCPServer(): McpServer {
const server = new McpServer({
name: 'shopify-agentic-mcp',
version: '0.1.0',
});
const deps = getDeps();
// ─── Tool 1: scout_inventory ───
server.tool(
'scout_inventory',
'Search the Shopify catalog for products matching query, category, and price filters.',
{
query: z.string().describe('Search query for product discovery'),
category: z.string().optional().describe('Product category filter'),
price_min: z.number().optional().describe('Minimum price in minor units (cents)'),
price_max: z.number().optional().describe('Maximum price in minor units (cents)'),
limit: z.number().optional().describe('Maximum number of results to return'),
},
async (params) => {
logger.info('scout_inventory called', { params });
const result = await scoutInventory(params);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(result),
},
],
};
},
);
// ─── Tool 2: negotiate_terms ───
server.tool(
'negotiate_terms',
'Negotiate capabilities, discounts, and shipping options between an agent and the merchant.',
{
cart_id: z.string().describe('Cart or checkout session ID'),
agent_profile_url: z.string().describe('URL to the agent UCP profile'),
discount_code: z.string().optional().describe('Discount code to apply'),
},
async (params) => {
logger.info('negotiate_terms called', { params });
const result = await negotiateTerms(
{
cart_id: params.cart_id,
agent_profile_url: params.agent_profile_url,
discount_code: params.discount_code,
},
deps.config,
);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(result),
},
],
};
},
);
// ─── Tool 3: execute_checkout ───
const checkoutDeps: ExecuteCheckoutDeps = {
sessionManager: deps.sessionManager,
verifier: deps.verifier,
mandateStore: deps.mandateStore,
guardrail: deps.guardrail,
feeCollector: deps.feeCollector,
storefrontAPI: deps.storefrontAPI,
};
server.tool(
'execute_checkout',
'Execute a full checkout with AP2 mandate chain verification (intent, cart, payment mandates).',
{
checkout_id: z.string().describe('Checkout session ID to complete'),
intent_mandate: z.string().describe('JWS compact token for the intent mandate'),
cart_mandate: z.string().describe('JWS compact token for the cart mandate'),
payment_mandate: z.string().describe('JWS compact token for the payment mandate'),
},
async (params) => {
logger.info('execute_checkout called', { checkout_id: params.checkout_id });
try {
const result = await executeCheckout(
{
checkout_id: params.checkout_id,
intent_mandate: params.intent_mandate,
cart_mandate: params.cart_mandate,
payment_mandate: params.payment_mandate,
},
checkoutDeps,
);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(result),
},
],
isError: !result.success,
};
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
logger.error('execute_checkout unhandled error', { error: message });
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({
success: false,
errors: [`Unhandled error: ${message}`],
}),
},
],
isError: true,
};
}
},
);
// ─── Tool 4: manage_cart ───
server.tool(
'manage_cart',
'Create, modify, or retrieve a Shopify cart (create, add, remove, get).',
{
action: z.enum(['create', 'add', 'remove', 'get']).describe('Cart operation to perform'),
cart_id: z.string().optional().describe('Existing cart ID (required for add/remove/get)'),
variant_id: z.string().optional().describe('Product variant ID (required for add/remove)'),
quantity: z.number().optional().describe('Quantity to add or set (default 1)'),
},
async (params) => {
logger.info('manage_cart called', { action: params.action, cart_id: params.cart_id });
try {
const result = await manageCart({
action: params.action,
cart_id: params.cart_id,
variant_id: params.variant_id,
quantity: params.quantity,
});
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(result),
},
],
};
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
logger.error('manage_cart error', { error: message });
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({ error: message }),
},
],
isError: true,
};
}
},
);
// ─── Tool 5: track_order ───
server.tool(
'track_order',
'Retrieve order status and fulfillment tracking information.',
{
order_id: z.string().describe('Shopify order ID to track'),
},
async (params) => {
logger.info('track_order called', { order_id: params.order_id });
const result = await trackOrder({ order_id: params.order_id });
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(result),
},
],
};
},
);
logger.info('MCP server initialized with 5 tools');
return server;
}