Skip to content

Documentation / MCP

The MCP server

HyperCRM exposes its API over the Model Context Protocol as one streamable-HTTP JSON-RPC endpoint, so an MCP client can work with patients, appointments, billing, forms, files and reporting — under the same practice scoping and role gates as the REST API.

Breaking changes in 0.2.0

Three tools were renamed when the write_* convention landed, and there are no aliases: create_patient and update_patient are now write_patient with action=create or update, and add_patient_note is write_patient_note with action=create. Two ways to do one thing measurably hurts tool selection, so the old names are gone rather than deprecated.

Which plan this needs

The REST API and the MCP server are included on the Complete plan and are not available on Essential — integrations are a larger-practice need and a real support burden, so they sit on the plan that pays for them. The pricing page lists every band. OAuth clients are created from practice settings once you are on Complete.

What you get

60 tools — 38 reads, 22 writes, across patients, appointments, billing and invoices, forms, files, the catalogue (chairs, services, tags), practice and people, analytics and search, notifications, and the website builder. Every one is backed by the same services, validation and permissions the REST API uses, so an agent cannot reach anything a signed-in member of your practice could not.

No MCP resources or prompts are exposed — resources/list and prompts/list both return empty. Tools are the whole surface.

Connecting

One endpoint, POST /api/v1/mcp, speaking JSON-RPC 2.0 over streamable HTTP:

{
  "mcpServers": {
    "hypercrm": {
      "type": "http",
      "url": "https://your-hypercrm-origin/api/v1/mcp"
    }
  }
}

OPTIONS /api/v1/mcp answers CORS preflight. The endpoint sets no Access-Control-Allow-Credentials header, which is safe precisely because every call needs an explicit bearer token rather than an ambient cookie — a cross-origin page cannot ride a signed-in user's session onto it.

Authorisation

Every tool call requires Authorization: Bearer <token>. The server implements the MCP authorisation spec: OAuth 2.1 with mandatory PKCE (S256), protected-resource and authorisation-server metadata documents, and dynamic client registration. A Firebase ID token is also accepted, which is the simpler path if you already have one.

Access tokens last about an hour and refresh tokens 30 days; the refresh token is rotated on every use, so a leaked one is single-use. Tokens are opaque random strings, never JWTs, and only their hash is stored.

Practice scoping

practiceId is always taken from the token and never from tool arguments. No tool's input schema even declares it, and the write paths strip any practiceId a client sends before it reaches a service. That is enforced by a test across all 60 tools rather than left to reviewer attention, because a single tool accepting a practice id would turn the whole server into a cross-tenant read.

Role gates

MCP enforces the same owner-only rules as the REST API. Reports, the audit log, every form tool (reads included), practice settings, invitations, staff changes, and every website write are owner-only; patients, appointments, files, notifications and the catalogue reads are open to any practice member. The tool catalogue marks each one, and the reasoning is worth stating plainly: without matching gates, MCP would be a way around permissions the REST API enforces.

Errors

A tool that fails still returns HTTP 200 with a valid JSON-RPC envelope. The failure is reported inside the result:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": true,
    "structuredContent": {
      "status": 400,
      "ok": false,
      "error": {
        "code": "validation_failed",
        "message": "Request validation failed.",
        "details": [{ "path": ["firstName"], "message": "firstName is required" }]
      }
    }
  }
}

That is the same error shape the REST API returns, so one error handler covers both surfaces. Transport-level problems — malformed JSON, an unknown method, bad tools/call arguments — return HTTP 400 with a JSON-RPC error object (-32700, -32600, -32601, -32602, -32603).

The one exception is a missing, invalid or expired credential: per the MCP authorisation spec that gets HTTP 401 with a WWW-Authenticate header pointing at the metadata document, which is what lets a client start the OAuth flow on its own.

What is deliberately not exposed

Not available over MCPWhy
The OAuth endpointsCircular — they are how a client gets its token in the first place.
Passkey/WebAuthn ceremoniesBrowser-only, and revoking a credential risks locking an account out.
Public booking, public form submission, the contact formUnauthenticated widgets. An agent-driven contact-form submitter is a spam vector.
Accepting a staff invitationA browser redemption flow belonging to the invited person, not the caller.
File and asset uploadsThe bytes go browser-to-storage directly and never pass through the server.
CSV exportTools always return JSON.

The OAuth flow, end to end

What a client does the first time it connects, and what your user sees while it happens.

  1. Discover

    The client calls the endpoint with no token, receives a 401 naming the protected-resource metadata document, and fetches that and the authorisation-server metadata.

  2. Register

    The client registers itself with a name and its redirect URIs, and receives a client id. No secret is issued — MCP clients are public clients.

  3. Ask the user

    The client opens a browser at the authorisation endpoint with PKCE parameters. After signing in, your user sees a consent screen naming the client and the account it would act as, with Approve and Deny.

  4. Approve

    Approving mints a one-time authorisation code and redirects back to the client with it.

  5. Exchange

    The client exchanges the code plus its PKCE verifier for an access token (about an hour) and a refresh token (30 days).

  6. Refresh

    On expiry the client exchanges the refresh token for a new pair. The old refresh token is invalidated on use, so each one works exactly once.

Next

All 60 tools

Every tool by domain, with its arguments, whether it writes or deletes, whether it reaches outside HyperCRM, and which role can call it.

REST API reference

The same capabilities over HTTP, with full field-level reference for patients and appointments.

Patient tools

Ten tools for the patient record, its clinical notes, files, billing entries and invoices.

Appointment tools

Four tools for bookings, chair blocks and computed availability.

Billing tools

Five tools for the billing ledger and the invoices issued from it.

Form tools

Five owner-only tools for intake and consent forms and their submissions.

File tools

Two tools: read a file with a signed link, or delete one permanently.

Analytics tools

Six tools for dashboard figures, the three reports, the audit log and search.

Notification tools

Two tools for the calling user's own inbox.

Practice and people tools

Eight tools for the practice profile, its members and staff invitations.

Website tools

Ten tools for the practice website and its blog, including the one that publishes it.

Catalogue tools

Six tools for chairs, services and patient tags.

Common questions

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.

No. The practice is resolved from the token, never from a tool argument, and no tool's input schema accepts a practice id — a test asserts that across all 60. An agent connected to your practice reaches exactly what a signed-in member of it could reach, and nothing else.

Every tool declares MCP annotations and tools/list returns them, so a client can tell reads from writes, and writes from deletes, without calling anything. Fifteen tools can delete or void data and four reach outside HyperCRM: sending an invitation email, publishing the website, and minting a public form link.

No. create_patient, update_patient and add_patient_note were removed in 0.2.0 rather than aliased, because offering two ways to do one thing measurably degrades a model's tool selection. Use write_patient and write_patient_note with an action argument.

Not today. Both resources/list and prompts/list return empty, and tools are the entire surface. A resource- or prompt-shaped view of your practice data is not something the server exposes today, so every integration goes through the tool list.