ClinicDesk MCP
Click on "Deploy 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., "@ClinicDesk MCPI had a tooth removed yesterday and my cheek is swollen, is that normal?"
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.
ClinicDesk MCP
A self-hosted MCP server that lets Alexa+ run a dental clinic's front desk — and answer the 2am question a patient cannot type.
Built for the Alexa+ track of the Amazon Developer Hackathon.
Try it live: clinicdesk-mcp.vercel.app — type a symptom and the page calls the running server.
MCP endpoint: https://clinicdesk-mcp.vercel.app/mcp — add it to any MCP host as a remote server.
Streamable HTTP · MCP spec revision 2025-11-25 · 11 tools · deploys to Cloudflare Workers or Vercel.
The problem this exists for
A patient who had a tooth out this morning wakes at 2am. The gauze is soaked, their cheek is swollen, and they do not know whether this is normal or an emergency. Their mouth hurts, their hands are occupied holding gauze, and the clinic closed at seven.
What they do instead is search, land on a forum, and either panic about nothing or ignore something that needed a hospital. Both happen constantly.
This is the case voice is genuinely better at than a screen — and it is a case where a general assistant guessing from its training data is exactly the wrong thing. ClinicDesk makes the assistant read the clinic's own written protocol back, and nothing else.
"Alexa, I had a tooth out yesterday and my cheek is swollen."
→ Swelling on day 1 after a tooth extraction is within what we expect,
so nothing is going wrong. Keep going with the aftercare.
"Alexa, I'm having trouble swallowing and my neck is swelling."
→ That needs to be seen now, not tomorrow. Please call the emergency
line on +20 100 555 0199 straight away, and if you are struggling to
breathe or swallow, go to the nearest emergency department instead
of waiting for a call back.The second one is a spreading infection heading for the airway. Getting that answer wrong is the only failure in this project that can actually hurt someone, so most of the engineering went there — see Safety.
Related MCP server: careflow-mcp
Quick start
git clone https://github.com/Y385471/clinicdesk-mcp && cd clinicdesk-mcp
npm install
# point it at your clinic database
cp .dev.vars.example .dev.vars # fill in SUPABASE_URL + SUPABASE_SERVICE_KEY
npm run db:setup # creates tables and seeds a demo clinic
npm run dev # → http://localhost:8787/mcp
npm test # 36 tests, including the triage safety batteryDeploy — it runs on either host, from the same source:
# Cloudflare Workers — one Durable Object per client, so sessions are stateful
npm run deploy
npm run secrets:push
npm run smoke -- https://clinicdesk-mcp.<account>.workers.dev/mcp
# Vercel — stateless function, static landing page
npx vercel login # once
npx vercel env add SUPABASE_URL production
npx vercel env add SUPABASE_SERVICE_KEY production
npx vercel deploy --prod
npm run smoke -- https://<project>.vercel.app/mcpsrc/tools.ts holds all eleven tools and knows nothing about either host;
src/index.ts and api/mcp.ts are the two transports, about fifteen lines
apart. The Worker keeps a session per client in a Durable Object. The Vercel
function is stateless — sessionIdGenerator: undefined, a fresh server per
request, JSON responses rather than an open SSE stream, since a function is
billed for the time a stream stays open and no tool here pushes to the client.
The Vercel function can be run and smoke-tested locally without a login:
npm run dev:vercel # compiles api/ and serves it on :8799
npm run smoke -- http://localhost:8799/api/mcpThree routes:
Route | What it serves |
| A page showing the server is live, with a panel that runs |
| The MCP endpoint. Connect any host here. |
| Plain JSON heartbeat. |
Run npm run smoke after every deploy. It does a real initialize → tools/list → tools/call and asserts the three dispositions. A deployment that has lost its secrets still returns 200 on /health and still serves the landing page — every tool just fails inside its result. That happened here once, and a heartbeat check would not have caught it.
The 11 tools
Every tool returns a speech field: one short line already phrased for the ear, plus the structured data alongside it. That is the central design decision of this server — the host is reading results out loud, and a JSON blob is unusable in that channel. The server's instructions tell the model to read speech verbatim and never paraphrase clinical text.
Front desk
Tool | What it does |
| Address, hours, closed days, clinic and emergency numbers. |
| Bookable treatments with appointment lengths, for mapping "my filling fell out" onto a procedure code. |
| Look up by phone. Returns the patient id and medical flags — allergies, anticoagulants, diabetes. |
| Create a record. Refuses to duplicate an existing phone number. |
| Real free slots: opening hours, closed days, which clinician does that treatment, and what is already booked. |
| Books a slot, re-checking it is still free at write time so two callers cannot take the same minute. |
| Upcoming bookings by phone. |
| Cancels by id and frees the slot. Marked |
After hours
Tool | What it does |
| The clinic's own protocol for a treatment: steps in order, plus the warning signs it wants to hear about. |
| Triage: |
| Logs a callback for the team with an urgency level. |
Safety: the part that actually mattered
check_symptom does not diagnose and does not reason freely about symptoms. It matches what the patient said against the red flags the clinic itself wrote down for that procedure, and returns one of three dispositions. The model is instructed to read the result verbatim.
Three rules hold the whole thing up:
1. Concepts, not phrasings. People do not speak in regex. "Can't swallow", "trouble swallowing", "hard to swallow" and "swallowing hurts so much I've stopped" are one emergency. Urgency is decided on concepts that co-occur — an airway word near a difficulty word, a spread site (neck, throat, eye, floor of mouth) near a swelling word — never on fixed strings.
2. Unrecognised escalates. Anything the rules cannot place goes to call_clinic. The server never reassures by default. Silence is not "fine".
3. A hard reassurance ceiling. Even after the normal-recovery list matches, the result is re-checked against the urgent concepts, and anything describing worsening can never come back as expected. There is exactly one way to reach a reassuring answer and it has three gates in front of it.
A real bug, kept as the first test
The first version shipped this:
"I'm having trouble swallowing and my neck is swelling" → expected ❌A spreading submandibular infection — the thing that closes an airway — told the patient nothing was wrong. The pattern only knew the literal phrase can't swallow, and the bare word swelling matched the normal-recovery list.
That case is now the first line of test/triage.test.ts, along with fifteen other phrasings of the same emergencies. The suite is 36 tests and runs in about a second. The asymmetry is deliberate and stated in the test file: this server may under-reassure, it may never over-reassure.
npm test
✓ urgent: I am having trouble swallowing and my neck is swelling
✓ urgent: my throat feels like it is closing
✓ urgent: the bleeding will not stop
✓ expected: my cheek is a bit swollen and it aches
✓ never reassures about something that is getting worse, even on day one
36 passedHow it is built
src/tools.ts The 11 tools and their instructions — host-agnostic
src/clinic.ts PostgREST client, slot generation, triage rules
src/index.ts Cloudflare Workers transport (Durable Object per session)
api/mcp.ts Vercel transport (stateless function)
public/ index.html — the page at /, live tool demo, no MCP client needed
test/ 36 tests, mostly clinical safety
scripts/ db setup and seeding, secret push, local Vercel run, smoke testTransport — Streamable HTTP via
McpAgentfromagents, served at/mcp.@modelcontextprotocol/sdknegotiates 2025-11-25, verified against a real client handshake, not assumed.Data — Supabase over PostgREST. Seven tables: patients, providers, procedures, appointments, aftercare protocols, callbacks, clinic info. Aftercare text lives in the database, not in the code, because it is clinical content a practice must be able to change without a deploy.
Scheduling — slots are generated from opening hours minus closed days minus that clinician's existing bookings, on a fifteen-minute grid, never inside the next hour. Deliberately explainable; a receptionist can follow it.
Annotations — every tool declares
readOnlyHint;cancel_appointmentdeclaresdestructiveHint. Hosts and reviewers can see the blast radius without reading the code.Credentials — the service key lives in Worker secrets and is never returned by a tool, logged, or written into a response.
The aftercare content is real clinical guidance, written by a practising dentist. That is the part a general model cannot improvise safely, and it is why the tool exists.
What it is not
Not a diagnostic tool. It routes; it does not decide what is wrong with you.
No prescribing, no dose changes, no medication advice beyond reading back what the clinic wrote.
No authentication on the MCP endpoint in this build — a production deployment would put the clinic's identity provider in front of
/mcpand scope every tool to the calling patient. The tool surface was designed for that: everything patient-facing already keys off a phone number rather than trusting free-text identity.
Licence
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Lets AI agents get leads on the phone: call, send SMS, and schedule callbacks for sales teams.
AI voice agents that make real phone calls: single calls or campaigns, with transcripts and notes.
Give AI agents a phone layer for consent-based calls, transcripts, summaries, and outcomes.
Give your AI agent a phone: place calls, navigate IVRs, wait on hold, get structured answers.
Related MCP Servers
- FlicenseAqualityDmaintenanceConnects AI assistants like Claude to Ravira's dental practice AI receptionist. Provides tools for patient queries, feature overviews, sample conversations, dental topic searches, and demo requests.5-
- AlicenseNot gradedqualityDmaintenanceEnables Claude and other AI assistants to trigger HIPAA-compliant patient task management workflows through natural language.1MIT
- FlicenseNot gradedqualityDmaintenanceAn MCP-native agentic copilot for dental practices that orchestrates multiple MCP tools to analyze X-rays, pull patient history, generate drafts, and propose recalls, streaming results live to a React dashboard.-
- AlicenseAqualityBmaintenanceEnables MCP-compatible clients to act as an AI receptionist for a small medical clinic, handling appointment booking and rescheduling, patient registration, intake, reminders, and FAQ answers.151MIT