MCP Server
Reservly includes a Model Context Protocol (MCP) server that lets AI assistants like Claude, ChatGPT, and others interact with your booking system through natural language. Customers can check availability, browse services, and book appointments through AI conversations.
Available Tools
The MCP server exposes seven tools. Each maps directly to a public REST API endpoint, so the data returned is identical.
| Tool | Description | REST Equivalent |
|---|---|---|
get_business_info | Get business profile, hours, contact, and social links | GET /info |
search_services | List available services with prices and durations | GET /services |
list_staff | Get staff members, optionally filtered by service | GET /staff |
check_availability | Find available time slots for a date and service | GET /slots |
list_events | Browse upcoming events with dates and capacity | GET /events |
list_rentals | Browse rental items with prices | GET /rentals |
book_appointment | Create a new booking with customer details | POST /book |
Claude Desktop Setup
To connect your Reservly business to Claude Desktop, add the following to your claude_desktop_config.json file. Replace YOUR-BUSINESS-SLUG with your actual business slug (found in Settings on your dashboard).
{
"mcpServers": {
"reservly": {
"command": "npx",
"args": [
"mcp-remote",
"https://reservly.io/api/mcp/YOUR-BUSINESS-SLUG"
]
}
}
}jsonOn macOS, the config file is located at ~/Library/Application Support/Claude/claude_desktop_config.json. On Windows, it is at %APPDATA%\Claude\claude_desktop_config.json.
That configuration is enough to browse — services, events, rentals, staff and availability all answer without a credential. To let the assistant actually book, it must also present an API key; see Authentication below.
Claude Code Setup
Claude Code speaks the HTTP transport directly, so no bridge is needed. Add the server once, with your key on the header if the assistant should be able to book:
claude mcp add --transport http reservly \ https://reservly.io/api/mcp/YOUR-BUSINESS-SLUG \ --header "Authorization: Bearer YOUR-API-KEY"bash
Run claude mcp list to check it shows as connected. Leave out the --header line for a browse-only connection.
n8n Setup
Use n8n's MCP Client node. Set the transport to HTTP Streamable, the endpoint to your MCP server URL, and authentication to Bearer Auth with a credential holding your API key. Pick the tool by name and pass its arguments as JSON. A refused call (wrong scope, revoked key) does not fail the node: its output carries the refusal text, so branch on it with an IF node.
HTTP Transport
The MCP server is available over HTTP at a single endpoint — note that there is no /sse path:
https://reservly.io/api/mcp/{slug}textThe protocol is JSON-RPC 2.0 over POST requests to that URL. Supported methods are initialize, tools/list, tools/call, and ping. The transport is Streamable HTTP in stateless mode — each request is independent, so no session tracking is required and the server scales horizontally.
There is no Server-Sent Events endpoint. The server never opens a long-lived stream, so a client that can only speak the older SSE transport cannot connect to it directly. Use mcp-remote (as in the Claude Desktop config above) to bridge such a client to this endpoint, or send plain JSON-RPC HTTP requests.
curl -X POST https://reservly.io/api/mcp/YOUR-BUSINESS-SLUG \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'bashAuthentication
Browsing is open; booking is not. The six read-only tools work with no credential at all, which is what lets an assistant answer questions about your business straight away. book_appointment writes a real reservation, so it always requires a key that your business issued.
| Request | Result |
|---|---|
| A read tool, no credential | Works. Not attributed to any key, so it is not logged. |
A read tool, with a valid read or all key | Works, and is recorded in your MCP activity log. |
A read tool, with a write key | Refused — a write key is write-only and can call book_appointment alone. The tool result reads This API key is not permitted to call search_services. (naming the tool), and the activity log records it as a scope refusal. |
| Any tool, with an invalid or revoked key | Refused. A credential that was presented and failed is never treated as anonymous — revoking a leaked key stops its reads immediately. |
book_appointment, no credential | Refused. |
Keys are created in Settings → Integrations on your dashboard, and are presented on the standard header:
Authorization: Bearer YOUR-API-KEYtext
A key can call book_appointment only when all three hold:
- It is enabled for AI assistants. Tick Enable for AI assistants on the key. This is off by default, and it is a separate switch from the key's scope — a key you created for the REST API stays inert over MCP until you turn it on deliberately.
- It carries the
writescope (orall). Areadkey can call the six read tools and nothing else; awritekey can callbook_appointmentand nothing else — it is refused on every read tool. Useallfor an assistant that both looks up and books. - It has not been revoked or expired. Revocation takes effect on the next request.
Every authenticated call is written to the activity log on the same Integrations page, with the tool name, the key it used and the outcome — so you can see what an assistant did on your behalf, and cut it off if you do not like the answer.
A refusal is an ordinary JSON-RPC result, not an HTTP error: the response is 200, the result carries isError: true, and its text says why (for example This API key is not permitted to call book_appointment. or This API key has been revoked.).
Rate Limits
- 60 requests per minute from one IP address. Every JSON-RPC request counts, including each element of a batch (at most 20 per batch). Over the limit the server answers HTTP
429withRetry-After: 60and a JSON-RPC error (code-32000, “Rate limit exceeded”). - 100 requests per minute per API key. A key over its limit gets a refusal result (
isError: true, “Rate limit exceeded for this API key”) rather than an HTTP error. From a single IP address the per-IP limit is reached first.
Tool Parameters
Your business slug is injected automatically by the MCP endpoint, so most tools need no arguments. search_services, list_events, list_rentals and list_staff take an optional locale for translated names, and list_staff an optional service_id. The two tools with required parameters are:
check_availability
| Parameter | Type | Required | Description |
|---|---|---|---|
date | string | Yes | Date to check (YYYY-MM-DD) |
service_id | string | Yes | Comma-separated service UUIDs |
staff_id | string | No | Filter slots by a specific staff member |
book_appointment
| Parameter | Type | Required | Description |
|---|---|---|---|
service_ids | string[] | Yes | Array of service UUIDs to book |
date | string | Yes | Booking date (YYYY-MM-DD) |
time | string | Yes | Booking time (HH:MM, 24-hour) |
customer_name | string | Yes | Customer full name |
customer_email | string | Yes | Customer email address |
staff_id | string | No | The staff member to book (a staffId from check_availability). Leave it out and, for a service a staff member performs, Reservly assigns the first staff member free at that time in the business's Auto-Assign order — the same choice its booking page makes when the customer does not pick. If nobody is free at that time the call is refused (409) and nothing is booked. |
customer_phone | string | No | Customer phone number |
party_size | number | No | Party size for group bookings (default 1) |
promo_code | string | No | Promo or session-package redemption code. A package code is reserved for one customer — it only applies when customer_email matches the buyer. |
class_schedule_id | string | No | Class session UUID. For a class-based service the server resolves the session from date + time, claims the seat, and rejects a time with no session — so you normally omit this. Send it only to disambiguate a date and time where two sessions of the same class start together. |
payment_choice | string | No | "online" or "at_door". Read only when the business lets the customer choose how to pay; omitted, the booking is paid at the door. |
A successful call returns the booking with its status: confirmed, or pending when the business uses owner approval or customer confirmation (Settings → Booking Defaults → Booking Confirmation).
Example Conversation
Here is how a typical AI-assisted booking conversation works. The AI assistant handles tool calls behind the scenes while the customer interacts in natural language.
Customer
What services does Luxe Salon offer?
search_servicesAI Assistant
Luxe Salon offers Haircut ($45, 60 min), Blowout ($35, 45 min), Color Treatment ($120, 120 min), and several other services. Would you like to book one?
Customer
Book me a haircut for tomorrow at 2pm.
check_availability → confirms 2:00 PM is openbook_appointment → booking confirmedAI Assistant
Done! Your haircut at Luxe Salon is booked for tomorrow at 2:00 PM. A confirmation has been sent to your email.