Skip to main content
Glama
shivanished

DoorDash MCP

by shivanished
README.md
# Poke × DoorDash CLI recipe

A [Poke](https://poke.com) recipe that lets Poke order DoorDash for you over text,
powered by DoorDash's official `dd-cli` (limited beta, announced 2026-07-15 by
[@andyfang](https://x.com/andyfang/status/2077516962515599799)).

Fork of [DanteMagg/poke-doordash-recipe](https://github.com/DanteMagg/poke-doordash-recipe),
upgraded for dd-cli **v0.2.1+** (mandatory `--intent`) and hardened around real
upstream failure modes (silently dropped cart items, no submit idempotency).

Poke installs recipe integrations as **MCP servers**, so this repo is a thin MCP
bridge: Poke → tunnel → `server.mjs` → `dd-cli` → DoorDash.

## Prerequisites

1. **dd-cli** — official binary from [doordash-oss/doordash-cli](https://github.com/doordash-oss/doordash-cli)
   (macOS Apple Silicon / Linux x86_64). Download the latest release tarball,
   verify the SHA256, and put the binary on PATH as `dd-cli` (or set `DD_CLI_BIN`).
   Full functionality is waitlist-gated — join at https://forms.gle/gvCQZvu9C1EKA6aM6.
2. Node **22.5+** (the submission journal uses the built-in `node:sqlite`).
3. A Poke account with Kitchen access.
4. Log in once via `dd-cli login` (interactive; credentials go to the OS keychain).
   Note: v0.2.2 gates even subcommand `--help` behind login.

## Run + publish

```bash
npm install
npm test                 # 19 tests against a fake dd-cli; no account needed
npm start                # MCP server on http://127.0.0.1:3737/mcp (loopback only)
npm run tunnel           # in a second terminal: tunnels to Poke and creates the recipe
```

`npm run tunnel` runs `npx poke@latest tunnel http://localhost:3737/mcp -n "DoorDash CLI" --recipe`,
which prints a shareable recipe link + QR code. (Log in first with `npx poke@latest login`.
The tunnel only forwards the port — start `npm start` before it.)

### If `poke login` is broken (cli-auth outage)

As of 2026-08-12, `POST poke.com/api/v1/cli-auth/code` returns an empty 200, so
`poke login` dies with "Unexpected end of JSON input" and web API keys don't
carry CLI scopes. Bypass the Poke CLI entirely:

```bash
openssl rand -hex 24 > .data/mcp-auth-token
MCP_AUTH_TOKEN=$(cat .data/mcp-auth-token) npm start   # /mcp now requires the bearer token
cloudflared tunnel --url http://localhost:3737         # prints a public https URL
```

Then in Poke's web app (Settings → Connections → add an MCP integration): URL =
`https://<cloudflared-host>/mcp`, API key = the token. The quick-tunnel URL
changes each cloudflared restart — update the connection when it does.

Then polish in [Kitchen](https://poke.com/kitchen) → your recipe. Use
[RECIPE.md](RECIPE.md) as the recipe's instruction text — it's the agent-facing
operating manual.

## Tools exposed

28 tools. Everything from the upstream bridge, plus `search_products_across_stores`:

- **Discovery:** `search_restaurants`, `find_nearby_stores` (grocery/retail/alcohol/
  convenience/pets), `get_menu`, `find_items`, `search_products_across_stores`
  (multi-store product hunt with deterministic diet/zero-sugar synonym expansion),
  `restaurant_item_details`, `item_details`, `store_details`, `build_grocery_list`
- **Account:** `list_addresses`, `payment_methods`, `doordash_status` (install /
  version / login / waitlist probe)
- **Cart:** `cart_list` (pre-flight before creating carts), `cart_add_items`,
  `cart_show`, `cart_remove_item`, `cart_delete` — all mutations are **read-back
  verified** (upstream can silently drop items) and return `verified: true` or a
  `cart_mismatch` with exactly what's missing
- **Promos:** `promo_list`, `promo_apply`, `promo_remove`
- **Orders:** `order_history`, `order_reorder`, `order_preview` (relays dd-cli's
  `--beautify` summary verbatim and mints a `preview_token`), `order_submit`,
  `order_status`, `order_receipt`, `checkout_url` (browser fallback for
  payment-method swaps)
- **Escape hatch:** `dd_cli` raw passthrough (`login`, `export-token`, and raw
  `order submit` blocked)

## Purchase safety

- `order_submit` **charges the default card immediately**. It requires a fresh
  single-use `preview_token` from `order_preview` — the server re-reads the cart
  at submit time and refuses if the contents, fulfillment, or schedule changed
  since the preview — plus `user_confirmed=true`, legitimate only after the
  preview was shown, the total approved, the card NAMED (brand + last4 from
  `payment_methods`), and the Dasher tip explicitly confirmed (in cents).
- Duplicate submits are blocked **durably** (SQLite journal in `.data/state.db`,
  override with `DD_MCP_STATE_DB`): a restart, a second server instance, or a
  Poke retry after a timeout cannot double-order. Ambiguous submits lock the
  cart and are never auto-retried.
- Every dd-cli service call carries a mandatory `--intent` (v0.2.1+) — always a
  deterministic description of the MCP tool being run, **never the user's own
  words**. CLIs without the flag are auto-detected.
- Cart writes auto-spaced ~8s apart (DoorDash rate limiting).
- Errors map to a stable taxonomy (`not_logged_in`, `waitlist_pending`,
  `rate_limited`, `cart_mismatch`, `preview_expired`, `duplicate_submit_blocked`,
  `ambiguous_submit`, …) with actionable messages the Poke agent can relay.
- Server binds to loopback only by default (`HOST` env to override); the Poke
  tunnel is the intentional external bridge. Structured logs carry tool/family/
  duration/error-type only — no payloads, no tokens, no card data.

## Environment variables

| Var | Default | Purpose |
| --- | --- | --- |
| `DD_CLI_BIN` | `dd-cli` | Path to the dd-cli binary |
| `PORT` | `3737` | MCP server port |
| `HOST` | `127.0.0.1` | Bind address |
| `DD_MCP_STATE_DB` | `.data/state.db` | SQLite path for the submission journal |
| `MAX_RESPONSE_CHARS` | `60000` | Tool-response truncation cap |
| `DD_MCP_QUIET` | – | `1` silences structured logs |