---
title: "MCP website tools — HyperCRM"
description: "The ten MCP tools for the practice's public website: pages, modules, FAQs and the blog, plus the one that publishes it to the open internet."
canonical: "https://hypercrm.app/docs/mcp/site"
lang: "en"
updated: "2026-07-26"
---

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

[Start the free trial](/dashboard) · [All 60 tools](/docs/mcp/tools)

## How to read this

Each tool below lists its arguments exactly as the input schema declares them — the type, whether it is required, and the permitted values where the schema fixes them. There are no argument descriptions: the schemas carry types, not prose, and inventing a sentence per argument would be writing fiction about a contract.

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

Every example is a complete `tools/call` request with this tool's required arguments filled in. Errors are returned inside a `200` response with `isError` set — see the [MCP overview](/docs/mcp) for that envelope, and the [full catalogue](/docs/mcp/tools) for every tool at a glance.

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-only, Any member_

Read the site with its pages, modules and FAQs. Returns null if no site has been created.

**Example call**

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

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the requested records" }
    }
  }
}
```

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### list_site_page_modules

_Read-only, Any member_

Read the content modules and FAQs of one page.

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `pageId` | `string` | Required |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_site_page_modules",
    "arguments": {
      "pageId": "0f1c7a2e-...."
    }
  }
}
```

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the requested records" }
    }
  }
}
```

**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. |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### check_site_slug_available

_Read-only, Owner only_

Check whether a slug is valid and free. Returns availability plus a reason when it is not.

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `slug` | `string` | Required |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "check_site_slug_available",
    "arguments": {
      "slug": "smile-dental"
    }
  }
}
```

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the requested records" }
    }
  }
}
```

**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. |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### write_site

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

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `action` | `create` \\| `update` \\| `publish` | Required |
| `slug` | `string` | Optional |
| `locale` | `en` \\| `el` | Optional |
| `templateId` | `string` | Optional |
| `brandColor` | `string` | Optional |
| `fontPairing` | `string` | Optional |
| `logoFileId` | `string` | Optional |
| `heroFileId` | `string` | Optional |
| `metaTitle` | `string` | Optional |
| `metaDescription` | `string` | Optional |
| `showServicePrices` | `boolean` | Optional |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_site",
    "arguments": {
      "action": "create"
    }
  }
}
```

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the written record" }
    }
  }
}
```

**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. |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### update_site_page

_Writes, Owner only_

Edit one page's metadata — title, slug, SEO.

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `pageId` | `string` | Required |
| `enabled` | `boolean` | Optional |
| `navLabel` | `string` | Optional |
| `navOrder` | `integer` | Optional |
| `metaTitle` | `string` | Optional |
| `metaDescription` | `string` | Optional |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "update_site_page",
    "arguments": {
      "pageId": "0f1c7a2e-...."
    }
  }
}
```

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the written record" }
    }
  }
}
```

**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. |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### write_site_module

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

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `action` | `create` \\| `update` \\| `delete` | Required |
| `pageId` | `string` | Optional |
| `moduleId` | `string` | Optional |
| `type` | `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 |
| `position` | `integer` | Optional |
| `enabled` | `boolean` | Optional |
| `content` | `object` | Optional |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_site_module",
    "arguments": {
      "action": "create"
    }
  }
}
```

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the written record" }
    }
  }
}
```

**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. |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### write_site_faq

_Writes, Can delete, Owner only_

action=create adds an FAQ to a page (requires pageId); update and delete require faqId.

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `action` | `create` \\| `update` \\| `delete` | Required |
| `pageId` | `string` | Optional |
| `faqId` | `string` | Optional |
| `question` | `string` | Optional |
| `answer` | `string` | Optional |
| `position` | `integer` | Optional |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_site_faq",
    "arguments": {
      "action": "create"
    }
  }
}
```

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the written record" }
    }
  }
}
```

**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. |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### list_blog_posts

_Read-only, Any member_

List the practice's blog posts — drafts and published alike — with their slugs, status and bodies.

**Example call**

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

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the requested records" }
    }
  }
}
```

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### list_blog_categories

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

**Example call**

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

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the requested records" }
    }
  }
}
```

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### write_blog_post

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

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `action` | `create` \\| `update` \\| `delete` | Required |
| `postId` | `string` | Optional |
| `title` | `string` | Optional |
| `slug` | `string` | Optional |
| `excerpt` | `string` | Optional |
| `body` | `string` | Optional |
| `categoryId` | `string` | Optional |
| `authorMembershipId` | `string` | Optional |
| `status` | `draft` \\| `published` | Optional |
| `metaTitle` | `string` | Optional |
| `metaDescription` | `string` | Optional |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_blog_post",
    "arguments": {
      "action": "create"
    }
  }
}
```

**Result**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "status": 200,
      "ok": true,
      "data": { "...": "the written record" }
    }
  }
}
```

**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. |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

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

### Can I export my data?

Yes. Every list in HyperCRM exports to CSV from the app, and your files can be downloaded at any time. Your practice data belongs to you, and there is no export fee or waiting period — on either plan, during the trial, and after you cancel.

### How much does HyperCRM cost?

HyperCRM is paid software. Every practice starts with a 30-day free trial of the full Complete plan, with no card required. After that, plans start at €19 and €29 a month excluding VAT, banded by how many practitioners you have. Every price is printed on the pricing page rather than quoted on a call.