Skip to content
Docs · MCP server

Let an agent read the diary

The Model Context Protocol is how an assistant asks another system for things. This page is the server we speak it with: what it can be asked, what a key is allowed to reach, and what happens when it is asked for more.

Mint a key in settings

API keys are on. A key belongs to an organization, so it is made on the organization: Settings, then Organizations, then the one you want, then its API keys tab. Tick the venues it may work at and the actions it may take at each of them, then copy the value, which is shown once. Point your client at the endpoint below and it works today. Everything the key changes is written down, and most of it a person can put back.

Choose the organization to make it on

What it is

A thin proxy in front of the same REST API the dashboard uses.

It holds no database connection and decides nothing on its own. Tenancy, plan limits, validation and rate limits are all enforced by the API, which is the only place they can be enforced once. Every tool below maps onto a route that already exists, gated on a permission that is already checked.

MCP is an open protocol, so any client that speaks it works. We do not have a favourite.

What a key is

One credential, one organization, and as little reach beyond that as you care to leave it.

  • Pinned to one organization

    The organization is chosen when the key is minted and cannot be changed after. A call that names another one is refused with a 403 that no permission can widen. An agent working across two companies needs two keys.

  • Narrowable to specific venues

    An organization may hold six restaurants, and a key does not have to reach all of them. Tick the venues it may work at and a call aimed anywhere else is refused, including a list call that names no venue at all.

  • Narrowable to specific actions

    Read the menu at both venues, take bookings only downtown. Actions are granted per venue, so the narrowing is a grid rather than a single switch.

  • Written down, and reversible

    Anything a key changes is recorded before the change happens, with the arguments, the record it touched and how it looked beforehand. If the record cannot be written the call does not run. Most changes can be put back from your settings by a person.

Scopes

A scope is the coarse permission a key carries. A write scope implies its matching read, so tick the narrowest one that does the job. The commercial numbers sit behind their own scope and are implied by nothing.

ScopeWhat it grants
org:read

Organization Read

View organization data
org:write

Organization Write

Open a venue and set its profile, opening hours and booking configuration
menu:read

Menu Read

View menus, sections, items and specials
menu:write

Menu Write

Create and update menus and items, and publish them
booking:read

Booking Read

View bookings, availability, resources and service types — not revenue or margin
booking:write

Booking Write

Create, confirm, reschedule and cancel bookings
sales:read

Inquiries Read

View event inquiries, the sales pipeline and what each stage is worth
event:read

Event Read

View events and their registrations
event:write

Event Write

Create, update and publish events
contact:read

Contact Read

View guest contacts and groups
contact:write

Contact Write

Create and update guest contacts and their group membership
loyalty:read

Loyalty Read

View visits, points and rewards
staff:read

Staff Read

View staff members, the rota and coverage — not pay or labour cost
staff:write

Staff Write

Create and update shifts and staff assignments
analytics:read

Analytics Read

View revenue, margin, covers, no-show rate and labour cost reports
agent:read

Agent Trail Read

See what this key itself has done — its own changes and reads, nobody else’s
agent:write

Agent Trail Undo

Let this key take back one of its own recent changes. Its own only, one at a time, within the hour — never another key’s, and never a whole afternoon

There is no admin scope on offer. It would satisfy every tool including the ones added after the key was minted, which is not something anyone can agree to by ticking a box.

Connecting

Two lines in your client config. The endpoint is a hosted streamable HTTP transport, so there is nothing to install and nothing to keep running.

Swap the placeholder for the key you copied when you minted it. We show a key once and never again, so if it is gone, mint another and revoke the old one.

claude mcp add --transport http guestavo https://mcp.guestavo.com/mcp \
  --header "Authorization: Bearer gvsk_your_key_here"

What refusal looks like

A missing permission comes back as a 403 naming the one it wanted, so the person who minted the key can fix it from settings without reading this page. A key aimed at the wrong organization or the wrong venue comes back as a 403 that no permission fixes.

The skill

A briefing you hand your agent, so it does not have to work the product out by trial and refusal.

One file: how to connect, what each permission opens, the order a venue has to be built in, every refusal it will meet and what each one means, and how it undoes its own mistake. It is generated from the same tool list the server advertises, so it cannot describe a version of the product that no longer exists.

