Skip to content

Connecting over MCP

Bottle speaks the Model Context Protocol at /mcp, so an assistant can read a business’s data and answer questions about it. Claude and ChatGPT both connect this way.

Add the address as a connector in your assistant, sign in when it asks, and choose the business and the permissions. See Connecting an assistant.

https://api.bottle.example/mcp

Authentication is OAuth 2.1 with PKCE. An API key will not work here: the MCP endpoint wants a person’s grant, not a business credential.

The flow is the standard one and needs no arrangement with us:

  1. GET /.well-known/oauth-protected-resource names the authorisation server. An unauthenticated request to /mcp returns a 401 pointing at it.
  2. GET /.well-known/oauth-authorization-server gives the endpoints.
  3. POST /oauth/register registers your client. Anyone may; it grants nothing.
  4. Send the person to the authorisation endpoint. They choose the business and the permissions.
  5. Exchange the code at /oauth/token with your verifier. S256 only.

Access tokens last an hour; refresh tokens are single use and rotate.

Which tools appear depends on what was granted, so a connection that may only read customers is not offered anything else.

Tool Needs What it does
get_business tenant:read Which business this connection is for.
list_customers customers:read Every customer, optionally filtered.
get_customer customers:read One customer in full.
list_delivery_addresses customers:read Where one customer takes delivery, with the driver notes.
list_merge_suggestions customers:read Customers that look like duplicates, each pair once.
list_products products:read What the business sells, with list prices in pence.
get_customer_prices products:read What one customer pays, with the list price beside it.
list_orders orders:read Orders, filtered by the day they are wanted.
get_order orders:read One order with its lines, what was delivered, and any refund due.
list_standing_orders orders:read Regular deliveries: how often, next due, what is already written.
get_subscription billing:read The plan this business is on, and what happens next.
list_bottle_invoices billing:read What Bottle has billed them, each with a link.
get_card_payments_account billing:read Their own Stripe account: money in it, the next payout, and anything Stripe needs from them. Read only.
list_rounds rounds:read A day’s rounds, each with its fit and stop times.
get_round rounds:read One round in full, every stop with a time of day.
list_locations rounds:read The bases rounds start from and end at.
list_vehicles rounds:read The fleet: patch, payload, load space, usual driver.
get_planning rounds:read How the day is reckoned: hours, loading, minutes a door.
list_drivers drivers:read Each driver, the vehicle they take and their hours.
list_members members:read Who works there, and their roles.
search tenant:read Finds anything the connection may read, by name, postcode, phone or number. See Search.
fetch tenant:read One customer, order or invoice in full, by an id search returned.

search and fetch exist because deep research connectors look for exactly those two names. search is also the quickest way for any assistant to get from “the Kingston fish bar” or “invoice 42” to a record: it covers every kind the connection’s scopes can read, and its ids (customer:<uuid>, order:<uuid>, invoice:<uuid>) work with fetch and, without the prefix, with the other tools.

The scope says what kind of thing; the role says how much of it. A driver’s own connection holds rounds:read, and list_rounds and get_round answer with their rounds only. Nothing about the other vehicle, nothing about the planning. An owner’s or admin’s connection with the same scope sees every round. Each stop carries what happened at that door: delivered, or the reason it could not be, with whatever the driver added.

An assistant cannot mark a drop delivered. That is a person at a door, with a phone in their hand, and it is the one record where being wrong means a customer is billed for gas they never got.

One thing: where a placed order is going, and the day, payment method and notes that go with it. That is the call that comes ten minutes after an order was taken, it corrects a record that already exists, and every change shows in the app immediately. An address must belong to the customer the order is for, and a delivered or cancelled order cannot be touched.

Everything else reads. Taking an order creates an obligation, changing a rate changes what somebody is charged, and merging two customers cannot be undone, so all three want a person looking at the screen. An assistant can list the duplicate pairs; it cannot merge them.

Grant orders:write only if you want that one change. A connection without it is not offered the tool.