Order food end-to-end

The canonical 7-tool Food journey - from address to placed order to delivery tracking.

The full food-ordering journey across Swiggy's Food MCP server. COD payment, ₹1000 cart cap. Pseudo-code in TypeScript; the same sequence works in any framework.

The flow

get_addresses


search_restaurants ──► get_restaurant_menu


                       update_food_cart ◄── fetch_food_coupons
                            │                       │
                            ▼                       │
                       get_food_cart ◄── apply_food_coupon


                       place_food_order


                       track_food_order

Step 1 - Resolve the delivery address

const addresses = await client.callTool({ name: "get_addresses" });
const home = addresses.data.find((a) => a.label === "Home") ?? addresses.data[0];
if (!home) throw new Error("User has no saved addresses; prompt them to add one.");

get_addresses returns label, addressId, and display text - never raw coordinates.

Step 2 - Find restaurants

const restaurants = await client.callTool({
  name: "search_restaurants",
  arguments: { addressId: home.id, query: "biryani" },
});

Check availabilityStatus for each result - only recommend those marked "OPEN". Sort by a mix of distance and rating; always surface distance when picking far restaurants so the user isn't surprised.

Step 3 - Browse the menu

const menu = await client.callTool({
  name: "get_restaurant_menu",
  arguments: { restaurantId: restaurants.data.restaurants[0].id },
});

Menus have categories, items, variants, and add-ons. Use search_menu for keyword search within (or across) restaurants.

Step 4 - Build the cart

await client.callTool({
  name: "update_food_cart",
  arguments: {
    restaurantId: menu.data.restaurantId,
    items: [
      { itemId: menu.data.items[0].id, quantity: 1 },
    ],
  },
});

Cart is tied to a single restaurant. Changing restaurant flushes the cart. Use flush_food_cart explicitly when the user starts over.

Step 5 - Apply a coupon (optional)

const coupons = await client.callTool({ name: "fetch_food_coupons" });
 
// v1 supports COD only - filter coupons that don't require online payment
const codCoupon = coupons.data.find((c) => !c.requiresOnlinePayment);
 
if (codCoupon) {
  await client.callTool({
    name: "apply_food_coupon",
    arguments: { code: codCoupon.code },
  });
}

Step 6 - Confirm and place the order

const cart = await client.callTool({ name: "get_food_cart" });
 
// Swiggy v1: hard ₹1000 cap on Builders Club orders
if (cart.data.total > 1000) {
  throw new Error("Cart exceeds ₹1000 cap - ask user to reduce items.");
}
 
// Surface to the user before placing
// "Your order is: <items>. Total ₹<total>. Place now? (yes / no)"
 
const order = await client.callTool({
  name: "place_food_order",
  arguments: { paymentMethod: "COD" },
});

Critical: place_food_order is not idempotent. If it fails with 5xx, call get_food_orders to check if the order actually placed before retrying. See ship to production.

Step 7 - Track the order

const status = await client.callTool({
  name: "track_food_order",
  arguments: { orderId: order.data.orderId },
});
 
// Poll no faster than every 10 seconds; delivery-partner ETA updates arrive at that cadence

Full agent prompt

Good system prompt for the agent driving this flow:

What can go wrong

Until the symbolic error-code registry ships (see errors), classify by error.message text and HTTP status. Expect:

  • Restaurant closed between search and order → re-run search_restaurants.
  • Coupon requires online payment → filter upstream; only COD is supported in v1.
  • Minimum order not met → prompt user to add items.
  • Upstream shedding / timeout → exponential backoff; capacity questions go to rate-limits.