Skip to main content
Glama
GlennRTC

HL7-Bridge MCP

by GlennRTC

HL7-Bridge MCP

Translate, validate and explain clinical messages between HL7 v2.x and FHIR R4 — as a tool for your AI agent.

CI License: Apache-2.0 MCP

IN   ORU^R01  ·  HL7 v2, lab result
     OBX|1|NM|1554-5^GLUCOSE^LN||95|mg/dL|70-105|N|||F

OUT  FHIR R4 Bundle  →  Patient · Observation · DiagnosticReport
     ✓ conforms to US Core
     ⚠ if PID-8 (sex) were empty:
       "US Core Patient requires gender"   →  Patient.gender

⚠️ Not a medical device. Not validated for clinical decisions. Mappings require human validation before any use with real data. See SECURITY.md.

What it is

A hospital's systems speak two "languages". HL7 v2 is the old, telegraphic one that labs and admissions use to announce "patient Juan Pérez was admitted" or "glucose 95 mg/dL". FHIR is the modern JSON one used by apps, portals and newer systems.

This is a translator between the two that also reviews the translation and explains errors in human language. It's not just another FHIR CRUD: it's the validated translation layer an agent needs so it doesn't have to talk directly to a raw FHIR server. It does three things:

  • Translates HL7 v2 → FHIR (and the reverse, in future phases).

  • Validates against profiles (US Core): that a lab's category isn't missing, that the patient's name is present…

  • Explains what failed and where — "the required PV1 segment for an admission is missing" — without opening the spec.

The moat isn't the MCP protocol, it's the mapping knowledge: instead of rules hidden in the code, it uses declarative maps (maps/, readable and versionable YAML) of the form "message PID-5 → Patient's name". Auditable, correctable translation without touching code.

Related MCP server: Healthcare FHIR MCP Server

Tools

Tool

Input

Output

parse_hl7v2

{ message }

{ ast } — segments → fields → components, separators read from MSH-1/2

map_v2_to_fhir

{ message, mapId?, fhirVersion? }

{ bundle, validation: { issues, explained } }

validate_message

{ payload, kind: "hl7v2"|"fhir", profile? }

{ issues } with location (segment-field or FHIRPath)

explain_error

{ issue }

{ humanMessage, location, hint }

Maps in v0.1: ADT^A01, ORU^R01, ORM^O01 → FHIR R4 (see maps/). Non-obvious mappings are marked TODO(mapeo) and are not guessed.

Note: the server's human-readable messages (message, humanMessage, hint) are currently emitted in Spanish. The examples below are translated for readability.

Call → result (one per tool)

Each tool is invoked via tools/call with these arguments. Output is abbreviated; the full curl and more cases (malformed, edge) are in USAGE.md.

// arguments
{ "message": "MSH|^~\\&|LAB|H|EMR|H|20260102||ORU^R01|MSG1|P|2.5\rPID|1||123456^^^H^MR||DOE^JOHN||19800101|M" }
// → result
{ "ast": {
  "encoding": { "field":"|","component":"^","repetition":"~","escape":"\\","subcomponent":"&" },
  "segments": [ /* MSH, PID → fields → components → subcomponents */ ]
} }
// message not starting with MSH → isError: { "error": { "code":"INVALID_HEADER", "location":"MSH" } }
// arguments
{ "message": "MSH|...|ORU^R01|...\rPID|...\rOBR|1||1|1554-5^GLUCOSE^LN\rOBX|1|NM|1554-5^GLUCOSE^LN||95|mg/dL|70-105|N|||F" }
// → result
{ "bundle": { "resourceType":"Bundle", "entry":[ /* Patient · Observation · DiagnosticReport */ ] },
  "validation": { "issues": [], "explained": [] } }        // clean if it conforms to US Core
// arguments: an ADT^A01 with no PV1 and no name
{ "kind": "hl7v2", "payload": "MSH|...|ADT^A01|...\rEVN|A01|20260101\rPID|1||123456^^^H^MR" }
// → result
{ "issues": [
  { "severity":"error", "code":"MISSING_SEGMENT", "location":"PV1",  "message":"Falta el segmento requerido PV1 para ADT^A01." },
  { "severity":"error", "code":"MISSING_FIELD",   "location":"PID-5","message":"Falta el campo requerido PID-5 para ADT^A01." }
] }
// arguments
{ "issue": { "severity":"information", "code":"CODED", "location":"OBX-11", "message":"OBX-11 tiene el valor 'F'." } }
// → result
{ "humanMessage": "OBX-11 tiene el valor 'F'. El valor 'F' significa \"Final\" (tabla HL7 0085).",
  "location": "OBX-11 (Observation result status)",
  "hint": "Revisa el mensaje de origen contra la especificación del perfil." }

