# Fritzy auth.md

How an AI agent or MCP client authenticates to Fritzy (https://fritzy.be) on behalf of a customer. What the connector does, its tools, the order flow and its errors: <https://fritzy.be/mcp> (markdown: <https://fritzy.be/mcp.md>).

## Who this is for

AI assistants and MCP clients (ChatGPT, Claude, …) acting for a Fritzy customer. Only the customer's own account can be linked: there are no API keys, service accounts or agent-only accounts.

## For people using ChatGPT or Claude

Add one connector with the URL <https://api.fritzy.be/mcp/fritzy>. Finding friteries and reading menus works at once. The first time the assistant needs the account (your orders, preparing an order), it asks you to sign in to Fritzy with your phone number and a one-time code, and to choose what it may do. If no sign-in prompt appears, open the connector in your assistant's settings and press Connect (in Claude: Settings > Connectors > Fritzy). You can disconnect it at any time in the Fritzy app: Account > Connected apps.

## What needs authentication

The MCP server <https://api.fritzy.be/mcp/fritzy> (Streamable HTTP) has:

- public tools, no authentication: `find_restaurants`, `get_restaurant`, `get_menu`;
- account tools, which need an OAuth access token for the customer: `whoami`, `list_my_orders`, `get_order_status` (scope `orders:read`), `quote_order`, `create_order`, `cancel_order` (scope `orders:write`), `reorder` (both).

Calling an account tool without a valid token answers HTTP 401 with `WWW-Authenticate: Bearer resource_metadata="…", scope="…"`; a token without the needed scope answers 403 `insufficient_scope` with the scopes to ask for. ChatGPT gets the same challenge inside the tool result (`_meta["mcp/www_authenticate"]`).

## 1. Discover

- Protected Resource Metadata (RFC 9728): <https://api.fritzy.be/.well-known/oauth-protected-resource/mcp/fritzy>
- Authorization server (issuer <https://fritzy.be>), RFC 8414 metadata: <https://fritzy.be/.well-known/oauth-authorization-server>

## 2. Register your client

No pre-registration. Supported methods, in order of preference:

1. **Client ID Metadata Document**: use the https URL of your client metadata document as `client_id` (`client_id_metadata_document_supported: true`). It must list your `redirect_uris` and use `token_endpoint_auth_method: none`.
2. **Dynamic Client Registration** (RFC 7591): POST to <https://api.fritzy.be/oauth/register> with your `redirect_uris`.

Redirect URIs must be https, or http on `localhost` / `127.0.0.1` (any port).

## 3. Get the customer's consent

Authorization code flow with PKCE (`S256` only) and a resource indicator (RFC 8707):

- `GET https://api.fritzy.be/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&code_challenge=…&code_challenge_method=S256&state=…&scope=orders:read%20orders:write&resource=https%3A%2F%2Fapi.fritzy.be%2Fmcp%2Ffritzy`
- The customer signs in on https://app.fritzy.be with their phone number and a one-time code, sees your client's name and host, and chooses what to allow. They may grant fewer scopes than you asked (for example view-only).
- The response carries `code`, `state` and `iss` (RFC 9207): check `iss` equals `https://fritzy.be`.

Scopes:

- `orders:read`: see the customer's orders and their status.
- `orders:write`: prepare pickup orders that the customer pays themselves, and cancel them while unpaid.

## 4. Exchange and use the token

- POST to <https://api.fritzy.be/oauth/token> (form-encoded): `grant_type=authorization_code`, `code`, `code_verifier`, `redirect_uri`, `client_id`, `resource`.
- Access tokens are opaque, last 1 hour and only work on <https://api.fritzy.be/mcp/fritzy>. Send them as `Authorization: Bearer <token>` on every MCP request.
- Refresh tokens last 60 days and rotate on every use: `grant_type=refresh_token`. A refresh token used twice revokes the link.

## 5. Revocation

- POST to <https://api.fritzy.be/oauth/revoke> (RFC 7009) with the token.
- The customer can disconnect your client at any time in the Fritzy app: Account → Connected apps. Your tokens then stop working (401): ask the customer to connect again.

## Money

You never pay and never see card details. `create_order` returns a pay link: the customer pays on their phone, and the friterie only receives the order once it is paid. Always show the customer the items, pickup time and total from `quote_order` and get their confirmation before `create_order`.
