Model Context Protocol

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 registration
WebMCP-ready. While you're signed in and browsing Courtbooker, the same tools are also exposed to your browser's built-in AI via navigator.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-only

List every publicly-visible residential community on Courtbooker. No authentication required — useful for agents to discover communities before a resident signs in.

Input

No parameters.

Output
  • sitesobject[]required
list_sitesList my communitiesRead-only

List 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.

Input

No parameters.

Output
  • sitesobject[]required

    Communities the caller belongs to. Use `site_id` for other tools.

list_courtsList courtsRead-only

List 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.

Input
  • site_idstring (uuid)required

    Site UUID from list_sites.

  • sporttennis | padel

    Filter by sport.

Output
  • courtsobject[]required
list_available_slotsList available slotsRead-only

Return 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.

Input
  • site_idstring (uuid)required

    Site UUID from list_sites or list_public_sites.

  • datestringrequired

    Local calendar date YYYY-MM-DD in the community's timezone.

  • sporttennis | padel
  • court_idstring (uuid)
  • from_timestring

    Local HH:MM lower bound (inclusive) on slot start.

  • to_timestring

    Local HH:MM upper bound (exclusive) on slot start.

Output
  • siteobjectrequired

    Echo of the site the results belong to.

  • datestringrequired

    The queried local calendar date (YYYY-MM-DD).

  • timezonestringrequired

    IANA timezone the times are interpreted in.

  • slot_minutesintegerrequired

    Community slot size in minutes. Pass this as `duration_minutes` to create_reservation.

  • min_notice_minutesintegerrequired

    Slots starting sooner than this are omitted.

  • courtsobject[]required
  • notestring

    Present on unauthenticated calls: explains that the public default schedule was used.

create_reservationBook a court

Book 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.

Input
  • court_idstring (uuid)required
  • start_atstring (date-time)required

    ISO 8601 start time (UTC or with offset).

  • duration_minutesintegerrequired

    Duration in minutes. Must match the community's slot size (usually 60).

  • apartment_idstring (uuid)required

    REQUIRED residence UUID (the resident id the booking is made under). Obtain it from `list_sites` — never fabricate it.

Output
  • reservationobjectrequired

    The row that was inserted. Its `id` is the `reservation_id` for cancel_reservation.

cancel_reservationCancel a reservationMutates data

Cancel 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.

Input
  • reservation_idstring (uuid)required
  • reasonstring
Output
  • reservationobjectrequired

    The updated row with status='cancelled'.

list_my_reservationsList my reservationsRead-only

List 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`).

Input
  • site_idstring (uuid)
  • include_pastboolean

    Include reservations that already ended. Default false.

  • include_cancelledboolean

    Include cancelled reservations. Default false.

  • limitinteger
Output
  • reservationsobject[]required

    Ordered by start_at ascending — index 0 is the next upcoming booking.

join_waitlistJoin a slot's waitlist

Subscribe 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.

Input
  • court_idstring (uuid)required
  • start_atstring (date-time)required

    ISO 8601 start time of the booked slot (UTC or with offset).

  • duration_minutesintegerrequired

    Duration in minutes. Must match the community's slot size (usually 60).

Output
  • waitlist_entry_idstring (uuid)required

    The waitlist entry id. Pass this as `waitlist_id` to leave_waitlist.

  • court_idstring (uuid)required
  • start_atstring (date-time)required
  • end_atobjectrequired
  • notestringrequired

    Human-readable explanation of what happens next.

leave_waitlistLeave a waitlistMutates data

Remove 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.

Input
  • waitlist_idstring (uuid)required
Output
  • removedbooleanrequired

    True when the entry was removed; false if it no longer existed.

list_my_waitlistList my waitlist entriesRead-only

List 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.

Input

No parameters.

Output
  • entriesobject[]required

    Active 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.