Skip to main content
Glama

Recall — AI Front Desk Agent for Small Clinics

Built for the Amazon Developer Hackathon: Build, Ship, ShapeAlexa+ track (self-hosted MCP server) + AWS Builder mini challenge (Amazon Bedrock integration).

Open source under the MIT License.

What it does

Small clinics (dental, in this build) lose real revenue to three quiet front-desk failures: missed calls that never get a callback, patients who are overdue for a recall visit and never get reminded, and completed visits that never get asked for a review. Recall is a voice-accessible agent — reachable through Alexa+ via a self-hosted MCP server — that surfaces all three and drafts the outbound message for each one using Amazon Bedrock, so the front desk (or the clinic owner) can just say "how does the front desk look today" and act on what comes back.

Related MCP server: mcp-ratchet-clinical-charting

Architecture

                 ┌─────────────────────────┐
   Browser ───▶  │  frontend/app.py         │   simulated Alexa+ chat UI
  (demo video)   │  (MCP client)            │   for the demo video
                 └───────────┬─────────────┘
                              │ MCP over Streamable HTTP
                              ▼
                 ┌─────────────────────────┐
   Alexa+  ───▶  │  app/mcp_server.py       │   Streamable HTTP at /mcp
 (or any MCP     │  (7 tools)               │   THIS is the Alexa+ track deliverable
  client)        └───────────┬─────────────┘
                              │
                              ▼
                 ┌─────────────────────────┐
                 │  app/agent_logic.py      │   business logic + spoken replies
                 └───────┬─────────┬───────┘
                         │         │
                         ▼         ▼
          ┌───────────────────┐ ┌──────────────────────────┐
          │ app/data_store.py │ │ app/bedrock_client.py     │  ← AWS Builder
          │ (mock clinic data)│ │ (Bedrock Converse API)    │    mini-challenge
          └───────────────────┘ └──────────────────────────┘

Neither of us has Alexa+ Preview device access, so the demo uses the explicitly-permitted path from the hackathon rules: a real MCP server (the actual submission requirement) plus a simulated Alexa+ web front-end. The front-end is itself an MCP client: a keyword router stands in for Alexa+'s language understanding, calls the MCP server over Streamable HTTP, and speaks the tool's reply. Every answer in the demo is a real tool call, labeled with the tool that served it.

The server speaks both MCP protocol generations: the initialize handshake up to spec 2025-11-25, and the stateless 2026-07-28 protocol. Both are exercised by scripts/smoke_test.py.

The 7 MCP tools

Tool

Purpose

get_daily_frontdesk_summary

One-line spoken summary: missed calls / recalls due / reviews pending

list_missed_calls

Missed calls not yet texted back

draft_and_send_callback

Bedrock-drafts + "sends" a callback text for one call

list_due_recalls

Patients not yet reminded whose recall is overdue or due within 14 days

draft_and_send_recall

Bedrock-drafts + "sends" a recall reminder

list_pending_reviews

Completed visits eligible for a review ask

draft_and_send_review_request

Bedrock-drafts + "sends" a review request

Every tool returns structured JSON (structuredContent plus an outputSchema) including a spoken_summary written to be read aloud ("Priya Nataraj called about an hour ago, probably about rescheduling").

