clinic-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@clinic-mcpFind available slots for general practice at clinic_north next Monday."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
clinic-mcp
A reference Model Context Protocol server for clinic scheduling and intake. Built in TypeScript with strict typing, structured errors, and tenant isolation enforced at the data layer. The data is synthetic. This is not clinical software.
The goal is to show what a production-shaped MCP server looks like for a vertical that demands data isolation and grounded outputs: the same shape of code I write at Rentive, with mock data and a different domain so the patterns are reviewable without leaking anything proprietary.
Why MCP
LLM applications keep reinventing the same wiring: ad-hoc function definitions per provider, bespoke argument parsing, no shared transport, no consistent error model. MCP is a small open protocol that fixes the wiring layer. A server exposes a list of typed tools over stdio (or HTTP), and any MCP-aware client (Claude Desktop, IDE integrations, custom agents) can discover and call them with the same machinery.
For domain backends, that means you write tools once and they work everywhere. For agent builders, it means you stop hand-rolling tool schemas and start composing servers.
Related MCP server: tenant-scoped-crm
Architecture
flowchart LR
Client["MCP client<br/>(Claude Desktop, custom agent)"]
Server["clinic-mcp server"]
Tools["Tools<br/>find_available_slot<br/>book_appointment<br/>record_intake<br/>search_protocols<br/>escalate_to_oncall"]
Store["ClinicStore<br/>tenant-scoped accessors"]
Seed[("seed.json<br/>synthetic clinics, providers,<br/>patients, protocols")]
Client -->|stdio JSON-RPC| Server
Server --> Tools
Tools --> Store
Store --> SeedEvery tool takes a clinic_id and the store enforces that all reads and writes are scoped to that clinic. Cross-tenant access throws TenantMismatchError rather than silently returning the wrong row. This mirrors the row-level-security pattern a production deployment would enforce in Postgres, surfaced here in application code so the guarantee is reviewable in one file (src/store/index.ts).
Run it locally
Requires Node 20+ and pnpm.
git clone https://github.com/dominikstefanski/clinic-mcp.git
cd clinic-mcp
pnpm install
pnpm test # 29 tests
pnpm typecheck
pnpm dev # boots the server on stdioThe server reads src/store/seed.json at startup and serves two synthetic clinics: clinic_north (general practice, cardiology, dermatology) and clinic_west (pediatrics, general practice).
Wire into Claude Desktop
Add this to your Claude Desktop config (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json). Replace the path with your local clone.
{
"mcpServers": {
"clinic-mcp": {
"command": "npx",
"args": ["-y", "tsx", "/absolute/path/to/clinic-mcp/src/server.ts"]
}
}
}Restart Claude Desktop. The five tools will appear under the connections menu. Try a prompt like "Find a general practice opening at clinic_north next Monday morning."
Tool reference
All tools return { ok: true, ...result } on success or { ok: false, error: { code, message } } on failure. Inputs are validated with zod; MCP-level argument errors are returned as validation errors with field details.
find_available_slot
Find open appointment slots for a specialty in a date range, skipping conflicts.
Field | Type | Notes |
| string | Required |
| enum |
|
| string | Inclusive ISO 8601 start |
| string | Exclusive ISO 8601 end |
| int | 15 to 120, default 30 |
| int | 1 to 50, default 10 |
book_appointment
Create an appointment. Requires a caller-supplied idempotency_key; replays return the original appointment instead of double-booking. Voice agents will retry, so this is non-optional.
Field | Type | Notes |
| string | Required |
| string | Must belong to |
| string | Must belong to |
| string | ISO 8601 |
| int | 15 to 120, default 30 |
| string | 1 to 500 chars |
| string | 8 to 128 chars, caller-supplied |
Returns { appointment, idempotent_replay }.
record_intake
Persist a structured intake note and assign a triage level.
Field | Type | Notes |
| string | Required |
| string | Must belong to |
| string[] | 1 to 20 entries |
| int | 1 to 10, patient-reported |
| string | ISO 8601 |
| string | Optional, max 2000 chars |
Triage rule: severity >= 8 is urgent, >= 5 is elevated, otherwise routine.
search_protocols
Keyword search over the clinic's protocol library. Returns ranked snippets the model can cite when answering.
Field | Type | Notes |
| string | Required |
| string | 1 to 500 chars |
| int | 1 to 20, default 5 |
The current implementation is a naive TF score with title weighting (3x). It exists to demonstrate the interface of a retrieval tool; production deployments would swap the backend for vector search (see Design notes).
escalate_to_oncall
Mark an existing appointment as urgent and reassign it to the clinic's on-call provider.
Field | Type | Notes |
| string | Required |
| string | Must belong to |
| string | 1 to 500 chars, appended to the appointment's reason |
Returns { appointment, on_call_provider, reassigned }.
Design notes
Tenant isolation is enforced at the store, not the tool. Tools accept a clinic_id and pass it down. The store validates ownership on every accessor and throws TenantMismatchError on mismatch. If you add a new tool tomorrow, you cannot accidentally leak across clinics; the store will not let you.
Idempotency on writes. book_appointment requires an idempotency_key. Real callers (voice agents, retry loops, network blips) will repeat requests, and a healthcare system that responds to retries by creating duplicate appointments is a healthcare system that loses trust on day one.
Structured errors over thrown strings. Every domain failure is a typed DomainError subclass with a stable code. The MCP wrapper turns them into { ok: false, error: { code, message } }. Clients can branch on code instead of regexing message.
The retrieval tool is a stand-in. search_protocols uses an in-memory TF score so the repo runs without external services. In production this is the seam where you wire in Pinecone, pgvector, or your retrieval backend of choice. The tool's input/output contract stays the same.
Time handling is simplified. Provider working hours are interpreted in UTC for clarity. A real deployment would respect each clinic's timezone (already in the schema). Calling this out explicitly so reviewers know it's intentional, not an oversight.
What this isn't
Not clinical software. The triage rule is a toy and the protocol corpus is hand-written prose. Do not use it for anything that touches real patients.
Not HIPAA-compliant. The data is fake, the storage is in-memory, there is no audit log. Production would need all of that and then some.
Not a complete EMR or scheduling backend. The point is to show the MCP-server shape, not to ship a clinic system.
License
MIT. See LICENSE.
Available Tools
5 toolsbook_appointmentBook an appointmentB
Create an appointment for a patient with a provider. Requires an idempotency_key; repeated calls with the same key return the original appointment instead of double-booking.
| Name | Required | Description | Default |
|---|---|---|---|
| clinic_id | Yes | ||
| provider_id | Yes | ||
| patient_id | Yes | ||
| start_iso | Yes | Appointment start, ISO 8601 | |
| duration_minutes | No | ||
| reason | Yes | ||
| idempotency_key | Yes | Caller-supplied key. Repeated calls with the same key return the original appointment instead of double-booking. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses idempotency behavior (returns original on repeat), which is useful for safe retries, but omits other behavioral traits like required permissions, side effects, or error states.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences: one states the core purpose, the other adds critical idempotency detail. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has 7 parameters, no output schema, and zero annotations, the description is incomplete. It does not explain return value, error handling, or scheduling constraints like business hours.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 29% (2/7 params described). The description does not add meaning for undocumented parameters (e.g., clinic_id, provider_id, patient_id, duration_minutes, reason) beyond their names, failing to compensate for low coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that the tool creates an appointment for a patient with a provider, distinguishing it from sibling tools like find_available_slot (which finds slots) and record_intake (which records intake).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not provide guidance on when to use this tool versus alternatives like find_available_slot, nor does it specify prerequisites or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
escalate_to_oncallEscalate an appointment to the on-call providerC
Mark an existing appointment as urgent and reassign it to the clinic's on-call provider.
| Name | Required | Description | Default |
|---|---|---|---|
| clinic_id | Yes | ||
| appointment_id | Yes | ||
| reason | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries full burden. It only states 'mark as urgent and reassign' without detailing side effects (e.g., notifications, status changes) or reversibility.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence is efficient but omits critical details; adequate but could include a brief second sentence without losing conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Lacks output description and any mention of consequences or constraints (e.g., irreversibility), leaving the agent uninformed for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, and description adds no parameter details beyond names. 'reason' is not explained as the reason for urgency; clinic_id/appointment_id are self-evident but lack any validation context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('mark as urgent and reassign') and resource ('existing appointment'), and clearly distinguishes from sibling tools like book_appointment or find_available_slot.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no guidance on when to use this tool vs alternatives, no prerequisites (e.g., appointment must exist), and no conditions to avoid (e.g., already escalated).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_available_slotFind available appointment slotsB
Return open appointment slots for a given specialty in a date range. Filters by clinic_id and skips conflicts with existing appointments.
| Name | Required | Description | Default |
|---|---|---|---|
| clinic_id | Yes | ||
| specialty | Yes | ||
| from_iso | Yes | Inclusive start of the search window, ISO 8601 | |
| to_iso | Yes | Exclusive end of the search window, ISO 8601 | |
| duration_minutes | No | ||
| limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses that it 'skips conflicts with existing appointments,' indicating a read-only, availability-checking behavior. However, it does not mention whether it requires authentication, whether it is idempotent, or any side effects. The behavioral disclosure is partial but not misleading.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the main action. Every word serves a purpose: what it returns, the key filters, and the conflict avoidance. No fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite moderate complexity (6 params, no output schema), the description omits important details: return format (list of slot times?), ordering, pagination, error conditions. The sibling 'book_appointment' suggests a workflow, but no linkage is provided. The description is too sparse for complete agent understanding.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is low (33%, only from/to have descriptions). The description adds meaning for specialty and date range, and mentions clinic_id filtering. But it does not explain duration_minutes or limit parameters. Given low coverage, the description partially compensates but leaves gaps.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states 'Return open appointment slots' with specific constraints ('given specialty in a date range', 'Filters by clinic_id'). This distinguishes it from sibling tools like 'book_appointment' (booking) and 'record_intake' (recording). The verb and resource are immediately clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not provide any guidance on when to use this tool versus alternatives, such as before booking an appointment or when an immediate slot is needed. It describes what it does but lacks explicit context for selection among siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
record_intakeRecord patient intakeB
Persist a structured intake note (symptoms, severity, onset) for a patient and assign a triage level (routine / elevated / urgent).
| Name | Required | Description | Default |
|---|---|---|---|
| clinic_id | Yes | ||
| patient_id | Yes | ||
| symptoms | Yes | ||
| severity | Yes | Patient-reported severity, 1 (mild) to 10 (worst imaginable) | |
| onset_iso | Yes | When symptoms began, ISO 8601 | |
| notes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations; description only states it persists and assigns triage level, but fails to disclose side effects, authorization needs, or how triage is determined.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence of 15 words, no redundancy, efficient and direct.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema, no description of return value (e.g., triage level or record ID), insufficient to fully specify tool behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Description mentions 'symptoms, severity, onset' but omits 'clinic_id', 'patient_id', and 'notes'. Schema coverage is 33%, leaving param gaps unaddressed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('persist') and the resource ('structured intake note') with key fields, distinguishing it from sibling tools like 'book_appointment'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus alternatives. No mention of prerequisites or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_protocolsSearch clinic protocol documentsB
Keyword search over the clinic's protocol library. Returns ranked snippets the model can cite when answering.
| Name | Required | Description | Default |
|---|---|---|---|
| clinic_id | Yes | ||
| query | Yes | ||
| limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Without annotations, the description carries full burden. It discloses that the tool returns ranked snippets that can be cited, which is informative, but lacks details on side effects, authentication, or rate limits. The behavior is adequately described for a simple read-only search.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise with two front-loaded sentences, each adding value: it states the action and the return format. No extraneous words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the lack of output schema and parameter descriptions, the description is incomplete. It does not specify return structure beyond 'snippets', nor does it clarify the role of clinic_id. The tool requires more context for reliable use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the description does not explain any parameters (clinic_id, query, limit). The agent must infer semantics solely from the parameter names and constraints, which is insufficient.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool performs keyword search over the clinic's protocol library and returns ranked snippets for citation, making its purpose distinct from sibling tools that handle appointments, escalation, slot finding, and intake recording.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for searching protocols via keyword, but does not provide explicit guidance on when to use this tool versus alternatives or when not to use it.
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. Dates show when Glama detected each change.
5 tool updates
v0.1.0- First observed
book_appointment - First observed
escalate_to_oncall - First observed
find_available_slot - First observed
record_intake - First observed
search_protocols
TDQS
Each tool has a clearly distinct purpose: booking appointments, escalating to on-call, finding slots, recording intake, and searching protocols. No two tools overlap in functionality.
All tool names follow a consistent verb_noun pattern in snake_case, making it easy to predict the action and resource for each tool.
With exactly 5 tools, the server is well-scoped for a clinic domain, covering core patient-facing and administrative workflows without being overly heavy or thin.
The set covers appointment creation, urgent escalation, slot availability, intake recording, and protocol search. However, it lacks read/update/delete for appointments and any patient or provider management, which are notable gaps.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Hosted MCP server for the Healthie EHR & telehealth API: patients, appointments, charting, tasks.
Hosted MCP server for Cliniko — patients, appointments, availability, and invoices for AI agents.
MCP server for Modern Treasury — payment orders, transactions, counterparties and ledgers.
- StytchOAuthdev.stytch.mcp
The Stytch MCP server is a reference implementation that demonstrates remote MCP server authentication and authorization using Stytch Connected Apps. It provides OAuth 2.1-compliant authorization (including PKCE), Dynamic Client Registration, and validates Stytch-issued access tokens to enable AI agents to securely interact with external services through permissioned access, supporting scopes like openid, email, profile, and manage:project_data.
Related MCP Servers
- FlicenseAqualityBmaintenanceA learning MCP server providing synthetic FHIR patient data with read tools and a gated write workflow (propose → human approve → commit) with structured audit logging.10-
- AlicenseNot gradedqualityCmaintenanceA reference MCP server demonstrating safe agent access to multi-tenant CRM data with tenant isolation enforced in the data layer, role-based permissions, and human confirmation on writes.MIT
- AlicenseAqualityBmaintenanceA Claude-compatible MCP server that exposes health-domain tools over 100% synthetic data, built with security and compliance in mind.4MIT
- AlicenseNot gradedqualityBmaintenanceAn MCP server reference implementation adding enterprise layers (identity, authorization, audit) around MCP, with sample shipment status and delayed shipment tools over a stateless transport.Apache 2.0
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/dominikstefanski/clinic-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server