Skip to main content
Glama
srhtdmrkl

osha-recordkeeping-mcp

by srhtdmrkl

OSHA Recordkeeping MCP — 29 CFR Part 1904

Детерминированный сервер Model Context Protocol, который помогает менеджеру по охране труда ответить на вопрос, возникающий каждый раз, когда кто-то пострадал: подлежит ли это регистрации по стандарту OSHA?

Одиннадцать инструментов проводят инцидент от кто-то пострадал до корректной записи в журнале, каждый возвращает обоснованное решение со ссылкой на нормативный акт, а не пересказ правила моделью. Лицензия MIT, бесплатно для использования.

Только для справки и триажа — не юридическая консультация и не медицинское заключение. Каждое решение содержит ссылку на CFR и дату последней проверки исходных данных по eCFR, так что логика рассуждения поддаётся аудиту, а не просто декларируется.

Использование

git clone https://github.com/srhtdmrkl/osha-recordkeeping-mcp.git
cd osha-recordkeeping-mcp && npm install && npm run build

Затем добавьте его в claude_desktop_config.json в Claude Desktop:

{
  "mcpServers": {
    "osha": { "command": "node", "args": ["/absolute/path/to/dist/index.js"] }
  }
}

Используйте абсолютный путь к вашему бинарному файлу node, если вы пользуетесь nvm — Claude Desktop не читает ваш shell-профиль, поэтому голый node не будет найден.

Сопутствующий Skill содержит процедуру: когда цепочка применима, что нужно установить до вызова и какие вопросы инструменты не могут решить.

Related MCP server: Quellgeist

Зачем существует этот инструмент

Оценка регистрируемости производственной травмы по 29 CFR Part 1904 выполняется при каждом инциденте. Неверные решения несут прямые риски комплаенса: завышенная регистрация искусственно завышает общий коэффициент регистрируемых инцидентов (TRIR), а заниженная влечёт предписания OSHA по 29 CFR 1904.4.

Регистрируемость по Part 1904 оценивает несколько независимых триггеров: общие критерии (смерть, дни отсутствия, ограничение работы, потеря сознания, диагнозы PLHCP по 1904.7), специальные правила для конкретных случаев (уколы иглой, медицинское отстранение, потеря слуха, туберкулёз по 1904.8–1904.12) и классификацию лечения. Для лечения 1904.7(b)(5)(ii) определяет закрытый, перечисленный список из 14 пунктов мер первой помощи. Реализация этих закрытых нормативных правил внутри типизированных инструментов заменяет интерполяцию LLM по тексту нормативных актов воспроизводимой логикой поиска.

Разделение труда: LLM описывает, инструмент решает

Вызывающая модель делает то, в чём она сильна — читает запутанное описание инцидента и сопоставляет его с каноническими кодами (типы лечения, исходы). Инструмент делает то, что модель не должна делать для юридического решения — применяет закрытый список детерминированно и возвращает ответ со ссылкой на нормативный акт. Инструмент никогда не принимает свободный текст описания лечения; он принимает контролируемый словарь, чтобы решение было воспроизводимым.

Основной инструмент: osha_assess_recordability

Входные данные. Модель сопоставляет описание с этими полями; она никогда не передаёт свободный текст.

Поле

Значение

work_related

1904.5 — берите из osha_assess_work_relatedness, не оценивайте здесь сами

new_case

1904.6 — берите из osha_assess_new_case

outcomes

death, days_away_from_work, restricted_work_or_transfer, loss_of_consciousness

significant_diagnoses

cancer, chronic_irreversible_disease, fractured_or_cracked_bone, punctured_eardrum (1904.7(b)(7))

specific_case_criteria

триггеры 1904.8-1904.12 — укол иглой, медицинское отстранение, потеря слуха, туберкулёз, контакт с кровью с диагнозом

plhcp_recommendations_not_followed

Три места, где рекомендация обязывает, даже если работник её проигнорировал (1904.7(b)(3)(ii), (b)(4)(viii), (b)(5)(v))

medical_removal_was_voluntary_and_early

Защита для 1904.9(b)(3) — досрочное отстранение не регистрируется

tuberculosis_test_was_pre_employment

Защита для 1904.11(b)(1) — положительный результат при приёме на работу не является профессиональным

treatments

