# Ordering MCP Quickstart

:::info[Preview]
The Ordering MCP at `/mcp2` is in preview and available on request. Your Gett contact provisions your server
addresses. The earlier server at `/mcp` keeps running and is documented in [Ordering MCP at /mcp](./mcp-server).
:::

Put Gett ordering inside Claude, ChatGPT or your own agent. Each person signs in with their own Gett account, so there
is no API key or token for you to handle, and every order is attributed to your partner account.

## What you'll need

- **Your server addresses.** One URL for Live and one for Sandbox, provisioned by your Gett contact. Not a Gett partner
  yet? [Contact our partnerships team](mailto:partnerships@gett-tech.com).
- **An MCP client.** Claude, ChatGPT, [MCP Inspector](https://github.com/modelcontextprotocol/inspector) for testing,
  or your own client built on an MCP SDK.
- **A Google account** to sign in with while you test.

## Your server URL

```
https://api.gett.co/mcp2/{partnerStem}
```

The last segment, the **partner stem**, selects one environment of your partner account:

| Example URL | Environment | What it reaches |
|---|---|---|
| `https://api.gett.co/mcp2/acme-live` | Live | Live stores. Orders are real and payment methods are charged. |
| `https://api.gett.co/mcp2/acme-sandbox` | Sandbox | Sandbox stores only. [Test cards](#test-in-sandbox) decide the outcome and nothing is charged. |

- **The URL is the only switch.** No header or parameter changes the environment, and a Sandbox URL cannot reach Live
  stores, or the reverse.
- **Every order placed through a URL is attributed to your partner account.**
- **Each URL is a separate sign-in.** An access token issued for your Sandbox URL is refused by your Live URL.
- **Stems are exact.** A stem is 3 to 64 lowercase letters, digits and hyphens, and does not start or end with a
  hyphen. A URL that names no active server, such as a mistyped stem or one not yet provisioned, returns `404`.

## How sign-in works

Your client discovers everything it needs from the server, so there is nothing to configure:

1. A request without a token gets `401`, with a `WWW-Authenticate` header naming the URL's protected-resource metadata
   at `https://api.gett.co/.well-known/oauth-protected-resource/mcp2/{partnerStem}`.
2. That document points to the authorization server metadata at
   `https://api.gett.co/.well-known/oauth-authorization-server/mcp2/{partnerStem}`.
3. The client identifies itself with a client ID metadata document, or registers dynamically if it does not have one.
   Clients are public: no client secret is involved.
4. The person continues with Google on gett.co. Their name and email come from their Google account; if it does not
   provide them, sign-in stops and asks the person to add them there first. gett.co then asks for anything else ordering
   needs that the account is missing (a phone number, an address or a payment method), and the person approves the
   connection.
5. The client exchanges the authorization code, with PKCE (`S256`), for an access token with the `ordering` scope and a
   refresh token. Refresh tokens rotate: each use returns a new one.

The account a person orders with belongs to your partner. It is separate from their gett.co account and from accounts
under other Gett partners, so saved addresses and payment methods do not carry over between them.

## Connect a client

### Claude

1. In Claude, open **Customize → Connectors**, select **+**, then **Add custom connector**.
2. Enter a name and your server URL. Leave the OAuth fields under **Advanced settings** empty.
3. Select **Add**, then **Connect**, and sign in.

On Team and Enterprise plans, an Owner adds the connector under **Organization settings → Connectors** and each member
connects it. Claude connects from Anthropic's servers rather than from your machine. See Anthropic's
[custom connector guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp).

### ChatGPT

1. Turn on **Developer mode**. Where the switch is, and whether you can use it, depends on your plan and workspace; a
   workspace admin may need to allow it.
2. Create an app with your server URL and **OAuth** authentication.
3. Complete sign-in. ChatGPT lists the tools it found.

When Gett changes a tool, refresh the app's metadata in ChatGPT to pick up the change. See OpenAI's
[developer mode guide](https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt) and
[connecting an MCP server](https://developers.openai.com/plugins/deploy/connect-chatgpt).

### MCP Inspector

Start the web UI, then connect with the **Streamable HTTP** transport and your server URL:

```bash
npx @modelcontextprotocol/inspector@latest
```

Or list the tools from a terminal:

```bash
npx @modelcontextprotocol/inspector@latest --cli https://api.gett.co/mcp2/{partnerStem} --transport http --method tools/list
```

The command opens your browser to sign in and waits for the redirect on `http://127.0.0.1:6276/oauth/callback`.

### Your own client

Use an MCP SDK with OAuth support; it performs the discovery and sign-in described above. With the
[C# SDK](https://github.com/modelcontextprotocol/csharp-sdk):

```csharp
using ModelContextProtocol.Authentication;
using ModelContextProtocol.Client;

var transport = new HttpClientTransport(new HttpClientTransportOptions
{
    Endpoint = new Uri("https://api.gett.co/mcp2/acme-sandbox"),
    TransportMode = HttpTransportMode.StreamableHttp,
    OAuth = new ClientOAuthOptions
    {
        RedirectUri = new Uri("http://127.0.0.1:8765/callback"),
        DynamicClientRegistration = new DynamicClientRegistrationOptions { ClientName = "Acme ordering agent" },
        AuthorizationCallbackHandler = async (context, cancellationToken) =>
        {
            // Your code: open the sign-in page in the person's browser and wait for the redirect.
            var redirect = await SignInInBrowserAsync(context.AuthorizationUri, cancellationToken);
            return new AuthorizationResult { Code = redirect["code"], State = redirect["state"], Iss = redirect["iss"] };
        },
    },
});

await using var client = await McpClient.CreateAsync(transport);

foreach (var tool in await client.ListToolsAsync())
    Console.WriteLine(tool.Name);
```

A client that hosts a client ID metadata document sets `ClientMetadataDocumentUri` instead of
`DynamicClientRegistration`. The [TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) does the same
through an `authProvider` on its Streamable HTTP transport.

## How requests work

The server speaks Streamable HTTP at MCP revision `2026-07-28`:

- **Stateless.** Every message is a `POST` to your URL, and there is no `Mcp-Session-Id`. Clients on `2026-07-28` skip
  the `initialize` handshake; older clients may still send it. `GET` and `DELETE` return `405`.
- **Routable headers.** Clients on `2026-07-28` send `Mcp-Method` and `Mcp-Name`, which must match the request body.
- **Older clients still connect.** A client on `2025-11-25` gets the same tools, but cannot show inline prompts, as
  described below.
- **State follows the person, not the connection.** The cart and the chosen fulfillment mode are stored for the
  signed-in person, so they survive reconnects.

## Place an order

| Step | Tools | What happens |
|---|---|---|
| 1 | [`viewCart`](./tools#viewcart) | Changes nothing. Shows whether the person already has a cart, which survives reconnects. |
| 2 | [`confirmFulfillment`](./tools#confirmfulfillment) | Starts a new order: shows the saved address, settles pickup or delivery, and clears the cart. |
| 3 | [`discoverStores`](./tools#discoverstores) | Finds stores near the saved address for that mode. |
| 4 | [`browseMenu`](./tools#browsemenu), [`searchMenu`](./tools#searchmenu), [`getItemOptions`](./tools#getitemoptions) | Explores a menu. An item with required options needs them chosen, and `getItemOptions` lists them. |
| 5 | [`addToCart`](./tools#addtocart), [`viewCart`](./tools#viewcart), [`updateCartItem`](./tools#updatecartitem), [`removeFromCart`](./tools#removefromcart), [`clearCart`](./tools#clearcart) | Builds the cart. The first item binds the cart to its store. |
| 6 | [`reviewOrder`](./tools#revieworder) | Prices the order with the store: subtotal, fees, taxes and tip. |
| 7 | [`confirmPayment`](./tools#confirmpayment) | Records the person's approval of the total and the saved payment method, asked for as described below. |
| 8 | [`placeOrder`](./tools#placeorder) | Places the order and charges the saved payment method. |

Begin with `viewCart`, because `confirmFulfillment` clears the cart every time it commits, even to the same mode. If
`viewCart` answers `fulfillment_not_set`, there is no cart yet. If the cart has items, show them to the person and ask
whether to start over. To keep working on that cart, skip `confirmFulfillment` and `discoverStores`: the cart's store
and the pickup or delivery choice still stand.

### Questions for the person

`confirmFulfillment` and `confirmPayment` need an answer from the person. How the answer arrives depends on the client:

- **Clients on `2026-07-28`** show an inline prompt. The server asks for input, and the client retries the call with the
  answer attached. This pattern is called Multi Round-Trip Requests.
- **Older clients** cannot show one. `confirmFulfillment` returns the saved address with `awaitingMode: true`; the agent
  asks in chat and calls again with `mode: "delivery"` or `mode: "pickup"`. `confirmPayment` needs `confirm: true`,
  passed only after the agent has shown the total and payment method and the person has agreed. Without it, the call
  returns `missing_required_field`.

On a client that can show the prompt, `confirm: true` does not skip it.

### Placing safely

- `placeOrder` requires an `idempotencyKey`. Gett also derives the key it places the order with from the reviewed
  order, so retrying the same reviewed order cannot place it twice, even with a new key.
- If an order was placed but the reply never arrived, call `placeOrder` again within 12 hours of the first attempt. It
  returns that order and places nothing new, until the cart, the pickup or delivery choice, or the review changes. After
  12 hours it answers `empty_cart`: have the person check their orders on gett.co rather than ordering again.
- While the outcome of a submitted order is unknown, the cart cannot be edited. Call `placeOrder` again to learn the
  outcome, even if the person has since removed their saved card. If that call fails, the outcome is still
  unknown: have the person check their orders on gett.co. If no order was placed, `clearCart` starts over, and it works
  even while the profile is incomplete.
- A failure that says nothing was charged is settled: follow its `agentGuidance`. After `cart_stale`, the guidance says
  whether the cart was cleared or left as it is; check it with `viewCart` before adding items again.
- If the store's total has changed since review, `placeOrder` returns `order_total_different`. Review and confirm
  again.
- A delivery goes to the address `confirmFulfillment` set, even if the person's default address changes afterwards. If
  that delivery address is changed or removed after `confirmFulfillment`, `reviewOrder` answers `fulfillment_not_set`
  and `placeOrder` answers `order_not_reviewed`: call `confirmFulfillment` again, which clears the cart. If it answers
  `no_saved_address`, the person must save an address first.
- Changing the cart after review clears the review and the approval, so both happen again.
- A successful order empties the cart. The fulfillment mode is kept for the next order.

### Widgets

On hosts that support [MCP Apps](https://github.com/modelcontextprotocol/ext-apps), `discoverStores`, `browseMenu`,
`viewCart` and `reviewOrder` show interactive views. A view can add or remove items and re-price the order. Placement is
handed back to the agent, and always goes through `confirmPayment`.

## Test in Sandbox

Use your Sandbox URL. It reaches only Sandbox stores, and placing an order there contacts no payment processor.

1. When you sign in, save an address near the Sandbox test location, `1 Sandbox Plaza, New York, NY 10001`. Stores are
   found near the saved address, so this is what makes the test store appear.
2. Save one of the [test payment cards](/distribution-partners/marketfront-api/getting-started#test-payment-cards) as
   the payment method. The card decides whether the order is approved or declined, and with which error.
3. Order from the test store as usual.

## Errors and limits

| Response | Meaning | What to do |
|---|---|---|
| `404` | The URL names no active server. | Check the stem. |
| `401` with `WWW-Authenticate` | No token, an expired token, or a token issued for a different URL. | Your client signs in again for this URL. |
| `403` with `insufficient_scope` | The access token does not carry the `ordering` scope. | Your client signs in again, asking for `ordering`. |
| `405` | The request was a `GET` or `DELETE`. | Send `POST`. |
| `429` with `Retry-After` | A rate limit was reached. | Wait the number of seconds in `Retry-After`, then retry. |

Rate limits apply:

- per network address, on every request, with tighter limits on the sign-in pages and on client registration;
- per access token, across all tools, and on `2026-07-28` clients also per tool;
- on the token endpoint, per authorization code or refresh token.

A tool that fails still returns a result, with `isError: true`, a `structuredContent.errorCode`, and `agentGuidance`
telling the agent how to recover. Every code is listed in the [error code reference](./tools#error-codes).

## Compared with the Ordering MCP at /mcp

| | `/mcp2/{partnerStem}` | `/mcp` |
|---|---|---|
| Credential | The person's own sign-in, over OAuth | A Marketfront session token created with your API key |
| Partner and environment | Chosen by the URL | Carried by the token |
| Protocol | `2026-07-28`, stateless; `2025-11-25` clients supported | `2025-11-25`, stateless |
| Inline prompts | Multi Round-Trip Requests, on `2026-07-28` clients | Form elicitation, on clients that advertise it |
| Tools | The same thirteen | The same thirteen |

## What's next

- [Tools reference](./tools): every tool's description, parameters and output fields, generated from the server.
- [Marketfront API Getting Started](/distribution-partners/marketfront-api/getting-started): the Sandbox test store
  and test cards in detail.
