Skip to content

MCP reference / Appointments

Appointment tools

Six tools covering bookings, chair-blocking entries and computed availability. Overlapping bookings on the same chair are rejected rather than silently accepted.

How to read this

Each tool below lists its arguments exactly as the input schema declares them: the type, whether it is required, the permitted values where the schema fixes them, and what the schema says the argument means. None of it is written here. An amount in cents, a default that applies when a field is left out, which action needs which id: those sentences come from the tool itself.

practiceId is never an argument. It comes from your token, and the write paths strip it if a client sends one anyway.

A tool that writes shows one example per action, because what an action needs is not what the input schema marks required: the schema requires action and leaves the rest to the action. Every example is a complete tools/call request whose arguments are checked, when this page is built, against the same object the handler parses. Errors come back inside a 200 response with isError set: the MCP overview has that envelope, and the full catalogue lists every tool at a glance.

Appointments

Bookings, chair-blocking entries, and derived availability.

get_day_agenda

Read-onlyAny member

The diary for a day or up to 7 days, by name: live bookings with patient, service, chair and practitioner, blocked time apart, counts, and whether the practice is closed.

Arguments

ToolTypeRequired?What it means
datestringOptionalYYYY-MM-DD on the practice's clock. Defaults to today.
tostringOptionalLast day, for a range of up to 7 days.
practitionerstringOptionalA name, as the practice said it.
chairstringOptionalA name, as the practice said it.
patientstringOptionalA name, as the practice said it.
servicestringOptionalA name, as the practice said it.
timestringOptionalHH:mm on the practice's clock, only when one was named: the bookings that start then.
locationIdstringOptionalOnly this location: an id from list_locations, only when the practice named a location.

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_day_agenda",
    "arguments": {}
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

find_booking_slots

Read-onlyAny member

When a booking fits, by name: a verdict on a requested time, free times spread over the day with the chairs they are free on and a ready booking draft, and the patient's own bookings that day. An ambiguous name is answered with the candidates, never a guess.

Arguments

