Documentation / MCP
All 60 tools
Grouped by domain. Bold arguments are required. Every tool is marked with what it can do — read, write, delete, or reach outside HyperCRM — and with the role needed to call it.
Reads are one tool each; writes are grouped
Every read is its own tool, so a model picking one has an obvious choice. The create, update and delete surface of a resource collapses into a single write_* tool with a required action argument — which keeps the tool list small enough to reason about and each write coarse enough to carry a meaningful "can delete" warning.
How to read this
38 of the 60 tools are read-only and safe to call speculatively. 15 can delete or void data. Four reach outside HyperCRM and deserve a confirmation prompt in any client: sending an invitation email, publishing the website to the public internet, and minting a public form link.
practiceId is never an argument — it comes from the token. Where a tool takes an action, the action decides which other arguments are required; the description says which.
Patients
The patient record, its notes, and the sub-resources hanging off it.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| list_patients | searchstatustagIdlimitoffset | Read-onlyAny member | List patients in the practice, with optional search, status and tag filters. |
| get_patient | patientId | Read-onlyAny member | Read a single patient by id. |
| list_patient_notes | patientId | Read-onlyAny member | List every clinical note recorded against a patient. |
| list_patient_files | patientId | Read-onlyAny member | List files attached to a patient. Use get_file for a download URL. |
| list_patient_billing_entries | patientId | Read-onlyAny member | List the billing entries recorded against a patient, with balance: what the patient owes now, the same figure as the ledger on the patient's page. Quote it rather than adding entries up. A receipt entry is a payment, not a document. |
| list_patient_invoices | patientId | Read-onlyAny member | List the invoices already issued for a patient. |
| get_patient_invoice_preview | patientId | Read-onlyAny member | Compute the draft invoice for a patient's unbilled entries without issuing it. Use write_invoice with action=issue to issue one. |
| write_patient | actionpatientIdfirstNamelastNamedobgenderemailphoneMobileaddresspostcodecountrystatusnotifyByEmailnotifyBySms | WritesCan deleteAny member | Create, update or delete a patient. action=create requires firstName and lastName; update and delete require patientId. Deleting is permanent. |
| write_patient_note | actionpatientIdnoteIdtitlebodyisNext | WritesCan deleteAny member | Create, update or delete a note on a patient. action=create requires body; update and delete require noteId. |
| toggle_patient_tag | patientIdtagId | WritesAny member | Add the tag to the patient if absent, remove it if already present. |
Appointments
Bookings, chair-blocking entries, and derived availability.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| list_appointments | fromtochairId | Read-onlyAny member | List appointments in a time window. Pass chairId to restrict to one chair. |
| get_appointment | appointmentId | Read-onlyAny member | Read a single appointment by id. |
| get_appointment_availability | chairIddatedurationMinutes | Read-onlyAny member | Compute the free slots on a chair for one calendar day, honouring the practice's opening hours, timezone and existing bookings. |
| write_appointment | actionappointmentIdchairIdpatientIdtypetitlestartAtendAtcolorserviceRefnotes | 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. Overlapping bookings on the same chair are rejected. |
Billing and invoices
The ledger of billable entries, and the invoices issued from it.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| list_billing_entries | fromto | Read-onlyOwner only | List billing entries across the whole practice, optionally bounded by a date range (the practice's own days, both included). Use list_patient_billing_entries for one patient, and for what a patient owes. |
| get_billing_entry | entryId | Read-onlyAny member | Read a single billing entry by id. |
| get_invoice | invoiceId | Read-onlyAny member | Read an issued invoice and its frozen line items. Use get_patient_invoice_preview for the unissued draft. |
| get_appointment_charge | appointmentId | Read-onlyAny member | Read what charging an appointment would take: whether it can be charged, and the catalog service matching it, with its id and price. Use it before charging an appointment with write_billing_entry. |
| write_billing_entry | actionentryIdpatientIddescriptionamountquantityserviceIdoccurredAtvoidReason | WritesCan deleteAny member | Create, update or void a billing entry. action=create requires patientId; update and void require entryId. Voiding is a reversal, not a delete — the entry stays on the ledger marked void. A receipt entry records a payment and gets a payment number (HCRM-0001); it is not a document. Receipts and tax invoices are issued with write_invoice. |
| write_invoice | actionpatientIdinvoiceIdvoidReason | WritesCan deleteAny member | Issue an invoice for a patient's unbilled entries (action=issue, requires patientId) or void an issued one (action=void, requires invoiceId). Issuing freezes the current line items. |
Forms
Intake and consent forms. Every tool here is owner-only, reads included, because submissions carry clinical answers.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| list_forms | None | Read-onlyOwner only | List the practice's intake and consent forms. |
| get_form | formId | Read-onlyOwner only | Read one form with its field definitions. |
| list_form_submissions | formId | Read-onlyOwner only | List every submission for a form, newest first. |
| get_form_submission | formIdsubmissionId | Read-onlyOwner only | Read a single submission and its answers. |
| write_form | actionformIdpatientIdtitledescriptionisActivefields | WritesCan deleteReaches outsideOwner only | action=create builds a form; update edits metadata; delete removes it; replace_fields swaps the whole field list; create_link mints a PUBLIC single-use link that expires in 3 days (optionally bound to a patient, whose record the answers then overwrite); seed_defaults installs the pack's templates. |
Files
Uploads are browser-to-storage and have no tool; these read and remove what is already stored.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| get_file | fileId | Read-onlyAny member | Read a file's metadata with a short-lived signed download URL. Use list_patient_files to find ids. |
| delete_file | fileId | WritesCan deleteAny member | Permanently delete a file. |
Analytics and search
Aggregates and cross-entity search. The three reports and the audit log are owner-only.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| get_dashboard_kpis | None | Read-onlyAny member | Headline figures — today's appointments, outstanding balances, recent activity counts. |
| get_appointments_report | fromtogroupBy | Read-onlyOwner only | Appointment counts bucketed over a date range. groupBy is day, week or month. |
| get_revenue_report | fromtogroupBy | Read-onlyOwner only | Revenue totals bucketed over a date range. groupBy is day, week or month. |
| get_patient_acquisition_report | fromtogroupBy | Read-onlyOwner only | New-patient counts bucketed over a date range. groupBy is day, week or month. |
| list_audit_log | typefromtolimitoffset | Read-onlyOwner only | Read the practice audit trail, newest first. Filter by entity type and a date range. |
| search | qlimit | Read-onlyAny member | Cross-entity search over the practice — patients, appointments and more. |
Notifications
The calling user's own inbox. Not practice-wide.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| list_notifications | unreadOnlylimitoffset | Read-onlyAny member | List your notifications, newest first, with the unread count. Pass unreadOnly=true to skip read entries. |
| mark_notifications_read | actionnotificationId | WritesAny member | Mark one notification read (action=one, requires notificationId) or the whole inbox (action=all). |
Practice and people
The practice profile, its members, and staff invitations.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| get_practice | None | Read-onlyAny member | Read the practice profile and settings. |
| get_me | None | Read-onlyAny member | Read the calling user, their practice, their membership role and the practice's vertical pack. |
| list_practice_members | None | Read-onlyAny member | List staff members and their roles. |
| list_invitations | None | Read-onlyOwner only | List pending staff invitations. |
| update_practice | nameaddressphoneemailwebsitecountrycurrencypackIdworkTimeStartworkTimeEndcalendarSlotMinutesreminderLeadMinutestaxPercentageincludeTaxInInvoicestimezonemfaRequired | WritesOwner only | Update the practice. Profile fields and settings fields may be mixed in one call; any unrecognized field is rejected. |
| update_me | locale | WritesAny member | Update your own preferences. Only the UI locale is settable. |
| report_bug | descriptionexpectedpagePath | WritesAny member | Report a bug in HyperCRM itself. The report is saved and emailed to the HyperCRM team, with who sent it attached. Up to 5 per user per hour. Leave out patient details. |
| write_practice_member | actionmembershipIdrole | WritesOwner only | action=update_role sets a membership to owner or assistant; action=remove takes the member off the practice. Both are rejected if they would leave the practice with zero owners. |
| write_invitation | actioninvitationIdemailrole | WritesCan deleteReaches outsideOwner only | action=create SENDS A REAL EMAIL with a single-use join link that expires in 3 days; action=revoke deletes a pending invitation. |
Website builder
The practice's public marketing site. Writes affect a draft until published; publishing is the one tool here that changes what the public sees.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| get_site | None | Read-onlyAny member | Read the site with its pages, modules and FAQs. Returns null if no site has been created. |
| list_site_page_modules | pageId | Read-onlyAny member | Read the content modules and FAQs of one page. |
| check_site_slug_available | slug | Read-onlyOwner only | Check whether a slug is valid and free. Returns availability plus a reason when it is not. |
| write_site | actionslugtitledescriptionthemeIdisPublished | WritesCan deleteReaches outsideOwner only | action=create sets up the site; update edits settings; publish PUSHES THE CURRENT DRAFT LIVE to the public internet, immediately visible to anyone. |
| update_site_page | pageIdtitleslugmetaTitlemetaDescriptionisPublished | WritesOwner only | Edit one page's metadata — title, slug, SEO. |
| write_site_module | actionpageIdmoduleIdtypepositioncontent | WritesCan deleteOwner only | action=create adds a content module to a page (requires pageId); update and delete require moduleId. Changes affect the draft until the site is published. |
| write_site_faq | actionpageIdfaqIdquestionanswerposition | WritesCan deleteOwner only | action=create adds an FAQ to a page (requires pageId); update and delete require faqId. |
| list_blog_posts | None | Read-onlyAny member | List the practice's blog posts — drafts and published alike — with their slugs, status and bodies. |
| list_blog_categories | None | Read-onlyAny member | List the categories a post can be filed under. Call this before writing a post if you intend to set one: the id must belong to this site. |
| write_blog_post | actionpostIdtitleslugexcerptbodycoverFileIdcategoryIdauthorTeamMemberIdstatusmetaTitlemetaDescription | WritesCan deleteOwner only | Write, edit or remove a blog post. create needs title, slug and body; update and delete need postId. A post is created as a DRAFT, and changing its status alone does not put it on the live site — the site itself has to be published. |
Catalogue
Chairs, services and tags — the lists the rest of the product picks from.
| Tool | Arguments | Access | What it does |
|---|---|---|---|
| list_chairs | None | Read-onlyAny member | List the treatment chairs (bookable resources). |
| list_services | isPublic | Read-onlyAny member | List the service catalogue. Pass isPublic=true for services offered on the public booking page. |
| list_tags | None | Read-onlyAny member | List all tags defined for the practice. |
| write_chair | actionchairIdnamecolorisActive | WritesCan deleteOwner only | Create, update or delete a treatment chair. update and delete require chairId. |
| write_service | actionserviceIdnamedescriptiondurationMinutespriceisPublicisActive | WritesCan deleteOwner only | Create, update or delete a service, or seed the vertical pack's default catalogue with action=seed_defaults. update and delete require serviceId. |
| write_tag | actiontagIdnamecolor | WritesCan deleteAny member | Create, update or delete a patient tag. update and delete require tagId. Deleting a tag removes it from every patient carrying it. |
Common questions
Patient records are encrypted in transit and at rest, files are stored privately and served through short-lived signed links, and every change is written to an audit log. Staff accounts sign in with passkeys rather than shared passwords.
So the tool list stays small enough for a model to reason about, and so each write tool is coarse enough to carry a meaningful warning. A client can tell that write_patient may delete something and prompt accordingly; twelve narrow tools would each need their own annotation and would crowd out the reads.
The four marked as reaching outside HyperCRM — creating an invitation sends a real email, publishing the website makes it public immediately, and creating a form link mints a URL anyone holding it can open. The fifteen marked as able to delete are worth confirming too, particularly deleting a patient, which is permanent.
Yes. Form submissions carry clinical answers, so the whole domain sits behind the owner role rather than just its writes. That mirrors the REST API exactly — MCP is not a route around a permission the HTTP surface enforces.