Контролируемые коды. Коды первой помощи берутся из закрытого списка; два кода — ни первая помощь, ни медицинское лечение (1904.7(b)(5)(i))

Первые три массива обязательны, намеренно. Значение по умолчанию [] нельзя отличить от «я проверил, и их не было», поэтому значение по умолчанию позволяет недостаточно специфицированному описанию вернуть уверенный, цитируемый ложный отрицательный результат — направление занижения, которое влечёт предписание.

Выходные данные. RuleRecord, значение которого содержит recordable, basis, triggering_factors (каждый со своей ссылкой на подпункт), severe_injury_reporting_note, когда может применяться 1904.39, log_entry_notes для последствий, которые критерий накладывает на сам журнал, и under_specified, когда вообще ничего не было указано. Слой регистрации добавляет determination_final и clarification_required — см. Элицитация ниже.

Логика определения (детерминированная) — дерево решений 1904.4(b)(2) по порядку:

  1. Если work_related равно false → не регистрируется (1904.5).

  2. Если new_case равно false → новая запись не создаётся, но обновите существующую, если изменились количество дней или исход (1904.6). Дерево направляет сюда; оно не просто останавливается.

  3. Иначе, если присутствует любой specific_case_criteriaрегистрируется по 1904.8-1904.12, вообще не обращаясь к списку первой помощи.

  4. Иначе, если присутствует любой outcomeрегистрируется (общие критерии регистрации, 1904.7(b)(1)).

  5. Иначе, если присутствует любой significant_diagnosisрегистрируется, даже если была оказана только первая помощь (1904.7(b)(7)).

  6. Иначе, если любой treatment не входит в закрытый список первой помощи → регистрируется (медицинское лечение сверх первой помощи, 1904.7(b)(5)(i)).

  7. Иначе → не регистрируется (только первая помощь, и нет критерия конкретного случая).

Шаг 3 существует потому, что 1904.4(a)(3) — это дизъюнкция: 1904.7 или конкретные случаи 1904.8-1904.12. Без него загрязнённый укол иглой, обработанный очисткой и пластырем, возвращал «не регистрируется» — со ссылкой на нормативный акт — в то время как инструмент конфиденциальности того же сервера правильно называл его случаем конфиденциальности. Критерии регистрации, которые вообще не обращаются к списку первой помощи, должны проверяться до него, а не после.

Поверхность протокола (все три примитива MCP)

Этот сервер использует полный протокол, а не только Tools:

  • Tools — одиннадцать определений, перечисленных в разделе Цепочка триажа инцидента ниже. Каждый объявляет outputSchema и возвращает типизированный structuredContent, а не JSON-строку, и каждый аннотирован readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false — безопасные, повторяемые, чистые поиски.

  • Resources — все пятнадцать наборов данных доступны напрямую, так что клиент может загрузить справочные данные как контекст, а не только через вызов инструмента. Модель данных — это продукт; Resources делают её видимой. URI — osha://data/<id>, где <id> — ключ в src/datasets.ts — например, osha://data/first-aid-treatments, osha://data/partially-exempt-industries, osha://data/privacy-cases.

  • Prompttriage_incident проводит один инцидент через всю цепочку как единый пользовательский рабочий процесс: область применения → регистрирующий работодатель → связь с работой → новый случай → ограниченная работа → потеря слуха → регистрируемость → срок отчётности → колонка журнала 300 → случай конфиденциальности → предприятие.

  • Элицитацияosha_assess_recordability разрешает один пограничный случай, который не должен угадывать: безрецептурная vs. рецептурная сила лекарства. Не рецептурная сила — первая помощь; рецептурная — медицинское лечение и регистрируется. Когда описание молчит, модель передаёт medication_unspecified_strength, и сила разрешается путём вопроса человеку — никогда не выбором инструмента.

    Двухуровневое разрешение. Элицитация — необязательная возможность MCP, поэтому сервер проверяет getClientCapabilities() и выбирает свой канал:

    Клиент объявляет elicitation

    Канал

    Результат

    Да

    Сервер запрашивает пользователя напрямую

    Разрешено за один вызов инструмента

    Нет

    Возвращает determination_final: false + clarification_required

    Модель спрашивает в чате, затем повторно вызывает с разрешённым кодом

    В любом случае вопрос доходит до человека, и инструмент никогда не угадывает. Предварительный результат остаётся консервативным — recordable: true, основание помечено ожидает подтверждения — и clarification_required несёт вопрос, причину по CFR и точный код лечения для отправки обратно для каждого ответа.

    В какой уровень попадает конкретный клиент, стоит проверить, а не предполагать. Проверено здесь: чат-клиент Claude Desktop не объявляет возможность elicitation, и MCP Inspector 0.15.0 или 1.0.0 тоже. Агентные клиенты могут отличаться, и клиент, который спрашивает пользователя через собственный механизм, выглядит извне одинаково. Сервер записывает elicitation=supported|NOT supported в stderr при подключении — запустите его под любым клиентом и прочитайте эту строку.

