---
title: Connect an MCP client
description: Connect an AI assistant with user consent and tool-specific permissions.
sidebar:
  order: 4
---

:::note[Before relying on a client]
The MCP server owns its tool definitions and schemas, with stable camelCase names such as `searchProducts`. Local
tests use a fake tenant; check each client against the real tenant before relying on it.
:::

Add your deployment's **MCP resource origin** as a Streamable HTTP MCP server in Claude, ChatGPT, Cursor or VS Code.
For the existing personal deployment that origin is `https://mcp.tarnandtimber.com`; it is an example, not a shared
production service. When hosting your own shop, use the origin you set as `shop_mcp_origin`
([setup](/getting-started#configure-your-own-deployment)). Keep that canonical identifier exact, without a trailing slash.

The server's 401 challenge points to protected-resource metadata, and that metadata lists the authorization servers to use: the shop's sign-in service (`auth.issuer`, for Poppy agents) and the Auth0 tenant (the same issuer when both share the login domain). The authorization server metadata at that issuer advertises Dynamic Client Registration (`registration_endpoint`), Client ID Metadata Documents (`client_id_metadata_document_supported`), PKCE `S256` and the `resource` parameter. The server accepts two kinds of token, both issued for this exact URL; a token for the Shop API is refused.

**Standard MCP clients** connect the usual way and need no Poppy support:

1. Dynamic Client Registration: the client reads the metadata, registers at `registration_endpoint`, then runs the authorization code flow with PKCE and `resource` set to the server URL.
2. Client ID Metadata Documents: the client uses the URL of its metadata document as `client_id`. A tenant admin registers each such client once (Auth0 "manual CIMD"), so a new client works after that registration.

Either way you sign in and consent to the client and its permissions, and the client gets a plain OAuth access token with no Poppy Session. The server calls the shop on your behalf with the tool's own scopes, and refusals name the OAuth scope (for example `orders:write`). Registered clients are third-party clients that reach only these MCP resources, with agent scopes only.

**Poppy agents** sign in at the shop's sign-in service and get a Session-backed token. Its permissions are the ones the token carries that the live Session still grants, and refusals name the account scope (`poppy:read`, `poppy:write` or `checkout`).

Signed-in calls use your account cart; a signed-out Poppy Session has its own cart. The assistant cannot add or change saved cards, manage sign-in security, delete your account, or use backoffice operations. Confirm your cart, shipping address and total before letting it check out using a saved card.

| Tools                                             | OAuth scope            | Poppy scope   |
| ------------------------------------------------- | ---------------------- | ------------- |
| `searchProducts`, `getProduct`, `listCollections` | `catalog:read`         | none          |
| `getCart`                                         | `cart:read`            | none          |
| `addCartItem`, `updateCartItem`, `removeCartItem` | `cart:write`           | none          |
| `checkoutSavedCard`                               | `checkout`             | `checkout`    |
| `getProfile`, `listAddresses`                     | `account:read`         | `poppy:read`  |
| `addAddress`, `deleteAddress`                     | `account:write`        | `poppy:write` |
| `listPaymentMethods`                              | `payment_methods:read` | `poppy:read`  |
| `listOrders`, `getOrder`                          | `orders:read`          | `poppy:read`  |
| `cancelOrder`, `requestReturn`                    | `orders:write`         | `poppy:write` |

There is no request idempotency: every call to a tool such as `checkoutSavedCard` acts, and an `idempotency_key` argument is ignored. Before retrying a call that may have timed out, read the orders (`listOrders`) to see whether it already went through.

If a call gets `insufficient_scope`, authorize the named scopes: a Poppy Session is told the account scope it needs (`poppy:read`, `poppy:write` or `checkout`) and a signed-out Session gets `sign_in_required`; a standard client is told the OAuth scope above. If it gets `invalid_token`, sign in again. A 503 with `Retry-After: 5` means authorization infrastructure is unavailable; retry later. An API business error appears in the tool's reply with its stable code.

See [the request flow](/architecture/request-flows/mcp-on-behalf-of), [token exchange](/architecture/auth0/token-exchange), and [shop proxies](/architecture/shop-domain-proxies).