The send tools only act on items that are actually pending: they refuse anything already handled (so a repeated request doesn't text a patient twice) and recall reminders more than 14 days before the due date. If Bedrock can't produce a usable draft, nothing is sent, the tool returns ok: false with the reason, and the item stays on the list to retry.

"Send" is stubbed (logged, not actually delivered) — swapping in Amazon SNS or Twilio for real SMS delivery is a single-function change in agent_logic.py, called out as the natural next step in SUBMISSION.md.

AWS services used

Service

How Recall uses it

Where

Amazon Bedrock — Converse API

Drafts every patient-facing message (callback texts, recall reminders, review requests). Each draft is one bedrock-runtime converse() call with a system prompt that sets the clinic's voice and SMS constraints.

app/bedrock_client.py

Claude on Amazon Bedrock

Default model Claude Haiku 4.5, invoked through the global cross-region inference profile global.anthropic.claude-haiku-4-5-20251001-v1:0 from us-east-1. Any Converse-capable model works via BEDROCK_MODEL_ID.

.env / host environment

AWS IAM

Programmatic credentials (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY) for an identity allowed to call bedrock:InvokeModel.

.env / host secrets

Not used yet: Amazon SNS (SMS delivery is stubbed, see above).

Setup notes for Bedrock, as of September 2026:

  • Current Claude models must be called through an inference profile ID (global. or us. prefix). The bare anthropic.… model ID is rejected for on-demand throughput.

  • Claude 3.5 Sonnet is end-of-life on Bedrock.

  • Model access is enabled by default, but the first Anthropic model call in an AWS account requires Anthropic's one-time use-case form: open the Bedrock console, pick the model in the model catalog, and submit it.

When a draft fails, the tool's error names the cause ("I couldn't draft a message for Jordan Alvarez (AccessDeniedException), so nothing was sent") and the full error is in the server log:

Reason

Usual cause

NoCredentialsError

No AWS keys in .env or the environment

AccessDeniedException

Anthropic use-case form not submitted, or the IAM identity lacks bedrock:InvokeModel

ValidationException

Model ID wrong: bare anthropic.… ID, or a retired model

Running it locally

python -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate
pip install -r requirements.txt

cp .env.example .env              # Windows: copy .env.example .env
# edit .env: AWS keys (loaded automatically, no export needed)

python -m app.bedrock_client      # checks Bedrock wiring; exit 0 = real draft

# Terminal 1 — the real MCP server (Alexa+ track deliverable)
python -m app.mcp_server          # Streamable HTTP on http://localhost:8001/mcp

# Terminal 2 — the simulated Alexa+ front-end, which calls Terminal 1 over MCP
python -m frontend.app            # http://localhost:8000

The server terminal logs every tool call as it happens. To drive a different MCP server from the front-end, such as the deployed one, set RECALL_MCP_URL=https://<host>/mcp in .env.

No AWS account handy? Set BEDROCK_MOCK=1 in .env and every draft is a clearly-labeled [MOCK] placeholder that counts as sent, so the whole flow runs offline. It is off by default on purpose: without it, missing credentials make sends fail visibly instead of quietly sending placeholders. Record the demo and deploy with real credentials and BEDROCK_MOCK unset.

Sends change the demo data. The committed dataset (data/seed.json) is never modified: its dates are written relative to a fixed moment and shifted to "now" when the runtime state (data/state.json, gitignored) is created, so the demo always happens today — calls missed within the last few hours, one recall overdue, one due in three days. Start over at any time with:

python -m app.data_store reset

Verifying the MCP server

python scripts/smoke_test.py http://127.0.0.1:8001/mcp

This runs a raw JSON-RPC round trip (no MCP SDK), calls all 7 tools through the MCP SDK client, checks refusals and error paths, and confirms both protocol generations negotiate. It exits non-zero on any failure and prints WARN for drafts that came from BEDROCK_MOCK. Point it at the deployed URL to verify the public server the same way. Against a local server without AWS keys, start the server with BEDROCK_MOCK=1, otherwise the send checks fail (correctly).

For a visual check, run the MCP Inspector (npx @modelcontextprotocol/inspector) and connect with the Streamable HTTP transport to http://localhost:8001/mcp. mcp dev app/mcp_server.py is not the right tool here: it launches a separate stdio copy of the server (and requires uv), not the HTTP server this project deploys.

Deploying

Deploy the MCP server to Render with the included Blueprint (render.yaml): a free Docker web service in the Virginia region, next to Bedrock in us-east-1.

  1. Push this repo to GitHub.

  2. In Render: New → Blueprint, select the repo, and enter AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY when prompted.

  3. The MCP endpoint is https://<service>.onrender.com/mcp; https://<service>.onrender.com/health returns {"status": "ok"}.

Free Render instances sleep after 15 minutes without traffic, so the first request after a quiet period takes about a minute. Each restart also resets the demo data, dated to the restart.

Any other container host works the same way: build the Dockerfile and set the variables from .env.example as environment secrets. Only the MCP server needs to be publicly reachable for the submission — the simulated front-end only needs to run locally for recording the demo video.

What's mocked vs real

  • Real: the MCP server (Streamable HTTP, 7 working tools with structured output), the simulated front-end's MCP calls to it, the Amazon Bedrock Converse API integration, all business logic and state changes.

  • Mocked for the demo: the clinic's data source (data/seed.json stands in for a practice-management system like Dentrix/Open Dental), SMS delivery (drafted and logged, not actually sent), and Alexa+'s language understanding (a keyword router in the simulated front-end).

See SUBMISSION.md for the drafted Devpost submission text and product-feedback answers.

Related MCP Connectors

Related MCP Servers