Происхождение и принудительное устаревание

RuleRecord<T> (см. src/types.ts) несёт нормативные правила: cfr_cite, source_url, last_verified, и где eCFR предоставляет их, amendment_history и editorial_note. Поля effective_date нет; наборы данных несут текущий текст eCFR, а last_verified указывает дату нормативной проверки.

Устаревание принудительно через scripts/check-decay.ts, который завершает сборку, если last_verified любой записи превышает порог устаревания. Он запускается при push и по еженедельному расписанию CI, проверяет цели source_url подразделов и проверяет записи editorial_note в eCFR.

Цепочка триажа инцидента (поставляется)

Одиннадцать детерминированных инструментов, которые проводят один инцидент от «кто-то пострадал» до корректной записи в журнале:

  1. osha_check_recordkeeping_obligation (1904.1, 1904.2) — вопрос, который предполагает каждое другое определение: обязано ли это предприятие вообще вести учёт? Освобождение по размеру измеряется по всей компании на пиковой численности за последний календарный год — не по среднему значению, не по одному объекту. Освобождение по отрасли привязано к предприятию, и инструмент разрешает его по закрытому списку из 82 кодов Приложения A, когда ему дан код NAICS. Оба освобождения частичны: обязанность сообщать о тяжёлых травмах по 1904.39 сохраняется при любом из них — именно этот вывод освобождённый работодатель ошибочно упускает.

  2. osha_determine_recording_employer (1904.31) — пороговый вопрос, а не шаг: когда пострадавший не числится в платёжной ведомости, является ли этот случай вообще делом данного работодателя? Решает повседневный надзор, а не платёжная ведомость. Временный работник, числящийся в ведомости агентства, но чью работу вы направляете ежедневно, подлежит учёту у вас; тот же временный работник под надзором агентства — нет. Самозанятые полностью вне сферы действия OSH Act, а владельцы или партнёры единоличного предприятия не считаются работниками для целей ведения учёта. Пункт (b)(4) требует, чтобы случай был зарегистрирован ровно один раз — никогда в обоих журналах.

  3. osha_assess_work_relatedness (1904.5) — ворота, на которых держится всё остальное, и до сих пор единственное юридическое суждение, которое этот проект передавал модели. Пункт 1904.5(a) предполагает связь с работой для всего, что возникает в рабочей среде; пункт 1904.5(b)(2) — закрытый список из девяти исключений, которые могут её опровергнуть. Вердикт намеренно трёхзначныйwork_related, not_work_related или requires_judgment — потому что 1904.5 содержит пути, которые само регулирование возлагает на работодателя: неясное происхождение (1904.5(b)(3)), командировочный статус (b)(6), работа на дому (b)(7) и вывод о «исключительности», от которого зависит каждое исключение. Принудительное сведение их к булеву значению означало бы, что инструмент угадывает самый спорный вывод в части 1904. Он также разрешает случаи, которые память переворачивает. ДТП с автомобилем при поездке на работу на территории компании подпадает под исключение (b)(2)(vii); падение при скольжении на той же территории не покрыто ни одним исключением и остаётся связанным с работой. А психическое заболевание инвертирует обычное направление — не связано с работой, если только работник сам не предоставит заключение PLHCP (b)(2)(ix).

  4. osha_assess_new_case (1904.6) — новая запись в журнале 300 или обновление уже существующей? Второе условие конъюнкции 1904.4(a). Оно разделяет два случая рецидива, которые регулирование намеренно разводит: эпизод, вызванный воздействием на рабочем месте, — это новый случай (b)(2) — профессиональная астма, спровоцированная на линии, — тогда как хроническое заболевание, симптомы которого повторяются без воздействия, регистрируется только один раз (b)(1). Обратите внимание: (b)(1) — не закрытый список: регулирование говорит, что «примеры могут включать» рак, асбестоз, биссиноз и силикоз, поэтому инструмент спрашивает о характере состояния, а не сопоставляет названия болезней. Это также единственный инструмент в сервере, который делегирует полномочия внешнему авторитету. Согласно (b)(3) работодатель не обязан консультироваться с PLHCP, но если он уже проконсультировался, то обязан следовать рекомендации — поэтому заключение PLHCP полностью переопределяет логику правил, а противоречащие заключения возвращают requires_judgment, поскольку их взвешивание — прямо обязанность работодателя.

  5. osha_evaluate_restricted_work (1904.7(b)(4)) — действительно ли ограничение учитывается? Не каждое, и обе ошибки перемещают случаи в журнал или из него. Ограничение, ограниченное днём травмы, не учитывается (b)(4)(iii); сниженная выработка при сохранении всех обычных функций — не учитывается (b)(4)(vi); «обычные функции» означают действия, выполняемые не реже одного раза в неделю (b)(4)(ii). Частичная смена учитывается (b)(4)(v), а переводы разделяют колонку ограничений (b)(4)(x). Интересный случай — (b)(4)(vii): когда расплывчатая рекомендация, например «лёгкий труд», не может быть уточнена у PLHCP, случай должен быть зарегистрирован как ограниченная работа. Это регулирование разрешает собственную неопределённость в сторону учёта — и единственное правило по умолчанию в пользу записи во всей части 1904.

  6. osha_evaluate_hearing_loss (1904.10) — единственный критерий учёта в части 1904, который является чистой арифметикой, и единственный инструмент здесь, который вычисляет, а не ищет. Два теста должны быть выполнены в одном и том же ухе: стандартный сдвиг порога на 10 дБ относительно базового уровня и общий уровень слуха 25 дБ или более выше аудиометрического нуля, каждый усреднённый на частотах 2000, 3000 и 4000 Гц. STS в одном ухе и уровень 25 дБ в другом не регистрируются. Возрастная корректировка применяется только к тесту сдвига, никогда к тесту 25 дБ.

  7. osha_assess_recordability (1904.4) — подлежит ли учёту? (якорь, выше)

  8. osha_check_severe_injury_reporting (1904.39) — должно ли это быть сообщено в OSHA, и к какому сроку? Возвращает фактическую временную метку дедлайна (8 часов для смертельного случая, 24 для госпитализации / ампутации / потери глаза), вычисленную с момента, когда работодатель узнал о происшествии, проверяет окно допустимости с момента инцидента и указывает, не истёк ли уже срок.

  9. osha_classify_300_log_entry (1904.29) — какая колонка результата в журнале 300 (G/H/I/J) по правилу наиболее серьёзного исхода, колонка типа травмы/заболевания и количество дней, ограниченное 180.

  10. osha_check_privacy_case (1904.29(b)(6)-(9)) — может ли имя работника вообще попасть в журнал? Второй закрытый список, и закрытый в обоих направлениях: (b)(7) перечисляет шесть случаев, вызывающих озабоченность по конфиденциальности, а (b)(8) запрещает считать таковым что-либо ещё — поэтому работодатель не может расширить его из сочувствия, как и не может его игнорировать. Возвращает буквальную запись в журнале ("privacy case") плюс следующие обязательства: отдельный конфиденциальный список ((b)(6)), усмотрение при описании случая, когда само описание может идентифицировать работника ((b)(9)), и редактирование, когда записи передаются кому-либо, кроме представителя правительства ((b)(10)).

  11. osha_route_to_establishment_log (1904.30) — в журнал какого предприятия, последний вопрос об отдельном инциденте. Правило противоречит интуиции: случай следует за местом, а не за человеком. Тот, кто пострадал, прикрывая смену на другом заводе работодателя, регистрируется в журнале этого завода, что меняет число, определяющее его сайтовый TRIR. Травма вне всех предприятий — на объекте клиента, в пути, удалённо — попадает в журнал того объекта, где работник обычно работает.

