HIPAA-compliant multi-agent electronic prescribing platform. Nine domain MCP servers orchestrated by a LangGraph StateGraph, backed by MongoDB. Built with FastAPI, FastMCP, and Claude AI agents.
| Tool | Minimum version | Check |
|---|---|---|
| Docker Desktop (or Engine + Compose plugin) | 24.x | docker --version |
| Docker Compose plugin | v2 | docker compose version |
| Python | 3.12+ | python3 --version |
| mongosh (optional) | any | mongosh --version |
Apple Silicon (M1/M2/M3): Docker Desktop works out of the box. No extra steps needed.
Podman users: Set
CONTAINER_ENGINE=podmanin your shell before running anymakeorscripts/commands.
You will also need an Anthropic API key (sk-ant-...) for the LLM agent nodes. Get one at console.anthropic.com.
This runs all 13 containers (MongoDB, nginx, gateway, orchestrator, 9 MCP agents) with a single script.
# 1. Clone and enter the repo
git clone <repo-url> erx-platform
cd erx-platform
# 2. Run the installer (checks prereqs, generates keys, builds image, seeds DB)
chmod +x scripts/install.sh
./scripts/install.sh
# 3. Open the UI
open http://localhost # macOS
xdg-open http://localhost # Linux
# Windows: browse to http://localhostLogin: admin / erx_test_2026
That's it. The installer takes 3–5 minutes on first run (Docker image build).
scripts/install.sh performs every first-time step automatically:
- Checks that Docker and Python 3.12 are available
- Copies
.env.example→.env(skipped if.envalready exists) - Generates
PHI_ENCRYPTION_KEYandSECRET_KEYand writes them into.env - Prompts for your
ANTHROPIC_API_KEYand writes it into.env - Builds the shared Docker image (
erx-platform:latest) - Starts all 13 containers (
infra/podman-compose.yml) - Waits for MongoDB and the gateway to become healthy
- Initialises all 11 MongoDB collections and their indexes
- Seeds reference data: patients, drugs, prescribers, providers, pharmacies, SOPs
- Prints the URL, credentials, and demo instructions
To tear everything down and start completely fresh (wipes the MongoDB volume):
./scripts/reset.sh # clean slate — reference data only (good for demos)
./scripts/reset.sh --full # also load 80 historical orders
./scripts/reset.sh --edge # also load 17 edge-case test scenariosreset.sh is safe to run at any time. It stops all containers, removes the data volume, starts fresh, and re-seeds.
After a fresh reset the database has reference data (patients, drugs, prescribers) but no orders. Use the demo controller to submit named scenarios one at a time:
python scripts/demo.py list # show all available scenarios
python scripts/demo.py submit routine-non-cs # statin — fastest path to APPROVED
python scripts/demo.py submit routine # OxyContin CII, FL patient
python scripts/demo.py submit controlled-tx # cross-state CII (FL prescriber → TX patient)
python scripts/demo.py submit rems # Fentora — REMS workflow
python scripts/demo.py submit prior-auth # Revlimid — PA required
python scripts/demo.py submit clozapine # Clozaril — REMS + ANC monitoring
python scripts/demo.py submit denial-candidate # OIG-excluded prescriber → DENIED
python scripts/demo.py submit bulk # submit all 7 at once
python scripts/demo.py clear # wipe all orders for a clean slateEach submit prints the order ID and a direct link: http://localhost/orders/<id>
The demo script connects directly to MongoDB — no JWT token needed. By default it assumes mongodb://localhost:27017. Inside the container network use --uri mongodb://erx-mongodb:27017.
If you prefer to run Python services directly on your machine (faster iteration, debugger support):
# 1. Start only MongoDB in Docker
make mongo
# 2. Install Python dependencies
make install # pip install -e .[dev]
# 3. Generate keys and set up .env
make gen-keys # prints PHI_ENCRYPTION_KEY and SECRET_KEY
cp .env.example .env # then paste the keys in
# 4. Init the database
make init-db # creates collections + indexes
make seed # seeds all domain data
# 5. Start services in separate terminals
python -m agents.patient.server # port 8101
python -m agents.provider.server # port 8102
python -m agents.prescriber.server # port 8103
python -m agents.orders.server # port 8104
python -m agents.drug.server # port 8105
python -m agents.sig.server # port 8106
python -m agents.pharmacy.server # port 8107
python -m agents.eligibility.server # port 8108
python -m agents.prior_auth.server # port 8109
python -m orchestrator.main # Change Stream watcher
uvicorn services.gateway.main:app --reload --port 8000The UI is then at http://localhost:8000 (no nginx proxy in this mode).
make help Show all commands
# Full stack
make build Build the Docker image (required before make dev)
make dev Start all 13 containers
make dev-down Stop all containers
make setup First-time full-stack setup (build + start + init-db + seed)
make reset Tear down + wipe + reseed (calls scripts/reset.sh)
# Database (local mode — Python running on host)
make init-db Create collections + indexes
make seed Seed all domain data
make seed-edge Seed 17 edge-case scenarios
make seed-fresh Drop + reseed from scratch
make verify-seed Print document counts for all collections
# Database (full-stack mode — runs inside gateway container)
make init-db-full Create collections + indexes (via container exec)
make seed-full Seed all domain data (via container exec)
# Logs
make logs Tail gateway + orchestrator logs
make logs-all Tail all container logs
make logs-gateway Tail a specific service (replace 'gateway' with any service name)
# Quality
make lint ruff + mypy + bandit
make fmt Auto-fix formatting
make test pytest
make test-cov pytest with HTML coverage report (htmlcov/index.html)
# Setup helpers
make gen-keys Generate PHI_ENCRYPTION_KEY and SECRET_KEY
make install pip install -e .[dev]
Copy .env.example to .env and fill in these required values:
| Variable | Required | Description |
|---|---|---|
ANTHROPIC_API_KEY |
Yes | Claude API key — powers all LLM agent nodes |
PHI_ENCRYPTION_KEY |
Yes | Fernet key for PHI field encryption. Generate: make gen-keys |
SECRET_KEY |
Yes | JWT signing key. Generate: make gen-keys |
MONGODB_URL |
No | Default: mongodb://localhost:27017 (local) or mongodb://erx-mongodb:27017 (container) |
LOG_FORMAT |
No | console (dev, default) or json (production) |
All other variables have sensible defaults. The full list is in .env.example.
Browser
└─ nginx :80
└─ FastAPI Gateway :8000
├─ /auth/* JWT token endpoint
├─ /api/* REST JSON (Bearer-protected)
└─ / HTMX UI (Jinja2 templates)
Gateway → LangGraph Orchestrator
└─ StateGraph processes each PrescriptionOrder through 9 domain nodes:
patient → provider → prescriber → drug → sig → eligibility → prior_auth → pharmacy → complete
Each node calls its domain MCP server via SSE:
mcp-patient :8101 Patient lookup + PHI validation
mcp-provider :8102 Provider credentialing
mcp-prescriber :8103 DEA/state license + OIG exclusion check
mcp-drug :8104 NDC lookup, schedule, REMS flag
mcp-sig :8105 Sig parsing + clinical validation
mcp-eligibility :8106 PBM eligibility + formulary (mock NCPDP D.0)
mcp-prior-auth :8107 PA submission + status tracking (mock payer)
mcp-pharmacy :8108 Pharmacy routing + SureScripts check
MongoDB (11 collections):
patients, providers, prescribers, drugs, sigs, orders,
pharmacies, eligibility_checks, prior_auths,
state_sop_modules, audit_log
All SOP rules live in state_sop_modules — nothing hardcoded. To add or update a rule: insert a new document (version+1, status=ACTIVE), set the old one to SUPERSEDED. No deploy needed.
"Gateway not ready" during install/reset
The gateway takes 15–30 s to start on first run because it connects to all 9 MCP servers. If it times out, check:
make logs-gateway # look for errorsThen re-run ./scripts/reset.sh — it's idempotent.
"Workspace still starting" or MongoDB connection refused
MongoDB needs ~10 s after container start. The install/reset scripts wait for it automatically. If you're running commands manually, wait for:
docker compose -f infra/podman-compose.yml ps # all services should show "healthy" or "running"init-db or seed fail with "Connection refused"
In full-stack mode, use the container-aware targets:
make init-db-full
make seed-fullThe bare make init-db / make seed connect to localhost:27017 and only work in local dev mode.
PHI fields show as encrypted strings in the UI
The PHI_ENCRYPTION_KEY in .env doesn't match the key used to encrypt the seed data. Run a fresh reset:
./scripts/reset.shThis drops the database and re-seeds with the current key.
Port 80 already in use
Something else is using port 80 (Apache, another nginx). Stop it, or change the nginx port in infra/podman-compose.yml and update the health check URLs in scripts/.
Podman instead of Docker
export CONTAINER_ENGINE=podman
./scripts/install.sh # or reset.sh — both honour CONTAINER_ENGINE
make dev # Makefile also honours it| What | Value |
|---|---|
| UI login | admin / erx_test_2026 |
| MongoDB (local mode) | mongodb://localhost:27017 (no auth) |
| MongoDB (full-stack) | mongodb://erx-mongodb:27017 (internal network, no auth in dev) |
erx-platform/
├── agents/ 9 FastMCP domain servers (one per domain)
├── orchestrator/ LangGraph StateGraph + node functions
├── services/gateway FastAPI gateway — REST API + HTMX UI
├── shared/ db.py, logging.py, security.py, audit.py
├── schemas/ canonical_schemas.py — all Beanie models
├── sops/ sop_injection.py — DB-driven SOP prompt assembly
├── db/ Seed data, init scripts, edge-case scenarios
├── infra/ podman-compose.yml, nginx config, mongo init
├── scripts/
│ ├── install.sh First-time setup
│ ├── reset.sh Full teardown + reseed
│ └── demo.py Demo order controller
├── .env.example Environment variable template
├── Makefile Developer commands
└── README.md ← you are here