Developer guide

Receive and send HL7 over MLLP in Python

MLLP is how most HL7 v2 interfaces talk to each other. With MessageFoundry you wire an MLLP listener and sender in plain Python — no proprietary channel GUI, no XML — then route and transform each message with ordinary code you can version-control and test.

MLLP — the Minimal Lower Layer Protocol — frames HL7 v2 messages over TCP so a receiver knows where each message starts and ends. MessageFoundry speaks it natively: declare a connection, and the engine runs the listener and hands every message to your code.

1. Install the engine

MessageFoundry installs from PyPI. Create a virtual environment and install it — no C compiler needed for the default install.

terminal
python -m venv .venv
# macOS / Linux:   . .venv/bin/activate
# Windows (PS):    .venv\Scripts\Activate.ps1

pip install messagefoundry

2. Define an MLLP inbound and outbound connection

Drop a Python module into a config directory. Declare an inbound MLLP listener (name it, give it a port, point it at a router) and an outbound MLLP sender (a downstream host and port).

config/lab_oru.py
from messagefoundry import MLLP, Send, inbound, outbound, router, handler

# receive HL7 v2 over MLLP on port 2575
inbound("IB_Lab_ORU", MLLP(port=2575), router="oru_router")

# send to a downstream system over MLLP
outbound("OB_EHR_ORU", MLLP(host="10.0.0.21", port=6661))

3. Route and transform each message

A router decides where a message goes by returning handler names. A handler transforms it and sends it on. Fields are addressed by HL7 path (MSH-9.1, OBR-25, …); returning an empty list leaves a message unrouted, and returning None filters it.

config/lab_oru.py
@router("oru_router")
def route(msg):
    if msg["MSH-9.1"] != "ORU":
        return []                    # UNROUTED — not a lab result
    return ["to_ehr"]

@handler("to_ehr")
def to_ehr(msg):
    if msg["OBR-25"] == "X":       # drop corrected-in-error
        return None                  # FILTERED
    return Send("OB_EHR_ORU", msg)

4. Run the engine

Point MessageFoundry at your config directory and a store database. It opens the MLLP listener, persists every message before it's acknowledged, and routes it through your code.

terminal
python -m messagefoundry serve --config config --db messagefoundry.db

# MLLP listening on 0.0.0.0:2575 — every message is stored, then routed

Validate the config before it ships with the commit gate: python -m messagefoundry check --config config.

5. Send a test HL7 message

Send any HL7 v2 message to port 2575 with your MLLP client of choice (the repository ships a small send_mllp.py helper). An ORU result routes to the downstream connection; anything else is logged unrouted.

terminal
python samples/send_mllp.py samples/messages/oru_r01.hl7
# → ORU routed to OB_EHR_ORU; non-ORU logged UNROUTED

MLLP over TLS

MessageFoundry supports MLLP-over-TLS, and TLS is enabled by default for connections whose peer supports it — so HL7 traffic is encrypted in transit without extra work. Many older ancillary systems still can't speak TLS; for those, MessageFoundry also supports plaintext MLLP, which remains HIPAA-compliant when it stays inside your organization's secure network perimeter. You choose per connection. The exact certificate and TLS settings live in the Configuration reference and the Deployment & network-exposure guide.

Why route HL7 in code? Because your interfaces become ordinary Python — diffable, reviewable, unit-testable, and driven by one config repo across Test and Production. See how that compares to channel-based engines →