This copy is the same for everybody, so it lists every tool the product has rather than the ones one key can call. Once an agent is connected it is told what its own key opens, and that is the narrower and truer answer.

Read the skill

Or put it straight where an agent looks for it:

mkdir -p ~/.claude/skills/guestavo && curl -fsSL https://guestavo.com/docs/mcp/skill.md -o ~/.claude/skills/guestavo/SKILL.md

The tools

75 tools

Generated from the registry the server itself reads, so this list cannot drift from what a client will see. Grouped the way the permission screen groups them, which is the way an owner decides.

No tool here messages a guest. Booking tools that would normally send a confirmation or a cancellation email run with that suppressed when the caller is a key, because an email to a real person is the one thing no undo button can take back.

See which modules are switched on

org:read
  • get_organizationRead onlyGET /api/organizations/:organizationId

    Read the organization: its name, locale and which product modules (menu, booking, events, outreach, loyalty, staff) are switched on. Call it when an action is refused because a module is off — only an owner can switch one on.

Find our venues

org:read
  • list_propertiesRead onlyGET /api/organizations/:organizationId/properties

    List the venues (properties) in an organization. Start here to get the propertyId that menu, booking, event and staff tools ask for.

  • get_propertyRead onlyGET /api/properties/:propertyId

    Read one venue in full: address, timezone, opening hours and booking configuration. Use it to find out whether a venue is bookable at all before asking why get_booking_availability returns nothing.

Open a venue and set it up

org:write
  • create_propertyChanges dataPOST /api/organizations/:organizationId/properties

    Create a venue: name, URL slug, type, address and timezone. Set its opening hours and booking settings afterwards with the set_property_ tools. A key narrowed to particular venues cannot call this — the new venue is not one of them.

  • update_property_profileChanges dataPATCH /api/properties/:propertyId/profile

    Change a venue’s name, description, address, city, country or timezone. Only the fields sent are written. The URL slug is not changeable here, because a printed QR code resolves through it.

  • set_property_operating_hoursChanges dataPATCH /api/properties/:propertyId/operating-hours

    Set a venue’s opening hours. Only the days you name change; the rest of the week is left alone, and exceptions merge by date, so adding one holiday cannot drop the others. removeExceptionDates takes one back off.

  • set_property_booking_settingsChanges dataPATCH /api/properties/:propertyId/booking-settings

    Configure how a venue takes bookings: interval, lead time, buffer, auto-confirm. enabled must be true or no slot is ever offered. publicBookingEnabled publishes the venue and requireStaffShiftCoverage can close its diary — confirm both.

Read the menu

menu:read
  • list_menusRead onlyGET /api/menus

    List an organization's menus with their status (draft, published, archived). Supports a search term.

  • get_menuRead onlyGET /api/menus/:menuId

    Read one menu in full: its categories, the items filed under each, prices, allergens and dietary tags.

  • list_menu_itemsRead onlyGET /api/menu-items

    Search the dish catalogue by name across the whole organization. Items live independently of the menus they appear on, so use this to find a dish when the menu is unknown.

  • list_menu_categoriesRead onlyGET /api/menu-categories

    List the organization's dish sections (starters, mains, wine) as catalogue records. A section is filed here once and placed on any number of menus, so use this to find the categoryId add_category_to_menu needs.

  • list_menu_specialsRead onlyGET /api/menus/:menuId/specials

    List a menu's daily specials with the dates each one runs.

Build and edit the menu

