Model Context Protocol

HL7 v2.5.1 Reference
MCP Server

A Model Context Protocol server that gives LLMs structured access to the HL7 v2.5.1 standard — segment definitions, field metadata, message structures, code tables, a message parser, and a validator with deep profiles for ORM^O01, ADT^A08, ADT^A31, ADT^A40 and ORU^R01. Built for PACS administrators, healthcare IT engineers, and interface analysts.

Protocol MCP Streamable HTTP Standard HL7 v2.5.1 + IHE RAD extensions Runtime Node.js 18+ License MIT
Note — static hosting: GitHub Pages serves only static files. This page is the documentation and download hub. To use the MCP server, download server.js below and run it locally (or deploy it to Railway, Render, or Cloudflare Workers). The endpoint will be http://localhost:3000/mcp by default.

Available Tools

Eight tools covering the full HL7 v2.5.1 reference surface.

list_segments
List all supported HL7 v2.5.1 segments with their names and field counts.
no parameters
get_segment
Full definition of a segment — every field with data type, length, optionality, repeatability, and a detailed description.
segment
get_field
Look up a single field by segment and sequence number. Returns data type, length, optionality, and description.
segmentfield
list_message_types
List all supported HL7 message types and events with short descriptions.
no parameters
get_message_structure
Get the required and optional segment sequence for a message type (e.g., ADT^A01, ORM^O01, ORU^R01, ADT^A40).
messageType
lookup_code_table
Look up values in a standard HL7 code table by table number. Supports optional text filter. Tables: 0001, 0003, 0004, 0007, 0008, 0074, 0078, 0085, 0119, 0123, 0155, 0190, 0357.
tableNumberfilter?
parse_message
Parse a pipe-delimited HL7 message into structured JSON. Strips MLLP framing, labels every field by name, and extracts a key-identifiers summary — MRN, prior MRN, order control, accession (reporting which field it came from), Study Instance UID and result status.
message
validate_message
Validate against v2.5.1 rules and return errors, warnings and advisory notes separately. Base checks run on any message; five message types get a deep profile.
message

Deep validation profiles

Beyond the base MSH, delimiter, timestamp and code-table checks that run on every message, these five types are validated against their specific semantics:

MessageWhat the profile checks beyond the basics
ORM^O01 ORC-1 against table 0119; that an order number exists in ORC-2 or ORC-3; that action codes (CA, DC, HD, RL, RP, SC, XO) actually identify an existing order; SC sent without ORC-5, the classic duplicate-worklist cause; ORC↔OBR pairing across the repeating ORDER group; OBR-4 present; accession recoverable from OBR-18/2/3; and ZDS Study Instance UID validated as a real DICOM UID — digits and dots only, ≤64 chars, no leading-zero components.
ADT^A08 EVN present and second; EVN-1 agrees with MSH-9.2; PV1 required per the ADT_A01 structure; PV1-2 against table 0004; missing PV1-19 flagged, since a visit-level update with no visit number can land on the wrong encounter; MRG rejected outright.
ADT^A31 PV1 required by the ADT_A05 structure even though A31 is person-level; warns when PV1-19 is populated, because a person-level event carrying a visit number is the usual A08/A31 mix-up; notes when Patient Class is something other than N.
ADT^A40 MRG required; PID↔MRG pairing across the repeating PATIENT group of ADT_A39, so multi-merge messages are checked pair by pair; MRG ordered after its PID; self-merge detection when PID-3 equals MRG-1; and a warning when the two identifiers come from different assigning authorities.
ORU^R01 OBX segments grouped under their owning OBR; OBX appearing before any OBR flagged; OBR-25 against table 0123 with an explicit callout when a table 0085 code is used by mistake; OBX-11 against 0085; cross-checks OBR-25 against every OBX-11 so a "Final" report containing preliminary lines is caught; NM values that are not numeric; and repeated OBX-3 within a group with no OBX-4 Sub-ID.

