@neolifehealth/mcp
v0.0.2
Published
neolife MCP server — drive the compliant telehealth fulfillment rail from an AI agent over the Model Context Protocol.
Downloads
327
Maintainers
Readme
@neolifehealth/mcp — AI-agent interface to the fulfillment rail
A Model Context Protocol server that exposes the neolife telehealth-fulfillment platform as a set of tools an AI agent can call. Every platform capability — find a patient, search the pharmacy catalog, draft a compounded-prescription order, run a compliance intake, check order status — is an MCP tool. The same tool surface powers neolife's own conversational agent and a clinic's own AI agent.
The design principle: an agent can assemble and drive work, but it can never perform a licensure act. Drafting an order or running an intake is fair game for an agent; approving clinical content is not, and there is deliberately no tool on this surface that lets an agent do it.
Deeper design rationale:
../../docs/prd/AI-MCP-DESIGN.mdand the machine-API / developer-platform roadmap in../../docs/prd/DEVELOPER-PLATFORM.md.
How it fits together
The server is a thin stdio wrapper over the neolife HTTP API. It holds no business logic of its own — each tool call maps to one API request, and all of the guardrails (provider-approval gate, idempotency replay, tenant scoping) are enforced server-side by the API, not by the MCP layer. This is deliberate: an agent cannot reach a privileged code path that a direct API caller couldn't also reach, so the safety story is the same for both.
flowchart LR
A[AI agent<br/>Claude / clinic agent] -->|MCP tool call<br/>over stdio| B[neolife MCP server<br/>packages/mcp/src/server.ts]
B -->|typed HTTP| C[neolife API<br/>apps/api]
C -->|approval gate<br/>idempotency<br/>auth + audit| D[(Postgres)]
C -->|approved orders only| E[Pharmacy adapter<br/>LifeFile]Tools exposed
Defined in src/server.ts; the typed HTTP client is src/client.ts.
| Tool | Purpose | Mutates? |
|------|---------|----------|
| findPatient | Search patients by name/email fragment | no |
| searchCatalog | Search the pharmacy catalog by name, drug, or LifeFile product id | no |
| draftOrder | Assemble a draft compounded-Rx order from a natural-language request; low-confidence fields are amber-flagged | no (draft only) |
| getOrderStatus | Status, tracking, and timeline for an order id | no |
| submitOrder | Submit an already provider-approved order to the pharmacy — routes through the server-side approval gate | yes (gated) |
| listQuestionnaires | List the clinic's versioned intake / follow-up question sets | no |
| submitIntake | Run a compliance intake by answering a question set; returns the deterministic verdict and, when eligible, queues it for licensed-provider review | yes |
| listPendingIntakes | List intakes awaiting provider review (or filter by status) | no |
| getIntake | Fetch one intake's full provider packet (Q/A, flags, derived values, routing, certificate) | no |
There is intentionally no reviewIntake / approveOrder tool. Clinical review is a licensure
act and is not reachable from the agentic surface. The intake smoke test
(src/smoke-intake.ts) asserts reviewIntake is absent.
Safety model
Provider approval is always required
draftOrder never auto-submits — it returns a DRAFT with a note that a licensed provider must
approve before it can be filled. submitOrder routes through the API's server-side approval gate: if
the order has not been approved by a licensed provider, submission is refused (the API returns a
403, surfaced back to the agent as an error). There is no unguarded submit path an agent can reach.
The order-submission smoke test (src/smoke.ts) proves this end-to-end: it drafts an
order, calls submitOrder on it, and asserts the response says the order was not approved by a
licensed provider.
Idempotency keys are stable UUIDv5 — retries are safe, and carry no PHI
submitOrder sends an Idempotency-Key header derived deterministically from the order id via an
RFC-4122 v5 (SHA-1) UUID over a fixed neolife namespace (see uuidV5 / idempotencyKey in
src/client.ts). Because the key is a pure function of the order id — never random,
never time-based — an agent that re-fires the same submit produces the same key, so the server
replays the first result instead of dispatching a second physical shipment. This complements the
API's server-side atomic single-dispatch claim.
Two properties matter for agents:
- Retry-safe. A dropped connection or a nervous agent retrying is harmless.
- No PHI in keys. The key is a hash of an opaque order UUID, not of patient data, so it is safe to log and to pass across process boundaries.
The namespace constant
NEOLIFE_IDEMPOTENCY_NAMESPACEmust never change — changing it would produce different keys for the same logical operation and silently break retry dedup.
Structured errors help agents self-correct
The client surfaces the API's structured error code, message, and request_id on non-2xx
responses, so an agent gets an actionable failure (and a request id to quote) rather than an opaque
500.
HIPAA posture
The MCP server is designed to run inside the HIPAA environment, alongside the API, and PHI only
ever flows to a BAA-covered model. See ../../docs/prd/AI-MCP-DESIGN.md
§5 for the full posture.
Configuration
All configuration is via environment variables (referenced by name only — never commit secret values):
| Variable | Purpose | Default |
|----------|---------|---------|
| NEOLIFE_API_URL | Base URL of the neolife API. Set to http://localhost:4000 to point at a local stack. | https://api.neolife.health |
| NEOLIFE_API_KEY | Machine API key (nk_live_ / nk_sandbox_ / rk_…). Scopes the MCP to one tenant with real permissions and audit — the production auth path. | unset |
| NEOLIFE_MCP_USER | Fallback demo user id header (x-neolife-user-id), used only when no API key is set. Dev / click-in only. | a demo user id |
Auth precedence: if NEOLIFE_API_KEY is set, requests use Authorization: Bearer … (the tenant-scoped
production path). Otherwise the client falls back to the demo x-neolife-user-id header for local/click-in
development. Either way, submitOrder still hits the server-side provider-approval gate.
Install & run
This package lives in the neolife monorepo and depends on the @neolifehealth/mcp workspace deps. From the
repo root:
pnpm installRun the server (it speaks MCP over stdio, so it expects to be launched by an MCP client, not curled directly):
# from packages/mcp
pnpm start # tsx src/server.ts
# or during development
pnpm devOn connect it logs the resolved API URL to stderr:
neolife MCP server connected (API: https://api.neolife.health)Registering with an MCP client
Point any MCP client at the server binary over stdio. For example, in a Claude Desktop / Claude Code
style mcpServers config:
{
"mcpServers": {
"neolife": {
"command": "npx",
"args": ["-y", "@neolifehealth/mcp"],
"env": {
"NEOLIFE_API_KEY": "nk_sandbox_…"
}
}
}
}The package also declares a neolife-mcp bin pointing at dist/server.js.
Example agent flow
A typical order-assembly chain — the agent walks find → catalog → draft, then a human provider
approves out-of-band, and only then can submitOrder succeed:
1. findPatient({ query: "john smith" })
→ [{ id: "…", name: "John Smith", dob: "…" }]
2. searchCatalog({ query: "testosterone cypionate" })
→ [{ id: "…", name: "…", lfProductID: "…" }]
3. draftOrder({ request: "the standard TRT protocol for John Smith, overnight to his home" })
→ { orderId: "…", status: "DRAFT", amberFields: [...],
note: "Draft only. A licensed provider must approve before this can be filled." }
--- a licensed provider reviews and approves the draft in the console (not an agent action) ---
4. submitOrder({ orderId: "…" })
→ { id: "…", status: "SUBMITTED", lfOrderId: "…" }
# if step 3's draft was NOT provider-approved, this instead returns an error:
# "…not approved by a licensed provider"You can exercise the whole surface locally against a running API with the smoke tests:
pnpm exec tsx src/smoke.ts # orders: drafts, then proves the submit guard rejects unapproved
pnpm exec tsx src/smoke-intake.ts # intake: lists pending intakes, asserts no reviewIntake tool existsRelated docs
../../docs/prd/AI-MCP-DESIGN.md— where AI is used, the agent loop, internal vs external MCP, HIPAA posture.../../docs/prd/DEVELOPER-PLATFORM.md— machine API keys, the "Stripe for Rx fulfillment" roadmap, and how MCP complements the HTTP API and CLI.
