Skip to main content
Glama
mayankgupta-bluebash

Healthcare Appointment Scheduling MCP Server

README.md
# Healthcare Appointment Scheduling MCP Server

Local **mock** Model Context Protocol (MCP) server for healthcare appointment scheduling. Designed for development and testing with **Retell AI**.

All data is stored in local JSON files. There are **no** real EHR, calendar, or database integrations.

## Features

- Official `@modelcontextprotocol/sdk` with **Streamable HTTP** transport
- Zod-validated tool inputs
- Persistent local JSON storage (`data/`)
- Health endpoint for uptime checks
- Structured JSON tool responses for voice agents
- Request/response logging for debugging Retell tool calls

## Requirements

- Node.js **20+**
- npm 9+

## Installation

```bash
cd mcp
cp .env.example .env
npm install
```

## Run locally

Development (auto-reload):

```bash
npm run dev
```

Production build:

```bash
npm run build
npm start
```

Server URLs (default):

| Endpoint | URL |
|----------|-----|
| Health | `http://localhost:3000/health` |
| MCP | `http://localhost:3000/mcp` |

Health response:

```json
{
  "status": "ok",
  "service": "healthcare-mcp-server"
}
```

## Environment variables

Copy `.env.example` to `.env`:

```env
PORT=3000
HOST=0.0.0.0
```

- `PORT` — HTTP port
- `HOST` — bind address (`0.0.0.0` required when exposing via ngrok / Cloudflare Tunnel)

## Expose with ngrok

Retell AI needs a public HTTPS URL.

1. Start the MCP server locally (`npm run dev`).
2. In another terminal:

```bash
ngrok http 3000
```

3. Copy the HTTPS forwarding URL, for example:

```text
https://abc123.ngrok-free.app
```

4. MCP endpoint for Retell:

```text
https://abc123.ngrok-free.app/mcp
```

### Cloudflare Tunnel (alternative)

```bash
cloudflared tunnel --url http://localhost:3000
```

Use the generated HTTPS URL + `/mcp`.

## Connect to Retell AI

1. Open your Retell AI agent / MCP settings.
2. Add a custom MCP server URL pointing to:

```text
https://<your-public-host>/mcp
```

3. Save and refresh tools so Retell can call `tools/list`.
4. Confirm the agent can see all eight tools listed below.
5. Place a test call and watch local server logs for `[MCP] TOOL CALL` / `TOOL RESPONSE`.

This server uses Streamable HTTP with session IDs (`Mcp-Session-Id`) and JSON responses (`enableJsonResponse: true`), which works with standard MCP HTTP clients.

## Available MCP tools

| Tool | Purpose |
|------|---------|
| `search_patients` | Find patients by first/last name, DOB, phone, or email |
| `create_patient` | Create a patient (with duplicate checks) |
| `get_providers` | List active providers (optional specialty/name filter) |
| `get_appointment_types` | List active appointment types / durations |
| `get_available_slots` | List open slots for provider + type + date |
| `get_appointments` | List a patient's future scheduled appointments by `patient_id` |
| `create_appointment` | Book an appointment (prevents double booking) |
| `update_appointment` | Reschedule / update an appointment |
| `cancel_appointment` | Soft-cancel (status=`cancelled`, record kept) |
| `request_prescription_refill` | Request a prescription refill for a patient |

## Example inputs and outputs

### `search_patients`

Input:

```json
{
  "first_name": "John",
  "last_name": "Doe"
}
```

Output:

```json
{
  "success": true,
  "count": 1,
  "patients": [
    {
      "id": "patient_001",
      "first_name": "John",
      "last_name": "Doe",
      "full_name": "John Doe",
      "date_of_birth": "1990-01-15",
      "phone_number": "5551234567",
      "email": "john.doe@example.com"
    }
  ]
}
```

### `create_patient`

Input:

```json
{
  "first_name": "Alex",
  "last_name": "Rivera",
  "date_of_birth": "1993-04-02",
  "phone_number": "5559998888",
  "email": "alex.rivera@example.com"
}
```

### `get_available_slots`

Input:

```json
{
  "provider_id": "provider_001",
  "appointment_type_id": "appt_type_002",
  "date": "2026-09-10"
}
```

