MCP server: operate your account from your coding agent
nisebookings runs a hosted Model Context Protocol server on top of the
Management API. Connect Claude Code, Codex, Cursor or VS Code to it and the agent can read
your catalogue, find free times, plan and change appointments, and configure the account with tool calls —
the same operations, scopes and validation as /api/v1, without writing HTTP code.
| Endpoint | https://app.nisebookings.com/api/mcp |
| Transport | Streamable HTTP, stateless: POST one JSON-RPC message, application/json response (no SSE, no session id) |
| Auth | Authorization: Bearer nk_… — a Management API key. OAuth is not supported yet. |
| Protocol versions | 2025-06-18 (default), 2025-03-26, 2024-11-05 |
1. Get a key
In the admin, go to Settings → API → New key and pick the scopes the agent may use (table in the
Management API quickstart). The key is shown once. Every tool call is checked
against the key's scopes exactly like the REST call it maps to: a missing scope comes back as a tool
error with status 403, not as a connection failure, so a read-only key is a safe way to start.
| You want the agent to… | Scopes |
|---|---|
| look around (catalogue, schedules, settings, appointments) | catalog:read, settings:read, customers:read, bookings:read |
| plan, move and cancel appointments | add customers:write, bookings:write |
| set up services, staff, schedules, opening hours, coupons | add catalog:write, settings:write |
| look at or complete payments | payments:read / payments:write |
Just trying it? curl -s -X POST https://app.nisebookings.com/api/sandbox (no auth) returns a 24-hour
sandbox account with an all-scopes apiKey — connect MCP with that. At most 5 per hour per IP; over that you get 429 {"error":{"code":"rate_limited"}} and both the Retry-After header and the message say how long to wait (a sliding window, so usually a minute or two, not a full hour). Its mail is
never delivered; list_outbox shows what would have been sent.
Keep the key out of version control: every snippet below reads it from an environment variable or a prompt. Revoke it in Settings → API when you no longer need it; every write that reaches the database is audited per key (rate-limited rejections and the sandbox delete are not).
2. Connect
Claude Code
claude mcp add --transport http nise-booking https://app.nisebookings.com/api/mcp \
--header "Authorization: Bearer $NISE_API_KEY"
The shell expands $NISE_API_KEY once, so the key is stored in your user config (~/.claude.json), not
in the project. To share the server with a team, commit a .mcp.json that expands the variable at
start-up instead, so each member uses their own key:
{
"mcpServers": {
"nise-booking": {
"type": "http",
"url": "https://app.nisebookings.com/api/mcp",
"headers": { "Authorization": "Bearer ${NISE_API_KEY}" }
}
}
}
Check with claude mcp list, or /mcp inside a session.
Cursor (.cursor/mcp.json or ~/.cursor/mcp.json)
{
"mcpServers": {
"nise-booking": {
"url": "https://app.nisebookings.com/api/mcp",
"headers": { "Authorization": "Bearer ${env:NISE_API_KEY}" }
}
}
}
Codex (~/.codex/config.toml)
[mcp_servers.nise-booking]
url = "https://app.nisebookings.com/api/mcp"
bearer_token_env_var = "NISE_API_KEY"
Codex sends Authorization: Bearer <value of NISE_API_KEY>. Export the variable in the shell that starts
Codex. A trusted project can use .codex/config.toml instead.
VS Code / GitHub Copilot (.vscode/mcp.json)
{
"inputs": [
{ "type": "promptString", "id": "nise-api-key", "description": "nisebookings API key (nk_…)", "password": true }
],
"servers": {
"nise-booking": {
"type": "http",
"url": "https://app.nisebookings.com/api/mcp",
"headers": { "Authorization": "Bearer ${input:nise-api-key}" }
}
}
}
VS Code asks for the key once and stores it securely; the file itself holds no secret and can be committed.
Any other client
Any MCP client that supports Streamable HTTP with a custom header works. To check the endpoint by hand:
curl -s https://app.nisebookings.com/api/mcp -H "Authorization: Bearer $NISE_API_KEY" \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
3. Tools
Tool names are snake_case; arguments are camelCase and go to the Management API unchanged, so the field
names, defaults and error codes in openapi.yaml and the quickstart apply.
Read-only tools carry readOnlyHint; deletes and whole-object replacements carry destructiveHint, so
clients can auto-approve reads and ask before anything changes. whoami and find_slots work with any
valid key. The client gets each tool's full description and JSON Schema from tools/list.
Everything else under /api/v1 — extras, compound-service parts, notification templates, events and
ticket types, packages, coupon edits, payment completion and refunds, the Mollie key, webhook edits,
secret rotation and delivery retries — goes through
api_request {method, path, query?, body?} with path relative to /api/v1 (for example
{"method":"POST","path":"/services/<id>/extras","body":{"title":"Wassen","price":5}}). Its description
lists every route.
| Tool | Does | Scope | Kind |
|---|---|---|---|
whoami |
Who am I | any key | read |
list_services |
List services | catalog:read | read |
get_service |
Get service | catalog:read | read |
create_service |
Create service | catalog:write | write |
update_service |
Update service | catalog:write | write |
list_categories |
List categories | catalog:read | read |
list_staff |
List staff | catalog:read | read |
get_staff |
Get staff member | catalog:read | read |
create_staff |
Create staff member | catalog:write | write |
update_staff |
Update staff member | catalog:write | write |
get_staff_schedule |
Get staff schedule | catalog:read | read |
set_staff_schedule |
Replace staff schedule | catalog:write | write (destructive) |
list_staff_services |
List services of a staff member | catalog:read | read |
assign_staff_service |
Assign service to staff member | catalog:write | write |
unassign_staff_service |
Unassign service from staff member | catalog:write | write (destructive) |
get_business_hours |
Get business hours | settings:read | read |
set_business_hours |
Replace business hours | settings:write | write (destructive) |
list_holidays |
List days off | catalog:read | read |
add_holiday |
Add day off | catalog:write | write |
delete_holiday |
Delete day off | catalog:write | write (destructive) |
get_settings |
Get settings | settings:read | read |
update_settings |
Update settings | settings:write | write |
list_customers |
List customers | customers:read | read |
get_customer |
Get customer | customers:read | read |
create_customer |
Create customer | customers:write | write |
update_customer |
Update customer | customers:write | write |
list_appointments |
List appointments | bookings:read | read |
get_appointment |
Get appointment | bookings:read | read |
create_appointment |
Create appointment | bookings:write | write |
update_appointment |
Reschedule / update appointment | bookings:write | write |
delete_appointment |
Delete appointment | bookings:write | write (destructive) |
add_customer_to_appointment |
Add customer to appointment | bookings:write | write |
update_booking |
Update booking status | bookings:write | write (destructive) |
find_slots |
Find free slots | any key | read |
list_outbox |
List sent e-mails | settings:read | read |
list_payments |
List payments | payments:read | read |
get_payment |
Get payment | payments:read | read |
list_events |
List events | catalog:read | read |
list_coupons |
List coupons | catalog:read | read |
generate_coupons |
Generate coupons | catalog:write | write |
list_webhooks |
List webhook endpoints | settings:read | read |
create_webhook |
Create webhook endpoint | settings:write | write |
delete_webhook |
Delete webhook endpoint | settings:write | write (destructive) |
test_webhook |
Send test webhook | settings:write | write |
list_webhook_deliveries |
List webhook deliveries | settings:read | read |
api_request |
Raw management API request | per route | write (destructive) |
4. Results and errors
- A tool result is the API's JSON response as text (and as
structuredContentwhen it is an object). A204comes back asok. - An API error is a tool result with
isError: trueand the API's error body plus its HTTPstatus:403 forbidden(scope missing),404 not_found,409 slot_unavailable/capacity_exceeded,422 validation(seefields) and422 warnings. - Warnings and
force. Creating or moving an appointment runs the admin's checks (overlap, duration, outside the schedule or in a break, day off). Any of these returns422 warningswith the reasons infields. Show them to the human; resend withforce: trueonly when they agree. notifydefaults tofalseon appointment and booking writes, so the agent never mails a customer unless asked.- Lists take
limit(1–100, default 50) andoffsetand return{data, total}. - Webhooks.
create_webhookreturns the signingsecret(whsec_…) once — hand it to the human or store it where the receiver reads it. Thentest_webhookand, a second later,list_webhook_deliveriesto see whether the receiver answered 2xx. Payloads and verification: Webhooks. - A missing or invalid key fails the whole HTTP request with
401andWWW-Authenticate: Bearer; the client shows the server as failed to connect.
5. Verify the connection
Ask the agent: "Use nise-booking: who am I, and what are the next free times for the first service?"
It should call whoami, list_services and find_slots. If the client reports the server as failed,
the key is missing, wrong or revoked (401). If a tool returns 403, the key lacks that scope.
The whole flow (initialize, tool list, slot search, customer + appointment create/update/delete, scope check) is covered by an automated end-to-end test.
Limits
- API keys only; OAuth (and therefore "Connect" buttons in hosted assistants such as claude.ai) is not supported yet.
- No resources, prompts or server-sent notifications — tools only, and the tool list does not change.
- One JSON-RPC message per request (no batches). Browser origins other than the app's own are refused.
- MCP calls run through the same handlers in process:
/api/v1tool calls share the key's 120 requests/min budget (per server process) and always speak the latest API version, while the public-API tools (find_slots, …) have no client address and are counted per tenant, not per key. - Not available through MCP either: creating accounts (a sandbox is created with
POST /api/sandbox, no key) and dashboard statistics. Deleting a sandbox early:api_request{"method":"DELETE","path":"/sandbox"}.