Область применения: Часть 1904, и ничего больше

Всё здесь отвечает на один вопрос — кто-то пострадал; что OSHA требует от меня записать и сообщить? Это 29 CFR Part 1904 от начала до конца, и одиннадцать инструментов выше — это определения, которые она навязывает.

Распространение: сервер и Skill

Два артефакта, потому что они отвечают на разные вопросы. Сервер решает; Skill знает, когда его спросить.

Сервер — три точки входа, один движок

Точка входа

Транспорт

Для

dist/index.js

stdio

Claude Desktop, локальная разработка

dist/http.js

Streamable HTTP

контейнер или node-хостинг

src/worker.ts

Streamable HTTP

Cloudflare Workers

Все три вызывают один и тот же createServer() с одними и теми же одиннадцатью инструментами и пятнадцатью наборами данных — ничего в src/tools/ не знает, какой из них работает. Эта переносимость возникла из двух более ранних решений, а не из усилий по переносу: определения — чистые функции, а datasets.ts — единственная точка контакта с JSON.

Каждый вариант без состояния — новый сервер на каждый запрос, без идентификаторов сессий, ничего не хранится между вызовами, потому что каждый инструмент — это чистый поиск по встроенным данным. /health сообщает возраст каждого набора данных относительно порога устаревания и возвращает 503, когда один из них устаревает, поэтому размещённое развёртывание контролируется по тому же правилу, что и сборка.

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 deploy