menu:write
  • create_menuChanges dataPOST /api/menus

    Create a menu for a venue: an empty shell with a name and a URL slug. Fill it with add_category_to_menu and add_item_to_menu_category, then publish_menu. Nobody sees it until it is published.

  • update_menuChanges dataPATCH /api/menus/:menuId

    Rename a menu or change its slug, description, language, currency or display options. This edits the menu itself and not what is on it; a dish is edited with update_menu_item.

  • create_menu_categoryChanges dataPOST /api/menu-categories

    Create a dish section in the catalogue — starters, mains, desserts. It is on no menu until add_category_to_menu places it, and one section can be placed on several menus.

  • update_menu_categoryChanges dataPATCH /api/menu-categories/:categoryId

    Rename a catalogue dish section or change its description or image. The change lands on every menu that section appears on.

  • create_menu_itemChanges dataPOST /api/menu-items

    Create a dish in the catalogue with its price, allergens and dietary tags. It is on no menu until add_item_to_menu_category files it under a section of one.

  • update_menu_itemChanges dataPATCH /api/menu-items/:itemId

    Edit a catalogue dish: name, description, base price, allergens, dietary tags. It changes on every menu it appears on — to change one menu only, use set_menu_item_availability on that placement.

  • add_category_to_menuChanges dataPOST /api/menus/:menuId/categories

    Put an existing catalogue section on a menu at a given position. Returns the assignmentId that add_item_to_menu_category needs — that id, not the categoryId, is what dishes are filed under.

  • add_item_to_menu_categoryChanges dataPOST /api/menus/:menuId/categories/:assignmentId/items

    Put a catalogue dish on one menu under one section, optionally with a price override for that menu alone. Takes the assignmentId add_category_to_menu returned, not a categoryId.

Publish the menu and change what is on today

menu:write
  • create_menu_specialChanges dataPOST /api/menus/:menuId/specials

    Add a daily special to a menu for a given date or date range.

  • set_menu_item_availabilityChanges dataPATCH /api/menus/:menuId/categories/:assignmentId/items/:itemAssignmentId

    Change the availability note or price override of one item as it appears on one menu. This is how a dish comes off tonight, or goes lunch-only, without being deleted.

  • publish_menuChanges dataPOST /api/menus/:menuId/publish

    Publish a menu so the venue's public page and QR code serve it. Draft edits are invisible to guests until this runs.

  • archive_menuChanges dataPOST /api/menus/:menuId/archive

    Take a menu out of service: it stops being served publicly and is filed as archived rather than deleted. publish_menu brings it back. There is no way to delete a menu here, by design.

Read the sales pipeline

sales:read
  • list_inquiriesRead onlyGET /api/inquiries

    List event inquiries for an organization, filterable by venue, stage, owner and whether the next action is overdue. This answers "what has come in, and what has nobody chased".

  • get_inquiryRead onlyGET /api/inquiries/:inquiryId

    Read one inquiry: who asked, what for, when they want it, what stage it is at and the notes on it.

  • get_inquiry_pipelineRead onlyGET /api/inquiries/pipeline

    Count each pipeline stage and what it is worth, plus how many open inquiries are past their next action date. Money is reported per currency and never summed across them.

Read the diary

booking:read
  • get_booking_availabilityRead onlyGET /api/public/bookings/availability/slots

    Free booking slots at a venue on one date, optionally narrowed by party size, service type or resource. Call before create_booking. Name the venue by propertyId or by propertySlug.

  • list_booking_resourcesRead onlyGET /api/booking-resources

    List the bookable resources at a venue — tables, rooms, chairs, courts — with their capacities.

  • list_booking_service_typesRead onlyGET /api/booking-service-types

    List the service types a venue takes bookings for (dinner service, treatment, rental) with their durations.

  • list_booking_blocked_timesRead onlyGET /api/booking-blocked-times

    List a venue’s closures — holidays, maintenance windows, a table out of service — over an optional date range. This is why a date with opening hours can still offer no slots.

  • list_bookingsRead onlyGET /api/bookings

    List bookings for an organization, filterable by venue, date range, status and guest name. This answers "who is coming in tonight".

  • get_bookingRead onlyGET /api/bookings/:bookingId

    Read one booking: guest, party size, time, assigned resources, status and notes.

Set the diary up: services, tables, closures

