osha-recordkeeping-mcp
OSHA Recordkeeping MCP — 29 CFR Part 1904
Un servidor determinista del Model Context Protocol que ayuda a un gerente de seguridad a responder la pregunta que enfrenta cada vez que alguien resulta herido: ¿es esto registrable según OSHA?
Once herramientas siguen un incidente desde alguien resultó herido hasta una entrada de registro correcta, cada una devuelve una determinación citada en lugar del recuerdo del modelo sobre la regla. Licencia MIT y uso gratuito.
Solo referencia y triaje: no es asesoramiento legal ni una determinación médica. Cada determinación incluye su cita del CFR y la fecha en que los datos subyacentes se verificaron por última vez contra eCFR, de modo que el razonamiento es auditable en lugar de afirmado.
Cómo usarlo
git clone https://github.com/srhtdmrkl/osha-recordkeeping-mcp.git
cd osha-recordkeeping-mcp && npm install && npm run buildLuego añádelo al claude_desktop_config.json de Claude Desktop:
{
"mcpServers": {
"osha": { "command": "node", "args": ["/absolute/path/to/dist/index.js"] }
}
}Usa una ruta absoluta a tu binario node si usas nvm: Claude Desktop no carga tu perfil de shell, por lo que un node simple no se resolverá.
El Skill complementario contiene el procedimiento: cuándo se aplica la cadena, qué establecer antes de llamar y qué no pueden decidir las herramientas.
Related MCP server: Quellgeist
Por qué existe esta herramienta
La evaluación de la registrabilidad de lesiones laborales según 29 CFR Part 1904 ocurre en cada incidente. Las determinaciones incorrectas conllevan riesgos directos de cumplimiento: el sobre-registro infla artificialmente la Tasa de Incidentes Registrables Totales (TRIR), mientras que el sub-registro conlleva citaciones de OSHA según 29 CFR 1904.4.
La registrabilidad según la Parte 1904 evalúa múltiples desencadenantes independientes: criterios generales (muerte, días de ausencia, restricción laboral, pérdida de conciencia, diagnósticos de PLHCP según 1904.7), reglas de casos específicos (pinchazos con aguja, retiro médico, pérdida auditiva, TB según 1904.8–1904.12) y clasificación del tratamiento. Para el tratamiento, 1904.7(b)(5)(ii) define una lista cerrada y enumerada de 14 elementos de tratamientos de primeros auxilios. Implementar estas reglas regulatorias cerradas dentro de herramientas tipadas reemplaza la interpolación del LLM sobre el texto regulatorio con lógica de búsqueda reproducible.
División del trabajo: el LLM narra, la herramienta decide
El modelo que llama hace lo que se le da bien: leer una narrativa de incidente desordenada y mapearla a códigos canónicos (tipos de tratamiento, resultados). La herramienta hace lo que un modelo no debe hacer para una determinación legal: aplicar la lista cerrada de forma determinista y devolver una respuesta citada. La herramienta nunca acepta descripciones de tratamiento en texto libre; acepta un vocabulario controlado para que la determinación sea reproducible.
La herramienta ancla: osha_assess_recordability
Entrada. El modelo mapea la narrativa a estos campos; nunca pasa texto libre.
Campo | Significado |
| 1904.5 — proporcionar desde |
| 1904.6 — proporcionar desde |
|
|
|
|
| Desencadenantes de 1904.8-1904.12: pinchazo con aguja, retiro médico, pérdida auditiva, TB, exposición a patógenos sanguíneos con diagnóstico |
| Los tres casos en que una recomendación vincula incluso cuando el empleado la ignoró (1904.7(b)(3)(ii), (b)(4)(viii), (b)(5)(v)) |
| Protección para 1904.9(b)(3): retirar a alguien temprano no es registrable |
| Protección para 1904.11(b)(1): un positivo en el examen físico de contratación no es ocupacional |
| Códigos controlados. Los códigos de primeros auxilios provienen de la lista cerrada; dos códigos no son ni primeros auxilios ni tratamiento médico (1904.7(b)(5)(i)) |
Los primeros tres arreglos son obligatorios, deliberadamente. Un valor predeterminado de [] no se puede distinguir de "verifiqué y no había ninguno", por lo que establecerlos por defecto permite que una narrativa insuficientemente especificada devuelva un falso negativo seguro y citado, la dirección de sub-registro que atrae una citación.
Salida. Un RuleRecord cuyo valor incluye recordable, basis, triggering_factors (cada uno con su propia cita de subcláusula), severe_injury_reporting_note cuando 1904.39 puede estar en juego, log_entry_notes para las consecuencias que un criterio impone al propio registro, y under_specified cuando no se afirmó nada en absoluto. La capa de registro añade determination_final y clarification_required — ver Elicitación más abajo.
Lógica de determinación (determinista) — el árbol de decisión de 1904.4(b)(2) en orden:
Si
work_relatedes falso → no registrable (1904.5).Si
new_casees falso → sin entrada nueva, pero actualiza la existente si los conteos de días o el resultado han cambiado (1904.6). El árbol enruta aquí; no se detiene simplemente.De lo contrario, si hay algún
specific_case_criteriapresente → registrable según 1904.8-1904.12, sin consultar en absoluto la lista de primeros auxilios.De lo contrario, si hay algún
outcomepresente → registrable (criterios generales de registro, 1904.7(b)(1)).De lo contrario, si hay algún
significant_diagnosispresente → registrable incluso si solo se administraron primeros auxilios (1904.7(b)(7)).De lo contrario, si algún
treatmentno está en la lista cerrada de primeros auxilios → registrable (tratamiento médico más allá de primeros auxilios, 1904.7(b)(5)(i)).De lo contrario → no registrable (solo primeros auxilios y ningún criterio de caso específico).
El paso 3 existe porque 1904.4(a)(3) es una disyunción: 1904.7 o los casos específicos de 1904.8-1904.12. Sin él, un pinchazo con aguja contaminada tratado con limpieza y una venda devolvía "no registrable", con una citación adjunta, mientras que la herramienta de privacidad del mismo servidor lo llamaba correctamente caso de privacidad. Los criterios de registro que nunca consultan la lista de primeros auxilios deben verificarse antes que ella, no después.
Superficie del protocolo (las tres primitivas de MCP)
Este servidor usa el protocolo completo, no solo Tools:
Tools — las once determinaciones enumeradas en La cadena de triaje de incidentes más abajo. Cada una declara un
outputSchemay devuelvestructuredContenttipado, no una cadena JSON, y cada una está anotada conreadOnlyHint: true,destructiveHint: false,idempotentHint: true,openWorldHint: false— búsquedas puras, seguras y reintentables.Resources — los quince conjuntos de datos se exponen directamente, de modo que un cliente puede cargar los datos de referencia como contexto en lugar de acceder a ellos solo mediante una llamada de herramienta. El modelo de datos es el producto; Resources es lo que lo hace visible. Las URIs son
osha://data/<id>, donde<id>es la clave ensrc/datasets.ts— p. ej.osha://data/first-aid-treatments,osha://data/partially-exempt-industries,osha://data/privacy-cases.Prompt —
triage_incidentrecorre un incidente a través de toda la cadena como un único flujo de trabajo invocado por el usuario: alcance → empleador que registra → relación con el trabajo → caso nuevo → trabajo restringido → pérdida auditiva → registrabilidad → plazo de notificación → columna del Log 300 → caso de privacidad → establecimiento.Elicitación —
osha_assess_recordabilityresuelve el único caso límite que no debe adivinar: medicación de venta libre (OTC) frente a medicación de prescripción. La fuerza no prescrita es primeros auxilios; la fuerza de prescripción es tratamiento médico y registrable. Cuando la narrativa es silenciosa, el modelo pasamedication_unspecified_strengthy la fuerza se resuelve preguntando a un humano, nunca eligiendo la herramienta.Resolución en dos niveles. La elicitación es una capacidad opcional de MCP, por lo que el servidor verifica
getClientCapabilities()y elige su canal:El cliente anuncia
elicitationCanal
Resultado
Sí
El servidor pregunta al usuario directamente
Resuelto en una llamada de herramienta
No
Devuelve
determination_final: false+clarification_requiredEl modelo pregunta en el chat y luego vuelve a llamar con el código resuelto
De cualquier manera, la pregunta llega a un humano y la herramienta nunca adivina. El resultado provisional se mantiene conservador —
recordable: true, base marcada como pendiente de confirmación — yclarification_requiredlleva la pregunta, la razón del CFR y el código de tratamiento exacto a enviar de vuelta para cada respuesta.En qué nivel cae un cliente determinado vale la pena verificarlo en lugar de asumirlo. Verificado aquí: el cliente de chat de Claude Desktop no anuncia capacidad de
elicitation, y tampoco MCP Inspector 0.15.0 o 1.0.0. Los clientes agénticos pueden diferir, y un cliente que pregunta al usuario mediante su propio mecanismo se ve idéntico desde fuera. El servidor registraelicitation=supported|NOT supporteden stderr al conectar — inícialo bajo cualquier cliente y lee esa línea.
Procedencia y caducidad forzada
RuleRecord<T> (ver src/types.ts) lleva reglas regulatorias: cfr_cite, source_url, last_verified y, donde eCFR las proporciona, amendment_history y editorial_note. No hay campo effective_date; los conjuntos de datos llevan el texto actual de eCFR, y last_verified indica la fecha de verificación regulatoria.
La caducidad se aplica mediante scripts/check-decay.ts, que hace fallar la compilación si el last_verified de cualquier registro supera su umbral de caducidad. Se ejecuta en cada push y en un horario semanal de CI, valida los destinos de source_url de las subpartes y verifica las entradas de editorial_note de eCFR.
La cadena de triaje de incidentes (incluida)
Once herramientas deterministas que siguen un incidente desde "alguien resultó herido" hasta una entrada de registro correcta:
osha_check_recordkeeping_obligation(1904.1, 1904.2) — la pregunta que presupone cualquier otra determinación: ¿debe este empleador llevar registros en absoluto? La exención por tamaño se mide en toda la empresa con el empleo máximo del último año natural — no un promedio, no un solo centro. La exención por sector se aplica al establecimiento, y la herramienta la resuelve contra la lista cerrada de 82 códigos del Apéndice A cuando se le proporciona un código NAICS. Ambas son parciales: el informe de lesiones graves del 1904.39 sobrevive a cualquiera de las dos, que es la inferencia que un empleador exento se equivoca peligrosamente al hacer.osha_determine_recording_employer(1904.31) — una pregunta de umbral, no un paso: cuando la persona lesionada no está en nómina, ¿es siquiera un caso de este empleador? La supervisión día a día decide, no la nómina. Un temporal en nómina de una agencia cuyo trabajo usted dirige a diario es suyo para registrar; el mismo temporal bajo la supervisión de la agencia no lo es. Los autónomos quedan fuera de la Ley OSH por completo, y los propietarios o socios de una empresa individual no son empleados a efectos de registro. El(b)(4)exige que el caso se registre exactamente una vez — nunca en ambos registros.osha_assess_work_relatedness(1904.5) — la puerta sobre la que descansa todo lo demás, y hasta ahora el único juicio jurídico que este proyecto delegaba en el modelo. El 1904.5(a) presume la relación con el trabajo para cualquier cosa que surja en el entorno laboral; el 1904.5(b)(2) es una lista cerrada de nueve excepciones que pueden desvirtuarla. El veredicto es deliberadamente tri-valente —work_related,not_work_relatedorequires_judgment— porque el 1904.5 contiene vías que el propio reglamento asigna al empleador: origen poco claro (1904.5(b)(3)), situación de viaje (b)(6), trabajo en casa (b)(7) y la constatación de "únicamente" de la que depende toda excepción. Forzar esas vías a un booleano sería que la herramienta adivinara en la decisión más disputada de la Parte 1904. También zanja casos que la memoria recuerda al revés. Un accidente de vehículo de motor durante el trayecto en el aparcamiento de la empresa queda exceptuado en virtud del (b)(2)(vii); un resbalón y caída en el mismo aparcamiento no está cubierto por ninguna excepción y sigue siendo relacionado con el trabajo. Y la enfermedad mental invierte la dirección habitual — no relacionada con el trabajo a menos que el empleado aporte voluntariamente una opinión de un PLHCP (b)(2)(ix).osha_assess_new_case(1904.6) — ¿una entrada nueva en el Registro 300, o una actualización de una ya existente? La segunda condición de la conjunción del 1904.4(a). Separa los dos casos de recurrencia que el reglamento distingue deliberadamente: un episodio causado por una exposición en el lugar de trabajo es un caso nuevo (b)(2) — asma ocupacional desencadenada en la línea de producción — mientras que una enfermedad crónica cuyos síntomas reaparecen sin exposición se registra una sola vez (b)(1). Nótese que el (b)(1) no es una lista cerrada: el reglamento dice que los "ejemplos pueden incluir" cáncer, asbestosis, bisinosis y silicosis, así que la herramienta pregunta por el carácter de la afección en lugar de cotejar nombres de enfermedades. Es también la única herramienta del servidor que se remite a una autoridad externa. En virtud del (b)(3), un empleador no está obligado a consultar a un PLHCP, pero si lo ha consultado debe seguir la recomendación — así que una opinión de un PLHCP anula por completo la lógica de la regla, y las opiniones contradictorias devuelvenrequires_judgmentporque sopesarlas es expresamente tarea del empleador.osha_evaluate_restricted_work(1904.7(b)(4)) — ¿cuenta realmente la restricción? No todas cuentan, y ambos errores mueven casos hacia el registro o fuera de él. Una restricción limitada al día de la lesión no cuenta (b)(4)(iii); una producción reducida mientras se siguen desempeñando todas las funciones habituales no cuenta (b)(4)(vi); "funciones habituales" significa actividades realizadas al menos una vez por semana (b)(4)(ii). Un turno parcial sí cuenta (b)(4)(v)), y los traslados comparten la columna de restricción (b)(4)(x)). La interesante es la (b)(4)(vii): cuando una recomendación vaga como "trabajo ligero" no puede aclararse con el PLHCP, el caso debe registrarse como trabajo restringido. Esa es la regulación resolviendo su propia duda a favor del registro — y la única regla de registro por defecto en la Parte 1904.osha_evaluate_hearing_loss(1904.10) — el único criterio de registro de la Parte 1904 que es aritmética pura, y la única herramienta aquí que calcula en lugar de consultar. Dos pruebas deben cumplirse ambas en el mismo oído: un cambio de umbral estándar de 10 dB respecto a la línea base, y un nivel auditivo total de 25 dB o más por encima del cero audiométrico, cada uno promediado a 2000, 3000 y 4000 Hz. Un STS en un oído y un nivel de 25 dB en el otro no se registra. El ajuste por edad se aplica solo a la prueba del cambio, nunca a la prueba de los 25 dB.osha_assess_recordability(1904.4) — ¿es registrable? (el ancla, arriba)osha_check_severe_injury_reporting(1904.39) — ¿debe notificarse a OSHA, y para cuándo? Devuelve la marca de tiempo límite real (8 horas para un fallecimiento, 24 para hospitalización / amputación / pérdida de un ojo) calculada desde el momento en que el empleador tuvo conocimiento del hecho, comprueba la ventana de elegibilidad desde el incidente y señala si el plazo ya está vencido.osha_classify_300_log_entry(1904.29) — qué columna de resultado del Registro 300 (G/H/I/J) según la regla del resultado más grave, la columna de tipo de lesión o enfermedad, y los recuentos de días limitados a 180.osha_check_privacy_case(1904.29(b)(6)-(9)) — ¿puede el nombre del empleado figurar en el registro en absoluto? Una segunda lista cerrada, y cerrada en ambas direcciones: el(b)(7)enumera los seis casos de privacidad, y el(b)(8)prohíbe tratar cualquier otra cosa como tal — así que un empleador no puede ampliarla por compasión ni ignorarla. Devuelve la entrada literal del registro ("privacy case") más las obligaciones que conlleva: la lista confidencial separada ((b)(6)), la discreción al describir el caso cuando la sola narrativa pudiera identificar al empleado ((b)(9)), y la supresión de datos cuando los registros se entregan a cualquier persona que no sea un representante gubernamental ((b)(10)).osha_route_to_establishment_log(1904.30) — qué Registro 300 de qué establecimiento, la última pregunta sobre un incidente individual. La regla va contra la intuición: un caso sigue al lugar, no a la persona. Alguien que se lesiona cubriendo un turno en otra planta del empleador se registra en el registro de esa planta, lo que mueve el número que impulsa su TRIR del centro. Una lesión lejos de todo establecimiento — en las instalaciones del cliente, en tránsito, en remoto — va al registro del centro donde el empleado trabaja normalmente.
Alcance: Parte 1904, y nada más
Todo esto responde a una pregunta — alguien se ha lesionado; ¿qué exige OSHA que registre y notifique? Eso es el 29 CFR Parte 1904 de principio a fin, y las once herramientas anteriores son las determinaciones que impone.
Distribución: un servidor y una Skill
Dos artefactos, porque responden a preguntas distintas. El servidor decide; la Skill sabe cuándo consultarlo.
El servidor — tres puntos de entrada, un mismo motor
Punto de entrada | Transporte | Para |
| stdio | Claude Desktop, desarrollo local |
| Streamable HTTP | un contenedor o un host de node |
| Streamable HTTP | Cloudflare Workers |
Los tres llaman al mismo createServer() sobre las mismas once herramientas y quince conjuntos de datos — nada en src/tools/ sabe cuál está en ejecución. Esa portabilidad vino de dos decisiones anteriores más que de un esfuerzo de portado: las determinaciones son funciones puras, y datasets.ts es el único punto de contacto con el JSON.
Cada variante es sin estado — un servidor nuevo por petición, sin ids de sesión, sin nada retenido entre llamadas, porque cada herramienta es una consulta pura sobre datos incluidos. /health informa de la antigüedad de cada conjunto de datos frente a su umbral de caducidad y devuelve 503 cuando uno se queda obsoleto, de modo que un despliegue alojado se vigila con la misma regla que la compilación.
npm run start:http # node host — PORT=3000 MCP_PATH=/mcp by default
npm run smoke:http # boots it, drives it with a real client, checks /health
npm run dev:worker # wrangler dev — runs under workerd, not Node
npm run smoke:worker # boots workerd and drives it with a real client
npm run deploy:worker # wrangler deploysmoke:worker es la única comprobación que ejecuta las herramientas bajo workerd. Los otros dos smokes se ejecutan en Node y estructuralmente no pueden ver un built-in de node colándose en una ruta de código — que es exactamente lo que un despliegue sacaría a la luz primero. nodejs_compat está deliberadamente DESACTIVADO en wrangler.toml para que ese fallo sea ruidoso en desarrollo en lugar de silencioso.
El worker acepta solo POST. Un servidor sin estado no inicia mensajes, así que el flujo SSE de GET no lleva nada y permanecería abierto para siempre; workerd cancela una petición cuya respuesta nunca se completa. El 405 con Allow: POST es la forma que tiene el protocolo de decir que no existe flujo servidor-a-cliente.
Sin autenticación, a propósito. El servidor expone texto normativo publicado, no almacena nada y no tiene efectos secundarios, así que el control de acceso pertenece delante de él — Cloudflare Access o una capa OAuth — en lugar de a medio implementar dentro.
Limitación de velocidad
La limitación de velocidad es la excepción, y vive en el worker en lugar de delante de él. El despliegue está en workers.dev, que no es una zona de la cuenta, así que una regla de limitación de velocidad del WAF no tiene dónde anclarse. Declarada como un binding [[ratelimits]] en wrangler.toml, lo que también significa que se revisa, versiona y viaja con el despliegue en lugar de vivir en un panel que nadie compara.
300 peticiones por minuto por IP de cliente. Deliberadamente generoso: el limitador se basa en la IP, y un equipo de seguridad detrás de un único NAT corporativo comparte una sola clave. Una cadena de triaje son unas 15 peticiones, así que varias personas trabajando a la vez superan legítimamente las 150/minuto. Esto está dimensionado para cortar a un modelo en bucle sobre un error — el fallo que de verdad amenaza un despliegue alojado — no para medir el uso normal. Superar el límite devuelve 429 con Retry-After.
/health está por encima de la comprobación, así que un monitor de disponibilidad que sondea con una programación fija nunca puede ser lo que agote el presupuesto. La aplicación se hace por centro de datos en lugar de coordinarse globalmente, así que es un corte más que una cuota exacta.
Lo que reciben las herramientas
El servidor no recupera nada y no almacena nada. Pero los argumentos sí fluyen hacia dentro, y describen un incidente real, así que "sin datos de usuario" es una afirmación sobre el almacenamiento que no dice nada sobre el tránsito. La distinción merece enunciarse con claridad, porque es la que un equipo de EHS tiene que evaluar.
Ninguna entrada es un identificador. No hay ningún campo en ningún esquema para un nombre, número de empleado, fecha de nacimiento, dirección o narrativa de texto libre — cada entrada es un atributo del caso (work_related, days_away_from_work, treatments) y la determinación no necesita nada más. Eso es una propiedad de los esquemas, no una política: no hay campo donde poner un nombre. La Skill también instruye al modelo para no trasladar identidad a una llamada, así que la restricción se mantiene en ambos extremos — véase Pase hechos, nunca identidades.
Algunos atributos son sensibles de todos modos. osha_check_privacy_case toma exactamente las categorías que enumera el 1904.29(b)(7) — sexual_assault, mental_illness, hiv_hepatitis_or_tuberculosis, contaminated_needlestick_or_sharps. El reglamento las señala precisamente porque son las que no deben aparecer en un registro que un compañero pueda leer. Solo-atributos tampoco es lo mismo que anónimo: un código de naturaleza más una fecha de incidente en un establecimiento de nueve personas puede identificar a alguien ante cualquiera que trabaje allí.
Dónde aterriza eso depende del transporte, y solo del transporte:
Punto de entrada | Dónde van los argumentos |
stdio | permanecen en la máquina que ejecuta el servidor; el host de IA todavía los ve |
node HTTP / worker | cruzan la red hasta quienquiera que opere ese despliegue |
No se registra nada en ningún sentido — ni registro de solicitudes, ni registro de llamadas a herramientas, ni exitosas ni fallidas. Esto es deliberado. Un registro de estos argumentos sería un repositorio regulado en sí mismo, en un servidor que por lo demás no tiene nada que regular, y las determinaciones ya son reproducibles a partir de las entradas más la versión del conjunto de datos last_verified que se incluye en cada respuesta. La procedencia en la respuesta cumple la función que tendría un registro de auditoría, sin la retención.
Por lo tanto: si está tratando casos reales bajo GDPR o HIPAA, ejecute stdio, o auto-aloje el worker en una infraestructura que usted controle. Apuntar datos de incidentes regulados a una copia alojada por terceros de este servidor significa enviar atributos de lesiones a un tercero con el que no tiene ningún acuerdo. Las determinaciones son funciones puras sobre JSON incluido — auto-alojar cuesta un wrangler deploy y no cambia nada de las respuestas.
Validación de Host y Origin
La autenticación trata de quién puede preguntar. La validación de host trata de que un navegador sea inducido a preguntar en nombre de otro, lo que ningún gateway ascendente puede corregir a posteriori — así que esa parte se maneja en src/httpGuard.ts y se aplica a ambos puntos de entrada HTTP.
El ataque que cierra es el DNS rebinding: un dominio atacante se re-resuelve a 127.0.0.1, el navegador trata la solicitud como de mismo origen y la envía sin preflight, y un servidor MCP ejecutado localmente responde. La cabecera Host es lo que aún lo delata — lleva el dominio del atacante — así que una lista de permitidos de host con coincidencia exacta es la comprobación que funciona.
Variable | Node ( | Worker |
| dirección de enlace, por defecto | — |
| por defecto | sin definir = sin restricciones |
| sin definir = sin restricciones | sin definir = sin restricciones |
Ambos aceptan una lista separada por comas; * desactiva la comprobación cuando un gateway delante es el que decide. Origin solo se comprueba cuando la cabecera está presente, porque los clientes MCP que no son de navegador no envían una. /health queda fuera de la lista de permitidos — un monitor de disponibilidad no es el modelo de amenaza.
El punto de entrada de node usa por defecto solo loopback. Una implementación en contenedor o con proxy inverso se alcanza por otro nombre y debe establecer MCP_ALLOWED_HOSTS; falla con un 403 que nombra la cabecera que vio, lo que es un arreglo de cinco segundos. El defecto opuesto es un agujero que nadie nota. Ten en cuenta que la dirección de bind no es la protección — el bind a 0.0.0.0 sigue siendo el defecto para que los contenedores funcionen, y la lista de permitidos es lo que hace que eso sea seguro.
Las solicitudes rechazadas reciben 403 con un error JSON-RPC -32000. Los cuerpos sobredimensionados (>1 MB) reciben 413, el JSON malformado recibe 400 — los errores del cliente no se registran como incidentes.
La Skill
skills/osha-incident-triage/ lleva el procedimiento: cuándo se aplica la cadena, qué hay que establecer antes de llamar, cómo presentar una determinación y qué no pueden decidir las herramientas. Contiene orientación de proceso para el triaje de incidentes y no lleva lógica regulatoria directamente.
Estructura
La lógica de determinación es pura y comprobable; el servidor es el cableado que la rodea.
src/
index.ts stdio entry point — Claude Desktop, local development
http.ts Streamable HTTP entry point — container / node host
worker.ts Cloudflare Workers entry point — same server, fetch handler
server.ts createServer() factory + registerRuleTool/toolResult helpers
httpGuard.ts Host/Origin allowlisting shared by both HTTP entry points —
one implementation, since Node and workerd share no middleware
datasets.ts the one place JSON assets are loaded and named — static imports,
so the same module resolves with or without a filesystem
types.ts RuleRecord, Provenance, and the ruleRecord() constructor
md.d.ts ambient declaration letting SKILL.md be imported as a string,
so the worker can serve it without a filesystem
tools/ one pure function per determination — no MCP imports except
medicationStrength.ts, which owns the elicitation exchange
registrations/
tools/ one registerX.ts per tool + a barrel; metadata and summaries
resources.ts generated from the DATASETS table
prompts.ts triage_incidentAñadir una herramienta significa un archivo en src/tools/ (la regla), uno en src/registrations/tools/ (cómo se describe y resume) y una línea en el barril. El prefijo register mantiene esos nombres de archivo distintos de sus contrapartes en src/tools/ en la barra de pestañas del editor.
Dos invariantes que vale la pena mantener: la procedencia se ensambla solo con ruleRecord(), y los Recursos se generan desde la misma tabla DATASETS de la que cargan las herramientas, así que un conjunto de datos no puede publicarse bajo una ruta que ninguna herramienta lea.
Integración continua
.github/workflows/ci.yml ejecuta typecheck, build, pruebas unitarias y suites de humo en Node 20 y 22.
La comprobación de decaimiento y npm audit se ejecutan ambos con los mismos disparadores que el resto de CI — push, pull request y un horario semanal — mantenidos como trabajos separados para que cada fallo sea legible por sí mismo en lugar de una X roja entre varias. npm audit bloquea el build en avisos de vulnerabilidad de producción de bajo nivel; los avisos de dependencias de desarrollo se informan, no bloquean. El horario semanal es lo que detecta un aviso publicado contra una versión de dependencia ya en main, donde nada más empujaría para disparar una re-comprobación.
tsconfig.json excluye test/, así que tsc --noEmit comprueba solo src/ y ts-jest comprueba los archivos de test durante npm test.
Control de cambios y versionado
CHANGELOG.md rastrea todos los cambios en dos dimensiones de publicación independientes:
Código: La lógica de determinación, los esquemas de herramientas y los transportes del servidor siguen Semantic Versioning.
Datos regulatorios: los conjuntos de datos JSON de eCFR incluidos en
src/data/. Re-verificar un conjunto de datos contra el texto actual de eCFR mueve su fechalast_verifiedy se publica como actualización de parche, incluso cuando el texto regulatorio no ha cambiado, manteniendo procedencia auditable para los equipos de cumplimiento.Decaimiento forzado: CI ejecuta
scripts/check-decay.tssemanalmente para fallar el build si algún conjunto de datos supera su umbral de decaimiento de 365 días sin re-verificación manual.Publicaciones: las etiquetas de versión (
vX.Y.Z) en GitHub disparan.github/workflows/release.ymlpara ejecutar los conjuntos de validación completos (typecheck, tests, smokes, decay, audit) y crear una Release de GitHub verificada.
Desarrollar
npm install
npm run build # tsc + copy src/data → dist/data
npm test # jest — deterministic logic
npm run check-decay # build-breaking staleness trap
npm run smoke # end-to-end: spawn the server, list tools/resources/prompts, call a toolConéctate localmente con npx @modelcontextprotocol/inspector, o añádelo a claude_desktop_config.json apuntando a dist/index.js.
npm run smoke:worker y npm run dev:worker necesitan Node 22 o más nuevo, porque wrangler lo necesita. Todo lo demás funciona en Node 20, y engines se queda en >=20 deliberadamente: ese campo es una afirmación sobre quién puede instalar y ejecutar el servidor, que solo necesita el SDK y zod. Wrangler es herramienta de desarrollo y nunca llega a un consumidor, así que su requisito no es el del paquete. CI mantiene Node 20 en la matriz exactamente por esa razón y omite solo el smoke de worker allí.
Evaluaciones
evals/recordkeeping-evals.xml — cuarenta y siete preguntas que comprueban si un LLM llega a la respuesta correcta a través de las herramientas, lo que los tests unitarios no pueden cubrir. Cada respuesta se produjo conduciendo el servidor construido con un cliente MCP real; el rastro está en evals/README.md. La mayoría de las cuarenta y siete tienen una respuesta incorrecta intuitiva que un modelo que razona desde la memoria alcanzará.
Descargo de responsabilidad
Solo referencia y triaje. No es asesoramiento legal, no es una determinación médica. Los casos límite de registrabilidad requieren frecuentemente un PLHCP o asesoría. La relación con el trabajo (1904.5) se determina solo a lo largo de sus rutas deterministas; el origen poco claro, el estado de viaje, el trabajo en casa y cualquier hallazgo de "únicamente" no establecido devuelven requires_judgment en lugar de un veredicto.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA deterministic MCP server for legal intake triage that provides practice-area lookup, conflict screening, matter validation, follow-up drafting, and triage logging with a hard conflicts gate.Apache 2.0
- AlicenseAqualityAmaintenanceFirst-line incident triage you can trust: ranked root-cause hypotheses where every claim cites a real evidence handle — and the agent abstains rather than guess.11MIT
- AlicenseNot gradedqualityCmaintenanceMCP server for US workplace-safety standards (OSHA 29 CFR parts 1900–1990). Enables querying safety regulations via natural language through the Pipeworx gateway.14MIT
- FlicenseNot gradedqualityCmaintenanceProvides policy-grounded triage of Trust & Safety reports via MCP, with tools for triage, policy search, and operational telemetry.
Related MCP Connectors
Diagnoses, drugs & lab codes: ICD-11, SNOMED, LOINC, RxNorm, MeSH, ATC, CID-10. 37 tools, MIT.
FDA medical-device regulatory intelligence from keyless openFDA datasets.
Read-only tools over the Psychopathia Machinalis nosology: 79 conditions, 11 tools.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/srhtdmrkl/osha-recordkeeping-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server