smoke:worker — единственная проверка, которая запускает инструменты под workerd. Два других smoke-теста работают на Node и структурно не могут увидеть, как встроенный узел проникает в путь кода — а именно это проявилось бы при развёртывании первым. nodejs_compat намеренно выключен в wrangler.toml, чтобы этот сбой был громким в разработке, а не тихим.

Воркер принимает только POST. Сервер без состояния не инициирует сообщений, поэтому поток GET SSE не несёт ничего и оставался бы открытым вечно; workerd отменяет запрос, ответ которого никогда не завершается. 405 с Allow: POST — это способ протокола сказать, что потока «сервер-клиент» нет.

Без аутентификации, намеренно. Сервер предоставляет опубликованный нормативный текст, ничего не хранит и не имеет побочных эффектов, поэтому контроль доступа должен быть перед ним — Cloudflare Access или слой OAuth — а не полуреализован внутри.

Ограничение скорости

Ограничение скорости — исключение, и оно живёт в воркере, а не перед ним. Развёртывание находится на workers.dev, который не является зоной в аккаунте, поэтому правилу ограничения скорости WAF не к чему прикрепиться. Объявлено как привязка [[ratelimits]] в wrangler.toml, что также означает, что оно проверяется, версионируется и путешествует вместе с развёртыванием, а не живёт в панели управления, которую никто не диффузит.

300 запросов в минуту на IP-адрес клиента. Намеренно щедро: ограничитель ключует по IP, и команда безопасности за одним корпоративным NAT делит один ключ. Одна цепочка триажа — примерно 15 запросов, поэтому несколько человек, работающих одновременно, легитимно превышают 150/мин. Это рассчитано на отсечение модели, зацикливающейся на ошибке — именно тот сбой, который реально угрожает размещённому развёртыванию, — а не на ограничение нормального использования. Превышение лимита возвращает 429 с Retry-After.

/health находится выше проверки, поэтому монитор аптайма, опрашивающий по расписанию, никогда не сможет исчерпать бюджет. Ограничение применяется по каждому центру обработки данных, а не глобально скоординировано, поэтому это скорее отсечка, чем точная квота.

Что получают инструменты

Сервер ничего не извлекает и ничего не хранит. Но аргументы всё же поступают внутрь, и они описывают реальный инцидент, поэтому «нет пользовательских данных» — это утверждение о хранении, которое ничего не говорит о передаче. Это различие стоит сформулировать прямо, потому что именно его должна оценить команда EHS.

Ни один вход не является идентификатором. Нигде в схемах нет поля для имени, номера сотрудника, даты рождения, адреса или свободного текстового описания — каждый вход — это атрибут случая (work_related, days_away_from_work, treatments), и для определения больше ничего не нужно. Это свойство схем, а не политика: нет поля, куда можно вписать имя. Skill также инструктирует модель не переносить идентичность в вызов, поэтому ограничение действует на обоих концах — см. Передавайте факты, никогда не идентичности.