ToolTypeRequired?What it means
datestringRequiredYYYY-MM-DD on the practice's clock.
timestringOptionalHH:mm on the practice's clock, only when one was named.
partOfDaystring: morning | afternoon | eveningOptionalOnly when named. Morning ends 12:00, evening starts 17:00.
daysintegerOptionalDays from date to look at. Defaults to 1.
patientstringOptionalA name, as the practice said it.
servicestringOptionalA name, as the practice said it. Its own length is used.
practitionerstringOptionalA name, as the practice said it.
chairstringOptionalA name, as the practice said it.
locationIdstringOptionalOnly this location: an id from list_locations, only when the practice named a location.

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "find_booking_slots",
    "arguments": {
      "date": "2026-11-02"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

list_appointments

Read-onlyAny member

List appointments in a time window. Pass chairId to restrict to one chair.

Arguments

ToolTypeRequired?What it means
fromstringRequiredStart of the window. ISO-8601 datetime with an explicit offset or Z, e.g. "2026-07-08T09:00:00Z" or "2026-07-08T12:00:00+03:00". A datetime without one is rejected — do not send "2026-07-08T09:00:00". A time a person says is on the practice's clock (get_practice → settings.timezone): send it with that zone's offset for that date, not as Z.
tostringRequiredEnd of the window. ISO-8601 datetime with an explicit offset or Z, e.g. "2026-07-08T09:00:00Z" or "2026-07-08T12:00:00+03:00". A datetime without one is rejected — do not send "2026-07-08T09:00:00". A time a person says is on the practice's clock (get_practice → settings.timezone): send it with that zone's offset for that date, not as Z.
chairIdstringOptionalRestrict to one chair. Use list_chairs for ids.
practitionerIdstringOptionalRestrict to one practitioner's appointments. Use list_practice_members for ids.
patientIdstringOptionalRestrict to one patient's appointments. This is how a question about one person's bookings is answered — searching their name by text also matches names that contain it.

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_appointments",
    "arguments": {
      "from": "2026-11-09T09:00:00.000Z",
      "to": "2026-11-09T09:00:00.000Z"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

get_appointment

Read-onlyAny member

Read a single appointment by id.

Arguments

ToolTypeRequired?What it means
appointmentIdstringRequired

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_appointment",
    "arguments": {
      "appointmentId": "d4398f97-d439-4c3a-9e21-7b84c5d10f33"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

get_appointment_availability

Read-onlyAny member

Free times for one calendar day on every chair, or on one chair or for one person, honouring the practice's opening hours, timezone and existing bookings. Times that have passed are left out, and a requested time gets a verdict.

Arguments

ToolTypeRequired?What it means
datestringRequiredThe calendar day to check, YYYY-MM-DD.
timestringOptionalA time the practice asked about, HH:mm on its clock. Send it only when the practice named a time.
partOfDaystring: morning | afternoon | eveningOptionalOnly times starting in this part of the day, by the server's bounds: morning before 12:00, afternoon 12:00 to 17:00, evening from 17:00. Send it only when the practice named one.
chairIdstringOptionalOnly this chair. Send it only when the practice named a chair.
practitionerIdstringOptionalOnly times this person is free (a practice-member id, from list_practice_members), on any chair that is free too. Send it only when the practice named the person.
serviceIdstringOptionalThe service being booked; its own length is used. Send this whenever the caller named a treatment.
durationMinutesintegerOptionalLength of the booking when no service says it: moving a booking keeps the length it already has. Overrides serviceId. Defaults to 30 when neither is given.

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_appointment_availability",
    "arguments": {
      "date": "2026-11-02"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

write_appointment

WritesCan deleteAny member

Create, update or cancel an appointment. action=create requires chairId, startAt and endAt; update and cancel require appointmentId. type is appointment, reminder or unavailable; blocked time with no chair and no practitioner may name one locationId to close only that location. Overlapping bookings on the same chair are rejected.

Arguments

ToolTypeRequired?What it means
actionstring: create | update | confirm | decline | cancelRequired
appointmentIdstringOptionalThe appointment to update, confirm, decline or cancel.
expectedDatestringOptionalRequired for action=update, decline and cancel. The calendar day (YYYY-MM-DD, the practice's clock) of the booking named in appointmentId, as you just described it to the user — "her Thursday appointment" means the date of that Thursday. It is checked against the booking: if they disagree, nothing happens and the answer says which day the booking is really on. Send the day you told the user, not the day you infer from the id.
typestring: appointment | reminder | unavailableOptionalRequired for action=create. appointment = a patient booking; reminder = a note on the calendar; unavailable = blocked-out time.
datestringOptionalThe day of the booking, YYYY-MM-DD, on the practice's clock. Send this with startTime and endTime and you do not have to work out any offset — the server does it. This is the easy way and the one to use for a time somebody said out loud.
startTimestringOptionalWhen it starts, HH:MM on the practice's clock — 10:00 means ten in the morning there. Needs date. This WINS over startAt if both are sent, because it is the time the practice was shown.
endTimestringOptionalWhen it ends, HH:MM on the practice's clock. Needs date. This wins over endAt if both are sent.
startAtstringOptionalWhen it starts, as an exact instant. Use this ONLY for a value a tool handed you — the startAt of a free slot — and then send NOTHING else about the time. For a time somebody said, send date and startTime and no startAt: converting it yourself is how a 10:00 booking got stored at 07:00. ISO-8601 datetime with an explicit offset or Z, e.g. "2026-07-08T09:00:00Z" or "2026-07-08T12:00:00+03:00". A datetime without one is rejected — do not send "2026-07-08T09:00:00". A time a person says is on the practice's clock (get_practice → settings.timezone): send it with that zone's offset for that date, not as Z.
endAtstringOptionalWhen it ends, as an exact instant. For a time somebody said, send date and endTime instead. ISO-8601 datetime with an explicit offset or Z, e.g. "2026-07-08T09:00:00Z" or "2026-07-08T12:00:00+03:00". A datetime without one is rejected — do not send "2026-07-08T09:00:00". A time a person says is on the practice's clock (get_practice → settings.timezone): send it with that zone's offset for that date, not as Z.
chairIdstringOptionalThe chair being booked. Use list_chairs for ids. Required on a booking when the practice has more than one chair.
patientIdstringOptionalThe patient being seen, if there is one.
practitionerIdstringOptionalThe practitioner the appointment is with — a practice-member id, from list_practice_members. Optional: unassigned booking requests have none. For type=unavailable it is the person who is away (a seminar, leave): send it only when the practice named that person, and that person's free times then leave the block out. Leave it out when nobody in particular is away. Assigning someone to a type=appointment marks them as one of the practice's practitioners; blocked time never does.
titlestringOptionalCalendar label, only when the user dictated one. Omitted, the calendar shows the patient and the service. A title that only repeats names already on the booking is not written (see ignoredFields in the answer).
colorstringOptionalOptional calendar colour override. Defaults to the chair's colour; sending the chair's own colour is not written.
serviceRefstringOptionalOptional free-text reference to the service being performed.
notesstringOptionalOptional free-text notes on the booking.
locationIdstring | nullOptionalBlocked time with no chair and no practitioner only: the location it closes, an id from list_locations, only when one was named. Omitted, the whole practice closes.

Example call

action=create

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_appointment",
    "arguments": {
      "action": "create",
      "type": "appointment",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "startAt": "2026-11-09T09:00:00.000Z",
      "endAt": "2026-11-09T09:30:00.000Z",
      "chairId": "6f708192-2d3e-4f80-9b4c-5d6e7f8091a2"
    }
  }
}

action=update

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_appointment",
    "arguments": {
      "action": "update",
      "appointmentId": "2b3c4d5e-8f9a-4b6c-9d0e-1f2a3b4c5d6e",
      "expectedDate": "2026-11-09",
      "startAt": "2026-11-09T10:00:00.000Z",
      "endAt": "2026-11-09T10:30:00.000Z"
    }
  }
}

action=confirm

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_appointment",
    "arguments": {
      "action": "confirm",
      "appointmentId": "2b3c4d5e-8f9a-4b6c-9d0e-1f2a3b4c5d6e",
      "chairId": "6f708192-2d3e-4f80-9b4c-5d6e7f8091a2"
    }
  }
}