Try it (Claude Code)

This is an MCP server, so the natural way to use it is from an MCP client. In Claude Code you register it once with claude mcp add and then just ask in plain language — Claude picks the tool and fills the arguments for you. No HTTP, no ports, no curl.

1. Register it as a stdio server. tsx runs the TypeScript source directly — no build step, nothing to go stale:

npm ci
claude mcp add hl7-bridge -- npx tsx "$(pwd)/src/server/index.ts"

Prefer a compiled binary? npm run build then register node "$(pwd)/dist/server/index.js" instead — but rebuild after every source change.

2. Verify with claude mcp list, and inside Claude Code with /mcp (you should see the four hl7-bridge tools). To remove it later: claude mcp remove hl7-bridge.

3. Ask in natural language. Paste a message and let Claude call map_v2_to_fhir:

Map this HL7 v2 lab result to FHIR and tell me if it conforms to US Core: MSH|^~\&|LAB|H|EMR|H|20260102||ORU^R01|1|P|2.5 PID|1||123456^^^H^MR||DOE^JOHN||19800101|M OBR|1||1|1554-5^GLUCOSE^LN OBX|1|NM|1554-5^GLUCOSE^LN||95|mg/dL|70-105|N|||F

You get a Bundle with Patient · Observation · DiagnosticReport and clean validation ("issues": []). Observation.category = laboratory (which US Core requires) is populated as a deliberate constant documented in the map — HL7 v2 has no field that carries it and an ORU^R01 is always laboratory —, not a guessed value.

The value is in what it warns about. Now drop the trailing M (PID-8, sex) from that PID segment — ...||19800101|M...||19800101|. Because gender does come from a message field, its absence is real debt, and validation reports it with an exact location and a human explanation:

"issues": [
  { "severity":"error", "code":"PROFILE_REQUIRED", "location":"Patient.gender",
    "message":"US Core Patient requiere gender." }
],
"explained": [
  { "humanMessage":"US Core Patient requiere gender.", "location":"Patient.gender",
    "hint":"El perfil FHIR exige este elemento (must-support). Ajusta el mapa o el mensaje de origen para poblarlo." }
]

The difference with category is one of origin: gender has a v2 field (PID-8) that was left empty; category is a constant with no field. The full test set for all 4 tools (valid, malformed, edge cases) is in USAGE.md.

Not using Claude Code? To drive the server over HTTP from another system (or raw curl), see the HTTP transport, protocol and per-tool calls in USAGE.md.

Why use it vs. letting the agent translate directly

An LLM can approximate HL7 v2 → FHIR on its own. The value here isn't the conversion — it's that the conversion is repeatable, auditable, and refuses to guess.

Concern

With this MCP

LLM translating directly

Determinism

Same input → identical output every run (declarative YAML maps)

Non-deterministic; same message can map differently across runs/models

Won't invent data

Refuses to guess — unknown coding system → CODING_NO_SYSTEM warning, no system fabricated; unclear mapping → TODO(mapeo)

Hallucinates plausible values (a wrong system URI, an invented gender)

Parsing

Reads separators from MSH-1/2; handles repetitions, subcomponents, escapes, \r/\n/\r\n

Usually assumes |^~\& and mis-parses vendor quirks silently

Validation

Checks US Core requirements, returns exact location + human reason

"Looks conformant" — nothing actually validated it

Failure mode

Fails loudly with a structured error (MAP_INVALID, INVALID_HEADER)

Fails silently — a partial/wrong Bundle that looks fine

Auditability

Mapping rule lives in versioned YAML you can diff and correct

Rule lives in a prompt/weights; you can't inspect why it mapped that way

The gender case above is the thesis: it flags real debt (empty PID-8) with an exact location, and does not flag the deliberate category constant. An LLM alone would likely invent a gender or stay silent.

Where it doesn't add much: coverage is narrow today (R4 only; ADT^A01, ORU^R01, ORM^O01, OUL^R22 partial — outside those you get a clean MAP_NOT_FOUND); validation is minimal US Core, not the full IG; and it's still not a clinical safety net — output requires human validation. For a one-off, throwaway translation where repeatability and correctness don't matter, the plain LLM is faster.

Bottom line: use it when the output feeds a real system and a wrong-but-plausible Bundle is worse than a loud failure. For casual exploration, it may be overhead you don't need.

Usage