Quick Start

  1. Download the server

    Right-click and save, or clone the repo.

    shell
    curl -O https://coffeemilktea.github.io/mcp/server.js
  2. Install dependencies

    shell
    npm install @modelcontextprotocol/sdk express cors zod
  3. Run the server

    shell
    node server.js
    # HL7 v2.5.1 MCP server listening on http://localhost:3000/mcp
  4. Add to Claude Desktop

    Edit ~/Library/Application Support/Claude/claude_desktop_config.json (Mac) or %APPDATA%\Claude\claude_desktop_config.json (Windows).

    claude_desktop_config.json
    {
      "mcpServers": {
        "hl7-reference": {
          "type": "http",
          "url": "http://localhost:3000/mcp"
        }
      }
    }

    Restart Claude Desktop. The eight tools will appear in the tool panel.

  5. Or use with Claude Code

    Add to your project's .claude/settings.json:

    .claude/settings.json
    {
      "mcpServers": {
        "hl7-reference": {
          "type": "http",
          "url": "http://localhost:3000/mcp"
        }
      }
    }

Deploy Remotely

Run on Railway or Render for a persistent endpoint you can share across machines.

Railway (one command)

shell
railway init # answer prompts
railway up    # deploys; gives you https://yourapp.up.railway.app

Set PORT env var if needed. Railway auto-detects Node.js.

Render

Create a new Web Service, point to the repo, set Start Command to node server.js. The deployed URL becomes your MCP endpoint — use https://yourapp.onrender.com/mcp.

Environment variables

VariableDefaultDescription
PORT3000HTTP port the server listens on

Supported HL7 Content

Segments

CodeNameNotes
MSHMessage HeaderAll 21 fields, delimiter rules, version check
MSAMessage AcknowledgmentACK codes AA/AE/AR/CA/CE/CR
EVNEvent TypeRecorded vs. planned date, reason codes
PIDPatient IdentificationAll 38 v2.5.1 fields, deprecated ones included and marked
PV1Patient VisitPatient class, location, attending/referring/admitting physicians
ORCCommon OrderOrder control codes, placer/filler numbers
OBRObservation RequestOBR-18 accession, OBR-25 result status, diagnostic service section
OBXObservation/ResultAll value types, abnormal flags, result status
NTENotes and CommentsSource codes, formatted text
MRGMerge Patient InformationPrior patient ID — used in ADT^A40
AL1Patient Allergy InformationAllergen type, severity, reaction
DG1DiagnosisICD codes, diagnosis type, priority
IN1InsurancePlan, company, insured relationship
SCHScheduling Activity InformationSIU/SRM messages
ERRErrorHL7 error condition codes (Table 0357)
ZDSStudy Instance UIDIHE RAD extension — one field only, RP data type

Message Types

TypeDescription
ADT^A01Admit/Visit Notification
ADT^A02Transfer a Patient
ADT^A03Discharge/End Visit
ADT^A04Register a Patient
ADT^A05Pre-Admit a Patient
ADT^A08Update Patient Information
ADT^A11Cancel Admit/Visit Notification
ADT^A13Cancel Discharge/End Visit
ADT^A28Add Person Information
ADT^A31Update Person Information
ADT^A40Merge Patient — Patient Identifier List
ORM^O01Order Message (with IHE RAD ZDS)
ORR^O02Order Response
ORU^R01Unsolicited Observation Result
ACKGeneral Acknowledgment
SIU^S12Notification of New Appointment Booking

Code Tables

TableName
0001Administrative Sex (F/M/O/U/A/N)
0003Event Type (A01–A45, O01, R01, T02…)
0004Patient Class (I/O/E/P/B/U…)
0007Admission Type (Accident/Elective/Emergency/Routine/Urgent)
0008Acknowledgment Code (AA/AE/AR/CA/CE/CR)
0074Diagnostic Service Section ID (RAD/CT/NMR/LAB/CH/HM…)
0078Abnormal Flags (L/H/LL/HH/A/N/U/D/S/R/I…)
0085Observation Result Status — OBX-11 (C/D/F/I/N/O/P/R/S/U/W/X)
0119Order Control Codes (NW/CA/DC/HD/RL/RP/SC/OK/UA…)
0123Result Status — OBR-25 (O/I/S/A/P/C/R/F/X/Y/Z — no M, N, U or W)
0155Accept/Application Acknowledgment Conditions (AL/ER/NE/SU)
0190Address Type (H/B/M/C/O/P/TM…)
0357Message Error Condition Codes (0/100/101/200/204…)

