AI agent integrations
Courtbooker publishes a public MCP server so ChatGPT, Claude, Gemini, Cursor and any MCP-compatible assistant can book, list and cancel court reservations on your behalf — with your community's rules enforced server-side.
https://courtbooker.app/mcpStreamable HTTP · OAuth 2.1 · Dynamic client registrationnavigator.modelContext. No extension, no OAuth round-trip — browser agents that implement the WebMCP proposal can call the tools directly as you.1. Add the connector
In ChatGPT / Claude / Cursor, add a custom connector and paste https://courtbooker.app/mcp.
2. Approve the connection
You'll be redirected to Courtbooker to sign in and approve. Your assistant acts as you — same access, same rules.
3. Ask in plain language
Ask about availability, next reservations, or book a slot. Cancel anytime from the chat.
Example prompts
Once connected, these are the sorts of things you can ask your assistant.
- "When is my next tennis reservation?"
- "Can I book a padel court this afternoon between 4pm and 8pm?"
- "Book me the 18:00 tennis slot on Court 1 tomorrow."
- "Cancel my Saturday morning booking."
- "What tennis slots are free this Sunday between 10 and 12?"
- "If the 7pm padel slot is taken, put me on the waitlist."
Available tools (10)
Every tool your assistant can call on Courtbooker. Live from /.well-known/mcp.json.
list_public_sitesList public communitiesRead-onlyList every publicly-visible residential community on Courtbooker. No authentication required — useful for agents to discover communities before a resident signs in.
list_public_sitesList public communitiesRead-onlyList every publicly-visible residential community on Courtbooker. No authentication required — useful for agents to discover communities before a resident signs in.
No parameters.
sitesobject[]required
list_sitesList my communitiesRead-onlyList residential communities the authenticated user belongs to,
with their site_id, name, slug, timezone, role, and residence.
Call this first for any user-scoped question ('my bookings',
'available slots today') to obtain the correct `site_id`.
If it returns more than one community, ask the user which one.
list_sitesList my communitiesRead-onlyList residential communities the authenticated user belongs to, with their site_id, name, slug, timezone, role, and residence. Call this first for any user-scoped question ('my bookings', 'available slots today') to obtain the correct `site_id`. If it returns more than one community, ask the user which one.
No parameters.
sitesobject[]requiredCommunities the caller belongs to. Use `site_id` for other tools.
list_courtsList courtsRead-onlyList active courts for a community, optionally filtered by sport
(tennis or padel). Useful when the user asks 'which courts do we
have' or to resolve a court name to an id. For availability, use
`list_available_slots` instead.
list_courtsList courtsRead-onlyList active courts for a community, optionally filtered by sport (tennis or padel). Useful when the user asks 'which courts do we have' or to resolve a court name to an id. For availability, use `list_available_slots` instead.
site_idstring (uuid)requiredSite UUID from list_sites.
sporttennis | padelFilter by sport.
courtsobject[]required
list_available_slotsList available slotsRead-onlyReturn bookable slots for a community on a given local calendar date.
Use this to answer questions like 'can I book tennis this afternoon' or
'what padel slots are free tomorrow between 18:00 and 21:00'.
Public — callable without authentication. Unauthenticated calls see
the community's public availability view (default 08:00-22:00 hours,
60-minute slots); signed-in residents get the exact opening hours,
closures and booking rules. Booking itself always requires auth.
Inputs:
- site_id: from `list_sites` (signed in) or `list_public_sites`.
- date: YYYY-MM-DD in the community's local timezone.
- sport: 'tennis' or 'padel' to filter courts.
- court_id: restrict to a single court.
- from_time / to_time: optional local HH:MM window (24h). Slots must
START within [from_time, to_time). Use for 'between 4 and 8pm'.
Response: per court, `free_slots` with `start_at`/`end_at` in ISO-8601
UTC plus `start_local`/`end_local` HH:MM in the site's timezone. Slots
in the past or violating min-notice are omitted. Also returns each
court's existing reservations for that day for context.
list_available_slotsList available slotsRead-onlyReturn bookable slots for a community on a given local calendar date. Use this to answer questions like 'can I book tennis this afternoon' or 'what padel slots are free tomorrow between 18:00 and 21:00'. Public — callable without authentication. Unauthenticated calls see the community's public availability view (default 08:00-22:00 hours, 60-minute slots); signed-in residents get the exact opening hours, closures and booking rules. Booking itself always requires auth. Inputs: - site_id: from `list_sites` (signed in) or `list_public_sites`. - date: YYYY-MM-DD in the community's local timezone. - sport: 'tennis' or 'padel' to filter courts. - court_id: restrict to a single court. - from_time / to_time: optional local HH:MM window (24h). Slots must START within [from_time, to_time). Use for 'between 4 and 8pm'. Response: per court, `free_slots` with `start_at`/`end_at` in ISO-8601 UTC plus `start_local`/`end_local` HH:MM in the site's timezone. Slots in the past or violating min-notice are omitted. Also returns each court's existing reservations for that day for context.
site_idstring (uuid)requiredSite UUID from list_sites or list_public_sites.
datestringrequiredLocal calendar date YYYY-MM-DD in the community's timezone.
sporttennis | padelcourt_idstring (uuid)from_timestringLocal HH:MM lower bound (inclusive) on slot start.
to_timestringLocal HH:MM upper bound (exclusive) on slot start.
siteobjectrequiredEcho of the site the results belong to.
datestringrequiredThe queried local calendar date (YYYY-MM-DD).
timezonestringrequiredIANA timezone the times are interpreted in.
slot_minutesintegerrequiredCommunity slot size in minutes. Pass this as `duration_minutes` to create_reservation.
min_notice_minutesintegerrequiredSlots starting sooner than this are omitted.
courtsobject[]requirednotestringPresent on unauthenticated calls: explains that the public default schedule was used.
create_reservationBook a courtBook a court for the authenticated user. Use this after confirming
an available slot with `list_available_slots` — pass one of the
returned `court_id` + `start_at` pairs and the site's `slot_minutes`
as `duration_minutes` (usually 60).
Requires an authenticated resident. `apartment_id` (the resident's
residence) is REQUIRED — read it from `list_sites`, which returns the
caller's `apartment_id` per community.
Recommended flow:
1. `list_sites` -> pick the community's site_id AND the caller's
apartment_id in that community.
2. `list_available_slots` with sport/date/time-window.
3. Show the user the candidate slot and confirm.
4. Call `create_reservation` with that slot and the apartment_id.
Enforced server-side by RLS and booking rules (min notice, per-day
and per-week quotas, max concurrent, days-in-advance). Errors from
those rules are returned verbatim so you can relay them to the user.
If the insert fails because the slot is already booked, offer to call
`join_waitlist` with the same court_id/start_at/duration_minutes so
the user is emailed if the slot frees up.
create_reservationBook a courtBook a court for the authenticated user. Use this after confirming an available slot with `list_available_slots` — pass one of the returned `court_id` + `start_at` pairs and the site's `slot_minutes` as `duration_minutes` (usually 60). Requires an authenticated resident. `apartment_id` (the resident's residence) is REQUIRED — read it from `list_sites`, which returns the caller's `apartment_id` per community. Recommended flow: 1. `list_sites` -> pick the community's site_id AND the caller's apartment_id in that community. 2. `list_available_slots` with sport/date/time-window. 3. Show the user the candidate slot and confirm. 4. Call `create_reservation` with that slot and the apartment_id. Enforced server-side by RLS and booking rules (min notice, per-day and per-week quotas, max concurrent, days-in-advance). Errors from those rules are returned verbatim so you can relay them to the user. If the insert fails because the slot is already booked, offer to call `join_waitlist` with the same court_id/start_at/duration_minutes so the user is emailed if the slot frees up.
court_idstring (uuid)requiredstart_atstring (date-time)requiredISO 8601 start time (UTC or with offset).
duration_minutesintegerrequiredDuration in minutes. Must match the community's slot size (usually 60).
apartment_idstring (uuid)requiredREQUIRED residence UUID (the resident id the booking is made under). Obtain it from `list_sites` — never fabricate it.
reservationobjectrequiredThe row that was inserted. Its `id` is the `reservation_id` for cancel_reservation.
cancel_reservationCancel a reservationMutates dataCancel a reservation the authenticated user owns (or has admin
rights over). Sets status to 'cancelled'. Use `list_my_reservations`
first to look up the `reservation_id` — never guess it.
Idempotent: cancelling an already-cancelled reservation is a no-op.
Community cancellation-deadline rules are enforced server-side and
returned as errors if violated.
cancel_reservationCancel a reservationMutates dataCancel a reservation the authenticated user owns (or has admin rights over). Sets status to 'cancelled'. Use `list_my_reservations` first to look up the `reservation_id` — never guess it. Idempotent: cancelling an already-cancelled reservation is a no-op. Community cancellation-deadline rules are enforced server-side and returned as errors if violated.
reservation_idstring (uuid)requiredreasonstring
reservationobjectrequiredThe updated row with status='cancelled'.
list_my_reservationsList my reservationsRead-onlyList reservations belonging to the authenticated user. Use this to
answer 'when is my next booking', 'what do I have on Saturday', or to
look up a reservation_id before calling `cancel_reservation`.
Defaults: upcoming, non-cancelled, oldest-first (next up first),
limit 50. Set `include_past` or `include_cancelled` to widen the
result. Filter by `site_id` when the user belongs to multiple
communities.
Each item includes the court name, sport, site name, and start/end
times both in ISO-8601 UTC and formatted in the community's local
timezone (`start_local`, `end_local`).
list_my_reservationsList my reservationsRead-onlyList reservations belonging to the authenticated user. Use this to answer 'when is my next booking', 'what do I have on Saturday', or to look up a reservation_id before calling `cancel_reservation`. Defaults: upcoming, non-cancelled, oldest-first (next up first), limit 50. Set `include_past` or `include_cancelled` to widen the result. Filter by `site_id` when the user belongs to multiple communities. Each item includes the court name, sport, site name, and start/end times both in ISO-8601 UTC and formatted in the community's local timezone (`start_local`, `end_local`).
site_idstring (uuid)include_pastbooleanInclude reservations that already ended. Default false.
include_cancelledbooleanInclude cancelled reservations. Default false.
limitinteger
reservationsobject[]requiredOrdered by start_at ascending — index 0 is the next upcoming booking.
join_waitlistJoin a slot's waitlistSubscribe the authenticated user to the waitlist for a slot that is
already booked by someone else. Use this when `create_reservation`
fails because the slot is taken (or when `list_available_slots` shows
the desired time under `reservations`) — offer the user to join the
waitlist for that exact court_id + start_at + duration.
If the blocking reservation is cancelled, everyone on the waitlist is
emailed a claim link; the slot goes to whoever books first (first
come, first served). Joining does NOT reserve anything by itself.
Idempotent: joining the same slot twice returns the existing entry.
Server-side checks (slot actually booked, waitlist enabled for the
community, caller is a resident) are returned verbatim as errors.
join_waitlistJoin a slot's waitlistSubscribe the authenticated user to the waitlist for a slot that is already booked by someone else. Use this when `create_reservation` fails because the slot is taken (or when `list_available_slots` shows the desired time under `reservations`) — offer the user to join the waitlist for that exact court_id + start_at + duration. If the blocking reservation is cancelled, everyone on the waitlist is emailed a claim link; the slot goes to whoever books first (first come, first served). Joining does NOT reserve anything by itself. Idempotent: joining the same slot twice returns the existing entry. Server-side checks (slot actually booked, waitlist enabled for the community, caller is a resident) are returned verbatim as errors.
court_idstring (uuid)requiredstart_atstring (date-time)requiredISO 8601 start time of the booked slot (UTC or with offset).
duration_minutesintegerrequiredDuration in minutes. Must match the community's slot size (usually 60).
waitlist_entry_idstring (uuid)requiredThe waitlist entry id. Pass this as `waitlist_id` to leave_waitlist.
court_idstring (uuid)requiredstart_atstring (date-time)requiredend_atobjectrequirednotestringrequiredHuman-readable explanation of what happens next.
leave_waitlistLeave a waitlistMutates dataRemove one of the authenticated user's waitlist entries so they stop
receiving notifications for that slot. Use `list_my_waitlist` first
to look up the `waitlist_id` — never guess it.
Idempotent: leaving an entry that was already removed returns
`removed: false` instead of an error.
leave_waitlistLeave a waitlistMutates dataRemove one of the authenticated user's waitlist entries so they stop receiving notifications for that slot. Use `list_my_waitlist` first to look up the `waitlist_id` — never guess it. Idempotent: leaving an entry that was already removed returns `removed: false` instead of an error.
waitlist_idstring (uuid)required
removedbooleanrequiredTrue when the entry was removed; false if it no longer existed.
list_my_waitlistList my waitlist entriesRead-onlyList the authenticated user's active waitlist entries (future slots
only). Use this to answer 'which slots am I waiting for?' or to look
up a `waitlist_id` before calling `leave_waitlist`.
Each entry includes the community, court, sport, slot times in
ISO-8601 UTC, and a status: 'waiting' until the slot frees up,
'notified' once the claim email was sent.
list_my_waitlistList my waitlist entriesRead-onlyList the authenticated user's active waitlist entries (future slots only). Use this to answer 'which slots am I waiting for?' or to look up a `waitlist_id` before calling `leave_waitlist`. Each entry includes the community, court, sport, slot times in ISO-8601 UTC, and a status: 'waiting' until the slot frees up, 'notified' once the claim email was sent.
No parameters.
entriesobject[]requiredActive future waitlist entries for the caller.
Ready to try it?
Sign in to Courtbooker, then paste the MCP URL into your favourite AI assistant. The connection is scoped to your account — you can revoke it any time from your profile.
