Skip to main content
Glama

Carebit MCP server

An MCP server that lets Claude, ChatGPT and other MCP clients work with a Carebit private-practice Organization: clinicians and their diaries, bookable slots, bookings, patients, invoices, payments, the staff task queue, services and locations, and (when enabled) creating staff tasks, creating bookings and cancelling bookings. It is built from Carebit's public developer documentation: the OpenAPI 3.1 document at carebit.dev/openapi.json and the guides on authentication, testing, pagination and filtering, idempotency and retries, validation errors and rate limits.

Once it's connected, someone at the practice can ask things like:

  • "What does Dr Ada Example have on tomorrow, and are there gaps?"

  • "When is the next free new-patient slot with Dr Example?"

  • "Which invoices are overdue, and how much is outstanding?"

  • "What was refunded this month?"

  • "What's still open in the task queue for Jo?"

  • With writes enabled: "Add a task for Kim to chase the insurer by Friday." / "Book Priya Shah a follow-up with Dr Example at 9 on the 12th." / "Cancel that booking, it was made in error."

Tools

Tool

What it does

API calls

get_organization

The Organization the credential belongs to (name, type, contact details, address, currency, time zone) and, by default, the token's granted scopes and developer project.

GET /v1/organization, GET /v1/token

list_clinicians

Clinicians with display name, title and specialty. Practice email addresses only on request.

GET /v1/clinicians

get_clinician

One clinician.

GET /v1/clinicians/{id}

get_clinician_agenda

A clinician's diary for up to 45 days: bookings, availability and unavailability intervals in time order. Patient names are shown; contact details and the booking's free-text information only on request.

GET /v1/clinician_agenda

find_availability

Bookable slots for a clinician and service variant: every slot in a date range (up to 45 days), or the next slot from a date (up to 8 months ahead).

GET /v1/availability_slots or GET /v1/next_availability_slot

list_bookings

Bookings in a time window (30 days, or 90 with patient_id), or recall bookings by status without a window, with the documented filters clinician_id, patient_id, status, updated_since.

GET /v1/bookings

get_booking

One booking: status, times, clinician, patient, service and variants, location, payor, recall and cancellation details.

GET /v1/bookings/{id}

search_patients

Connected patients with the documented exact-match filters first_name, last_name, date_of_birth, email, phone_number and ids[] (up to 25). The API has no partial-name search.

GET /v1/patients

get_patient

One patient.

GET /v1/patients/{id}

list_invoices

Invoices with status, totals, outstanding and paid amounts and line items; filters booking_id, patient_id.

GET /v1/invoices

get_invoice

One invoice with its line items.

GET /v1/invoices/{id}

list_payments

Payments with status, amount, method type, payor type and refunds; filters patient_id, paid_at_from, paid_at_to.

GET /v1/payments

list_human_tasks

The staff task queue, newest first; filters patient_id, staff_member_id, document_id, is_completed.

GET /v1/human_tasks

list_services

Services with duration, tax rate and their variants (the service_variant_id that availability and booking need); filter is_bookable_online. Names and descriptions go through the redaction unless asked.

GET /v1/services

list_locations

The Organization's locations with addresses.

GET /v1/locations

create_human_task

Adds a task to the queue with content, due date, urgency, optional patient, document and staff-member assignees. Writes only.

POST /v1/human_tasks

create_booking