Notes:

- Provider hours: **Monday–Friday 09:00–17:00**
- Duration comes from the appointment type
- Existing **scheduled** appointments are excluded

### `get_appointments`

Input:

```json
{
  "patient_id": "patient_001"
}
```

Output:

```json
{
  "success": true,
  "patient_id": "patient_001",
  "count": 1,
  "appointments": [
    {
      "id": "appointment_001",
      "patient_id": "patient_001",
      "provider_id": "provider_001",
      "appointment_type_id": "appt_type_002",
      "date": "2026-09-10",
      "start_time": "10:00",
      "end_time": "10:15",
      "status": "scheduled",
      "notes": "Follow-up for blood pressure check"
    }
  ]
}
```

Past and cancelled appointments are excluded.

### `create_appointment`

Input:

```json
{
  "patient_id": "patient_001",
  "provider_id": "provider_001",
  "appointment_type_id": "appt_type_002",
  "date": "2026-09-10",
  "start_time": "09:00",
  "notes": "Blood pressure follow-up"
}
```

### Error response shape

```json
{
  "success": false,
  "error": {
    "code": "SLOT_NOT_AVAILABLE",
    "message": "The requested appointment slot is not available..."
  }
}
```

Common codes: `PATIENT_NOT_FOUND`, `PATIENT_ALREADY_EXISTS`, `PROVIDER_NOT_FOUND`, `APPOINTMENT_TYPE_NOT_FOUND`, `APPOINTMENT_NOT_FOUND`, `SLOT_NOT_AVAILABLE`, `INVALID_DATE`, `INVALID_INPUT`.

## JSON data files

Located under `data/`:

| File | Contents |
|------|----------|
| `patients.json` | Patient demographics |
| `providers.json` | Providers (active/inactive) |
| `appointment-types.json` | Visit types and durations |
| `appointments.json` | Scheduled / cancelled appointments |
| `prescription-refills.json` | Prescription refill requests |

Behavior:

- Tools read and write these files dynamically
- Missing files are created as `[]`
- Writes use a temp file + rename to reduce corruption risk
- Changes survive server restarts

Seed data includes 12 patients, 7 providers (6 active), 7 appointment types (6 active), and several sample appointments.

## Project structure

```text
src/
  index.ts          # HTTP server + Streamable HTTP transport
  server.ts         # MCP server + tool registration
  tools/            # MCP tool definitions
  services/         # Business logic
  utils/            # JSON storage, IDs, dates, responses
  types/            # Shared TypeScript types
data/               # Persistent mock JSON data
```

## Inspecting logs (Retell debugging)

When Retell calls a tool, the server prints:

```text
[MCP] Incoming POST /mcp ...
[MCP] TOOL CALL search_patients {...}
[MCP] TOOL RESPONSE search_patients {...}
```

Tips:

1. Keep `npm run dev` in a visible terminal while testing Retell.
2. Confirm `/health` is reachable through the tunnel before configuring Retell.
3. If tools are missing, verify the MCP URL ends with `/mcp`.
4. If sessions fail, ensure the client preserves `Mcp-Session-Id` after `initialize`.

## Manual smoke test (curl)

Initialize:

```bash
curl -s http://localhost:3000/health
```

List tools (after starting a session with an MCP client) is easiest with the SDK client. A quick end-to-end check (server must already be running):

```bash
npm run test:smoke
```

## Troubleshooting

| Issue | Fix |
|-------|-----|
| Port already in use | Change `PORT` in `.env` |
| Retell cannot reach server | Confirm ngrok is running and URL uses `/mcp` |
| Empty tool list | Restart server, re-add MCP URL in Retell, check initialize logs |
| Double booking allowed? | Should not happen — check `appointments.json` status is `scheduled` |
| Weekend slots empty | Expected — only Mon–Fri 09:00–17:00 |
| Duplicate patient created | Duplicate phone or name+DOB returns `PATIENT_ALREADY_EXISTS` |
| Data reset | Re-copy seed JSON from git or restore `data/*.json` |

## License

MIT

Maintenance

ActivityMaintained
ResponsivenessNo issues