Skip to main content
Glama
tas1337

MCP A2A AP2 Food Delivery & Payments

by tas1337
food-agents.ts7.26 kB
// ============================================================================= // FOOD DELIVERY AGENTS (A2A Protocol) // ============================================================================= // Mock agents for DoorDash, UberEats, and Grubhub. // // These demonstrate A2A (Agent-to-Agent) protocol: // - Each agent registers with the registry // - Other agents discover them via registry // - Communication via JSON-RPC 2.0 over HTTP // // KEY DIFFERENCE FROM AP2: // - A2A: No mandates needed, agents freely communicate // - AP2: Requires user-signed mandates for payments (see stripe-agent.ts) // ============================================================================= import { createServer } from 'http'; import type { AgentCard, JSONRPCRequest, JSONRPCResponse, ServiceType } from '../../src/types.js'; import { getMockRestaurants, getMockMenu } from '../../mock-data/restaurants.js'; // ============================================================================= // CONFIG // ============================================================================= const REGISTRY_URL = `http://${process.env.REGISTRY_HOST || 'localhost'}:8004`; interface AgentConfig { agentId: string; name: string; port: number; service: ServiceType; capabilities: string[]; } // ============================================================================= // FOOD DELIVERY AGENT CLASS // ============================================================================= class FoodDeliveryAgent { private agentId: string; private name: string; private port: number; private service: ServiceType; private capabilities: string[]; private httpServer: ReturnType<typeof createServer> | null = null; constructor(config: AgentConfig) { this.agentId = config.agentId; this.name = config.name; this.port = config.port; this.service = config.service; this.capabilities = config.capabilities; } // --------------------------------------------------------------------------- // START SERVER & REGISTER WITH REGISTRY // --------------------------------------------------------------------------- async start(): Promise<void> { // Create HTTP server for JSON-RPC requests this.httpServer = createServer(async (req, res) => { if (req.method === 'POST') { const chunks: Buffer[] = []; for await (const chunk of req) chunks.push(chunk); const body = JSON.parse(Buffer.concat(chunks).toString()); const response = await this.handleRequest(body); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(response)); } else { res.writeHead(404); res.end(); } }); this.httpServer.listen(this.port, '0.0.0.0', () => { console.log(`🍔 ${this.name} running on port ${this.port}`); }); // Register with A2A registry so other agents can discover us const card: AgentCard = { agentId: this.agentId, name: this.name, version: '1.0.0', endpoint: `http://${this.agentId.replace('-agent-001', '-agent')}:${this.port}`, capabilities: this.capabilities, }; await fetch(`${REGISTRY_URL}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(card), }); console.log(`🍔 ${this.name} registered with registry`); } // --------------------------------------------------------------------------- // HANDLE JSON-RPC REQUEST // --------------------------------------------------------------------------- private async handleRequest(request: JSONRPCRequest): Promise<JSONRPCResponse> { const { method, params, id } = request; const p = params as Record<string, unknown>; let result: unknown; switch (method) { case 'agent/search_restaurants': result = await this.searchRestaurants(p.query as string); break; case 'agent/get_menu': result = await this.getMenu(p.restaurantId as string); break; case 'agent/get_delivery_estimate': result = await this.getDeliveryEstimate(); break; case 'agent/place_order': result = await this.placeOrder(); break; case 'agent/check_order_status': result = await this.checkOrderStatus(p.orderId as string); break; default: return { jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown method: ${method}` } }; } return { jsonrpc: '2.0', id, result }; } // --------------------------------------------------------------------------- // AGENT METHODS (returns mock data) // --------------------------------------------------------------------------- private async searchRestaurants(query: string) { return { restaurants: getMockRestaurants(query, this.service), service: this.service }; } private async getMenu(restaurantId: string) { return { menu: getMockMenu(restaurantId), service: this.service }; } private async getDeliveryEstimate() { return { estimatedTime: 25 + Math.floor(Math.random() * 20), deliveryFee: 2.99 + Math.random() * 4, minimumOrder: 15, service: this.service, }; } private async placeOrder() { return { orderId: `${this.service}-${Date.now()}`, status: 'confirmed', service: this.service, }; } private async checkOrderStatus(orderId: string) { return { orderId, status: 'preparing', currentStage: 'Restaurant is preparing your order', service: this.service, }; } stop(): void { this.httpServer?.close(); } } // ============================================================================= // AGENT INSTANCES // ============================================================================= const doorDashAgent = new FoodDeliveryAgent({ agentId: 'doordash-agent-001', name: 'DoorDash Agent', service: 'doordash', port: 8001, capabilities: ['search_restaurants', 'get_menu', 'get_delivery_estimate', 'place_order', 'check_order_status'], }); const uberEatsAgent = new FoodDeliveryAgent({ agentId: 'ubereats-agent-001', name: 'UberEats Agent', service: 'ubereats', port: 8002, capabilities: ['search_restaurants', 'get_menu', 'get_delivery_estimate', 'place_order', 'check_order_status'], }); const grubhubAgent = new FoodDeliveryAgent({ agentId: 'grubhub-agent-001', name: 'Grubhub Agent', service: 'grubhub', port: 8003, capabilities: ['search_restaurants', 'get_menu', 'get_delivery_estimate', 'place_order', 'check_order_status'], }); // ============================================================================= // START AGENT (based on env var) // ============================================================================= const agentName = process.env.AGENT_NAME || 'doordash'; const agents: Record<string, FoodDeliveryAgent> = { doordash: doorDashAgent, ubereats: uberEatsAgent, grubhub: grubhubAgent, }; const agent = agents[agentName.toLowerCase()]; if (!agent) { console.error(`Unknown agent: ${agentName}`); process.exit(1); } agent.start().catch((error) => { console.error(`Failed to start ${agentName} agent:`, error); process.exit(1); });

Latest Blog Posts

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/tas1337/mcp-a2a-ap2-im-hungry'

If you have feedback or need assistance with the MCP directory API, please join our Discord server