MCP reference / Forms
Form tools
Five tools covering intake and consent forms and their submissions. Reading a form, reading the answers and sending a form to a patient are open to any practice member. Building a form needs the owner's role.
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.
Forms
Intake and consent forms. Reading a form, reading the answers and sending a form to a patient are open to any practice member. Building a form is the owner's.
list_forms
List the practice's intake and consent forms.
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_forms",
"arguments": {}
}
}Result
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"structuredContent": { "...": "the requested records" },
"content": [{ "type": "text", "text": "<the same JSON>" }]
}
}Errors
| Code | Status | When |
|---|---|---|
| plan_feature_required | 403 | The practice's plan does not include this. MCP needs Complete. |
get_form
Read one form with its field definitions.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| formId | string | Required |
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_form",
"arguments": {
"formId": "b962ee2a-b962-4c3a-9e21-7b84c5d10f33"
}
}
}Result
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"structuredContent": { "...": "the requested records" },
"content": [{ "type": "text", "text": "<the same JSON>" }]
}
}Errors
| Code | Status | When |
|---|---|---|
| validation_failed | 400 | An argument is missing, of the wrong type, or outside its allowed values. |
| not_found | 404 | The id does not exist in your practice. A malformed id looks identical. |
| plan_feature_required | 403 | The practice's plan does not include this. MCP needs Complete. |
list_form_submissions
List one page of a form's submissions, newest first, with the total across all pages.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| formId | string | Required | |
| status | string: all | submitted | pending | Optional | submitted = completed forms, pending = links not yet filled in, all = both (default). |
| page | integer | Optional | 1-based page number. Default 1. |
| pageSize | integer | Optional | Submissions per page. Default 25, maximum 100. |
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_form_submissions",
"arguments": {
"formId": "b962ee2a-b962-4c3a-9e21-7b84c5d10f33"
}
}
}Result
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"structuredContent": { "...": "the requested records" },
"content": [{ "type": "text", "text": "<the same JSON>" }]
}
}Errors
| Code | Status | When |
|---|---|---|
| validation_failed | 400 | An argument is missing, of the wrong type, or outside its allowed values. |
| not_found | 404 | The id does not exist in your practice. A malformed id looks identical. |
| plan_feature_required | 403 | The practice's plan does not include this. MCP needs Complete. |
get_form_submission
Read a single submission and its answers.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| formId | string | Required | |
| submissionId | string | Required |
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_form_submission",
"arguments": {
"formId": "b962ee2a-b962-4c3a-9e21-7b84c5d10f33",
"submissionId": "a396314c-a396-4c3a-9e21-7b84c5d10f33"
}
}
}Result
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"structuredContent": { "...": "the requested records" },
"content": [{ "type": "text", "text": "<the same JSON>" }]
}
}Errors
| Code | Status | When |
|---|---|---|
| validation_failed | 400 | An argument is missing, of the wrong type, or outside its allowed values. |
| not_found | 404 | The id does not exist in your practice. A malformed id looks identical. |
| plan_feature_required | 403 | The practice's plan does not include this. MCP needs Complete. |
write_form
action=create_link is open to any member; every other action is owner only. create_link mints a PUBLIC single-use link that expires in 3 days (optionally bound to a patient, whose record the answers then update: never their name, and an email or mobile only where the practice holds none), and with send=email|sms mails or texts it to that patient; create builds a form; update edits metadata; delete removes it; replace_fields swaps the whole field list; seed_defaults installs the pack's templates.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| action | string: create | update | delete | replace_fields | create_link | seed_defaults | Required | |
| formId | string | Optional | The form to act on. Required for every action except create and seed_defaults. |
| patientId | string | Optional | For action=create_link only: bind the link to an existing patient. Omit to have the submission create a new patient from its own answers. The answers go onto the bound record, but NOT as a blanket overwrite (`submissionPatient.ts`): the patient's name is never written from a form, and an email or mobile only fills a blank rather than replacing what the practice holds. A submission that was not sent to a named patient, and reached a record by matching a contact detail, is weaker still: it fills blanks only, and writes no name, email, mobile or consent at all. So a form is not a way to correct a patient's details. |
| send | string: email | sms | Optional | For action=create_link only: send the link to that patient as well as returning it. Requires patientId, and goes only to the address or number on that patient's own record — a recipient cannot be typed in. sms spends the practice's credits. Omit to mint the link without sending, which is what to do when the practice wants to hand it over themselves. A send that fails still returns a usable link, with the reason on `delivery`: read the URL out rather than trying again. |
| name | string | Optional | The form's name. Required for action=create. Name it for what the form is for («Εγγραφή νέου ασθενούς», "Reminder consent"), from the practice's words or from its questions. Never a placeholder such as «Νέα φόρμα» or "New form": the practice reads this name on the confirmation card and on every link it sends. |
| description | string | Optional | Optional blurb shown to the patient above the form. |
| isActive | boolean | Optional | Whether the form can currently be sent out. |
| fields | array<object> | Optional | The form's questions, for action=create (saved with the new form) and action=replace_fields. Each item is { label, type } plus optional required, options (for type=select), bindTo and id — see the item schema for what each one does. On replace_fields it is a FULL replacement: existing fields left out are deleted, so send every field you want to keep, and send an existing field's id (from get_form) to keep its answers, which are keyed by field id. Array order is the order they are shown in. Two rules bite: a type=signature field must not set bindTo, and bindTo of notifyBySms, notifyByEmail or fileUseConsent is CONSENT, needs type=checkbox, and in the case of fileUseConsent is the patient agreeing their x-rays, scans and photos may be used for teaching or promotion. Do not put one on a form unless the practice asked for it. |
Example call
action=create
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_form",
"arguments": {
"action": "create",
"name": "New patient details",
"description": "Filled in before the first visit.",
"fields": [
{
"label": "Όνομα",
"type": "text",
"required": true,
"bindTo": "firstName"
},
{
"label": "Επώνυμο",
"type": "text",
"required": true,
"bindTo": "lastName"
},
{
"label": "Κινητό",
"type": "text",
"bindTo": "phoneMobile"
}
]
}
}
}action=update
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_form",
"arguments": {
"action": "update",
"formId": "81920314-4f50-41a2-9d6e-7f8091a2b3c4",
"isActive": false
}
}
}action=delete
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_form",
"arguments": {
"action": "delete",
"formId": "81920314-4f50-41a2-9d6e-7f8091a2b3c4"
}
}
}action=replace_fields
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_form",
"arguments": {
"action": "replace_fields",
"formId": "81920314-4f50-41a2-9d6e-7f8091a2b3c4",
"fields": [
{
"label": "Any allergies?",
"type": "textarea"
},
{
"label": "Signature",
"type": "signature",
"required": true
}
]
}
}
}action=create_link
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_form",
"arguments": {
"action": "create_link",
"formId": "81920314-4f50-41a2-9d6e-7f8091a2b3c4",
"patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
"send": true
}
}
}action=seed_defaults
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_form",
"arguments": {
"action": "seed_defaults"
}
}
}Result
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"structuredContent": { "...": "the written record" },
"content": [{ "type": "text", "text": "<the same JSON>" }]
}
}Errors
| Code | Status | When |
|---|---|---|
| validation_failed | 400 | An argument is missing, of the wrong type, or outside its allowed values. |
| not_found | 404 | The id does not exist in your practice. A malformed id looks identical. |
| plan_limit_reached | 403 | action=create past the forms the plan includes. Complete has no cap; on Essential the refusal says the limit. |
| plan_feature_required | 403 | The practice's plan does not include this. MCP needs Complete. |
| subscription_read_only | 403 | The 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.