booking:write
  • create_booking_service_typeChanges dataPOST /api/booking-service-types

    Define something a venue takes bookings for — dinner service, a treatment, a court hire — with its duration and optional price. Create these before resources, which reference them by id.

  • update_booking_service_typeChanges dataPATCH /api/booking-service-types/:serviceTypeId

    Rename a service type or change its duration, price or order. Send isActive false to retire one: it stops being offered and the bookings already taken against it keep saying what was booked. There is no delete, by design.

  • create_booking_resourceChanges dataPOST /api/booking-resources

    Add something a booking can be assigned to — a table, a room, a chair, a court — with its capacity and largest party. serviceTypeIds narrows it to particular services, so create those first.

  • update_booking_resourceChanges dataPATCH /api/booking-resources/:resourceId

    Change a bookable resource: name, capacity, largest party, which services it takes, its own weekly availability. Send isActive false to take it out of service without destroying which parties sat at it.

  • create_booking_blocked_timeChanges dataPOST /api/booking-blocked-times

    Close a venue, or one resource, for a period: a holiday, a private hire, maintenance. Times are the venue’s own wall clock, not UTC. recurrenceRule takes FREQ=DAILY or FREQ=WEEKLY, optionally with ;UNTIL=YYYY-MM-DD.

  • update_booking_blocked_timeChanges dataPATCH /api/booking-blocked-times/:blockedTimeId

    Move or shorten a closure, change its reason, or scope it to one resource. Shortening it to a past window is how a closure is lifted — there is no delete here, and the original times are what an undo restores.

Take and change bookings

booking:write
  • create_bookingChanges dataPOST /api/bookings

    Take a booking for a guest at a venue on a date and time. Check get_booking_availability first; an identical repeat is answered from the first call, not booked twice. No confirmation is sent, but the venue may still send its own reminder.

  • confirm_bookingChanges dataPOST /api/bookings/:bookingId/confirm

    Confirm a pending booking. This call emails nobody, but a confirmed booking becomes eligible for the venue's automatic reminder, which may reach the guest. Do not tell the caller the guest has been notified.

  • reschedule_bookingChanges dataPOST /api/bookings/:bookingId/reschedule

    Move an existing booking to a different date or time, keeping the guest and party size. The guest is not emailed about the change; the venue tells them.

  • cancel_bookingChanges dataPOST /api/bookings/:bookingId/cancel

    Cancel a booking, with an optional reason recorded against it. The reason does not reach the guest and no cancellation email is sent; the venue tells them.

Read events

event:read
  • list_eventsRead onlyGET /api/events

    List events, filterable by venue, status, category, search term, or upcoming-only.

  • get_eventRead onlyGET /api/events/:eventId

    Read one event: schedule, venue, capacity, ticketing and whether it is published.

  • list_event_registrationsRead onlyGET /api/events/:eventId/registrations

    List who has registered for an event, with party sizes and check-in state. This answers "how many are coming to the quiz".

Create, edit and publish events

event:write
  • create_eventChanges dataPOST /api/events

    Create an event at a venue. It starts as a draft and is not public until publish_event runs.

  • update_eventChanges dataPATCH /api/events/:eventId

    Change an event: title, description, dates, capacity, venue details, category or ticket links. Only the fields sent are written. Changes to a published event are live on the public page immediately.

  • publish_eventChanges dataPOST /api/events/:eventId/publish

    Publish a draft event so it appears on the venue's public page and starts taking registrations.

  • create_event_categoryChanges dataPOST /api/event-categories

    Create a category for the programme — live music, quiz, private hire — with a URL slug. It is on no event until create_event or update_event names its id. Slugs are unique within the organization.

  • update_event_categoryChanges dataPATCH /api/event-categories/:categoryId

    Rename a programme category or change its slug, description, colour, image or order. The change lands on every event filed under it, and a new slug changes the category’s public address.

Read the guest book

contact:read
  • list_contactsRead onlyGET /api/contacts

    Search guests by name, email or phone, optionally within one contact group. Use it to resolve a name to a contactId.

  • get_contactRead onlyGET /api/contacts/:contactId

    Read one guest: contact details, consent state, and the email and phone endpoints they can be reached on.

  • list_contact_groupsRead onlyGET /api/contact-groups

    List contact groups (segments), static and rule-based, with member counts. Use it to find the groupId for add_contact_to_group.

Add, correct and file guests

contact:write
  • create_contactChanges dataPOST /api/contacts

    Add a guest to the contact book. Creating a contact is not a marketing opt-in — consent is recorded separately.

  • update_contactChanges dataPATCH /api/contacts/:contactId

    Correct a guest’s details: name, email, phone, tags or custom fields. Only the fields sent are written, and at least one of email, phone or an external id must remain. This cannot change their marketing consent.

  • add_contact_to_groupChanges dataPOST /api/contact-groups/:groupId/members

    Put a guest into a contact group. Groups are how this product tags people — there is no separate tag. Adding somebody who is already in it changes nothing.

