MCP server v1
The same operations as the HTTP API, over the Model Context Protocol — so an assistant can read a diary and book into it without anybody writing a client first.
See also the HTTP API and webhooks.
Connecting
The same key that opens the HTTP API. There is nothing separate to enable, and the scopes mean the same things here.
- Server URL
- https://torim.ai/api/mcp
- Transport
- Streamable HTTP
- Header
- Authorization: Bearer torim_…
Clients configured from a file usually take this shape:
{
"mcpServers": {
"torim": {
"type": "http",
"url": "https://torim.ai/api/mcp",
"headers": {
"Authorization": "Bearer torim_…"
}
}
}
} Command-line clients generally take the same three values as flags. Nothing here is specific to one client.
Tools
Ten, and this is all of them. Each is one call into the same code the matching endpoint uses, so the rules — the slot re-derivation, the notifications, the double-booking protection — cannot differ between the two.
get_business — | Name, timezone, address and booking policy. Takes nothing: the key names the business. catalogue:read |
list_services — | Every service, retired ones included. A service `slug` is what the other tools take. catalogue:read |
list_team — | Who works here. An appointment is booked onto one of these by `id`. catalogue:read |
get_availability service, from?, days? | When a service can be booked. Ask this before booking or moving anything. catalogue:read |
list_appointments from?, to? | The diary, with customer names and phone numbers — cancelled appointments and pending requests included. `to` is inclusive, defaults to six days on, and the window may not exceed 62 days. appointments:read |
get_appointment id | One appointment, to confirm what was actually written. appointments:read |
create_appointment service, staff_id, start, customer_name, customer_phone?, customer_email?, note?, off_grid? | Book somebody in. The price comes from the service, never from the call. appointments:write |
cancel_appointment id | Call one off. Frees the slot and tells the waiting list, which may keep it for whoever has waited longest. appointments:write |
move_appointment id, start, off_grid? | Reschedule. One update under the same lock a booking takes. appointments:write |
list_closures — | Days the business is shut, from today on. catalogue:read |
An argument marked ? is optional. Instants are RFC 3339;
dates are YYYY-MM-DD; unknown arguments are rejected rather
than ignored. Every tool answers with the same JSON its endpoint would, as text content.
What a refusal looks like
A refusal comes back as a tool error result, not a JSON-RPC error, carrying the same stable code the HTTP surface uses:
{ "ok": false, "code": "slot_taken" } That is deliberate. slot_taken is not a malformed request, it
is an answer — a model that can read it can pick another time. Only a genuine server fault is
raised as a protocol error, because there is nothing useful for a model to do about one.
Authentication is the exception: a missing or dead key is refused before the protocol is
spoken at all, with the ordinary 401 and the ordinary { code, error } body.
Protocol notes
- Stateless. No session to establish and no
Mcp-Session-Idto keep alive — every call carries its own key and is answered on its own. - Plain JSON responses. Every tool is one question with one answer, so nothing streams.
- Instructions. The server tells a model two
things no single tool says: which business it is connected to, and that a time must come
from
get_availability. - No OAuth yet. Clients that insist on an OAuth flow rather than a header — some hosted connector UIs do — cannot connect today. Anything that accepts a bearer header can.
- Tools only. No resources and no prompts are served.
Something not answered here? Get in touch.