Skip to main content
Can My AI Agent Order Me a Burger With Mr D?

Can My AI Agent Order Me a Burger With Mr D?

· 12 min read
James Daniel Whitford
Software engineer and technical writer

Is it possible for an AI agent to use Mr D to buy me a burger in South Africa?

I recently saw a post about an MCP server that can order McDonald's in China.

Mr D gives an agent no supported way to do that. So I reverse-engineered its private API and built my own Mr D MCP server, which allowed me to order a burger from my agent over WhatsApp.

WhatsApp message: agent says it has the Mr D tools and asks what to eat and where to deliverWhatsApp message and photo confirming the RocoMamas order has landed on the delivery address's brickwork

Messages highlighted in red, next to the OpenClaw logo OpenClaw logo, are the agent. Everything else is me.

The full order happened over WhatsApp:

  • Ordering a burger
  • Choosing from restaurants and their menus
  • Paying
  • Tracking the order

This article will walk you through that order and explain the tools the agent used along the way.

This is an unofficial project, not a Mr D product

Mr D's payment flow doesn't always require 3D Secure approval, so the payment tool here is locked by default. To try it yourself, point your own agent at the repo and log in to your own Mr D account.

Why this wasn't easy

Getting an agent to order a burger from Mr D took real work, and none of it was Mr D's. There is no API documentation, no MCP server, and no supported way for an agent to search a menu, build a cart, or pay.

To make any of this possible, I had to:

  • Capture and reverse-engineer the private API that Mr D's own app and website already call internally.
  • Build a full MCP server on top of that API myself, tool by tool, matching Mr D's own request and response shapes with no documentation to check against.
  • Design and build my own safety gate around payment, since Mr D's platform doesn't provide one and a saved-card payment can settle with no bank approval step at all.

None of that is something an outside developer should have to do just to let an agent order food. A restaurant group like RocoMamas, or Mr D itself, could remove that barrier entirely by publishing an official ordering API or MCP server, documenting how payment should be confirmed safely, and giving agents a supported way in instead of leaving reverse-engineering as the only option.

Wondering what agent-ready ordering would look like for your own business? That's exactly the kind of agent experience work Ritza does, for hospitality, delivery, and any other product agents are starting to interact with directly.

Ordering a burger from my agent

I open the conversation with "I'm hungry." The agent responds that it has the Mr D tools connected, then asks for the two inputs it needs before it can do anything, what to order and where to deliver it.

The agent also warns that payments are currently blocked, a built-in safety mechanism I use while testing the agent so no accidental payments can go through.

WhatsApp message: agent says it has the Mr D tools and asks what to eat and where to deliver

Searching for restaurants

I ask "What are my options?" The agent calls search_restaurants and returns a list of nearby restaurants grouped by category, with star ratings and delivery time.

WhatsApp message listing nearby restaurants grouped by category with star ratings

search_restaurants calls two Mr D endpoints in parallel, then merges the results by restaurant ID:

  • dynamic: restaurant ID, menu ID, open status, distance, and ratings
  • semi_static: name, full address, and images

Together they make up a usable record. Both endpoints require a full street-level address block alongside latitude and longitude.

// search_restaurants merges two Mr D endpoints by restaurant id
const [dynamicRes, semiStaticRes] = await Promise.all([
mrdRequest(auth, `/landing-items/v3/restaurants/dynamic`, { query }),
mrdRequest(auth, `/landing-items/v3/restaurants/semi_static`, { query }),
]);
// dynamic: id, menu_id, open/online, distance, ratings
// semi_static: name, full address, images

Browsing the menu

I pick RocoMamas and ask what's on the menu. The agent calls get_menu and returns the full menu with prices, burgers, wings, ribs, combos, sides, and drinks.

WhatsApp message listing RocoMamas Signature Smashburgers with prices

get_menu is a single GET request against Mr D's flattened menu endpoint, using the menu_id returned by search_restaurants in the previous step. The flattened=true query parameter returns items as a flat list rather than nested by section, which is what the agent needs to match a spoken order against a specific item.

// get_menu returns the flattened menu for a given menu_id
await mrdRequest(auth, `/v2/menus/${menuId}`, { query: { flattened: "true" } });

Choosing an item

I order "1 Old Skool burger please." The agent quotes the price and asks two clarifying questions, protein choice and any extras or exclusions, before adding anything to a cart.

WhatsApp message: agent quotes the Old Skool burger price and asks about protein choice and extras

To add an item to a cart, Mr D needs a specific product ID and a set of option IDs for things like protein choice and extras. None of the tools ask for these directly, and none of them turn a plain-language order like "1 Old Skool burger" into the right IDs either.

The agent determined on its own that it needed to work this out itself:

  • It already has the full menu, IDs included, from the earlier get_menu call.
  • It matches my words against that menu to find the right item.
  • Wherever the menu shows more than one option, like protein choice, it asks me instead of guessing.

A production tool would state its requirements more explicitly instead of leaving the agent to figure this out on its own.

Building the cart and checking out

Before it builds the cart, the agent checks the delivery address against the one I gave it earlier. The geocoded result lands in a different suburb and postal code, so it asks me to confirm the address is still correct before it commits anything.

Once I confirm, it adds the burger to a cart and converts that cart into an order. The order comes back unpaid.

