pizzamcp
Version:
Model Context Protocol server for ordering Domino's pizza via conversational interface
244 lines (220 loc) • 8.02 kB
text/typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import { DominosOrderService } from './dominos-service.js';
// Zod schemas for tool parameters
const OrderPizzaSchema = z.object({
action: z.enum(['start', 'find_store', 'get_menu', 'add_item', 'remove_item', 'get_summary', 'set_customer_info', 'place_order']),
address: z.string().optional(),
item_name: z.string().optional(),
item_code: z.string().optional(),
size: z.string().optional(),
quantity: z.number().default(1).optional(),
customer_name: z.string().optional(),
customer_phone: z.string().optional(),
customer_email: z.string().optional(),
payment_info: z.object({
card_number: z.string(),
expiration: z.string(),
cvv: z.string(),
zip_code: z.string()
}).optional(),
tip_amount: z.number().optional(),
menu_category: z.string().optional(),
remove_index: z.number().optional()
});
class DominosMCPServer {
private server: Server;
private orderService: DominosOrderService;
constructor() {
this.server = new Server(
{
name: 'dominos-pizza-mcp',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
this.orderService = new DominosOrderService();
this.setupToolHandlers();
}
private setupToolHandlers(): void {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'order_pizza',
description: `Order Domino's pizza for delivery via natural conversation. This tool handles the complete ordering flow:
Actions available:
- start: Begin a new order session
- find_store: Find nearest delivery store for an address
- get_menu: Get menu for current store (optionally filtered by category)
- add_item: Add an item to the order
- remove_item: Remove an item from the order
- get_summary: Get current order summary with pricing
- set_customer_info: Set customer details (name, phone, email)
- place_order: Complete the order with payment information
The tool maintains order state across calls within a session.`,
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['start', 'find_store', 'get_menu', 'add_item', 'remove_item', 'get_summary', 'set_customer_info', 'place_order'],
description: 'The action to perform'
},
address: {
type: 'string',
description: 'Full delivery address (street, city, state, zip)'
},
item_name: {
type: 'string',
description: 'Name of the menu item to add'
},
item_code: {
type: 'string',
description: 'Internal product code (if known)'
},
size: {
type: 'string',
description: 'Size for the item (Small, Medium, Large, etc.)'
},
quantity: {
type: 'number',
description: 'Quantity of items to add',
default: 1
},
customer_name: {
type: 'string',
description: 'Customer name for delivery'
},
customer_phone: {
type: 'string',
description: 'Customer phone number'
},
customer_email: {
type: 'string',
description: 'Customer email address'
},
payment_info: {
type: 'object',
properties: {
card_number: { type: 'string', description: 'Credit card number (no spaces/dashes)' },
expiration: { type: 'string', description: 'Expiration date (MM/YY format)' },
cvv: { type: 'string', description: 'CVV security code' },
zip_code: { type: 'string', description: 'Billing ZIP code' }
},
description: 'Payment information for order'
},
tip_amount: {
type: 'number',
description: 'Tip amount in dollars'
},
menu_category: {
type: 'string',
description: 'Filter menu by category (Pizza, Sides, Drinks, etc.)'
},
remove_index: {
type: 'number',
description: 'Index of item to remove from order'
}
},
required: ['action']
}
}
]
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === 'order_pizza') {
try {
const validatedArgs = OrderPizzaSchema.parse(args);
const result = await this.handleOrderPizza(validatedArgs);
return {
content: [
{
type: 'text',
text: result
}
]
};
} catch (error) {
if (error instanceof z.ZodError) {
throw new McpError(
ErrorCode.InvalidParams,
`Invalid parameters: ${error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`
);
}
throw new McpError(
ErrorCode.InternalError,
`Error processing order: ${error instanceof Error ? error.message : String(error)}`
);
}
}
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
});
}
private async handleOrderPizza(args: z.infer<typeof OrderPizzaSchema>): Promise<string> {
switch (args.action) {
case 'start':
return this.orderService.startNewOrder();
case 'find_store':
if (!args.address) {
throw new Error('Address is required for find_store action');
}
return await this.orderService.findStore(args.address);
case 'get_menu':
return await this.orderService.getMenu(args.menu_category);
case 'add_item':
if (!args.item_name) {
throw new Error('Item name is required for add_item action');
}
return await this.orderService.addItem(
args.item_name,
args.size,
args.quantity || 1,
args.item_code
);
case 'remove_item':
if (args.remove_index === undefined) {
throw new Error('Remove index is required for remove_item action');
}
return this.orderService.removeItem(args.remove_index);
case 'get_summary':
return await this.orderService.getOrderSummary();
case 'set_customer_info':
return this.orderService.setCustomerInfo(
args.customer_name,
args.customer_phone,
args.customer_email
);
case 'place_order':
if (!args.payment_info) {
throw new Error('Payment information is required for place_order action');
}
return await this.orderService.placeOrder(
args.payment_info,
args.tip_amount || 5.00
);
default:
throw new Error(`Unknown action: ${args.action}`);
}
}
async run(): Promise<void> {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Domino\'s Pizza MCP server running on stdio');
}
}
// Start the server
const server = new DominosMCPServer();
server.run().catch(console.error);