Claude Code as an HL7 to FHIR Migration Agent for Hospitals

TL;DR
Hospitals still ship HL7 v2 pipes between systems in 2026.
Last updated: April 28, 2026
The Pipe That Will Not Die#
Walk into any mid-sized hospital integration team in 2026 and you will still find HL7 v2 messages flowing over MLLP between an old Epic interface, a lab system from a vendor that was acquired twice, and a homegrown bed-management app that nobody wants to touch. The org has a five-year FHIR mandate, a Mirth or Rhapsody engine in the middle, and one or two analysts who can read pipe-and-hat encoding without flinching. Everything else is tribal knowledge written into channel filters and JavaScript transformers.
For the broader MCP map, pair this with What Is MCP (Model Context Protocol)? A TypeScript Developer's Guide and The Complete Guide to MCP Servers; those pieces cover the concepts and server-selection layer behind this article.
This is the kind of work that looks tedious from the outside and terrifying from the inside. A wrong field map can put an allergy on the wrong patient. A dropped segment can lose a discharge time. The reason these migrations stall is not that FHIR is hard. It is that the existing v2 messages encode twenty years of local conventions that the spec never had opinions about, and nobody has the bandwidth to chase them all down.
Agentic coding tools change the math. Claude Code, scoped properly, is unusually good at this kind of grinding, well-bounded, high-context translation work. The trick is wiring it so it never sees PHI it should not see, never writes to a production interface engine, and always produces a diff that a human integrator can sign off on.
The Wedge#
The non-obvious wedge is not "use AI to write FHIR resources." Half the FHIR community has tried that and bounced off the spec. The wedge is "use a coding agent to migrate the transformer code, not the data." The transformer is just JavaScript or Python. The agent reads the v2 sample, reads the channel transformer, reads the FHIR profile the org has standardized on (US Core, Carin BB, Da Vinci, whatever), and proposes a new transformer plus a test fixture. PHI never leaves the synthetic-fixture loop. The output is a pull request, not a live deploy.
File Structure#
A workable repo for this looks like:
hl7-fhir-migration/
CLAUDE.md
channels/
adt-a08-bedboard/
v2-sample.hl7 # synthetic, Synthea-derived
current-transformer.js # exported from Mirth
target-profile.md # which FHIR profile + must-support fields
tests/
encounter.expected.json
patient.expected.json
skills/
fhir-validate/
SKILL.md
scripts/validate.sh # wraps HAPI validator
hl7-parse/
SKILL.md
scripts/parse.py # uses python-hl7
.claude/
settings.json # hooks, allowlist, deny PHI paths
mcp/
fhir-server.ts # local HAPI FHIR proxy, MCP wrapper
The CLAUDE.md does the heavy lifting. It tells the agent the org's profile choices, the deterministic ID strategy (almost always a hash of MRN plus assigning authority), the timezone rules, and the list of fields the compliance team cares about most: allergies, medications, code status, advance directives. Everything else is "best effort, flag for review."
Key Prompts#
The prompt pattern that works is two-pass.
Pass one is discovery. You point Claude Code at one channel folder and ask:
Read
v2-sample.hl7andcurrent-transformer.js. Produce a table of every v2 field the transformer touches, the FHIR resource and element it maps to under ourtarget-profile.md, and a confidence score. Flag anything where the source field is being parsed with a regex or a custom date format. Do not write code yet.
Pass two is implementation, scoped to one resource at a time:
Implement the Encounter mapping only. Write a new transformer in
channels/adt-a08-bedboard/transformer.ts. For every must-support field intarget-profile.md, write a passing test intests/encounter.expected.json. Use thefhir-validateskill on the output. If validation fails, fix the transformer, not the expected fixture.
The reason to split is cost and review. Discovery passes are cheap and make the human-readable artifact that the integration analyst actually wants. Implementation passes are bigger but bounded.
The MCP Server#
The single highest-leverage piece of glue is a local FHIR MCP server. It wraps a HAPI FHIR validator running in Docker and exposes three tools: validate_resource, search_profile, and diff_against_baseline. The agent calls validate_resource on every artifact it produces and gets back the same OperationOutcome a human would see in HAPI. Because the server is local and the fixtures are synthetic, no PHI ever crosses a network boundary.
A minimal server in TypeScript is around 120 lines using the official MCP SDK. It runs as a stdio child process of Claude Code, started from .mcp.json. The same server doubles as a skill backend if you prefer SKILL.md style invocations.
The Hook That Makes It Safe#
The non-negotiable piece is a PreToolUse hook in .claude/settings.json that blocks any read of files matching real PHI patterns. It looks for the org's MRN format, the v2 magic numbers in non-fixture directories, and any path under production/. If the agent tries to read a file outside channels/*/ or the synthetic fixtures, the hook exits non-zero and Claude Code refuses the tool call.
A second hook on PostToolUse runs git diff --stat after every Write and rejects diffs that touch the production Mirth export directory. Belt and suspenders, but in healthcare you wear both.
Risks and Guardrails#
The honest risks are these. First, synthetic data does not capture every local convention. Synthea will not produce the OBX-5 quirks a particular lab uses. Mitigation: keep a "weird messages" library, scrubbed and de-identified by a human, that the agent can train its mappings against. Second, FHIR profiles drift. US Core 7.0 broke things US Core 6.1 allowed. Pin the profile version in CLAUDE.md and bump deliberately. Third, audit. Anything the agent touches needs a paper trail that a HIPAA auditor can read. The PR-only output, combined with hook logs written to an append-only file, satisfies most audit asks.
SOC2 and HITRUST add one more requirement: the agent's transcripts themselves are records. Claude Code's --output-format jsonl plus a hook that ships transcripts to your existing SIEM closes that gap.
Minimal Next Step#
You can run the smallest version of this today, before talking to compliance, on synthetic data only.
mkdir hl7-fhir-migration && cd hl7-fhir-migration- Drop a Synthea-generated ADT^A08 message into
channels/adt-a08-bedboard/v2-sample.hl7 - Paste any open-source Mirth transformer from GitHub into
current-transformer.js - Write a six-line
CLAUDE.mdthat names US Core 7.0 as the target profile - Run
claudeand paste the discovery prompt above
You will get back a field-by-field mapping table in about ninety seconds. That table, on its own, is more documentation than most channels have today. Show it to the integration lead. The conversation about whether to let an agent write the next transformer goes very differently once they have read the first one.
The hospitals that win the FHIR transition will not be the ones with the biggest integration teams. They will be the ones whose teams stop hand-typing transformers and start reviewing them.
FAQ#
Does the agent ever touch real patient data?#
No, not in the workflow described here. The agent works only against synthetic, Synthea-derived HL7 samples and fixtures. A PreToolUse hook blocks reads of anything matching real MRN patterns or paths under production/, so PHI never enters the agent's context.
Why use an MCP server instead of just asking the agent to validate FHIR manually?#
A local FHIR MCP server wrapping HAPI FHIR gives the agent a deterministic validate_resource tool that returns the same OperationOutcome a human reviewer would see. That closes the loop (write, validate, fix) without the agent guessing at spec compliance from training data, which drifts as FHIR profile versions change.
Is this approach compliant with HIPAA and SOC2 audit requirements?#
The pattern helps: PR-only output, hook logs written to an append-only file, and synthetic-only data reduce audit surface. But agent transcripts themselves count as records under SOC2/HITRUST, so pair this with --output-format jsonl and ship transcripts to your existing SIEM to close that gap.
What if our hospital's HL7 messages use non-standard local conventions?#
Synthetic data will not capture every quirk (unusual OBX-5 usage, for example). Maintain a "weird messages" library of scrubbed, de-identified real messages that a human has cleared, and use it to stress-test the agent's mappings before trusting a transformer in production.
Get the next deep dive like this in your inbox
One email a week on Claude Code and the rest of the AI dev stack. Free.
Read next on Claude Code
Claude Code Agent Teams, Subagents, and MCP: The 2026 Playbook
Claude Code is turning into an orchestration layer for agent teams. Here is how subagents, MCP, hooks, and long context fit together in 2026.
9 min readHow to Make Claude Code 10x Better at Design: Image and Video Assets From the Agent Loop
Make Claude Code better at design by putting image and video generation in the agent loop with Higgsfield MCP and CLI, so sites ship with real assets.
8 min readAgentCanvas is a visual adapter for Claude Code and Codex
AgentCanvas is a visual adapter for Claude Code and Codex that puts artifacts, decisions and handoffs on one board, so the next agent or human can see them.
6 min readTechnical content at the intersection of AI and development. Building with AI agents, Claude Code, and modern dev tools - then showing you exactly how it works.