npm ci && npm test              # + npm run typecheck / lint / test:coverage
npm run build && npm start      # stdio server for local MCP clients (Claude Code, Claude Desktop)

Full setup, testing and deployment steps are in USAGE.md: the HTTP transport (POST /mcp, for hosting or driving the server from another system), the JSON-RPC protocol, a curl call per tool, the complete test set, and Render deployment.

Design principles

  • PHI-safe by default — the repo never contains real PHI; logs redact PID/NK1/GT1.

  • Narrow tools — each tool has typed input/output, not a "do whatever you want".

  • Determinism and explainability — every error carries structure + what and where in human language.

  • Fail loudly — a malformed message yields a structured error, never a silent partial result.

  • Not a medical device — no clinical decisions without human validation.

Stack and structure

TypeScript (Node ≥ 20) strict, official MCP SDK (@modelcontextprotocol/sdk), fhirpath.js

  • @types/fhir, Vitest. Typed errors (Hl7BridgeError with code, location, humanMessage).

/src/{server,parser,mapper,validator,errors}   # MCP · HL7 v2 · declarative mapping · FHIR profiles · errors
/maps                                          # declarative YAML maps (ADT, ORU, ORM…)
/test/fixtures                                 # synthetic messages + expected outputs

Detail in context/ARCHITECTURE.md (components, I/O contracts, map format) and context/PRD.md.

References

Mapping and validation decisions are grounded in these authoritative sources, not guessed:

License

Apache-2.0 at the core (parser, mapper, validator, MCP tools). See LICENSE.

Available Tools

4 tools
explain_errorA

Convierte un issue de validación en explicación humana: ubicación legible, significado de tablas HL7 y una pista accionable.

ParametersJSON Schema
NameRequiredDescriptionDefault
issueYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the burden of behavioral disclosure. It explains the output components (readable location, HL7 table meanings, actionable hint) which gives some transparency, but it does not explicitly state whether the tool is read-only or has any side effects, permissions, or rate limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence that conveys the tool's purpose and key deliverables without excess words. Every phrase earns its place, and the structure is efficient for an agent to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description gives the essential context: it consumes a validation issue and produces a human explanation with specific elements. However, without an output schema or annotations, it does not specify the return format (e.g., string, object) or any potential error conditions, leaving some gaps in completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has one 'issue' object with 0% schema description coverage, so the description must compensate. The description mentions 'issue de validación' and the output components (location, table meaning, hint), but it does not explain the individual properties (severity, code, location, message) or how they relate to the transformation, leaving the parameter semantics underspecified.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb 'Convierte' (converts) and identifies the resource as 'un issue de validación' (a validation issue), clearly stating the tool's function: turning validation issues into human-readable explanations. It distinguishes itself from sibling tools (parse, validate, map) by focusing on explaining errors rather than processing messages.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use the tool through the phrase 'Convierte un issue de validación', suggesting it is applied after validation has produced an issue. However, it does not explicitly state when not to use it or mention alternative tools, leaving usage boundaries implied rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

map_v2_to_fhirA

Mapea un mensaje HL7 v2 a un Bundle FHIR R4 con un mapa declarativo y valida el resultado contra US Core mínimo, explicando cada issue.

ParametersJSON Schema
NameRequiredDescriptionDefault
mapIdNo
messageYes
fhirVersionNo

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden of behavioral disclosure. It goes beyond simple mapping by explaining that it validates against US Core and explains each issue, which is useful context. However, it does not discuss error handling, limitations, or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence, front-loaded with the core action and no filler. Every phrase adds meaningful information about mapping, validation, and issue explanation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the essential mapping and validation behavior and provides a high-level output description, but it omits details on mapId and the fhirVersion options. Given no output schema or annotations, it is reasonably complete but has notable gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It identifies 'message' as an HL7 v2 message and mentions a declarative map, but it fails to explain mapId or fhirVersion. Moreover, it states 'FHIR R4' while the schema allows R6, potentially misleading the agent about the fhirVersion parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool maps an HL7 v2 message to an FHIR R4 Bundle using a declarative map and validates the result. It distinguishes this from siblings like validate_message or parse_hl7v2 by specifying the conversion and validation action.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for mapping HL7 v2 to FHIR and validating, but it does not explicitly state when to use this tool over siblings like validate_message or explain_error. No exclusions or alternative guidance is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

parse_hl7v2A

Parsea un mensaje HL7 v2 a un AST tipado (segmentos, campos, componentes) con los separadores leídos de MSH-1/MSH-2.