Некоторые атрибуты всё же чувствительны. osha_check_privacy_case принимает ровно те категории, которые перечисляет 1904.29(b)(7) — sexual_assault, mental_illness, hiv_hepatitis_or_tuberculosis, contaminated_needlestick_or_sharps. Регулирование выделяет их именно потому, что они не должны появляться в журнале, который может прочитать коллега. Только атрибуты — это также не то же самое, что анонимность: код характера плюс дата инцидента на предприятии из девяти человек может идентифицировать кого-то для любого, кто там работает.

Куда это попадает, зависит от транспорта, и только от транспорта:

Точка входа

Куда идут аргументы

stdio

остаётся на машине, где работает сервер; AI-хост всё равно его видит

node HTTP / worker

пересекает сеть к тому, кто управляет этим развёртыванием

Ничего не логируется ни в ту, ни в другую сторону — ни логирования запросов, ни логирования вызовов инструментов, успешных или неуспешных. Это сделано намеренно. Журнал таких аргументов был бы сам по себе регулируемым хранилищем на сервере, которому в остальном нечего регулировать, а сами определения и так воспроизводимы из входных данных и отметки last_verified в составе каждого ответа. Провенанс в ответе делает ту же работу, что и журнал аудита, но без хранения записей.

Итак: если вы работаете с реальными случаями в условиях GDPR или HIPAA, используйте транспорт stdio либо разворачивайте worker на инфраструктуре, которой управляете сами. Направлять регулируемые данные об инцидентах в чью-то чужую хостируемую копию этого сервера — значит передавать атрибуты травмы третьей стороне, с которой у вас нет соглашения. Определения — это чистые функции над входящими в комплект JSON-данными: самостоятельный хостинг стоит одного wrangler deploy и ничуть не меняет ответы.

Проверка Host и Origin

Аутентификация решает, кому можно спрашивать. Проверка Host защищает от того, чтобы браузер заставляли спрашивать от чужого имени, и никакой вышестоящий шлюз не может это наверстать задним числом — поэтому эта часть реализована в src/httpGuard.ts и применяется в обеих точках входа HTTP.

Закрывается здесь DNS rebinding: вредоносный домен пере-resolвляется на 127.0.0.1, браузер считает запрос same-origin и отправляет его без preflight, а локально запущенный MCP-сервер отвечает. Выдаёт атаку заголовок Host: он содержит домен атакующего, поэтому работает точная сверка с разрешённым списком хостов.

Переменная

Node (dist/http.js)

Worker

HOST

адрес привязки, по умолчанию 0.0.0.0

MCP_ALLOWED_HOSTS

по умолчанию localhost:$PORT, 127.0.0.1:$PORT, [::1]:$PORT

не задан = без ограничений

MCP_ALLOWED_ORIGINS

не задан = без ограничений

не задан = без ограничений

Обе переменные принимают список через запятую; * отключает проверку, когда решение принимает внешний шлюз. Origin проверяется только в случае присутствия заголовка, потому что небrowерные MCP-клиенты его не отправляют. /health находится вне разрешённого списка — мониторинг доступности не является угрозой.

Точка входа Node по умолчанию работает только через loopback. Развёртывание в контейнере или за reverse-proxy доступно по другому имени и обязано задать MCP_ALLOWED_HOSTS; при ошибке возвращается 403 с указанием увиденного заголовка — это чинится за пять секунд. Противоположное значение по умолчанию — это дыра, которую никто не замечает. Обратите внимание: адрес привязки не является защитой — по умолчанию остаётся 0.0.0.0, чтобы работали контейнеры, а безопасность обеспечивается именно разрешённым списком.

Отклонённые запросы получают 403 с ошибкой JSON-RPC -32000. Слишком большие тела (>1 MB) получают 413, невалидный JSON — 400 — ошибки клиента не регистрируются в журнале как инциденты.

The Skill

skills/osha-incident-triage/ содержит процедуру: когда применима цепочка шагов, что установить до вызова, как представить определение и что инструменты не могут решить. Это руководство по процессу разбора инцидентов, у него нет непосредственной регуляторной логики.

Структура

Логика составления определений чистая и тестируемая; сервер — лишь вязкаора вокруг неё.

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_incident

