HL7-Bridge MCP

HL7-Bridge MCP

Translates HL7 v2.x messages to FHIR R4, validates against profiles like US Core, and explains errors in natural language for AI agents.

Category
访问服务器

README

HL7-Bridge MCP

Servidor MCP que parsea, mapea y valida mensajes clínicos entre HL7 v2.x y FHIR R4, con explicación en lenguaje natural de por qué un mensaje no cumple un perfil. Pensado para que un agente de IA traduzca y valide mensajes clínicos sin hablar directamente con un servidor FHIR crudo.

⚠️ No es un dispositivo médico. No está validado para decisiones clínicas. Los mapeos requieren validación humana antes de cualquier uso con datos reales. Ver SECURITY.md.

En palabras simples

Los sistemas de un hospital hablan dos "idiomas" distintos. HL7 v2 es el idioma viejo y telegráfico con el que, desde hace décadas, un equipo de laboratorio o de admisión anuncia cosas como "ingresó el paciente Juan Pérez" o "el resultado de glucosa es 95 mg/dL". FHIR es el idioma moderno, en JSON, que usan las apps, portales de paciente y sistemas nuevos.

Este proyecto es un traductor entre esos dos idiomas que, además, revisa la traducción y explica los errores en lenguaje humano. Sirve para tres cosas:

  1. Traducir un mensaje HL7 v2 al formato FHIR moderno (y a la inversa, en fases futuras).
  2. Validar que el resultado cumple las reglas del estándar (perfiles como US Core): que no falte, por ejemplo, la categoría de una prueba de laboratorio o el nombre del paciente.
  3. Explicar qué falló y dónde — "falta el segmento PV1 requerido para un ingreso" — en vez de un error críptico. Un desarrollador clínico no necesita abrir la especificación.

Cómo lo hace: en lugar de reglas de traducción escondidas en el código, usa mapas declarativos (archivos maps/ legibles y versionables) que dicen "PID-5 del mensaje → nombre del Patient en FHIR". Eso hace la traducción auditable y corregible sin tocar código. Se expone como un servidor MCP, de modo que un agente de IA (Claude, por ejemplo) puede pedirle traducir o validar un mensaje como una herramienta más.

Tools

Tool Entrada Salida
parse_hl7v2 { message } { ast } — segmentos → campos → componentes, separadores leídos de MSH-1/2
map_v2_to_fhir { message, mapId?, fhirVersion? } { bundle, validation: { issues, explained } }
validate_message { payload, kind: "hl7v2"|"fhir", profile? } { issues } con location (segmento-campo o FHIRPath)
explain_error { issue } { humanMessage, location, hint }

Mapas soportados en v0.1: ADT^A01, ORU^R01, ORM^O01 → FHIR R4 (ver maps/). Los mapeos no obvios están marcados TODO(mapeo) y no se adivinan.

Probar el MCP: un ejemplo

Un mensaje HL7 v2 de resultado de laboratorio (glucosa) traducido a FHIR. Arranca el server en HTTP (PORT=3999 npm run start:http) y llama a map_v2_to_fhir:

extract() { sed -n 's/^data: //p' | python3 -c 'import sys,json; print(json.load(sys.stdin)["result"]["content"][0]["text"])'; }

curl -s -X POST localhost:3999/mcp \
  -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"map_v2_to_fhir","arguments":{"message":"MSH|^~\\&|LAB|H|EMR|H|20260102||ORU^R01|1|P|2.5\rPID|1||123456^^^H^MR||DOE^JOHN||19800101|M\rOBR|1||1|1554-5^GLUCOSE^LN\rOBX|1|NM|1554-5^GLUCOSE^LN||95|mg/dL|70-105|N|||F"}}}' | extract

Qué devuelve: un Bundle FHIR con dos recursos y una validación limpia —

  • Patient: identifier 123456, name DOE JOHN, gender male, birthDate 1980-01-01.
  • Observation: status final, code 1554-5 (GLUCOSE), valueQuantity 95 mg/dL, category = laboratory, subject → el Patient.
"validation": { "issues": [], "explained": [] }

La validación pasa sin issues: Observation.category (que US Core exige) se puebla como constante laboratory, porque HL7 v2 no tiene ningún campo que la porte y un ORU^R01 es siempre laboratorio. Es una asunción deliberada documentada en el mapa (maps/oru_r01_to_fhir_r4.yaml), no un valor adivinado de un campo del mensaje.

