---
title: "MCP tools — all 60, by domain"
description: "Every HyperCRM MCP tool: its arguments, whether it reads or writes, whether it can delete data or reach outside HyperCRM, and which role may call it."
canonical: "https://hypercrm.app/docs/mcp/tools"
lang: "en"
updated: "2026-07-26"
---

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

[Start the free trial](/dashboard) · [Back to MCP reference](/docs/mcp)

> **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` | `search`, `status`, `tagId`, `limit`, `offset` | Read-only, Any member | List patients in the practice, with optional search, status and tag filters. |
| `get_patient` | **`patientId`** | Read-only, Any member | Read a single patient by id. |
| `list_patient_notes` | **`patientId`** | Read-only, Any member | List every clinical note recorded against a patient. |
| `list_patient_files` | **`patientId`** | Read-only, Any member | List files attached to a patient. Use get_file for a download URL. |
| `list_patient_billing_entries` | **`patientId`** | Read-only, Any 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-only, Any member | List the invoices already issued for a patient. |
| `get_patient_invoice_preview` | **`patientId`** | Read-only, Any 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` | **`action`**, `patientId`, `firstName`, `lastName`, `dob`, `gender`, `email`, `phoneMobile`, `address`, `postcode`, `country`, `status`, `notifyByEmail`, `notifyBySms` | Writes, Can delete, Any member | Create, update or delete a patient. action=create requires firstName and lastName; update and delete require patientId. Deleting is permanent. |
| `write_patient_note` | **`action`**, **`patientId`**, `noteId`, `title`, `body`, `isNext` | Writes, Can delete, Any member | Create, update or delete a note on a patient. action=create requires body; update and delete require noteId. |
| `toggle_patient_tag` | **`patientId`**, **`tagId`** | Writes, Any 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` | **`from`**, **`to`**, `chairId` | Read-only, Any member | List appointments in a time window. Pass chairId to restrict to one chair. |
| `get_appointment` | **`appointmentId`** | Read-only, Any member | Read a single appointment by id. |
| `get_appointment_availability` | **`chairId`**, **`date`**, **`durationMinutes`** | Read-only, Any member | Compute the free slots on a chair for one calendar day, honouring the practice's opening hours, timezone and existing bookings. |
| `write_appointment` | **`action`**, `appointmentId`, `chairId`, `patientId`, `type`, `title`, `startAt`, `endAt`, `color`, `serviceRef`, `notes` | Writes, Can delete, Any 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` | `from`, `to` | Read-only, Owner 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-only, Any member | Read a single billing entry by id. |
| `get_invoice` | **`invoiceId`** | Read-only, Any member | Read an issued invoice and its frozen line items. Use get_patient_invoice_preview for the unissued draft. |
| `get_appointment_charge` | **`appointmentId`** | Read-only, Any 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` | **`action`**, `entryId`, `patientId`, `description`, `amount`, `quantity`, `serviceId`, `occurredAt`, `voidReason` | Writes, Can delete, Any 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` | **`action`**, `patientId`, `invoiceId`, `voidReason` | Writes, Can delete, Any 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-only, Owner only | List the practice's intake and consent forms. |
| `get_form` | **`formId`** | Read-only, Owner only | Read one form with its field definitions. |
| `list_form_submissions` | **`formId`** | Read-only, Owner only | List every submission for a form, newest first. |
| `get_form_submission` | **`formId`**, **`submissionId`** | Read-only, Owner only | Read a single submission and its answers. |
| `write_form` | **`action`**, `formId`, `patientId`, `title`, `description`, `isActive`, `fields` | Writes, Can delete, Reaches outside, Owner 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-only, Any member | Read a file's metadata with a short-lived signed download URL. Use list_patient_files to find ids. |
| `delete_file` | **`fileId`** | Writes, Can delete, Any 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-only, Any member | Headline figures — today's appointments, outstanding balances, recent activity counts. |
| `get_appointments_report` | `from`, `to`, `groupBy` | Read-only, Owner only | Appointment counts bucketed over a date range. groupBy is day, week or month. |
| `get_revenue_report` | `from`, `to`, `groupBy` | Read-only, Owner only | Revenue totals bucketed over a date range. groupBy is day, week or month. |
| `get_patient_acquisition_report` | `from`, `to`, `groupBy` | Read-only, Owner only | New-patient counts bucketed over a date range. groupBy is day, week or month. |
| `list_audit_log` | `type`, `from`, `to`, `limit`, `offset` | Read-only, Owner only | Read the practice audit trail, newest first. Filter by entity type and a date range. |
| `search` | **`q`**, `limit` | Read-only, Any 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` | `unreadOnly`, `limit`, `offset` | Read-only, Any member | List your notifications, newest first, with the unread count. Pass unreadOnly=true to skip read entries. |
| `mark_notifications_read` | **`action`**, `notificationId` | Writes, Any 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-only, Any member | Read the practice profile and settings. |
| `get_me` | None | Read-only, Any member | Read the calling user, their practice, their membership role and the practice's vertical pack. |
| `list_practice_members` | None | Read-only, Any member | List staff members and their roles. |
| `list_invitations` | None | Read-only, Owner only | List pending staff invitations. |
| `update_practice` | `name`, `address`, `phone`, `email`, `website`, `country`, `currency`, `packId`, `workTimeStart`, `workTimeEnd`, `calendarSlotMinutes`, `reminderLeadMinutes`, `taxPercentage`, `includeTaxInInvoices`, `timezone`, `mfaRequired` | Writes, Owner only | Update the practice. Profile fields and settings fields may be mixed in one call; any unrecognized field is rejected. |
| `update_me` | **`locale`** | Writes, Any member | Update your own preferences. Only the UI locale is settable. |
| `report_bug` | **`description`**, `expected`, `pagePath` | Writes, Any 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` | **`action`**, **`membershipId`**, `role` | Writes, Owner 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` | **`action`**, `invitationId`, `email`, `role` | Writes, Can delete, Reaches outside, Owner 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-only, Any member | Read the site with its pages, modules and FAQs. Returns null if no site has been created. |
| `list_site_page_modules` | **`pageId`** | Read-only, Any member | Read the content modules and FAQs of one page. |
| `check_site_slug_available` | **`slug`** | Read-only, Owner only | Check whether a slug is valid and free. Returns availability plus a reason when it is not. |
| `write_site` | **`action`**, `slug`, `title`, `description`, `themeId`, `isPublished` | Writes, Can delete, Reaches outside, Owner 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` | **`pageId`**, `title`, `slug`, `metaTitle`, `metaDescription`, `isPublished` | Writes, Owner only | Edit one page's metadata — title, slug, SEO. |
| `write_site_module` | **`action`**, `pageId`, `moduleId`, `type`, `position`, `content` | Writes, Can delete, Owner 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` | **`action`**, `pageId`, `faqId`, `question`, `answer`, `position` | Writes, Can delete, Owner only | action=create adds an FAQ to a page (requires pageId); update and delete require faqId. |
| `list_blog_posts` | None | Read-only, Any member | List the practice's blog posts — drafts and published alike — with their slugs, status and bodies. |
| `list_blog_categories` | None | Read-only, Any 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` | **`action`**, `postId`, `title`, `slug`, `excerpt`, `body`, `coverFileId`, `categoryId`, `authorTeamMemberId`, `status`, `metaTitle`, `metaDescription` | Writes, Can delete, Owner 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-only, Any member | List the treatment chairs (bookable resources). |
| `list_services` | `isPublic` | Read-only, Any member | List the service catalogue. Pass isPublic=true for services offered on the public booking page. |
| `list_tags` | None | Read-only, Any member | List all tags defined for the practice. |
| `write_chair` | **`action`**, `chairId`, `name`, `color`, `isActive` | Writes, Can delete, Owner only | Create, update or delete a treatment chair. update and delete require chairId. |
| `write_service` | **`action`**, `serviceId`, `name`, `description`, `durationMinutes`, `price`, `isPublic`, `isActive` | Writes, Can delete, Owner 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` | **`action`**, `tagId`, `name`, `color` | Writes, Can delete, Any 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

### How is patient data protected?

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.

### Why do the write tools take an action argument instead of being separate?

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.

### Which tools should my client always confirm before calling?

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.

### Are form tools really owner-only, even the reads?

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.