action=decline

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_appointment",
    "arguments": {
      "action": "decline",
      "appointmentId": "2b3c4d5e-8f9a-4b6c-9d0e-1f2a3b4c5d6e",
      "expectedDate": "2026-11-09"
    }
  }
}

action=cancel

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_appointment",
    "arguments": {
      "action": "cancel",
      "appointmentId": "2b3c4d5e-8f9a-4b6c-9d0e-1f2a3b4c5d6e",
      "expectedDate": "2026-11-09"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the written record" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
conflict409The chair or the practitioner is already booked for part of that time, or the booking is not in a state this action can take it from (a cancelled booking cannot be confirmed).
plan_feature_required403The practice's plan does not include this. MCP needs Complete.
subscription_read_only403The subscription has lapsed, so the practice is read-only and nothing may be written.

Common questions

Patient records are encrypted, both where they are stored and on their way to your screen. Files are private, and a link to one stops working after a short time. Changes to patient records are recorded, with who made them and when. There is no shared practice password: everyone has their own account. On the Complete plan, the practice owner can also require two-step sign-in for the whole team.

Yes. The practice owner can export the patient list, the reports and the activity log to a spreadsheet file (CSV) from the app, and anyone in the practice can download its files at any time. Your practice data belongs to you, and there is no export fee or waiting period. That holds on either plan, during the trial, and after you cancel.

HyperCRM is paid software. Every practice starts with a 30-day free trial of the Complete plan, with no card needed. After that, Essential starts at €19 a month and Complete at €34, VAT included. The price depends on how many practitioners you have. Every price is on the pricing page, so you never need a sales call.