---
title: "MCP patient tools — HyperCRM"
description: "The ten MCP tools for patient records, clinical notes, files and invoices: every argument, an example call, the result shape, and the errors each returns."
canonical: "https://hypercrm.app/docs/mcp/patients"
lang: "en"
updated: "2026-07-26"
---

_MCP reference / Patients_

# Patient tools

Ten tools covering the patient record, its clinical notes, and the sub-resources hanging off it. Each one with its arguments, an example call, and what it returns.

[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 patient record, its notes, and the sub-resources hanging off it.

### list_patients

_Read-only, Any member_

List patients in the practice, with optional search, status and tag filters.

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `search` | `string` | Optional |
| `status` | `string` | Optional |
| `tagId` | `string` | Optional |
| `limit` | `integer` | Optional |
| `offset` | `integer` | Optional |

**Example call**

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

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

### get_patient

_Read-only, Any member_

Read a single patient by id.

**Arguments**

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

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_patient",
    "arguments": {
      "patientId": "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. |

### list_patient_notes

_Read-only, Any member_

List every clinical note recorded against a patient.

**Arguments**

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

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_patient_notes",
    "arguments": {
      "patientId": "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. |

### list_patient_files

_Read-only, Any member_

List files attached to a patient. Use get_file for a download URL.

**Arguments**

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

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_patient_files",
    "arguments": {
      "patientId": "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. |

### list_patient_billing_entries

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

**Arguments**

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

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_patient_billing_entries",
    "arguments": {
      "patientId": "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. |

### list_patient_invoices

_Read-only, Any member_

List the invoices already issued for a patient.

**Arguments**

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

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_patient_invoices",
    "arguments": {
      "patientId": "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. |

### get_patient_invoice_preview

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

**Arguments**

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

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_patient_invoice_preview",
    "arguments": {
      "patientId": "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. |

### write_patient

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

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `action` | `create` \\| `update` \\| `delete` | Required |
| `patientId` | `string` | Optional |
| `firstName` | `string` | Optional |
| `lastName` | `string` | Optional |
| `dob` | `string` | Optional |
| `gender` | `string` | Optional |
| `email` | `string` | Optional |
| `phoneMobile` | `string` | Optional |
| `address` | `string` | Optional |
| `postcode` | `string` | Optional |
| `country` | `string` | Optional |
| `taxId` | `string` | Optional |
| `taxOffice` | `string` | Optional |
| `status` | `string` | Optional |
| `notifyByEmail` | `boolean` | Optional |
| `notifyBySms` | `boolean` | Optional |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_patient",
    "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. |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### write_patient_note

_Writes, Can delete, Any member_

Create, update or delete a note on a patient. action=create requires body; update and delete require noteId.

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `action` | `create` \\| `update` \\| `delete` | Required |
| `patientId` | `string` | Required |
| `noteId` | `string` | Optional |
| `title` | `string` | Optional |
| `body` | `string` | Optional |
| `isNext` | `boolean` | Optional |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_patient_note",
    "arguments": {
      "action": "create",
      "patientId": "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. |
| `unauthorized` | 401 | The bearer token is missing, invalid or expired. |

### toggle_patient_tag

_Writes, Any member_

Add the tag to the patient if absent, remove it if already present.

**Arguments**

| Tool | Type | Required? |
| --- | --- | --- |
| `patientId` | `string` | Required |
| `tagId` | `string` | Required |

**Example call**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "toggle_patient_tag",
    "arguments": {
      "patientId": "0f1c7a2e-....",
      "tagId": "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. |
| `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.