Creates a diary booking for a patient, service and variant at a UTC start time, optionally with clinician, location, end time, status, remote flag and information fields. notify_patient defaults to false here (the API's own default is true). Recall bookings are not supported. Writes only.

POST /v1/bookings

cancel_booking

Cancels a booking with an optional documented reason code and note. Writes only, marked destructive.

POST /v1/bookings/{booking_id}/cancellations

Not covered on purpose: staff members, payors, insurance companies and alternative payors, payment methods (stored cards), creating payments and refunds, creating invoices and quotes, patient creation and updates, patient connections, leads, letters, notes, photos, test results, digital forms, booking forms, lists, recall programmes, availability periods, reports, expirable files, transmissions, webhooks, PATCH /v1/bookings/{id} and PATCH /v1/human_tasks/{id}, and the organization search. The test suite asserts that the stored-card, payor, note, letter, test-result and report endpoints are never called.

Related MCP server: mcp-cliniko

Setup

Requires Node 18 or later.

npm install
npm run build

You need an API credential for your Carebit Organization. As the authentication guide documents: sign in to Carebit as a staff member, go to Settings > Developer platform, create a project with the scopes you need, and create an API credential (a client_id and a client_secret) for it. The server exchanges them for a Bearer access token at POST /oauth/token (OAuth 2.0 client credentials, form-encoded), which the guide's example says lasts an hour (expires_in: 3600).

The read tools need these scopes on the project: organization.read, clinicians.read, clinician_agenda.read, availability_slots.read, bookings.read, patients.read, invoices.read, payments.read, human_tasks.read, services.read, locations.read. The write tools need human_tasks.create, bookings.create and bookings.cancel. A tool whose scope is missing reports which one.

Claude Desktop: add this to claude_desktop_config.json:

{
  "mcpServers": {
    "carebit": {
      "command": "node",
      "args": ["/absolute/path/to/carebit-mcp/dist/index.js"],
      "env": { "CAREBIT_CLIENT_ID": "your-client-id", "CAREBIT_CLIENT_SECRET": "your-client-secret" }
    }
  }
}

Claude Code:

claude mcp add carebit -e CAREBIT_CLIENT_ID=your-client-id -e CAREBIT_CLIENT_SECRET=your-client-secret -- node /absolute/path/to/carebit-mcp/dist/index.js

Variable

Required

Meaning

CAREBIT_CLIENT_ID

yes

The API credential's client ID, sent in the token request body.

CAREBIT_CLIENT_SECRET

yes

The API credential's client secret, sent in the token request body. Never logged or included in an error message.

CAREBIT_SCOPE

no

Space-separated scopes to request on the token, e.g. bookings.read clinicians.read. When unset the token receives the project's full scope set, as the guide documents. Every scope named must be on the project or the token endpoint answers 400 invalid_scope.

CAREBIT_ALLOW_WRITES

no

true to register create_human_task, create_booking and cancel_booking. Off by default.

CAREBIT_BASE_URL

no

Defaults to https://api.carebit.co. Used by the tests.

Safety defaults

  • Read-only unless CAREBIT_ALLOW_WRITES=true. Read tools carry the MCP readOnlyHint annotation; cancel_booking is marked destructive; the two create tools are not.

  • Patient records are third-party health data. By default a patient's email, phone_number, phone, mobile (and their country codes), date_of_birth, nhs_number, address_line_1, address_line_2, city, county, postcode, country_code, internal_patient_id and patient_portal_add_payment_method_url are not returned; a clinician's email is not returned; a payor's name-and-address fields, insurance_policy_number, insurance_authorization_code, policy dates and notes are not returned; an invoice's payment_url (the Patient Portal page where that invoice can be paid) is not returned; and a booking's information_for_patient and information_for_staff_members (free text that may hold clinical detail) are withheld, with two booleans saying whether they exist. Patient names, sex and the Organization's own patient_number are returned, as are the Organization's own and its locations' contact details. In every other free-text field (names, specialties, task content, cancellation notes, invoice notes and line-item titles, payment and refund notes, service names and descriptions and variant descriptions, a booking's service name, and Carebit's own error messages) email addresses are replaced with [email redacted], phone-number-like sequences with [phone redacted], 10-digit NHS-number-shaped groups with [number redacted] and UK postcodes with [postcode redacted]. include_contact_details=true (on every read tool except get_organization, find_availability and list_locations, which return no personal data) returns all of it as stored, with one exception: a payment card number typed into free text (13 to 19 digits, optionally grouped by spaces or hyphens, that pass the Luhn check) is replaced with [card number redacted] whether or not contact details were requested, in every field this server returns, including the booking information and payor notes that are otherwise passed through as stored. That check catches full card numbers, not fragments: card ending 4242 cannot be told from an ordinary number and is left alone, and one arbitrary 13-to-19-digit string in ten passes Luhn and is redacted too. The phone match is a heuristic: it covers international numbers written with + or 00 (including the +44 (0)7700 … form), UK numbers with a bracketed area code such as (020) 7946 0958, and UK-style 0… numbers of 9 to 11 digits with spaces, dots or hyphens between groups; other digit strings that happen to start with 0 are redacted too, and any 10-digit group written nnn nnn nnnn or unbroken is redacted whether or not it is an NHS number, while UUIDs, timestamps and hyphenated references such as INV-1001 or POL-0001-000123 are left alone. Dates inside free text are not redacted.

  • Stored cards are never read: GET /v1/patients/{id}/payment_methods is not called, a payment's payment_method_id is never returned, a full card number typed into any free text is redacted as above, and there are no payment, refund or invoice-creation tools. No file is ever downloaded.

  • The access token is held in memory only, refreshed a minute before its documented expiry (or after half its lifetime when Carebit issues one shorter than two minutes, so it is still reused rather than fetched before every call), and fetched afresh once when a call answers 401; it never appears in logs or error messages, and neither does the client secret: should Carebit ever echo either in an error message, the value is replaced with [redacted] before the message is passed on. The refresh token Carebit issues alongside is not used: refresh tokens rotate on every use and would have to be stored, while a client-credentials grant can simply be repeated (the token endpoint allows 10 requests per minute per client_id). Only a JSON error message from the token endpoint is ever passed on, never its raw body; a non-JSON 200 from it is reported by its first 60 characters, redacted before they are cut.

  • Every write carries an Idempotency-Key header holding a fresh version-4 UUID, as the idempotency guide requires on every POST create; the same key is kept across retries of that operation. A 409 idempotency_conflict (the same key still in flight) is retried after Retry-After (1 second when absent, as documented) with the same key, at most twice. The same key is sent again on a 409 idempotency_conflict retry, on a 429 retry and on the single retry after a token refresh (401), as the rate-limits guide requires for any write retry. The code reports a response that carries Idempotency-Replayed: true as "already created (replayed)", but the tests never reach the replay path, because the mock stores nothing for a request it answered 409, 429 or 401 (see Status).

  • IDs are checked before any call is made: every identifier must look like a UUID (the spec types them all as RFC 4122 UUIDs). Dates take YYYY-MM-DD and real calendar dates only; times take YYYY-MM-DDTHH:MM:SSZ (UTC, the only form the API documents) or a bare date, which becomes the start of that UTC day, or its end for an upper bound. Offsets such as +01:00 and forms such as 05/10/2026 are refused locally. The documented limits are enforced before a request: a diary or slot range of at most 45 days inclusive, a bookings window of at most 30 days (90 with patient_id), no window with a recall status and a window with every other status including did_not_attend. create_booking refuses an end before the start; cancel_booking only accepts the documented reason codes.

  • Rate limits, from the rate-limits guide: 120 requests and 30 writes per minute per access token, 240 and 60 per developer project, 300 and 90 per Organization, 10 patient creates per token, 10 /oauth/token requests per minute per IP and per client_id, all on a rolling 60-second window at the Cloudflare edge; every 429 carries Retry-After: 60. Requests are spaced 500 ms apart, which keeps one server at the per-token ceiling; the project and Organization limits are shared with other integrations and are not modelled. A 429 is retried at most twice for any method, on the assumption that a request the edge rejected was not processed, waiting for Retry-After (whole or fractional seconds, or an HTTP-date; 2 s then 4 s when the header is absent or unreadable). Each wait is capped at 10 seconds, so one request waits at most 20 seconds in all; a tool call that makes several requests (pages, lookups) can still run past the MCP client's default 60-second request timeout. Past the cap: with Carebit's documented Retry-After: 60 the call therefore gives up at once and the message says to wait 60 seconds. The cap is per request, not per tool call: a list can make up to 20 requests in one call.

  • 502, 503 and 504 are retried the same way for GET and for POST /oauth/token only; when all three attempts fail the error says the service may be unavailable and to try again in a few minutes, without the gateway's HTML. A 500 and a connection failure are reported at once, without a retry, although the idempotency guide says to retry every 5xx and network failure. A POST is never retried after a gateway error, even though the idempotency guide says a retry with the same key is safe: this prototype does not repeat a write on the strength of documentation it could not test, and the error says to check with the matching list tool before repeating it (see Going to production).

  • A 200 whose body is not JSON (a proxy or a login page in the way) is reported as an error naming CAREBIT_BASE_URL, never as an empty list or an empty record. Every body excerpt in an error message (60 characters of a non-JSON 200, 300 of a non-JSON error body) is redacted before it is cut, so a contact detail that straddles the cut cannot leak in part. A list page that says has_more: true without a usable next_cursor (the guide says the two always agree) is reported as incomplete with a note, not as the whole collection.

  • Rejected client credentials produce a message that says which variables to fix and where credentials come from; a 401 that survives a token refresh says to check the credential and project; a 403 names the scope the endpoint needs; a 404 says the resource does not exist or is outside the Organization; a 422 passes on Carebit's message, code and, when several fields fail, every field-level error; and every error message, from the token endpoint included, carries the Carebit-Developer-Platform-API-Request-Id for support tickets whenever the response had the header (a gateway page from the edge may not carry it).

Tests

npm test

The test suite:

  1. Validates every fixture record against the component schemas in Carebit's published OpenAPI 3.1 document (Organization, Token, Clinician, Location, Service, Patient, Booking, ClinicianAgendaItem, AvailabilitySlot, Invoice, Payment, HumanTask) with Ajv 2020 and ajv-formats, including a negative control (a non-UUID id and an undeclared key are rejected, since every resource schema sets additionalProperties: false). It also checks that every scope the mock's project holds is one the spec defines and that the mock's client ID and secret are obviously fake values that do not appear in the spec. The spec is downloaded from carebit.dev/openapi.json to spec.json on the first run.

  2. Starts a local mock of the API: POST /oauth/token with the documented form-encoded client-credentials grant (answering the OAuthTokenResponse shape with a refresh token and scope, 401 invalid_client for wrong credentials, 400 invalid_scope for a scope not on the project), GET /v1/token, a 401 for any API call without a token the mock issued, a 403 for a token without the endpoint's scope, the endpoints used here with the documented list envelope and cursor pagination (limit 1 to 100 with a 422 above, cursor from next_cursor, has_more false and next_cursor null on the last page; every page capped at four records so the suite pages), the documented filters (the bookings window rules, recall statuses without a window, exact patient filters including ids[], the paid_at window omitting null paid_at, is_completed, is_bookable_online), the Idempotency-Key contract on writes (400 idempotency_key_required, replay with Idempotency-Replayed: true, 422 idempotency_key_reused, an injectable 409 idempotency_conflict with Retry-After: 1), the documented error envelope with field-level errors, 404s for unknown ids, a 422 for cancelling a cancelled booking, injected failures on any endpoint, and a one-off 429 on the first GET /v1/services. The second check validates the mock's token, list, record, write and error responses against the documented response schemas.

  3. Starts the built server and drives it over stdio with the official MCP client: 30 checks (32 in the whole suite, 124 requests, about 70 seconds) covering every tool, tool annotations, the token fetched with the documented form body (no scope unless CAREBIT_SCOPE is set, and that value when it is) before the first API call and reused as a Bearer header, a revoked token refreshed once with the call retried, a token near expiry refreshed before it expires, a 60-second token reused rather than fetched before every call, cursor pagination with limit 100 following next_cursor to has_more: false and continuing from a returned cursor with the original filters on every page, every documented filter passed through exactly (clinician_ids[], start_date/end_date, clinician_id/service_variant_id/from_date, start_time_from/start_time_to with bare dates converted, clinician_id, patient_id, status, updated_since, first_name, last_name, date_of_birth, email, phone_number, ids[] repeated, booking_id, paid_at_from/paid_at_to, staff_member_id, document_id, is_completed, is_bookable_online), the documented window and range limits refused before any request (a 31-day calendar month given as bare dates refused with the two exact windows to use instead, 30 bare days and an exact 30-day span accepted), redaction of contact details, NHS number, date of birth, address, third-party identifier, payment-method link, policy number, payor address, invoice payment link, stored-card id and the booking's free text by default and their return on request, and of emails, phone numbers, an NHS-shaped number and a UK postcode typed into specialties, task content, cancellation notes, invoice notes and payment notes, service names and descriptions, variant descriptions, line-item titles and a booking's service name redacted by default and returned as stored on request, full card numbers (Visa and Amex shapes, unbroken, spaced and hyphenated) redacted in task content, booking information, payor notes and the record a create tool echoes whether or not contact details were requested while a non-Luhn digit string and ending 4242 stay, the three write bodies validated against the spec's request schemas (POST /v1/bookings through its allOf/oneOf diary branch) with a version-4 UUID Idempotency-Key that changes per call and is kept across a 409 retry, a 429 retry and the retry after a token refresh, the 422 for an already-cancelled booking passed on with Carebit's message and code, the 429 retry waiting for Retry-After in the seconds, fractional-seconds and HTTP-date forms and falling back to 2 s without the header, giving up after three attempts on a persistent 429 and at once on the documented Retry-After: 60 with the request id in the message, a 502 retried for GET and never for POST, a GET failing three times with 503 reported with advice and without the gateway HTML (and with the request id and Carebit's message when the 503 carried them), a 200 with a non-JSON body reported as an error, body excerpts redacted before they are cut (an email address straddling the 60- and 300-character cuts), a list page with has_more: true and no cursor reported as incomplete, token-endpoint errors (400, 403, a non-JSON 200) carrying the request id and never the client secret even when the message echoes it, the write gate with the variable unset and set to false, the 401 for wrong client credentials naming the variables without echoing the secret, the 403 naming the missing scope, and that every request either carried the documented form body to /oauth/token with no Authorization header or carried a Bearer token the mock had actually minted (membership in the mock's issued set, not just the shape) to a documented method and path, that every POST carried an Idempotency-Key, that the secret never travelled in a query string, and that the stored-card, payor, note, letter, test-result, report and revoke endpoints were never called.

Status

This is a working prototype. It has not yet been run against the live API, because it was built without a Carebit Organization or API credential (Carebit has no sandbox; the testing guide says to test against production with an example patient, and a trial Organization is provided after a booked demo). Everything below is taken from the published spec and guides and should be confirmed on a real account:

  • The token endpoint: that client credentials sent as body fields (rather than HTTP Basic, which the spec also allows) are accepted, the exact status and error.code for wrong credentials (the spec documents 401; the mock's invalid_client code is the OAuth 2.0 convention), whether the API answers 401 for an expired token (the server refreshes a minute before the documented expiry and once on a 401 either way), and whether repeating the client-credentials grant every hour instead of using the refresh token has any side effect (the guide documents both grants; refresh tokens rotate on use).

  • Error bodies: the spec documents the envelope and the idempotency, scope and rate-limit codes; the codes the mock uses for 401, 403 and 404 (invalid_token, insufficient_scope, resource_missing) are placeholders. The server switches on idempotency_conflict only and passes every other message through with contact details redacted.

  • Pagination: the mock's cursor is opaque base64 and the server treats next_cursor as opaque, as documented; that the real cursor survives being sent back with the original filters and limit is assumed. starting_after is not used. What a limit between the page's actual size and 100 returns on a real account is not tested beyond the mock's cap.

  • GET /v1/clinician_agenda and GET /v1/availability_slots answer the list envelope but document no limit or cursor parameter; the server makes one request and reports has_more: true as a note. Whether the real endpoints ever page, and how many items a 45-day diary holds, is unknown.

  • The bookings window: whether start_time_to is compared inclusively as documented, what the API answers for a window of exactly 30 or 90 days, whether 30 days and 23:59:59 counts as exceeding 30 days (the server assumes it does, so a 31-day calendar month given as bare dates is refused with the two exact windows to use instead), and whether patient_id alone (without a window) is accepted for non-recall statuses (the server requires the window, as the description says).

  • GET /v1/next_availability_slot: the 404 for "no slot within 8 months" cannot be told apart from an unknown clinician or variant by status alone; the server's message says both.

  • The exact-match patient filters: whether first_name and last_name really ignore case and whether phone_number matching a national 07700 900123 against a stored E.164 number works as the parameter description says; the server sends the value as given.

  • POST /v1/bookings: which status values are accepted on creation (the schema lists arrived, confirmed, did_not_attend, unconfirmed, awaiting_recall; the tool offers unconfirmed and confirmed), whether omitting end_time derives it from the service duration as the mock does, what the API's default status is, and whether notify_patient: false really suppresses every patient email. The mock answers 201; the spec documents 201.

  • POST /v1/bookings/{booking_id}/cancellations: whether the Organization requires cancellation_reason (the tool leaves it optional and passes on the 422), which statuses can be cancelled, and that an attendance penalty invoice is applied as the description says.

  • POST /v1/human_tasks: the API default of is_remindable when omitted (the mock uses true), and whether an unknown assignee_id answers 404 as the mock does.

  • Retries: a 429 is retried on the assumption that a rate-limited request was not processed at the edge (with the same Idempotency-Key on a write), and a 409 idempotency_conflict is retried with the same key as the guide says; neither has been observed on a live account. A write is never retried after a 5xx although the guide says a retry with the same key is safe, and a 500 or a network failure is never retried for any method.

  • The Idempotency-Replayed header is read from the response and would be reported as "already created (replayed)"; no test reaches that path (the mock stores nothing for a write it answered 409, 429 or 401), and whether a replay of a create answers 201 (the guide says the original status) is untested.

  • Rate limits: the 500 ms spacing is derived from the documented 120 requests per minute per token and has not been measured against the edge; the write limit of 30 per minute is not separately throttled. The token refresh margin (a minute, or half the lifetime of a token shorter than two minutes) assumes Carebit's expires_in is accurate.

  • The Carebit-Developer-Platform-API-Request-Id header is read case-insensitively and included in error messages, from the token endpoint and the retry paths included, whenever the response carried it; its presence on every response, and on token-endpoint and edge (429, 5xx) responses in particular, is as documented and untested.

Going to production

This version runs locally over stdio, with the practice's own API credential. For practices to connect from claude.ai or ChatGPT without handling credentials, the next step is a remote server (Streamable HTTP) behind OAuth, hosted by Carebit, and then a listing in the Claude and ChatGPT connector directories. A production version, tested on a real Organization, would also retry writes after a 5xx with the same Idempotency-Key as the guide documents, use the refresh grant, cover the staff-member, payor, note and letter endpoints, and add the PATCH tools for completing tasks and confirming, arriving and rescheduling bookings.

Licence

MIT. Built by Alexandru Dragoș (alexandru.dragos96@gmail.com) with an AI agent (Claude) working under his direction.

Available Tools

15 tools
find_availabilityFind bookable slotsA
Read-only

Times a clinician can be booked for a service variant (list_services gives service_variant_id). With start_date and end_date (at most 45 days inclusive) it lists every slot in the range (GET /v1/availability_slots); without end_date it returns the next slot on or after start_date (today when omitted), searching up to 8 months ahead (GET /v1/next_availability_slot). Results account for existing bookings, unavailability, service duration, buffers and rooms, include services patients cannot book online, and ignore the Patient Portal's minimum notice.

ParametersJSON Schema
NameRequiredDescriptionDefault
end_dateNoLast date to include, YYYY-MM-DD; when given, every slot in the range is listed
start_dateNoFirst date to search, YYYY-MM-DD (defaults to today for the next-slot search)
clinician_idYes
service_variant_idYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint and openWorldHint; the description adds substantial behavioral context the annotations cannot convey: the 45-day inclusive range cap, the 8-month lookahead limit, and that results account for existing bookings, unavailability, service duration, buffers and rooms, include non-online-bookable services, and ignore the Patient Portal minimum notice.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core verb+resource, then the branching behavior, then the result semantics. Dense but nearly every clause carries distinct information; it runs slightly long with a stacked list of result considerations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description does describe what comes back in natural language (every slot in range vs. the next slot), plus the search bounds and defaults. It stops short of describing the slot payload structure, but an agent has enough to invoke and interpret it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 50%, so the description must compensate, and it does: it explains the start_date default (today) for next-slot search, the 45-day inclusive end_date constraint, and the source of service_variant_id. clinician_id semantics remain implied rather than stated.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Times a clinician can be booked for a service variant') and distinguishes the two operating modes by input shape. It also names the sibling that supplies service_variant_id (list_services), so an agent can tell it apart from get_clinician_agenda and get_booking without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clear conditional usage: with end_date it lists every slot in range, without it returns the next slot on or after start_date (today when omitted). It also tells the agent where to get service_variant_id. It does not explicitly name when to prefer alternatives like get_clinician_agenda, so it stops short of 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_bookingGet a bookingA
Read-only

One booking: status, times, clinician, patient (name by default), service and variants, location, payor, recall and cancellation details. Contact details and the free-text information fields only with include_contact_details.

ParametersJSON Schema
NameRequiredDescriptionDefault
booking_idYes
include_contact_detailsNoInclude patient contact details, date of birth, NHS number, address, payor details and the booking's information fields, and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover safety (readOnlyHint) and open-world scope, so the bar is lower, and the description adds real value: it discloses that patient name is returned by default while contact details and free-text fields are gated behind include_contact_details, implying a redaction-by-default behavior. It does not mention error behavior for a missing/invalid booking_id.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the resource scope and then the conditional behavior. The field enumeration is dense but each item is informative; nothing is redundant with the schema or annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description carries the return-value burden and does so by enumerating the returned entities plus the conditional redaction rule. Adequate for a 2-parameter read tool, though it omits any note on what happens when the booking_id is not found.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 50% (booking_id has no description, include_contact_details is well documented). The description compensates by clarifying what the flag unlocks at the field level and confirming patient name appears by default, adding meaning beyond the raw schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('One booking') and enumerates the domain objects returned (status, times, clinician, patient, service, location, payor, recall/cancellation). It implicitly contrasts with list_bookings by scoping to a single record, but never names the sibling explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied by 'One booking' versus the sibling list_bookings. There is no explicit statement of when to use this instead of list_bookings or search_patients, nor any precondition guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_clinicianGet a clinicianC
Read-only

One clinician by id.

ParametersJSON Schema
NameRequiredDescriptionDefault
clinician_idYes
include_contact_detailsNoInclude the clinician's practice email address, and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

C2.3/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds nothing beyond that, and notably omits the significant data-handling behavior around contact details: the schema says contact info is redacted by default unless include_contact_details is set.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is short, but this is under-specification rather than conciseness. A four-word fragment front-loads nothing useful and every omitted detail (id required, redaction default) had to be recovered from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and only minimal annotations, the description carries the burden for a tool that returns sensitive clinician data. The default-redaction behavior and the nature of the returned record are never surfaced, leaving an agent to infer them from the schema alone.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 50%: clinician_id carries a UUID pattern but no prose description, while include_contact_details has a rich schema description. The description text supplies no parameter meaning at all, so it fails to compensate for the undocumented required id parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The fragment 'One clinician by id.' does state a verb+resource (retrieve a single clinician) and implicitly distinguishes itself from list_clinicians. However it is a terse fragment rather than a clear sentence, and it offers no sibling differentiation beyond the singular/plural contrast.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No when-to-use guidance is given. It does not say when to prefer get_clinician over list_clinicians, get_clinician_agenda, or find_availability, nor does it mention that an id must be known beforehand.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_clinician_agendaGet a clinician's diaryA
Read-only

A clinician's diary for a date range (at most 45 days inclusive, as the API documents): bookings, availability intervals and unavailability intervals in time order, each with start and end time, location and services. Bookings carry the patient's name; the patient's contact details, date of birth, NHS number and address, and the booking's free-text information, only with include_contact_details. Availability shows the schedule, not bookable times: use find_availability for those.

ParametersJSON Schema
NameRequiredDescriptionDefault
end_dateYesLast date to include, YYYY-MM-DD (inclusive; at most 45 days after start_date)
start_dateYesFirst date to include, YYYY-MM-DD
clinician_idYes
include_contact_detailsNoInclude patient contact details, date of birth, NHS number, address, payor details and the booking's information fields, and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint and openWorldHint, so the description carries the behavioral burden and does so: it discloses the 45-day API limit, the time-ordered return composition, and the redaction regime (email, phone, NHS-shaped digit groups, postcodes redacted unless include_contact_details). This goes well beyond what the annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the resource and the return contents, then the PII gating rule, then the sibling redirect. Three dense, clause-heavy sentences; all earn their place, though the parenthetical and nested lists make it slightly heavy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description compensates by describing the shape of the response (intervals in time order, each with start/end time, location, services; bookings carry patient name). Nothing critical an agent needs before calling this tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is high (75%) and already documents the date formats and the include_contact_details flag in detail. The description still adds value by framing what that flag unlocks in terms of agenda content (patient contact details, DOB, NHS number, address, free-text booking information).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (clinician's diary) with explicit scope (date range, max 45 days) and enumerates the returned entity types: bookings, availability intervals and unavailability intervals. It also distinguishes itself from the similarly-named sibling find_availability, so an agent can route without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names the alternative: 'Availability shows the schedule, not bookable times: use find_availability for those.' It also states the precondition that gates sensitive fields (contact data only with include_contact_details) and the 45-day constraint, giving both when-to-use and a key when-not-to-use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_invoiceGet an invoiceA
Read-only

One invoice by id with its line items. Line-item titles and invoice notes are the practice's own text and go through the redaction unless include_contact_details is set.

ParametersJSON Schema
NameRequiredDescriptionDefault
invoice_idYes
include_contact_detailsNoInclude the Patient Portal payment link, and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds genuinely useful behavioral context beyond that: line-item titles and invoice notes are the practice's own free text and are subject to redaction unless include_contact_details is set. It does not disclose not-found/error behavior, but it meaningfully extends the annotation profile.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero waste. The core capability (one invoice by id with line items) is front-loaded, and the redaction caveat follows immediately. Nothing is repeated from the schema or annotations verbatim.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description appropriately states what is returned (the invoice plus its line items) and explains the redaction behavior that affects the response. For a read-only, 2-parameter tool this is nearly complete; only not-found/error behavior is unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 50%: invoice_id carries only a UUID pattern while include_contact_details has a rich schema description. The prose compensates by explaining that free-text fields (line-item titles, notes) are the redaction target, which clarifies the practical effect of include_contact_details beyond the schema's own wording.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource+scope: 'One invoice by id with its line items', which clearly distinguishes a single-record fetch from the sibling list_invoices. It falls short of 5 only because it never names the sibling explicitly and 'get/one' is left implicit rather than stated as a verb.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'by id' — you fetch a specific invoice when you already have its id — but there is no explicit when-to-use guidance and no exclusion or alternative named (e.g., use list_invoices to find an id first). Adequate but leaves routing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_organizationGet the OrganizationA
Read-only

The Organization this credential belongs to (name, type, contact details, address, currency, time zone) and, by default, the access token's granted scopes and developer project, so you know which other tools will work.

ParametersJSON Schema
NameRequiredDescriptionDefault
include_token_infoNoAlso call GET /v1/token for the granted scopes and project

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, so safety is covered. The description adds value beyond them by disclosing that token info (granted scopes, developer project) is bundled in by default and why, i.e. that the call performs an extra lookup and reveals credential capability.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One dense sentence with the resource and its returned fields front-loaded, followed by the default-behavior clause and its rationale. The parenthetical field list is justified since there is no output schema, though the sentence is long enough to be slightly unwieldy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully enumerates the returned fields, and it explains the one optional parameter's default and purpose. For a read-only, single-parameter tool with annotations, this is close to complete; only pagination/error behavior is untouched, which is minor here.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter has 100% schema coverage, including its default of true and the underlying GET /v1/token call, so the schema does the heavy lifting. The description's 'by default' only restates the default and adds no format or syntax detail — the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the resource (the Organization this credential belongs to) and enumerates what comes back (name, type, contact details, address, currency, time zone), which is more specific than a bare 'get'. It does not explicitly distinguish itself from siblings, but the resource is unmistakable and non-overlapping with get_clinician/get_booking/etc.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a concrete reason to call it — 'so you know which other tools will work' — which is real usage context for an agent deciding whether it needs this. No when-not or alternative routing is given, but there is no plausible sibling confusion to disambiguate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_patientGet a patientA
Read-only

One patient by id. Name, sex and patient number by default; contact details, date of birth, NHS number, address, third-party identifier and payment-method link only with include_contact_details.

ParametersJSON Schema
NameRequiredDescriptionDefault
patient_idYes
include_contact_detailsNoInclude patient contact details, date of birth, NHS number, address, third-party identifier and payment-method link, and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds meaningful behavioral context beyond them: it discloses that sensitive fields are withheld/redacted by default and only surfaced with include_contact_details, which is exactly the kind of data-scoping behavior an agent must know before calling.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single compact sentence pair, front-loaded with scope ('one patient by id') followed by the default vs. expanded return distinction. Every clause carries information about what the caller gets back.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description usefully enumerates what is returned by default and what requires the flag, which is the key completeness gap for this tool. Missing only error/not-found behavior and pagination concerns (mostly irrelevant for a single-record fetch).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 50%: include_contact_details has a very thorough schema description, while patient_id has only a pattern. The description restates the default/maximal field sets, reinforcing which parameter expands output, but adds little beyond the schema's own text on the flag.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('One patient by id'), which implicitly separates it from list/search siblings such as search_patients and list_bookings. It never names an alternative explicitly, so it falls just short of full sibling differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a clear conditional for the include_contact_details flag (default vs. full detail), which is real usage guidance. However, it offers no guidance on when to call get_patient versus search_patients, so selection between siblings is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_bookingsList bookingsA
Read-only

Bookings in the Organization. Diary queries need start_time_from and start_time_to (UTC; a bare date is taken as the whole day), a range of at most 30 days, or 90 days with patient_id, ordered by start_time. For the recall statuses awaiting_recall, overdue_for_recall, recall_expired and recall_canceled omit the time window (those bookings have no start_time; results are ordered by recall_due_date); did_not_attend still needs it. Filters are ANDed and sent as documented: clinician_id, patient_id, status, updated_since. Patient names are returned; their contact details and the booking's free-text information only with include_contact_details.

ParametersJSON Schema
NameRequiredDescriptionDefault
cursorNonext_cursor from a previous call, to continue where it stopped (send the same filters)
statusNo
patient_idNoOnly this patient's bookings (allows a 90-day window)
max_resultsNoMaximum number of records to return (pages of up to 100 are fetched until this is reached)
clinician_idNo
start_time_toNoInclusive upper bound for start_time, YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD (a bare date means the end of that day, 23:59:59Z, so a 31-day calendar month given as bare dates is 31 days and must be split)
updated_sinceNoOnly bookings updated since this UTC time
start_time_fromNoInclusive lower bound for start_time, YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD
include_contact_detailsNoInclude patient contact details, date of birth, NHS number, address, payor details and the booking's information fields, and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safe-read profile (readOnlyHint=true), and the description adds genuinely non-obvious behavior: ordering by start_time vs recall_due_date, the redaction default that include_contact_details turns off, and row/history constraints. Does not cover pagination/cursor mechanics beyond the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense but front-loaded, with the core resource stated first and constraints following in a logical order. Sentences are long and clause-heavy, but each clause carries a distinct rule rather than repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter read tool with no output schema, the description covers the tricky call-shaping rules (window size, recall statuses, include_contact_details effects) so an agent can invoke it correctly. It leaves the exact returned field set and cursor semantics to the schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 78%, so the schema does most of the work, but the description adds real semantics: bare-date handling, the 30/90-day window tied to patient_id, AND-ing of filters, and which filters suppress or require the time window. That is meaningfully more than the schema states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource and scope ('Bookings in the Organization'), and the body distinguishes normal diary queries from recall-status queries. It does not name a sibling tool to differentiate against (e.g., get_booking, get_clinician_agenda), so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit conditional rules: diary queries need start_time_from/start_time_to, a max 30-day window (90 with patient_id), and recall statuses must omit the time window while did_not_attend still needs it. It lacks guidance on when to prefer this over sibling list/get tools, so not a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_cliniciansList cliniciansA
Read-only

The Organization's clinicians with display name, title, specialty and id (the id other tools take). Practice email addresses only with include_contact_details.

ParametersJSON Schema
NameRequiredDescriptionDefault
cursorNonext_cursor from a previous call, to continue where it stopped (send the same filters)
max_resultsNoMaximum number of records to return (pages of up to 100 are fetched until this is reached)
include_contact_detailsNoInclude clinician practice email addresses, and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint and openWorldHint, so the description's disclosure that contact details are withheld by default and only surfaced via include_contact_details is genuinely additive behavioral context. It does not, however, cover pagination or result-ordering behavior beyond what the schema already states.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with no filler; the resource and returned fields are front-loaded and the conditional behavior is attached at the end. Every clause carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description helpfully enumerates the returned fields and the privacy default, which covers the main gaps. Pagination and filtering are only in the schema, but the description is otherwise sufficient for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all three parameters (cursor, max_results, include_contact_details) are already fully documented in the schema. The description's restatement of the email-gating flag adds no syntax or semantics beyond that, making the baseline 3 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the resource (the Organization's clinicians) and enumerates the returned fields (display name, title, specialty, id), so an agent knows exactly what comes back. It even flags that the id is the one other tools consume. It stops short of explicitly contrasting itself with the singular get_clinician sibling, so it is clear but not fully differentiating.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied: list clinicians here, and reach for include_contact_details only when email addresses are needed. There is no explicit when-to-use/when-not guidance and no mention of get_clinician for per-clinician detail or the agenda/availability siblings, so the routing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_human_tasksList staff tasksA
Read-only

The Organization's staff task queue (HumanTasks), newest first: content, due date, urgency, completion, assignees, patient and document ids. Filters sent as documented: patient_id, staff_member_id, document_id, is_completed (false for open tasks only).

ParametersJSON Schema
NameRequiredDescriptionDefault
cursorNonext_cursor from a previous call, to continue where it stopped (send the same filters)
patient_idNo
document_idNoOnly tasks about this document (a letter, test result, photo or booking form)
max_resultsNoMaximum number of records to return (pages of up to 100 are fetched until this is reached)
is_completedNotrue for completed tasks only, false for open tasks only
staff_member_idNoOnly tasks assigned to this staff member
include_contact_detailsNoInclude nothing extra (task content is the practice's own text), and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description still adds behavioral value beyond that: results are 'newest first' and it enumerates the returned material (content, due date, urgency, completion, assignees, patient and document ids), which matters because there is no output schema. It does not mention pagination or result caps, which the schema covers.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences with the resource, ordering, returned fields and filters all front-loaded; nothing is padding. The phrase 'Filters sent as documented' is slightly circular, but overall it is tight and skimmable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, enumerating the returned fields and the newest-first ordering is exactly the missing context an agent needs. Read-only behavior is covered by annotations, so the only remaining gaps are pagination/limit behavior, which the schema itself documents.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 86%, so the schema already documents nearly every parameter, and the description largely restates the filter names. Its one interpretive note ('is_completed (false for open tasks only)') duplicates the schema text almost verbatim, so it adds little beyond the structured fields. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific resource (the Organization's staff task queue / HumanTasks) and states the ordering (newest first) plus the fields returned, so an agent knows exactly what this lists. It doesn't explicitly contrast with any sibling, but no sibling tool overlaps this resource, so differentiation is implicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the enumerated filters ('Filters sent as documented: patient_id, staff_member_id, document_id, is_completed'), which tells the agent how to narrow results, but it never states when to prefer this tool over alternatives or when a filter combination is pointless. Adequate but no explicit when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_invoicesList invoicesA
Read-only

Invoices in the Organization with status, totals, outstanding and paid amounts (minor currency units), supply date, payor type, patient and booking ids and line items. Filters sent as documented: booking_id, patient_id (ANDed). The Patient Portal payment link only with include_contact_details.

ParametersJSON Schema
NameRequiredDescriptionDefault
cursorNonext_cursor from a previous call, to continue where it stopped (send the same filters)
booking_idNo
patient_idNo
max_resultsNoMaximum number of records to return (pages of up to 100 are fetched until this is reached)
include_contact_detailsNoInclude the Patient Portal payment link, and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds real behavioral value beyond that: amounts are in minor currency units, filters combine with AND, and include_contact_details also stops redaction of emails, phones, NHS-shaped digit groups and postcodes — a non-obvious side effect an agent must know before setting it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences, front-loaded with what the resource returns followed by filter and flag behavior. The long field enumeration is information-bearing rather than padding, though it slightly crowds out the filtering guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully enumerates the returned fields and units, and covers the filtering and redaction behavior an agent needs. Pagination via cursor/max_results is left entirely to the schema, which is acceptable but slightly thin for a paginated list tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 60% schema coverage, the description compensates by documenting three of five parameters and adding semantics the schema lacks: booking_id and patient_id are ANDed, and include_contact_details gates the payment link. It adds nothing for cursor/max_results, but those are already explained in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states the resource (invoices in the Organization) with a specific enumeration of the returned fields (status, totals, outstanding/paid amounts, supply date, payor type, ids, line items). It is clearly distinguishable from get_invoice and list_payments by naming what the list returns and which filters apply. It does not explicitly say 'list all' but the plural resource plus filters makes the verb unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It tells the agent that booking_id and patient_id are ANDed and that the Patient Portal payment link requires include_contact_details, which is actionable usage context. However, it never says when to prefer this tool over get_invoice (single invoice) or list_payments, so routing guidance is implied rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_locationsList locationsB
Read-only

The Organization's locations (clinics) with their addresses.

ParametersJSON Schema
NameRequiredDescriptionDefault
cursorNonext_cursor from a previous call, to continue where it stopped (send the same filters)
max_resultsNoMaximum number of records to return (pages of up to 100 are fetched until this is reached)

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds that results include addresses, but says nothing about pagination, result ordering, or empty-set behavior beyond what the schema's cursor/max_results fields already imply.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single compact sentence that front-loads the resource and adds the returned detail. It is efficient, though very terse and could carry one more clause without bloat.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only list tool with no output schema, the description should carry more of the return-shape burden; it only notes that addresses are included. Pagination is documented in the schema, so the main gap is a lack of any note on result contents or ordering.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%: both cursor and max_results are fully documented in the schema, including the paging semantics. The description adds no parameter-level meaning, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the specific resource ('locations (clinics)') and the return content ('with their addresses'), which is clearer than a bare restatement. It does not explicitly name a sibling to distinguish from, but the resource is unambiguous relative to siblings like list_clinicians or list_bookings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use guidance, no mention of prerequisites, and no reference to any alternative tool. The agent is left to infer that this is the tool for enumerating locations purely from the name and resource.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_paymentsList paymentsA
Read-only

Payments in the Organization with status, amount (minor currency units), method type, payor type, invoice and patient ids and any refunds. Filters sent as documented: patient_id, paid_at_from, paid_at_to (UTC; payments with no paid_at are omitted when a bound is set). Stored card details are never returned.

ParametersJSON Schema
NameRequiredDescriptionDefault
cursorNonext_cursor from a previous call, to continue where it stopped (send the same filters)
paid_at_toNoInclusive upper bound for paid_at, YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD (a bare date means the end of that day)
patient_idNo
max_resultsNoMaximum number of records to return (pages of up to 100 are fetched until this is reached)
paid_at_fromNoInclusive lower bound for paid_at, YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD
include_contact_detailsNoInclude nothing extra (internal notes are the practice's own text), and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A3.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint/openWorldHint already covering the safety profile, the description adds real behavioral content: payments with no paid_at are dropped when a bound is set, stored card details are never returned, and include_contact_details controls redaction of emails, phones, NHS-shaped digits and postcodes. These are non-obvious semantics an agent could not get from annotations alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences with no filler, and the most consequential constraint (paid_at omission) is embedded early. The phrasing 'Filters sent as documented' is slightly awkward and the opening is a noun phrase rather than a verb-led statement, but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description carries the burden of describing returns and does so by enumerating the returned fields. Pagination is left to the schema's cursor/max_results descriptions, and the redaction and null-paid_at behaviors are covered — adequate for a six-parameter read tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 83%, so the schema already documents cursor, paid_at bounds and max_results. The description reinforces the paid_at omission rule and the redaction behavior of include_contact_details, but adds little on syntax or formats beyond the schema; the 'minor currency units' note describes a return field, not a parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies the resource (payments) and enumerates exactly what each record carries — status, amount, method/payor type, invoice and patient ids, refunds — which cleanly separates it from siblings like list_invoices and list_bookings. It never uses an explicit verb such as 'List', but the scope is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no statement of when to call this versus alternatives (get_invoice, list_invoices, get_patient) or of any prerequisites. 'Filters sent as documented' gestures at filter usage but the guidance is implicit, and nothing tells the agent when this tool is the wrong choice.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_servicesList servicesA
Read-only

The Organization's services with duration, online-bookable flag, tax rate and their variants (id, price in minor units, clinician and location it is specific to). Variant ids are what find_availability and create_booking take. Filter sent as documented: is_bookable_online. Names and descriptions are the practice's own text and go through the redaction unless include_contact_details is set.

ParametersJSON Schema
NameRequiredDescriptionDefault
cursorNonext_cursor from a previous call, to continue where it stopped (send the same filters)
max_resultsNoMaximum number of records to return (pages of up to 100 are fetched until this is reached)
is_bookable_onlineNoOnly services patients can book online
include_contact_detailsNoInclude nothing extra (service names and descriptions are the practice's own text), and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, and the description adds genuinely new context: names and descriptions are the practice's own text and are redacted unless include_contact_details is set. That discloses a data-handling trait beyond the annotations. It does not cover pagination behavior beyond the schema cursor, so not a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with what is returned, then the sibling linkage, then the filter note. Dense but every sentence carries information. Slightly packed phrasing costs it the top mark.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the burden of describing return values, and it does so (fields, variants, price in minor units). It also flags the redaction caveat. It omits pagination/volume expectations and edge cases, so it is strong but not exhaustive.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description still adds meaning by explaining the consequence of include_contact_details (stops redacting emails, phones, NHS numbers, postcodes) and by reiterating the is_bookable_online filter. This enriches the schema rather than merely restating it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the resource (the Organization's services) and enumerates the returned shape (duration, online-bookable flag, tax rate, variants with id, price in minor units, clinician, location). It also links to siblings by stating variant ids are what find_availability and create_booking take, so an agent can tell what this tool feeds. The verb 'list' is only implicit in the noun phrase, keeping it short of a perfect 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The line 'Variant ids are what find_availability and create_booking take' gives clear workflow context: call this before those tools to obtain variant ids. It also points at the is_bookable_online filter. There is no explicit when-not or exclusion guidance, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_patientsSearch patientsA
Read-only

Patients with an active connection to the Organization, filtered with the exact-match filters the API documents (all case-insensitive; ANDed): first_name, last_name, date_of_birth, email, phone_number, or up to 25 ids. There is no partial-name search. Without filters the whole connected list is paged. Names, sex and patient number are returned; contact details, date of birth, NHS number and address only with include_contact_details (a search by email or phone still confirms that such a record exists).

ParametersJSON Schema
NameRequiredDescriptionDefault
idsNoOnly these patient ids (at most 25)
emailNoExact email address
cursorNonext_cursor from a previous call, to continue where it stopped (send the same filters)
last_nameNoExact last name
first_nameNoExact first name
max_resultsNoMaximum number of records to return (pages of up to 100 are fetched until this is reached)
phone_numberNoExact phone or mobile number, E.164 where possible (+447700900123); a number without + or 00 is treated as UK
date_of_birthNoExact date of birth, YYYY-MM-DD
include_contact_detailsNoInclude patient contact details, date of birth, NHS number, address, third-party identifier and payment-method link, and stop redacting email addresses, phone numbers, NHS-number-shaped digit groups and UK postcodes typed into names and other text

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly/openWorld annotations, it discloses what the default response returns (names, sex, patient number), that contact details, DOB, NHS number and address require include_contact_details, and that the flag also stops redaction of text fields. It further explains that an email/phone search still confirms a record exists even without contact details — meaningful disclosure the annotations do not cover.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three dense sentences with no filler: the connection scope and filter semantics come first, then the no-filter paging case, then return-field visibility. The parenthetical about email/phone existence confirmation is the only aside and it earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description correctly carries the burden of describing return fields and their conditional visibility, plus paging via cursor and max_results interaction. For a 9-parameter read tool with zero required parameters, nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds semantics the schema omits: filters are case-insensitive and ANDed, no partial-name matching, and the 25-id cap. It also explains the pagination interaction ('without filters the whole connected list is paged') rather than just the cursor parameter itself.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description precisely scopes the resource ('patients with an active connection to the Organization') and the operation (exact-match filtered lookup, paged list without filters), which clearly separates it from the single-record get_patient sibling. It never states an explicit verb (e.g. 'search'/'list') and names no sibling, so it falls just short of the top band.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete usage context: filters are ANDed and exact-match, up to 25 ids, and calling with no filters pages the entire connected list. It also sets a negative boundary ('There is no partial-name search'), steering the agent away from substring queries, but it never points to an alternative tool or says when get_patient should be used instead.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 15 tool updatesv0.1.0
    • First observedfind_availability
    • First observedget_booking
    • First observedget_clinician
    • First observedget_clinician_agenda
    • First observedget_invoice
    • First observedget_organization
    • First observedget_patient
    • First observedlist_bookings
    • First observedlist_clinicians
    • First observedlist_human_tasks
    • First observedlist_invoices
    • First observedlist_locations
    • First observedlist_payments
    • First observedlist_services
    • First observedsearch_patients

TDQS

B3.4/5.0

Scored across 15 tools

Disambiguation4/5

Most tools target distinct resources or actions, but the scheduling-related tools (get_clinician_agenda, find_availability, list_bookings) all return diary, availability, or booking data, creating some risk of misselection. The descriptions help clarify boundaries, but the overlap is not fully eliminated.

Naming Consistency5/5

All tool names use snake_case with a consistent verb_noun pattern (get_, list_, search_, find_). The verbs are semantically appropriate and predictable, with no mixing of conventions.

Tool Count4/5

15 tools is at the upper end of the typical 3-15 range and covers most entities in the clinic domain. Each tool earns its place, though some list/get pairs could potentially be consolidated without losing functionality.

Completeness2/5

The surface is almost entirely read-only and misses key lifecycle operations: no create_booking despite being referenced by find_availability and list_services, no update or cancel booking, no patient creation, and no invoice or payment write actions. These gaps would block common clinic workflows and cause agent failures.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables integration with the Cliniko practice management system through MCP tools and resources. Supports patient management, appointment scheduling, and practice data access through natural language interactions.
    -
  • F
    license
    B
    quality
    D
    maintenance
    Provides integration with the Cliniko API for healthcare practice management, enabling patient, appointment, invoice, and payment operations via natural language.
    27
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables LLMs and MCP clients to interact with patient records, encounters, and practice information through tools for clinical, scheduling, messaging, and administrative workflows.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables Claude, ChatGPT and other MCP clients to search a membership database's people and organisations, read a contact's summary and membership history, list events with ticket types and attendance lists, read invoices, and find and count segments. When writes are enabled, it also lets clients record who attended an event.
    MIT