Version Traps This Server Encodes

Most HL7 reference material circulating online is a blend of v2.3 through v2.9. These are the points where v2.5.1 specifically differs from what people expect, and where the reference data here has been pinned to the 2.5.1 standard.

TrapThe v2.5.1 position
CE vs CWE In v2.5.1 the coded fields are CE, not CWE — OBR-4, OBX-3, MSH-19, NTE-4, DG1-3, AL1-2/3/4, IN1-2/17, SCH-6/7/25. CWE replaced CE from v2.7. The exceptions are ERR-3 and ERR-5, which genuinely are CWE in 2.5.1 because ERR was rewritten in v2.5.
Table 0123 vs 0085 OBR-25 uses table 0123 (O, I, S, A, P, C, R, F, X, Y, Z). OBX-11 uses table 0085 (C, D, F, I, N, O, P, R, S, U, W, X). M, N, U and W are not valid in OBR-25 — putting an OBX-11 code there is the single most common ORU build error, and the validator names it explicitly.
PID-39 Does not exist in v2.5.1. PID ends at PID-38 (Production Class Code). Tribal Citizenship arrived in v2.6.
ZDS IHE RAD TF-2 defines exactly one field, and its data type is RP (Reference Pointer), not ST — components are Pointer^Application ID^Type of Data^Subtype, so the UID sits in ZDS-1.1. Anything in ZDS-2 onward is a site-local invention.
ORC in ORU ORC is optional inside the ORDER_OBSERVATION group of ORU^R01, and the whole PATIENT group — so PID too — is optional in the abstract syntax. Real interfaces require both, but a message without them is still conformant, so the validator warns rather than errors.
Repeating groups ADT_A39 repeats { PID [PD1] MRG [PV1] }, so one A40 can carry several merges. ORM_O01 repeats the ORDER group and ORU_R01 repeats both PATIENT_RESULT and ORDER_OBSERVATION. Flattened references that show one of each are wrong, and the validator pairs the repeats up properly.
SCH-9 vs SCH-11 Appointment Duration is SCH-9, with its units in SCH-10. SCH-11 is Appointment Timing Quantity (TQ).
Table 0078 The off-scale abnormal flags are < and >, not <= and >=.
OBR-18 accession Mapping the DICOM Accession Number (0008,0050) to OBR-18 is an IHE RAD convention, not an HL7 requirement. Sites also use OBR-2, OBR-3 or ORC-3. parse_message therefore reports which field it actually found the accession in rather than assuming.
MSH-3 / MSH-5 Optional in the standard, despite nearly every site treating them as mandatory. Reported as warnings, not errors.

Example Prompts

Once connected, you can ask Claude questions like:

natural language → tool call
# Field lookup
"What does PID-3 contain and what's its data type?"
 get_field(segment="PID", field=3)

# Message structure
"What segments are required in an ADT A40 merge message?"
 get_message_structure(messageType="ADT^A40")

# Parse a real message
"Parse this HL7 message and tell me the patient's MRN and accession number."
 parse_message(message="MSH|^~\&|RIS|HOSP|PACS|HOSP|...")

# Code table lookup
"What does ORC-1 code 'SC' mean?"
 lookup_code_table(tableNumber="0119", filter="SC")

# Validate before sending
"Validate this ORM^O01 and tell me what's missing."
 validate_message(message="MSH|^~\&|...")

# Merge integrity
"Is this A40 safe to send? Check the merge direction."
 validate_message(...)  → catches self-merge, unpaired
                              PID/MRG, cross-authority merge

# Report status consistency
"This ORU says Final but the EMR shows it as preliminary. Why?"
 validate_message(...)  → cross-checks OBR-25 vs OBX-11

# Segment overview
"Explain the ZDS segment and what it's used for in IHE."
 get_segment(segment="ZDS")