Skip to main content
plugin

healthclaw-guardrails — Claude Code plugin for FHIR security

This plugin filters reads of clinical FHIR data through PHI redaction and gates writes with a signed token. Use it when an agent handles medical records.

Install into Claude Code · Plugin marketplace All methods and platforms ↓
/plugin marketplace add anthropics/claude-plugins-community
/plugin install healthclaw-guardrails@claude-community
Open on GitHub

What healthclaw-guardrails does

healthclaw-guardrails sits between an AI agent and a FHIR server. On reads, it reduces names to initials, keeps only the birth year, and masks identifiers. Access events go to an audit trail separated by tenant.

Writes need an HMAC-signed token that expires after five minutes. Attempts to write Condition, MedicationRequest, or DiagnosticReport receive HTTP 428 until a person confirms them. Those changes therefore cannot be completed by the agent alone.

The plugin is for Claude Code. The project lists support for FHIR R4 US Core v9 and R6 v6.0.0-ballot3. Install it through Claude Code’s plugin marketplace: add the marketplace first, then install the plugin.

What it helps with

  • redact PHI when reading clinical records
  • gate clinical resource writes on human confirmation
  • require a time-limited token for write access
  • keep tenant-scoped records of data access

Example prompts for the agent

  • Find a clinical record and redact PHI before showing it to me.
  • Prepare a MedicationRequest write and ask a person to confirm it.
  • Record this FHIR data access in the audit trail for the current tenant.

How to install healthclaw-guardrails

Plugin marketplace

/plugin marketplace add anthropics/claude-plugins-community
/plugin install healthclaw-guardrails@claude-community

Step by step, with commands for each platform: Claude Code plugins: marketplaces, installing, and building your own

Questions about healthclaw-guardrails

How do I install healthclaw-guardrails in Claude Code?

In Claude Code, run /plugin marketplace add anthropics/claude-plugins-community first, followed by /plugin install healthclaw-guardrails@claude-community.

What does healthclaw-guardrails do?

It redacts PHI on FHIR reads, requires an HMAC-signed token for writes, and records access in a tenant-scoped audit trail.

Which writes need human confirmation?

Writes to Condition, MedicationRequest, and DiagnosticReport return HTTP 428 until a person confirms them.

Source text

SKILL.md · MIT
<div align="center">

<img src=".github/assets/healthclaw-logo.png" alt="HealthClaw — AI-Powered Healthcare Intelligence" width="440">

# HealthClaw Guardrails

### The open-source security layer between AI agents and clinical data.

*FHIR standardized how health data is structured. MCP standardized how AI connects to tools.*
***Nobody standardized the guardrails in between. This project does.***

<a href="https://healthclaw.io/#demo"><img src=".github/assets/healthclaw-2.0-demo-preview.jpg" alt="HealthClaw 2.0 demo video preview" width="640"></a>