The agent already knows a R20 service fee applies on top of the earlier quote. It tells me the real total upfront, instead of leaving me to find out at payment.

WhatsApp message confirming the order was created unpaid, with a service fee discrepancy flagged

Behind the scenes, add_to_cart creates the cart and the item in a single call, then checkout converts that cart into an order. Nothing in this step moves money, the order comes back unpaid.

// add_to_cart creates the cart with the item already in the body
const cart = await mrdRequest(auth, "/restaurant/v3/cart", {
ca: true,
method: "POST",
body: {
restaurant: { id: restaurantId },
menu_id: menuId,
items: [{ section_id, item_id, variant_id, quantity, options, extras, addons }],
customer: { address },
},
});
const cartId = cart.uuid; // not cart.id

// checkout converts the cart into an unpaid order
await mrdRequest(auth, `/restaurant/v3/cart/${cartId}/checkout`, {
ca: true,
method: "POST",
body: {},
});

Paying for the order

I say "Yes pay for me." The first payment attempt fails a 3D Secure check because the delivery address differs from the address saved against the card. The agent reports the failure and offers two ways to complete the challenge, open the order in the Mr D app, or use a direct PayU handoff URL.

WhatsApp message reporting the payment failed 3D Secure because the delivery address differs from the card's saved addressWhatsApp message offering two ways to complete 3D Secure approval, including a direct PayU handoff URL

Following the PayU URL opens a bank authentication screen in a browser.

PayU authenticating-transaction loading screen

Once the challenge clears, the agent reports the order as paid, with the order number, amount, and delivery address confirmed.

WhatsApp message confirming the order is paid, with the order number, amount, and delivery address

Payment runs through four tools:

  • set_payment_method registers which payment methods the order will accept.
  • list_saved_cards looks up the cards already saved on the account.
  • prepare_payment builds the payment request and returns a confirm token, without sending anything yet.
  • submit_payment is the only tool that can actually charge the card.

submit_payment is locked down deliberately. It refuses to run unless I have explicitly armed payments on the server, and the confirm token it receives matches the one prepare_payment just generated for this exact order. I built this gate myself, on top of Mr D's own systems, because I'd already seen a saved card go through with no bank approval step at all in an earlier test. I did not want an agent able to charge a real card without a deliberate, separate step from me first.

// prepare_payment is read-only, it builds the body and a confirm
// token, and sends nothing
const body = buildPaymentBody(intent);
const confirmToken = confirmTokenFor(intent.orderId, body);

// submit_payment refuses unless both locks pass
if (!paymentArmed()) throw new PaymentGateError("Payment is locked.");
if (confirmToken !== expected) throw new PaymentGateError("confirmToken mismatch.");

// only then does it send the real charge
await mrdRequest(auth, `/v3/users/${auth.userId}/orders/${intent.orderId}`, {
method: "PUT",
body,
});

Tracking the delivery

The agent offers to monitor the order and notify me only at meaningful points. I say yes. The agent polls the order and reports when it goes out for delivery, then I confirm delivery with a photo of the order at the address.

WhatsApp message: agent offers to poll for delivery updates every 7 minutes and notify only on key events

The agent set its own polling interval of 7 minutes and its own notification rule, pickup, nearby, delivered, or a problem. Once the order moves to out for delivery, it sends an update with the ETA.

WhatsApp message: agent notifies that the order is out for delivery with an ETA

When the order arrives, I close the loop with a photo of the order at the door, and the agent sends a short confirmation message.

WhatsApp message and photo confirming the order has landed at the delivery address

Mr D has no separate tracking API. Both tools are just reading different parts of the same order:

  • get_order_status reads the payment side, whether it's paid, how much, and what's still owing.
  • track_delivery reads the courier side, whether a driver has been assigned and the estimated delivery time.

The polling schedule and the decision about when to notify me were not platform features either. The agent chose both of those on its own.

// get_order_status and track_delivery both read the same order object
async function fetchOrder(auth, orderId) {
return mrdRequest(auth, `/v3/users/${auth.userId}/orders/${orderId}`);
}
// get_order_status extracts payment.status, paid, outstanding, total
// track_delivery extracts courier status, driver assigned, ETA

What this shows about Mr D's agent experience

Can an agent order you a burger in South Africa? Technically, yes, but only if you build the agent tooling yourself. Mr D gives an agent no supported path to place an order at all, so getting there meant reverse-engineering its private API and building an MCP server on top of it.

The use of agents to discover, use, and pay for products is only going to grow. Companies need to think about agent experience the same way they already think about user experience:

  • How does an agent find your product?
  • How does an agent understand what it offers?
  • How does an agent act on a user's behalf, safely and reliably, without a browser or a human in the loop?

Until that changes, "can an agent buy me a burger?" will keep being answered by developers doing the work these companies haven't done themselves.

We want to work with hospitality and delivery businesses looking to win this new market. At Ritza, our Engineering Writers work at agent speed with human-expert verification (no slop) to help South African hospitality and delivery brands build the agent experience their customers will start expecting.

About the author

James Daniel Whitford
James Daniel WhitfordSoftware engineer and technical writer

James Daniel Whitford is a software engineer and technical writer at Ritza. He writes about developer tooling, AI agents, and full-stack web development, and contributes hands-on tool comparisons to TechStackups.