Create and retire guest groups

contact:write
  • create_contact_groupChanges dataPOST /api/contact-groups

    Create a contact group (a segment) to file guests into. Names are unique within the organization. Leave definition out for a hand-picked list; a definition makes it a rule-based group the platform recomputes.

  • update_contact_groupChanges dataPATCH /api/contact-groups/:groupId

    Rename a contact group or change its description, colour or matching rules. Changing the rules of a rule-based group changes who a campaign addressed to it will reach.

  • archive_contact_groupChanges dataPOST /api/contact-groups/:groupId/archive

    Take a contact group out of use: it stops appearing in the ordinary listing and is filed as archived rather than deleted, keeping every membership. unarchive_contact_group brings it back. There is no delete, by design.

  • unarchive_contact_groupChanges dataPOST /api/contact-groups/:groupId/unarchive

    Put an archived contact group back into use. Refused if it is not archived, so this cannot be used to check whether one is.

Read visits and loyalty

loyalty:read
  • list_visitsRead onlyGET /api/loyalty/visits

    Visit history: recorded guest check-ins, filterable by guest, venue, status and date range. This answers "when was she last in".

Read the rota

staff:read
  • list_staff_membersRead onlyGET /api/staff-members

    List the people on a venue's team, with their roles and employment state.

  • list_staff_shiftsRead onlyGET /api/staff-shifts

    The rota over a date range: who is on, when, in what role, and which shifts are still open. Requires from and to dates.

  • get_staff_coverage_dayRead onlyGET /api/staff-coverage/day

    Coverage for one venue on one date: rostered hours against expected demand, hour by hour, so a gap is visible. This answers "are we short on Saturday".

  • get_staff_publish_impactRead onlyGET /api/staff-shifts/publish-impact

    What publishing the rota for a venue over a date range would take off sale: with requireStaffShiftCoverage on, an unpublished shift leaves a resource bookable and a published one constrains it. Read this before publishing.

Fill and publish the rota

staff:write
  • create_staff_shiftChanges dataPOST /api/staff-shifts

    File a shift on the rota at a venue: date, start and end in the venue’s own wall clock, optional role and break. Leave staffMemberId out for an open shift nobody is assigned to. It is a draft until published.

  • update_staff_shiftChanges dataPATCH /api/staff-shifts/:shiftId

    Move a shift, reassign it, edit its role or break, or send status published to put it on the rota and draft to take it off. Where the venue requires shift cover, publishing changes what guests can book — read get_staff_publish_impact first.

Read the reports, including money

analytics:read
  • get_booking_analyticsRead onlyGET /api/booking-analytics/summary

    Booking trend, capacity use, no-show rate, revenue and margin over a date range. The general "how did we do" number.

  • get_covers_reportRead onlyGET /api/booking-reporting/covers

    Booked covers by day and service type over a date range.

  • get_labour_cost_reportRead onlyGET /api/staff-reporting/labour-cost

    Rostered labour cost against booked covers for one venue over a date range, including cost per cover.

See what this key itself has done

agent:read
  • list_agent_actionsRead onlyGET /api/agent-actions

    List what THIS key has already done — its own changes and reads, newest first, with the id of each one and whether it can still be undone. Call it to check whether a write landed after a timeout, or before reporting back what you changed.

  • get_agent_action_revert_planRead onlyGET /api/agent-actions/:agentActionId/revert-plan

    Ask what undoing one of your own actions would do, and what it would refuse, without undoing anything. Call it before revert_agent_action when the change is not one you made seconds ago.

Let it take back its own recent changes

agent:write
  • revert_agent_actionChanges dataPOST /api/agent-actions/:agentActionId/revert

    Undo one change YOU made, named by its id from list_agent_actions. Refused if it was not yours, was already undone, is older than an hour, or the record has changed since — the refusal says which.

  • revert_last_agent_actionChanges dataPOST /api/agent-actions/revert-last

    Undo the last change you made — this conversation only, when your client sends a session id. Pass expectedTool to be refused rather than undo the wrong thing if the ledger disagrees with what you think you did last.