**[▶ Watch the 90-second demo](https://healthclaw.io/#demo)**<br/>
<sub>All records are synthetic. The chat ran on a third-party model, best of four takes. No clinician has signed off yet. [How it was made](docs/video/2.0-demo/disclosures.md).</sub>

<br/>

<!-- Project -->
[![Release](https://img.shields.io/badge/release-v2.1.0-f97316?style=flat-square)](https://github.com/aks129/HealthClawGuardrails/releases)
[![License](https://img.shields.io/badge/license-MIT-2dd4bf?style=flat-square)](LICENSE)
[![CI](https://img.shields.io/github/actions/workflow/status/aks129/HealthClawGuardrails/ci.yml?branch=main&style=flat-square&label=CI&logo=github)](https://github.com/aks129/HealthClawGuardrails/actions/workflows/ci.yml)
[![Code size](https://img.shields.io/github/languages/code-size/aks129/HealthClawGuardrails?style=flat-square&color=0ea5e9)](https://github.com/aks129/HealthClawGuardrails)

<!-- Community metrics -->
[![Stars](https://img.shields.io/github/stars/aks129/HealthClawGuardrails?style=flat-square&logo=github&color=eab308)](https://github.com/aks129/HealthClawGuardrails/stargazers)
[![Forks](https://img.shields.io/github/forks/aks129/HealthClawGuardrails?style=flat-square&logo=github&color=8b5cf6)](https://github.com/aks129/HealthClawGuardrails/network/members)
[![Issues](https://img.shields.io/github/issues/aks129/HealthClawGuardrails?style=flat-square&logo=github&color=ef4444)](https://github.com/aks129/HealthClawGuardrails/issues)
[![Contributors](https://img.shields.io/github/contributors/aks129/HealthClawGuardrails?style=flat-square&color=14b8a6)](https://github.com/aks129/HealthClawGuardrails/graphs/contributors)
[![Last commit](https://img.shields.io/github/last-commit/aks129/HealthClawGuardrails?style=flat-square&color=64748b)](https://github.com/aks129/HealthClawGuardrails/commits/main)

<!-- Stack & scope -->
[![Tests](https://img.shields.io/badge/tests-1490%2B%20Python%20%2B%20170%20Node-22c55e?style=flat-square)](#testing)
[![MCP tools](https://img.shields.io/badge/MCP%20tools-29-6366f1?style=flat-square&logo=anthropic)](#mcp-tools-29)
[![FHIR](https://img.shields.io/badge/FHIR-R4%20US%20Core%20v9-0ea5e9?style=flat-square)](#fhir-version-support)
[![Guardrail conformance](https://img.shields.io/endpoint?url=https%3A%2F%2Fapp.healthclaw.io%2Fr6%2Ffhir%2F%24conformance%3Fformat%3Dshields&style=flat-square)](#what-this-grade-means-and-what-it-doesnt)
[![Glama score](https://glama.ai/mcp/servers/aks129/HealthClawGuardrails/badges/score.svg)](https://glama.ai/mcp/servers/aks129/HealthClawGuardrails)
[![Python](https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white)](pyproject.toml)
[![Docker](https://img.shields.io/badge/docker-compose-2496ED?style=flat-square&logo=docker&logoColor=white)](#docker)

<br/>

**[Quick Start](#quick-start)** · **[MCP Tools](#mcp-tools-29)** · **[Recipes](docs/recipes/)** · **[Roadmap](ROADMAP.md)** · **[Claude Plugin](#install-as-a-claude-plugin)** · **[Architecture](#what-it-does)** · **[healthclaw.io](https://healthclaw.io)** · **[Contributing](CONTRIBUTING.md)** · **[Dev Guide](docs/development.md)**

</div>

---

> **What it is:** an open reference implementation of the FHIR × MCP guardrail layer — PHI redaction,
> immutable audit, step-up auth, and tenant isolation — that sits between *any* AI agent and *any* FHIR
> server. Built in the open as a community project, MIT-licensed. Not a product, not a pitch: if the
> pattern is useful, take it; if it's wrong, tell us or fix it.

**This is a community effort.** It's most useful when implementers, clinicians, and standards folks poke holes in it. Issues, PRs, and "you got the SDC extraction wrong" critiques are all welcome — start with **[CONTRIBUTING.md](CONTRIBUTING.md)** and the **[Code of Conduct](CODE_OF_CONDUCT.md)**.

**At a glance:** v2.1.0, with 4,000+ Python and 200+ Node tests across 29 MCP tools.
**[CareAgents](https://careagents.cloud)** is the hosted consumer app: passkey sign-in, advisors, and chat on the web. iMessage works where it's offered; Telegram is not open in the beta.
Two rails run end to end — real-world actions behind a provably out-of-band gate, and forms (`$populate` → human review → provenance PDF).
Standards: FHIR R4 US Core v9 and R6 v6.0.0-ballot3, HL7 SDC forms, NQF 0018.
Operations: lab interpreter (`$interpret`), care-gaps reminders (`$care-gaps`) with an embedded MCP-App view, and ChatGPT-connector `search`/`fetch`.
Connectors: Fasten TEFCA, HealthEx, HBO, Flexpa, Epic, MEDENT, Open Wearables, SMART Health Links.
Also a Claude Code plugin and OpenAI/Gemini adapters.

## Try it in 60 seconds — no clone, no keys

The hosted demo runs synthetic data behind the full guardrail stack:

```bash
# Watch the deployment grade its own guardrails (PHI redaction, audit, step-up, ...):
curl "https://app.healthclaw.io/r6/fhir/\$conformance?format=text"
```

Point any MCP client at the **public demo server** — URL `https://mcp-demo-production-ee2c.up.railway.app/mcp`,
no key required — then ask: *"Search my health records for lab results and explain them in plain
language."* The demo server is unauthenticated but hard-pinned to a synthetic demo tenant, so it can
only ever serve fake data. A separate **production endpoint** (`mcp-server-production-5112`) requires a
deployment-scoped `Authorization: Bearer <token>` — real records stay behind auth, always. Hosted
connectors cannot attach that header, so the demo URL above is the one to paste.
One-command installs:
`gemini extensions install https://github.com/aks129/HealthClawGuardrails` ·
`claude plugin marketplace add aks129/HealthClawGuardrails` ·
skills on [ClawHub](https://clawhub.ai/aks129/skills/fhir-r6-guardrails)

**Non-developer?** Step-by-step guides for Claude (web/desktop/phone), Perplexity,
ChatGPT, Grok, Meta Muse and Telegram — plus a 10-minute demo script — in [docs/quickstarts/](docs/quickstarts/README.md).

**Listed in:** [Official MCP Registry](https://registry.modelcontextprotocol.io) (`io.github.aks129/healthclaw-guardrails`) ·
[Glama](https://glama.ai/mcp/servers/aks129/HealthClawGuardrails) ([hosted connector](https://glama.ai/mcp/connectors/io.github.aks129/healthclaw-guardrails)) ·
[ClawHub](https://clawhub.ai/aks129/skills/fhir-r6-guardrails) (14 skills) ·
Gemini CLI Extensions · agent-skills discovery at [`/.well-known/agent-skills/`](https://healthclaw.io/.well-known/agent-skills/index.json)

## Release highlights

Full notes live in **[Releases](https://github.com/aks129/HealthClawGuardrails/releases)**.

| Version | Highlights |
| --- | --- |
| **v2.1.0** | **The check-in release** ([notes](docs/releases/2.1.0.md)): a `checkin` export answers a SMART Health Check-in request with US Core records, reached only by a one-use step-up token bound to its audience and the agreed items · insurance member, group and plan IDs in that export only, after consent · only allow-listed US Core and C4DIC profile claims leave the engine · step-up tokens refused outside their scope and audience · each record carries a code-only source tag · "no known allergy" is quoted as a recorded entry, never inferred · **CareAgents**: invite-only beta, patient connect journey, one assistant per person, iMessage by text, and a check-in web wallet behind a flag |
| **v2.0.0** | **The synthetic-beta release** ([notes](docs/releases/2.0.0.md)): guardrails on by default, Grade A held, and the docs claim only what is true on `main`. What is *not* in 2.0 is listed there: the #214 header gate, the MCP OAuth connector (off), the Curatr fix rail (dark), invite-only real records · **action rail**: the approval binds a digest of the payload the person saw, every kind has an approve page, and a decline is recorded as one · **redaction sweep**: identifier values removed, write responses redacted, surviving free text stripped · **Runs in front of a real FHIR server.** The proxy now authenticates to an upstream FHIR server with its own client credential, so an agent never holds one — with a runnable [Aidbox example](examples/aidbox-healthclaw-guardrails/) that stands the guardrails in front of Aidbox and asserts each property rather than narrating it · **access kernel** — `r6.access` becomes the one tenant reader, step-up gate, audit call and FHIR exit, adopted blueprint by blueprint · **security**: a caller-supplied seed bundle takes the ingest gate, not the mint gate (an unauthenticated write path, found and closed) · **MCP**: an expired session returns 404, so a client re-initializes instead of failing · **demo data**: multi-year synthetic blood-pressure history and a server-rendered trend chart, with home and clinic readings modelled distinctly · a [defect catalogue](docs/) wired into the PR gate, and drift guards that replay the published example's own claims against the running app |
| **v1.9.0** | **[CareAgents](https://careagents.cloud) — the hosted consumer experience**: sign up with a passkey, connect records through a pluggable connector marketplace (Fasten, Apple Health via Open Wearables, sample data), and spin up a guardrailed health agent reachable on web, Telegram, and iMessage · **advisor registry** — specialties ported from SmartHealthConnect (healthy-habits, care-completion, medication-refills, diet-exercise) as prompt-blocks over the guarded tool set, deferred ones honestly labeled · **versioned informed consent** enforced server-side (HTTP 428) before any real-record connection · **forms rail ships end-to-end** — `$populate` → per-item human review (NKA never inferred) → provenance-stamped PDF → signed expiring link · **error fidelity is conformance property seven (Grade A = 7/7)**, hardened across both MCP transports with a Python↔TypeScript drift guard · **MCP Apps** — care-gaps results embed an engine-served UI (`text/html; profile=mcp-app`) whose only fetch target is the guarded operation · security pass: fail-closed prod config, authenticated tenant reads, MCP transport auth, Alembic · SmartHealthConnect archived (skills frozen at v1.2.0; advisors are the live successors) |
| **v1.8.0** | **Real-actions foundation** — an agent can *propose* a real-world action (call, SMS, form) but `commit` only *submits* it (HTTP 202); execution happens through a separate approval that requires a single-use step-up credential and an expiry-guarded atomic claim, so the agent's own toolchain can never approve its own action (the action rail no longer accepts the spoofable `X-Human-Confirmed` header; direct FHIR writes still do, a known gap tracked in [#214](../../issues/214)) · **`ActionExecutor` plugin registry** — add a real-world capability behind the full guardrail rail in ~50 lines, no core changes ([extend it](ROADMAP.md#extending-the-action-rail)) · mandatory red-flag emergency screen; fail-loud rails (no silent simulation) · **durable execution** — attempt ledger, provider reconciliation, external-tick reaper, append-only action-event log · **reliability floor** — config preflight (`GET /r6/ops/preflight`), Postgres CI lane, MCP fetch timeouts, poller 409-storm detection, source-aware resource identity `(tenant, type, id)`, Fasten hardening + zombie-job reaper · public [ROADMAP](ROADMAP.md) + contributor on-ramp · fixes: upstream FHIR error fidelity, quality measures default to current year |
| **v1.7.0** | Preventive care-gaps engine (`Patient/$care-gaps`, USPSTF/ACIP/ADA + eCQM crosswalk) · patient connect flow: identity-verified Fasten onboarding mints a webhook-gated, read-scoped 30-day agent token · prescription transfer requests (`rx_transfer_request`, Schedule II refused) — 29 MCP tools · [per-agent quickstarts](docs/quickstarts/) (Claude/Perplexity/ChatGPT/Telegram) · HBO export→FHIR converter + embedded-XML PHI scrubber · hardening: fail-closed webhook verify, scoped tokens, serverless write guard, live-path contract tests · clinical fixes: SNOMED diabetes detection, inclusive panic thresholds, one-sided-range honesty |
| **v1.6.0** | Lab reference-range interpreter (`Observation/$interpret`) · NQF 0018 quality measure (`Measure/$evaluate-measure`) · [any-agent-framework adapters](docs/recipes/any-agent-framework.md) (OpenAI/Gemini) · [Medplum-in-front recipe](docs/recipes/healthclaw-in-front-of-medplum.md) · SMBP triage on 2025 AHA/ACC · ruff lint gate · all dependency advisories remediated |
| v1.5.0 | Read-auth hardening (tenant reads authenticated, not just scoped) · HL7 SDC forms — `$populate` / `$extract` |
| v1.4.0 | Six health-data connectors (Fasten TEFCA, HealthEx, Health Bank One, Flexpa, Epic, MEDENT) behind one guardrail stack |
| v1.3.0 | Wearables → FHIR Observations (8 providers, LOINC/UCUM mapping, device Provenance) |
| v1.2.0 | Compiled Truth — current state + append-only Provenance trail per resource |

## What It Does

This is a **vendor-neutral guardrail proxy** that sits between any AI agent and any FHIR server. Every request passes through:

- **PHI redaction** — Names truncated to initials, identifier values removed (system and type kept), addresses stripped, birth dates truncated to year
- **Immutable audit trail** — Every read/write logged with tenant, agent, timestamp
- **Step-up authorization** — HMAC-SHA256 tokens required for writes
- **Human-in-the-loop** — real-world actions (calls, SMS, forms): `commit` only *submits*. Execution requires a **provably out-of-band** single-use approval bound to the action, which the agent's own toolchain cannot satisfy. Direct clinical FHIR writes answer HTTP 428 until an `X-Human-Confirmed` header is present. The caller sets that header, so it is not a human gate: a known gap tracked in [#214](../../issues/214)
- **Tenant isolation** — Every query scoped to tenant, cross-tenant access blocked
- **Medical disclaimers** — Injected on all clinical resource reads
- **Compiled Truth** — Current state + append-only evidence trail for every resource

```text
AI Agent ──▶ MCP Server ──▶ Guardrail Proxy ──▶ Any FHIR Server
                              ↓                    (HAPI, Epic,
                         PHI redaction              Medplum, etc.)
                         Audit trail
                         Step-up auth
                         Human-in-the-loop
```

### How it differs from a plain FHIR MCP server

| | Typical FHIR MCP server | HealthClaw |
| --- | --- | --- |
| Redaction before the model | Returns the record as the server stores it | Before the agent sees the record: names cut to initials, identifier values removed, addresses stripped, birth dates cut to the year |
| Audit of every access | Left to the FHIR server behind it | Every read and write records an AuditEvent, with a PHI-free detail |
| Step-up for writes | The session credential covers writes | Each write needs a tenant-bound HMAC step-up token |
| Out-of-band approval for calls, texts and forms | Not in scope | The agent only proposes. A person approves on a separate page, bound to the payload they were shown |
| Tenant isolation | One credential, one view of the server | Every query is scoped to a tenant; cross-tenant access is refused |

Direct clinical FHIR writes are the exception to the approval row: they still gate on a header the caller sets ([#214](../../issues/214)).

## Prove it: guardrail conformance

The guardrails are **verifiable, not marketing.** A runnable harness probes any
deployment with synthetic data and emits a scorecard across all seven properties —
run it against your own instance (or ours):

```bash
python scripts/guardrail_conformance.py \
  --base-url https://app.healthclaw.io --tenant desktop-demo \
  --step-up-token "$(mint a token via POST /r6/fhir/internal/step-up-token)"
```

```text
HealthClaw Guardrail Conformance — https://app.healthclaw.io [tenant=desktop-demo]
  Grade: A   (7/7 properties)
  [PASS] PHI Redaction            [PASS] Human-in-the-Loop
  [PASS] Immutable Audit Trail    [PASS] Tenant Isolation
  [PASS] Step-Up Authorization    [PASS] Medical Disclaimers
  [PASS] Error Fidelity — A (local-fhir-only)
```

Or hit the **one-URL self-test** on any running deployment — no token needed, it
self-tenants internally and returns 200 at Grade A (503 otherwise):

```bash
curl "https://app.healthclaw.io/r6/fhir/\$conformance?format=text"
```

The local FHIR profile is Grade A: unsupported local-search inputs are rejected
or reported according to `Prefer: handling`, and every failure path is audited.
The same harness runs against the Flask test client as a **CI baseline**
(`tests/test_guardrail_conformance.py`). `--json` emits a machine-readable
report; `--mcp-url` additionally grades MCP `tools/call` error signaling as a
separate profile. For an authenticated MCP deployment, set `MCP_AUTH_TOKEN` or
pass `--mcp-auth-token`. Library API:
`from r6.conformance import LiveProbeClient, ProbeContext, run_conformance`.

### What this grade means (and what it doesn't)

The grade covers the **HealthClaw guardrail layer only** — a self-test of the
seven properties against synthetic data it just created. It is **not** a HIPAA
Security Rule assessment, a third-party audit, or a penetration test of your
deployment: infrastructure, BAAs, encryption at rest/in transit, and access
controls remain the deployer's responsibility (see
[Known Limitations](#known-limitations)). Because the harness is
deployment-agnostic, a third party *can* run it against any instance as one
input to a real assessment — it does not substitute for one. The report states
this scope itself in every output format.

## Install as a Claude Plugin

HealthClaw ships as a Claude Code plugin marketplace. Two plugins are available:

```bash
# Add the marketplace
claude plugin marketplace add aks129/HealthClawGuardrails

# Install the FHIR guardrail plugin (this repo)
claude plugin install healthclaw-guardrails@healthclaw-marketplace

# Install the personal-health companion plugin (frozen — upstream archived)
claude plugin install smarthealthconnect@healthclaw-marketplace
```

| Plugin | Skills | Source |
| --- | --- | --- |
| `healthclaw-guardrails` | curatr, fasten-connect, fhir-r6-guardrails, fhir-upstream-proxy, healthex-export, phi-redaction | [aks129/HealthClawGuardrails](https://github.com/aks129/HealthClawGuardrails) |
| `smarthealthconnect` | care-completion, diet-exercise, healthy-habits, kids-health, medication-refills, research-monitor | [aks129/SmartHealthConnect](https://github.com/aks129/SmartHealthConnect) *(archived — skills frozen at v1.2.0; live successors are CareAgents advisors)* |

Each skill is auto-discoverable — Claude loads it when your prompt matches the skill's trigger phrases (e.g. "check my care gaps", "redact this bundle", "run Curatr on my conditions").

**Not on Claude/MCP?** The same 28 guardrailed tools run on OpenAI, Gemini, LangChain, or plain HTTP via the framework-neutral bridge in [`adapters/`](adapters/) — see [Recipe: run HealthClaw tools on any agent framework](docs/recipes/any-agent-framework.md). Guardrails stay server-side, so no framework can bypass them.

## Quick Start

```bash
# Install dependencies
uv sync

# Apply deterministic database migrations
STEP_UP_SECRET=your-secret uv run flask --app main init-db
STEP_UP_SECRET=your-secret uv run flask --app main seed-demo --tenant-id desktop-demo

# Run (local mode with SQLite)
STEP_UP_SECRET=your-secret python main.py

# Run with upstream FHIR server
FHIR_UPSTREAM_URL=https://hapi.fhir.org/baseR4 STEP_UP_SECRET=your-secret python main.py

# Open browser
open http://localhost:5000            # Landing page with live demo
open http://localhost:5000/r6-dashboard  # Interactive dashboard
```

### Docker

```bash
docker-compose up -d --build

# macOS note: port 5000 conflicts with AirPlay Receiver — remap with:
# HOST_PORT=5050 docker-compose up -d --build

# Services:
# - fhir-mcp-guardrails (Flask, port 5000)
# - agent-orchestrator (MCP server, port 3001)
# - redis (port 6379)
```

## MCP Tools (29)

Tool names use underscores (not dots) for Claude Desktop / MCP client compatibility.

**Read tools** (no step-up for public tenants):

| Tool | Description |
| --- | --- |
| `context_get` | Retrieve pre-built context envelopes |
| `fhir_read` | Read a FHIR resource (redacted) |
| `fhir_search` | Search with patient, code, status, date filters |
| `fhir_validate` | Structural validation |
| `fhir_stats` | Observation statistics (count/min/max/mean) |
| `fhir_lastn` | Most recent N observations per code |
| `fhir_interpret_labs` | Lab reference-range interpretation (`$interpret`) — decision support, not diagnosis |
| `care_gaps` | Preventive-care gaps (`$care-gaps`) — screenings/immunizations that may be due, from the patient's own records |
| `guardrail_conformance` | Run the guardrail conformance self-test — graded A–F scorecard across all seven properties |
| `fhir_permission_evaluate` | R6 Permission access control evaluation |
| `fhir_subscription_topics` | List available SubscriptionTopics |
| `questionnaire_populate` | SDC `$populate` — pre-fill a Questionnaire for a subject |
| `curatr_evaluate` | Evaluate a FHIR resource for data quality issues |
| `action_status` | Poll a real-world action (call/SMS) |
| `search` | ChatGPT-connector-compatible search — thin wrapper over `fhir_search`, returns compact `{id, title, url}` results |
| `fetch` | ChatGPT-connector-compatible fetch by `ResourceType/id` — thin wrapper over `fhir_read`, returns `{id, title, text, url, metadata}` |

**Write tools** (require step-up token):

| Tool | Description |
| --- | --- |
| `fhir_propose_write` | Validate + preview without committing |
| `fhir_commit_write` | Commit with step-up auth + human-in-the-loop |
| `questionnaire_extract` | SDC `$extract` — extract resources from a completed QuestionnaireResponse |
| `curatr_apply_fix` | Apply patient-approved fixes with Provenance tracking |
| `action_propose` / `action_commit` | Propose / commit a real-world phone call or SMS |
| `rx_transfer_request` | Draft a pharmacy-transfer request call from active meds (Schedule II refused); commit via `action_commit` |
| `shl_generate` | Generate an encrypted SMART Health Link (QR). Off by default: withheld from `tools/list` and refused unless `MCP_ENABLE_SHL_GENERATE=true` |

**Utility tools:**

| Tool | Description |
| --- | --- |
| `fhir_get_token` | Issue a 5-minute step-up token (call before any write) |
| `fhir_seed` | Seed a tenant with demo Patient + Observations + Condition |
| `fhir_compiled_truth` | Current state + Provenance evidence timeline |

All tools add `_mcp_summary` with reasoning, clinical context, and limitations.

## Guardrail Demo

The 6-step demo at `/r6/fhir/demo/agent-loop` shows the full guardrail sequence:

1. **PHI Redaction** — Agent reads a patient, receives redacted data
2. **$validate Gate** — Agent proposes an Observation, validated before write
3. **Permission Deny** — No Permission rule exists, access denied with reasoning
4. **Permission Permit** — Permit rule created, re-evaluation succeeds
5. **Step-up + Human-in-the-loop** — Write requires both token and human confirmation
6. **Commit + Audit** — Write succeeds, full audit trail generated

## Comparison

| Feature | This Project | AWS HealthLake MCP | Medplum MCP | Raw FHIR API |
| --- | --- | --- | --- | --- |
| Works with any FHIR server | Yes | HealthLake only | Medplum only | N/A |
| PHI redaction on reads | Yes | No | No | No |
| Immutable audit trail | Yes | CloudTrail (separate) | Partial | No |
| Step-up auth for writes | Yes | IAM (separate) | Medplum auth | No |
| Human-in-the-loop | Yes | No | No | No |
| Permission $evaluate (R6) | Yes | No | No | No |
| Setup time | 10 seconds | 30+ minutes | 15+ minutes | Varies |

## FHIR Version Support

| Version | Profile | Status | Resources |
| --- | --- | --- | --- |
| R4 | US Core v9 | **Stable** | Patient, Condition, AllergyIntolerance, Immunization, MedicationRequest, Procedure, DiagnosticReport, CarePlan, CareTeam, Goal, DocumentReference, Coverage, ServiceRequest, Location, Organization, Practitioner, PractitionerRole, RelatedPerson, Specimen, FamilyMemberHistory |
| R6 | v6.0.0-ballot3 | Experimental | Permission, SubscriptionTopic, DeviceAlert, NutritionIntake, DeviceAssociation, NutritionProduct, Requirements, ActorDefinition |

Both R4 and R6 resources flow through the same guardrail stack (PHI redaction, audit, step-up auth, tenant isolation). R6 ballot resources may change before final release.

## Testing

```bash
# Python tests (1,490+ across 90+ files; includes action-rail, SDC, quality, labs, ops, CareAgents suites)
uv run python -m pytest tests/ -v
uv run python -m pytest tests/test_r6_routes.py::test_name -v   # single test

# MCP server tests
cd services/agent-orchestrator && npm ci && npm test

# Playwright end-to-end tests (UI + API, requires Flask on :5000)
cd e2e && npm ci && npx playwright install --with-deps chromium && npm test
cd e2e && npm run test:headed    # headed browser
cd e2e && npm run test:ui        # interactive UI mode
```

## API Endpoints

| Endpoint | Method | Description |
| --- | --- | --- |
| `/r6/fhir/metadata` | GET | CapabilityStatement |
| `/r6/fhir/health` | GET | Liveness probe (reports upstream status) |
| `/r6/fhir/{type}` | POST | Create resource (requires step-up) |
| `/r6/fhir/{type}` | GET | Search resources |
| `/r6/fhir/{type}/{id}` | GET | Read resource (redacted) |
| `/r6/fhir/{type}/{id}` | PUT | Update resource (requires step-up + ETag) |
| `/r6/fhir/{type}/$validate` | POST | Validate resource |
| `/r6/fhir/Questionnaire[/{id}]/$populate` | POST | SDC — pre-fill a QuestionnaireResponse from a subject |
| `/r6/fhir/QuestionnaireResponse/$extract` | POST | SDC — extract a transaction Bundle (`?dryRun=true` to preview) |
| `/r6/fhir/{type}/{id}/$deidentify` | GET | Conservative de-identification preview (expert review required) |
| `/r6/fhir/Observation/$stats` | GET | Observation statistics |
| `/r6/fhir/Observation/$lastn` | GET | Most recent observations |
| `/r6/fhir/Permission/$evaluate` | POST | R6 access control evaluation |
| `/r6/fhir/SubscriptionTopic/$list` | GET | Subscription topic discovery |
| `/r6/fhir/Bundle/$ingest-context` | POST | Bundle ingestion + context envelope |
| `/r6/fhir/context/{id}` | GET | Retrieve context envelope |
| `/r6/fhir/AuditEvent` | GET | Search audit events |
| `/r6/fhir/AuditEvent/$export` | GET | Export audit trail (NDJSON/Bundle) |
| `/r6/fhir/demo/agent-loop` | POST | 6-step guardrail demo |
| `/r6/fhir/oauth/*` | * | OAuth 2.1 + PKCE + SMART discovery |
| `/r6/fhir/{type}/{id}/$curatr-evaluate` | GET | Evaluate resource data quality (Curatr) |
| `/r6/fhir/{type}/{id}/$curatr-apply-fix` | POST | Apply patient-approved fixes with Provenance |

Local search accepts the parameters advertised by `/r6/fhir/metadata`.
Unknown parameters default to lenient handling (a bounded
`search.mode="outcome"` warning); `Prefer: handling=strict` returns a 400
`OperationOutcome`. Unsupported modifiers and malformed supported values always
return 400. `_count=0` and `_summary=count` are count-only searches. Self links
contain exactly the applied, URL-encoded parameters, and audit output never
echoes submitted filter values or arbitrary parameter names.

## Upstream Proxy

Connect to real FHIR servers while keeping all guardrails active:

```bash
FHIR_UPSTREAM_URL=https://hapi.fhir.org/baseR4 python main.py
```

- **Reads**: Fetched from upstream, then redacted + audited + disclaimers added
- **Searches**: Forwarded with all query params, results redacted per entry
- **Writes**: Validated locally first, then forwarded with step-up auth check
- **URL rewriting**: Upstream URLs never leak to clients

Tested with: HAPI FHIR R4/R5, SMART Health IT, Epic Sandbox.

**Put the guardrails in front of your FHIR server** — recipe for running the
redaction + audit + step-up + human-in-the-loop stack in front of **Medplum**
(the same pattern works for Aidbox, Google Cloud Healthcare, or any FHIR R4
server): [docs/recipes/healthclaw-in-front-of-medplum.md](docs/recipes/healthclaw-in-front-of-medplum.md).
A repeatable integration test (`tests/test_medplum_in_front.py`) proves a
Medplum-returned Patient comes back redacted + audited and writes are step-up
gated before reaching Medplum.

## Curatr — Patient-Owned Data Quality

Curatr is a patient-facing data quality skill that evaluates FHIR health records for
coding issues and lets the patient decide how to resolve them.

```text
1. Patient connects data → HealthClaw Guardrails deidentifies and loads it
2. OpenClaw calls curatr.evaluate → checks codes against live terminology APIs
3. Issues presented in plain language with impact and fix suggestions
4. Patient approves fixes → curatr.apply_fix updates resource + creates Provenance
5. Optional: generate a structured correction request for the source provider
```

**What Curatr checks on a Condition:**

| Check | Service | Example |
| --- | --- | --- |
| Deprecated code system | Local lookup (no network) | ICD-9-CM → critical |
| ICD-10-CM code validity | NLM Clinical Tables API | Invalid code → warning |
| SNOMED CT / LOINC validity | tx.fhir.org (HL7 public) | Unknown code → warning |
| RxNorm drug code | RXNAV API (NLM) | Missing RXCUI → warning |
| Display name accuracy | Cross-checked with canonical term | Mismatch → suggestion |
| Missing required fields | Structural | No clinicalStatus → warning |

Every fix creates a linked **Provenance** resource recording patient...

© aks129 · MIT · aks129/HealthClawGuardrails

Similar plugins

All plugins for Security →

From the same set2

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor