Skip to main content
Glama
clinic-mcp-server

clinic-mcp-server

by leomengineer

clinic-mcp-server

MCP Server: Company Data as Claude Tools.

Expose a clinic's knowledge base and structured records to Claude Desktop (or any MCP client) as callable tools — with the same grounding discipline as docs-rag-chatbot: read tools grounded with citations; write tools gated behind approval.

That contrast is the pitch. Anyone can wrap a search endpoint. Showing you thought about what happens when an agent can change something is what senior looks like.

Architecture

Claude Desktop (stdio MCP)
        │
        ▼
 clinic-mcp-server
   ├─ search_clinic_docs  → hybrid RRF over shared `chunks` (pgvector + tsvector)
   │                         + SIMILARITY_FLOOR gate → citations or "no relevant sources"
   ├─ get_patient_record  → Postgres `patients` (+ next confirmed appointment)
   ├─ list_appointments   → parameterized date-range query on `appointments`
   └─ create_appointment  → INSERT into `appointment_requests` (pending only)
                             NEVER writes confirmed `appointments`

Shares DATABASE_URL with docs-rag-chatbot (same Postgres). Retrieval code is local; the RAG chunks table is reused as-is. Clinic tables (patients, appointments, appointment_requests) are additive.

Related MCP server: Patient Data MCP Server

Prerequisites

  1. Postgres/pgvector running for docs-rag-chatbot with docs ingested (make up && make ingest there).

  2. Python 3.12 + uv.

Quick start (two minutes)

cd clinic-mcp-server
uv sync
cp .env.example .env   # DATABASE_URL should match docs-rag-chatbot

# Create clinic tables + seed Jordan Lee et al. (does not touch chunks)
make setup

# Smoke-test over stdio (or point Claude Desktop at it — see below)
make serve

Claude Desktop config

Add this to your Claude Desktop claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json). Use absolute paths:

{
  "mcpServers": {
    "clinic": {
      "command": "/Users/leovet/.cargo/bin/uv",
      "args": [
        "run",
        "--directory",
        "/Users/leovet/freelance/clinic-mcp-server",
        "python",
        "-m",
        "clinic_mcp"
      ]
    }
  }
}

Restart Claude Desktop. You should see four tools under the clinic server.

A ready-to-merge example lives at claude_desktop_config.example.json.

Tip: find uv with which uv if your install path differs.

Demo (~60s)

  1. Open Claude Desktop with the MCP server connected — show the four tools.

  2. Ask: "Do you accept Delta Dental PPO?"
    → Claude calls search_clinic_docs, answers with a source citation (e.g. 06_insurance_faq.md).

  3. Ask: "when is Jordan Lee's next appointment?"
    → Claude calls get_patient_record with jordan-lee, returns structured data (insurance, upcoming crown consult).

  4. Ask: "book Jordan a crown consult next Tuesday"
    → Claude calls create_appointment; response is pending_approval — nothing committed to the confirmed calendar.

  5. Close: "Read tools answer. The write tool proposes and waits for a human."

Tools

Tool

Mode

Behavior

search_clinic_docs(query, top_k=5)

read

Hybrid retrieval; below SIMILARITY_FLOOR returns status=no_relevant_sources (empty chunks) — Claude must not invent.

get_patient_record(patient_id)

read

Patient row + next confirmed appointment. Missing patient / DB down → tool error (not empty success).

list_appointments(date_from, date_to)

read

Confirmed appointments in an inclusive date window (clinic TZ). Empty list is valid; DB failure is an error.

create_appointment(patient_id, datetime, service)

write (gated)

Inserts appointment_requests.status='pending' only. Returns pending_approval.

Demo patient id: jordan-lee.

Config (.env)

Variable

Purpose

Default

DATABASE_URL

Shared Postgres (same as RAG app)

postgresql://rag:rag@localhost:5432/rag

SIMILARITY_FLOOR

Dense cosine gate for doc search

0.35

CLINIC_TZ

Timezone for date windows / naive datetimes

America/Los_Angeles

Makefile

make sync     # uv sync --extra dev
make setup    # schema + seed
make schema   # clinic tables only
make seed     # demo patients / appointments
make serve    # stdio MCP server
make test     # pytest

Safety contract

  • Grounded reads: search_clinic_docs refuses weak matches instead of returning noise Claude could cite.

  • Loud failures: DB outages raise [db_unavailable] … tool errors. Empty appointment lists are only returned when the query succeeded.

  • Guarded writes: create_appointment never inserts into appointments. A human must approve the pending request before anything is confirmed.

  • Out of scope (intentionally): auth, multi-tenancy, OAuth, remote hosting, MCP resources/prompts. Four tools done well beats twelve half-wired.

Tests

make test

Covers tool discovery/schemas, Pydantic validation, retrieval gate + citations, structured errors, patient/appointment lookups, and the invariant that write calls create pending requests only.

Available Tools

4 tools
create_appointmentCreate appointment (pending approval)A

Propose a new appointment — does NOT confirm it.

Inserts a row into appointment_requests with status=pending and returns
pending_approval. A human must approve before anything lands on the
confirmed appointments calendar. Calling this twice creates two pending
requests; it never silently books.

Inputs: service (required), datetime (required), patient_id (required). Returns: Write tool always returns a *pending* request — never a confirmed booking.

get_patient_recordGet patient recordA
Read-only

Look up a patient by id: name, insurance, last visit, and next confirmed appointment.

Raises a tool error if the patient does not exist or the database is down —
never returns an empty success that could be mistaken for 'no appointments'.

Inputs: patient_id (required). Returns: status, patient

list_appointmentsList appointmentsA
Read-only

List confirmed appointments between date_from and date_to (inclusive).

date_to must be on or after date_from. Empty list with status=ok means no
confirmed appointments in that window — distinct from a database failure,
which raises a tool error.

Inputs: date_to (required), date_from (required). Returns: count, status, date_to, date_from, +1 more

search_clinic_docsSearch clinic documentsA
Read-only

Hybrid-search the clinic knowledge base (pgvector + full-text, RRF-fused).

Returns cited chunks with source filename and scores when the best dense
similarity clears the SIMILARITY_FLOOR gate. Below that floor, returns
status=no_relevant_sources with an empty chunk list — do not fabricate
an answer from weak matches.

Inputs: query (required), top_k. Returns: query, chunks, status, message, +2 more

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A4.3/5.0

    Scored across 4 tools

    Disambiguation5/5

    Each tool addresses a distinct task: searching knowledge base, retrieving patient record, listing appointments, and creating appointment requests. No functional overlap exists.

    Naming Consistency5/5

    All tools follow a consistent verb_noun snake_case pattern (search_clinic_docs, get_patient_record, list_appointments, create_appointment).

    Tool Count5/5

    4 tools are well-scoped for a clinic management server, covering search, patient lookup, appointment listing, and appointment creation. Neither too few nor too many.

    Completeness3/5

    The set covers basic read and create operations, but lacks tools for updating patient records, confirming/cancelling appointments, or deleting requests, which are notable gaps for a full appointment management workflow.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers