MCP reference / Website
Website builder tools
Ten tools covering the practice's public site and its blog. Writes affect a draft; publishing is the single tool here that changes what the public can see, and it takes effect immediately.
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.
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.
get_site
Read the site with its pages, modules and FAQs. Returns null if no site has been created.
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_site",
"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. |
list_site_page_modules
Read the content modules and FAQs of one page.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| pageId | string | Optional | Page ids come from get_site. |
| page | string | Optional | The page by name: its navLabel as the practice wrote it, or its kind. Case and accents are ignored. |
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_site_page_modules",
"arguments": {}
}
}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. |
check_site_slug_available
Check whether a slug is valid and free. Returns availability plus a reason when it is not.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| slug | string | Required | Lowercase letters, numbers and hyphens, starting and ending with a letter or number. Becomes the site's public subdomain. |
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "check_site_slug_available",
"arguments": {
"slug": "..."
}
}
}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. |
| forbidden | 403 | The caller is not an owner of the practice. |
| plan_feature_required | 403 | The practice's plan does not include this. MCP needs Complete. |
write_site
action=create sets up the site; update edits settings; publish PUSHES THE CURRENT DRAFT LIVE to the public internet, immediately visible to anyone.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| action | string: create | update | publish | Required | |
| slug | string | Optional | The site's public subdomain. Required for action=create. Lowercase letters, numbers and hyphens; check it first with check_site_slug_available. |
| locale | string: en | el | Optional | Language the public site is written in. Required for action=create. |
| templateId | string | Optional | Optional layout template id. Leave unset for the default. |
| brandColor | string | Optional | The site's accent colour. |
| fontPairing | string | Optional | Optional named font pairing. Leave unset for the default. |
| logoFileId | string | null | Optional | An already-uploaded image to use as the logo, or null to remove the logo, after which the site's header shows the practice name as text. Files are uploaded through the dashboard, not over MCP. |
| heroFileId | string | null | Optional | An already-uploaded image for the hero, or null to remove it. |
| metaTitle | string | Optional | Site-wide SEO title. 70 characters or fewer. |
| metaDescription | string | Optional | Site-wide SEO description. 160 characters or fewer. |
| showServicePrices | boolean | Optional | Whether service prices are shown to the public. |
Example call
action=create
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_site",
"arguments": {
"action": "create",
"slug": "smile-dental",
"locale": "el"
}
}
}action=update
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_site",
"arguments": {
"action": "update",
"brandColor": "#1f5eff",
"metaTitle": "Smile Dental, Athens",
"showServicePrices": true
}
}
}action=publish
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_site",
"arguments": {
"action": "publish"
}
}
}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. |
| forbidden | 403 | The caller is not an owner of the practice. |
| 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. |
update_site_page
Edit one page's metadata — title, slug, SEO.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| pageId | string | Required | Page ids come from get_site. |
| enabled | boolean | Optional | Whether the page exists on the public site at all. Disabling removes it from the nav and 404s its URL. |
| navLabel | string | Optional | What the page is called in the site navigation. |
| navOrder | integer | Optional | Position in the navigation. Lower comes first. |
| metaTitle | string | Optional | SEO title for this page. 70 characters or fewer. |
| metaDescription | string | Optional | SEO description for this page. 160 characters or fewer. |
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "update_site_page",
"arguments": {
"pageId": "03142536-6172-43c4-9f80-91a2b3c4d5e6",
"enabled": true,
"navLabel": "Services",
"navOrder": 2
}
}
}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. |
| forbidden | 403 | The caller is not an owner of the practice. |
| 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. |
write_site_module
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. On update, `content` replaces the module's content whole rather than merging, so read it first and send it back complete.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| action | string: create | update | delete | Required | |
| pageId | string | Optional | The page to add to. Required for action=create. |
| moduleId | string | Optional | The module to update or delete. |
| type | string: hero | intro | services_teaser | team_teaser | why_us | testimonials | hours_map | cta | faq | service_grid | insurance_payment | story | team_grid | credentials | gallery | address_map | hours | contact_form | access_parking | booking_widget | service_detail | Optional | Which preset module to add. Required for action=create, and not changeable afterwards. |
| position | integer | Optional | Order on the page. Lower comes first. |
| enabled | boolean | Optional | Whether the module renders. Disabling hides it without deleting it. |
| content | object | Optional | Shape depends on `type`, and is validated strictly — an unrecognised key is rejected. hero: { heading (required, <=120 chars), subheading, imageFileId, ctaLabel, ctaHref }. intro and story: { heading, body } where body is required rich text. why_us, credentials, insurance_payment, access_parking: { heading, points: [{ title, body }] } with 1-6 points. testimonials: { heading, items: [{ quote, attribution }] } with 1-12 items. gallery: { heading, images: [{ fileId, alt }] } with 1-24 images. cta: { heading (required), body, label (required), href (required) }. The data-driven modules (services_teaser, service_grid, service_detail, team_teaser, team_grid, hours_map, address_map, hours, contact_form, booking_widget, faq) take { heading } and nothing else — their body comes from the practice's own services, team, hours and FAQs. Read get_site to see the current content of any module. |
Example call
action=create
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_site_module",
"arguments": {
"action": "create",
"pageId": "03142536-6172-43c4-9f80-91a2b3c4d5e6",
"type": "hero",
"position": 0,
"content": {
"heading": "Dentistry in the centre of Athens"
}
}
}
}action=update
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_site_module",
"arguments": {
"action": "update",
"moduleId": "14253647-7283-44d5-8091-a2b3c4d5e6f7",
"enabled": false
}
}
}action=delete
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_site_module",
"arguments": {
"action": "delete",
"moduleId": "14253647-7283-44d5-8091-a2b3c4d5e6f7"
}
}
}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. |
| forbidden | 403 | The caller is not an owner of the practice. |
| 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. |
write_site_faq
action=create adds an FAQ to a page (requires pageId); update and delete require faqId.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| action | string: create | update | delete | Required | |
| pageId | string | Optional | The page to add to. Required for action=create. |
| faqId | string | Optional | The FAQ to update or delete. |
| question | string | Optional | Required for action=create. Plain text, 300 characters or fewer. |
| answer | string | Optional | Required for action=create, and must be non-empty. A limited HTML subset (h2/h3, bold, italic, links, lists, blockquote); anything else is stripped on save. Plain text is fine. |
| position | integer | Optional | Order within the page's FAQ list. Lower comes first. |
Example call
action=create
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_site_faq",
"arguments": {
"action": "create",
"pageId": "03142536-6172-43c4-9f80-91a2b3c4d5e6",
"question": "Do you see children?",
"answer": "Yes, from the age of three.",
"position": 0
}
}
}action=update
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_site_faq",
"arguments": {
"action": "update",
"faqId": "25364758-8394-45e6-91a2-b3c4d5e6f708",
"answer": "Yes, from the age of three. The first visit is a look and a chat."
}
}
}action=delete
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_site_faq",
"arguments": {
"action": "delete",
"faqId": "25364758-8394-45e6-91a2-b3c4d5e6f708"
}
}
}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. |
| forbidden | 403 | The caller is not an owner of the practice. |
| 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. |
list_blog_posts
List the practice's blog posts — drafts and published alike — with their slugs, status and bodies.
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_blog_posts",
"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. |
list_blog_categories
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.
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_blog_categories",
"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. |
write_blog_post
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. The blog is the one exception to publishing: on a site that is already published, setting a post to published puts it in front of patients at once, and deleting a published post takes it down at once. A practice may also have a weekly schedule that writes posts by itself, so posts you did not create can appear.
Arguments
| Tool | Type | Required? | What it means |
|---|---|---|---|
| action | string: create | update | delete | Required | |
| postId | string | Optional | The post to update or delete. |
| title | string | Optional | Required for action=create. Plain text. |
| slug | string | Optional | Required for action=create. Lowercase letters, numbers and hyphens; it becomes the post's address and must be unique on this site. |
| excerpt | string | Optional | A short teaser for the blog index. Plain text. |
| body | string | Optional | Required for action=create. A limited HTML subset (h2/h3, bold, italic, links, lists, blockquote, images); anything else is stripped on save. Do not include an h1 — the page supplies the title. |
| categoryId | string | null | Optional | One of list_blog_categories' ids, or null to clear. A category from another site is refused. |
| authorMembershipId | string | null | Optional | A practice member to credit, or null for no named author — in which case the practice itself is the author. |
| status | string: draft | published | Optional | Defaults to draft on create. On a site that has been published, moving a post to published puts it live immediately; it does not wait for the next site publish. |
| metaTitle | string | Optional | Optional search-result headline. Falls back to the title. |
| metaDescription | string | Optional | Optional search-result summary, around 110-160 characters. Falls back to the excerpt. |
Example call
action=create
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_blog_post",
"arguments": {
"action": "create",
"title": "What to expect at your first visit",
"slug": "first-visit",
"body": "We start with a look, an X-ray if you need one, and a plan you agree to.",
"status": "draft"
}
}
}action=update
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_blog_post",
"arguments": {
"action": "update",
"postId": "36475869-94a5-46f7-a2b3-c4d5e6f70819",
"status": "published"
}
}
}action=delete
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_blog_post",
"arguments": {
"action": "delete",
"postId": "36475869-94a5-46f7-a2b3-c4d5e6f70819"
}
}
}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. |
| forbidden | 403 | The caller is not an owner of the practice. |
| 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.