Segundo ejemplo: un mensaje que sí genera deuda

El mismo mensaje, pero sin el sexo del paciente (PID-8 vacío). US Core exige gender y ese sí viene de un campo del mensaje, así que su ausencia es una deuda real que la validación reporta:

curl -s -X POST localhost:3999/mcp \
  -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"map_v2_to_fhir","arguments":{"message":"MSH|^~\\&|LAB|H|EMR|H|20260102||ORU^R01|1|P|2.5\rPID|1||123456^^^H^MR||DOE^JOHN||19800101|\rOBR|1||1|1554-5^GLUCOSE^LN\rOBX|1|NM|1554-5^GLUCOSE^LN||95|mg/dL|70-105|N|||F"}}}' | extract

El Bundle sale igual salvo que el Patient ya no trae gender, y la validación lo señala:

"validation": {
  "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." }
  ]
}

Esto es lo valioso: no solo tradujo, también avisó qué falta y por qué — con location exacta — en lenguaje humano. La diferencia con category es de origen: gender sí tiene un campo v2 (PID-8) que se dejó vacío, mientras category es una constante sin campo de origen.

El set de pruebas completo de las 4 tools (con mensajes válidos, malformados y casos límite) está en USAGE.md.

Desarrollo

npm ci
npm run typecheck   # tsc estricto
npm run lint
npm test            # vitest
npm run test:coverage

Ejecución

stdio (clientes MCP locales, p. ej. Claude Desktop):

npm run build && npm start

HTTP (Streamable HTTP, para hosting):

npm run build && npm run start:http   # POST /mcp, health en /healthz, puerto $PORT

Deploy en Render (free tier)

El repo incluye render.yaml como Blueprint. Conecta el repo en Render y desplegará un Web Service con POST /mcp. Notas del plan gratuito:

  • El servicio hiberna tras ~15 min de inactividad; la primera petición tras dormir tarda ~30-60 s.
  • El endpoint no tiene autenticación: úsalo solo con datos sintéticos.

Qué es este proyecto

No es un CRUD FHIR más: es la capa de traducción validada que un agente necesita para no hablar directamente con un servidor FHIR crudo. El diferenciador es el mapeo declarativo (mapas versionables) + validación contra perfiles (US Core, IPS) + explicación de por qué falla un mensaje. El foso es el conocimiento de mapeo y los vendor quirks de HL7 v2, no el protocolo MCP.

Principios de diseño (no negociables)

  1. PHI-safe by default. El repositorio NUNCA contiene PHI real. Todos los fixtures son sintéticos o de dominio público. Los logs redactan por defecto cualquier campo con datos de paciente (PID, NK1, GT1). Nunca se imprime un mensaje completo en logs de nivel INFO.
  2. Herramientas estrechas, no genéricas. Cada tool MCP tiene entrada/salida tipada y documentada, en lugar de un "haz lo que quieras".
  3. Determinismo y explicabilidad. Ante un error de validación, se devuelve estructura + explicación humana: qué falló y dónde (segmento-campo-componente) sin abrir la spec.
  4. No es un dispositivo médico. No se usa para decisiones clínicas sin validación humana.
  5. Fallar ruidosamente en parsing, silenciosamente nunca. Un mensaje malformado produce un error estructurado, jamás un resultado parcial silencioso.

Stack

TypeScript (Node ≥ 20), SDK MCP oficial (@modelcontextprotocol/sdk), ecosistema FHIR en JS (fhirpath.js, @types/fhir). Tests con Vitest. TypeScript estricto (strict, noUncheckedIndexedAccess); errores tipados (Hl7BridgeError con code, location, humanMessage).

Estructura del repositorio

/src
  /server        # arranque MCP, registro de tools
  /parser        # HL7 v2: segmentos, campos, componentes, tablas
  /mapper        # motor de mapeo declarativo v2<->FHIR
  /validator     # validación FHIR (perfiles) + explicación
  /errors        # tipos de error
/maps            # mapas declarativos (YAML): ADT, ORU, ORM...
/test/fixtures   # mensajes sintéticos + salidas esperadas

Ver context/ARCHITECTURE.md (componentes, contratos de I/O, formato de mapas) y context/PRD.md para el detalle.

Licencia

Apache-2.0 en el núcleo (parser, mapeador, validador, tools MCP). Ver LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选