Добавление инструмента — это один файл в src/tools/ (раacket правило), один в src/registrations/tools/ (как он описан и резюмируется) и одна строка в картеле. Префикс register держит эти имена файлов отличными от их собратьев в src/tools/ в панели вкладок редактора.

Полезно сохранять два инварианта: данные о происхождении собираются только функцией ruleRecord(), а Resources генерируются из той же таблицы DATASETS, откуда загружают инструменты, поэтому набор данных не может быть опубликован по пути, до которого ни один инструмент не читает.

Непрерывная интеграция

.github/workflows/ci.yml запускает проверку типов, сборку, модульные тесты и smoke-тесты на Node 20 и 22.

Проверка decay и npm audit запускаются по тем же триггерам, что и остальной CI — push, pull request, недельное расписание — в виде отдельных задач, чтобы любой из возможных отказов был хорошо виден сам по себе, а не одним красным крестом среди остальных. npm audit блокирует сборку, если появляются рекомендаций о уязвимостях в production-зависимостях; предупреждения о dev-зависимостях только сообщаются, но не блокируют. Еженедельное расписание ловит тот случай, когда рекомендация выходит для версии зависимости, уже находящейся на main, и никакой push-события не будет, чтобы запустить перепроверку.

tsconfig.json исключает test/, поэтому tsc --noEmit проверяет только src/, а ts-jest во время npm test дополнительно типобезопасность тестовых файлов.

Контроль изменений и версионирование

CHANGELOG.md описывает все изменения по двум независимым осям выпусков:

  • Код: логика определений, схемы инструментов и транспорты сервера следуют SemVersion.

  • Регуляторные данные: упакованные JSON-dataset вsrc/data/. Повторная сверка набора с текущим текстом eCFR обновляет дату last_verified и публикуется как patch-релиз, даже если регуляторный текст не изменился, — так сохраняется аудируемое происхождение для комплаенс-команд.

  • Принудительное устаревание: CI еженедельно запускает scripts/check-decay.ts, чтобы сборка падала, если любой набор превышает порог устаревания 365 дней без ручной повторной верификации.

  • Релизы: Тег версии (vX.Y.Z) required на GitHub запускает .github/workflows/release.yml, где выполняется полный набор проверок (typecheck, тесты, smoke тесты, decay, audit) и создаётся верифицированный GitHub Release.

Разработка

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 tool

Подключение локально — через npx @modelcontextprotocol/inspector или добавить в claude_desktop_config.json указание на dist/index.js.

npm run smoke:worker и npm run dev:worker требуют Node 22 или новее, потому что этого требует wrangler. Всё остальное работает на Node 20, а engines осознанно остаётся >=20 چهه это поле — заявление о том, кто может установить и запускать сервер, которому достаточно только SDK и zod. Wrangler — это утилита разработма, и до потребителя она вообще не доходит, поэтому её требование не является требованием пакета. Именно поэтому CI содержит Node 20 в матрице и пропускает там только worker-smoke.

Оценки

evals/recordkeeping-evals.xml — сорок семь вопросов, которые проверяют, достигает ли LLM правильного ответа через инструменты, и это нельзя проверитьюнит-тестами. Каждый ответ был сгенерирован обращением к собранному серверу реальным MCP-клиентом; трасса находится в evals/README.md. У большинства из, что стара этой — и ста сорока семи есть интуитивный неверный ответ, к которому без оружия потянется модель, рассуждающая по памяти.

Отказ от ответственности

Только справка и разбор инцидента. Не является юридической консультацией и не является медицинским заключением. Спорные случаи фиксации часто требуют участия PLHCP или юриста. Связь с работой (1904.5) устанавливается только по детерминированной ветке; неясное происхождение, командировка, работа из дома и неподтверждённый вывод «только по этой» — всё это возвращает requires_judgment, а не verdict.

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

  • A
    license
    Not graded
    quality
    C
    maintenance
    A 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
  • A
    license
    A
    quality
    A
    maintenance
    First-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.
    1
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for US workplace-safety standards (OSHA 29 CFR parts 1900–1990). Enables querying safety regulations via natural language through the Pipeworx gateway.
    14
    MIT

View all related MCP servers

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.

View all MCP Connectors

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/srhtdmrkl/osha-recordkeeping-mcp'

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