ParametersJSON Schema
NameRequiredDescriptionDefault
messageYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses a key behavioral detail—separators are read from MSH-1/MSH-2—which adds technical context. However, it does not mention error behavior, strictness, or other potential side effects, leaving some transparency gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured sentence that front-loads the primary action ('Parsea un mensaje HL7 v2 a un AST tipado') and then provides a necessary technical detail. No wasted words; every part earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simple signature (one parameter, no output schema), the description is reasonably complete. It describes both the input and the output format (typed AST) and a critical parsing behavior (separators from MSH-1/MSH-2). It could mention error handling or validation behavior, but the core functionality is sufficiently conveyed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has only one parameter ('message') with zero coverage in the description, so the description must compensate. It does clarify that the 'message' is an HL7 v2 message to be parsed and adds context about separator handling, which gives the parameter meaningful semantics despite lacking constraints or format details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific verb ('Parsea' = parses) and resource ('mensaje HL7 v2'), and details the output as a typed AST with segments, fields, and components. This distinguishes it from siblings like map_v2_to_fhir (mapping) and validate_message (validation), making the tool's purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage by explaining what the tool does, but it does not explicitly state when to use it or provide exclusion criteria or alternatives. Sibling tool names suggest context, but no direct guidance is given, so usage context is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

validate_messageA

Valida un mensaje HL7 v2 (segmentos/campos requeridos) o un Bundle FHIR (perfil US Core mínimo) y devuelve issues estructurados.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindYes
payloadYes
profileNo

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses that it returns structured issues, which indicates a non-throwing behavior and provides expectations. It also clarifies that FHIR validation uses a minimal US Core profile. However, with no annotations, it doesn't disclose error handling, permissions, or side effects. This is moderate but incomplete.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence that directly states the tool's purpose and output. No filler or redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool validates two different data formats and has an optional 'profile' parameter, but the description does not explain the profile's purpose or how validation results are structured. It also lacks guidance on input format for payload. Given the absence of output schema and annotations, the description is insufficient for full contextual understanding.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It does not explain the 'payload' or 'profile' parameters at all. The only parameter hinted at is 'kind' by mentioning HL7 v2 and FHIR, but the enum already covers that. The description adds no semantic value for parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Valida') and clearly states the resources (HL7 v2 with segments/required fields, FHIR Bundle with minimum US Core profile) and the output (structured issues). This distinguishes it from siblings like map_v2_to_fhir, explain_error, and parse_hl7v2.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The context is clear: this tool is for validation, not mapping, parsing, or error explanation. However, it does not explicitly mention alternatives or exclusion criteria, so it doesn't achieve a 5. The user can infer when to use it based on the verb and resource types.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 4 tool updatesv0.1.0
    • First observedexplain_error
    • First observedmap_v2_to_fhir
    • First observedparse_hl7v2
    • First observedvalidate_message

TDQS

A3.9/5.0
Disambiguation4/5

Each tool has a clear core purpose (parse, validate, map, explain), but map_v2_to_fhir and validate_message both perform validation, which could create confusion when an agent is deciding which tool to use for a raw input check. The descriptions are specific enough to generally disambiguate.

Naming Consistency5/5

All tool names follow a consistent verb-first snake_case pattern (parse, validate, map, explain), with clear object references. There is no mixing of casing or verb styles, making the set predictable.

Tool Count5/5

Four tools is a well-scoped count for an HL7-to-FHIR bridge workflow. Each tool covers a distinct step without unnecessary duplication, and nothing feels like filler.

Completeness4/5

The core pipeline (parse, validate, map, explain) is well covered, but a 'bridge' might imply bidirectional conversion. Since all tools are v2-to-FHIR oriented, a reverse mapping tool is a natural but missing addition, and there is no map management tool, which would be a minor gap.

Maintenance

ActivitySlowing
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables LLM-based agents to interact with FHIR healthcare data through natural language prompts, providing full CRUD operations on FHIR resources, document processing, and semantic search capabilities.
    13
    98
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Typed, validated FHIR tools for healthcare AI agents — build, read, validate, and code FHIR resources from a patient bundle, with terminology lookup and machine-readable validation reports built for fix-and-retry.
    8
    226
    Apache 2.0
  • A
    license
    A
    quality
    C
    maintenance
    Let AI agents read, validate and acknowledge EDI documents. Parses raw X12 and EDIFACT interchanges into structured JSON, validates envelope integrity, produces plain-language summaries, and generates 997 Functional Acknowledgments.
    4
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/GlennRTC/hl7-bridge-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server