Skip to main content
Glama

MCPDischarge — Межведомственная интероперабельность MCP

EHR × Аптека × Биллинг | RBAC | Границы PHI | FastMCP

CitiusTech Gen AI & Agentic AI Training — Проект 5


Проблема, которую не могут решить традиционные API

Пациент готов к выписке. Данные должны передаваться между тремя отделами, которые никогда не использовали общий протокол:

Traditional workflow (45 minutes, 15 manual handoffs):
  Ward nurse    → prints discharge note
  Ward nurse    → phones pharmacy to check drug availability
  Pharmacy      → calls back 2 hours later (drug out of stock)
  Nurse         → calls doctor to re-prescribe
  Doctor        → updates chart
  Nurse         → re-contacts pharmacy
  Pharmacy      → dispenses (brand name ≠ generic name — wrong drug dispensed?)
  Nurse         → separately calls billing department
  Billing clerk → manually re-enters ICD-10 codes from printed note
  Billing clerk → can see full medication list including controlled substances (HIPAA risk)
  Patient       → waits, often 4–6 hours post-clinical-readiness

MCP (Model Context Protocol) решает эту задачу с помощью стандартизированного, типизированного уровня вызова инструментов с принудительным применением RBAC:

MCP workflow (< 1 second, automated):
  DischargeAgent.EHR.get_discharge_medications()           ← structured, not free text
  DischargeAgent.Pharmacy.check_stock()                    ← semantic name matching
  DischargeAgent.Pharmacy.get_alternative()                ← out-of-stock resolution
  DischargeAgent.EHR.get_billing_safe_summary()            ← PHI stripped at source
  DischargeAgent.Billing.generate_invoice()                ← billing never sees clinical notes

Related MCP server: FHIR MCP Server

Архитектура

┌────────────────────────────────────────────────────────────────┐
│                 Discharge Coordination Agent                    │
│                   (MCP Client — role: discharge_coordinator)   │
└────────┬───────────────────┬───────────────────┬──────────────┘
         │ MCP calls         │ MCP calls          │ MCP calls
         ▼                   ▼                    ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│  EHR MCP Server │ │ Pharmacy Server  │ │ Billing Server   │
│  (port 8001)    │ │ (port 8002)      │ │ (port 8003)      │
│                 │ │                  │ │                  │
│ Tools:          │ │ Tools:           │ │ Tools:           │
│ • discharge_meds│ │ • check_stock    │ │ • get_charges    │
│ • diagnosis_cod │ │ • get_alternative│ │ • get_insurance  │
│ • billing_safe  │ │ • get_price      │ │ • gen_invoice    │
│   _summary      │ │ • dispense_req   │ │                  │
│ [RBAC enforced] │ │ [RBAC enforced]  │ │ [RBAC enforced]  │
└─────────────────┘ └─────────────────┘ └─────────────────┘

PHI Boundary:
  EHR → Billing path uses get_billing_safe_summary()
  PHI fields blocked: name, DOB, MRN, discharge_note, attending_physician
  Billing receives: ICD-10 codes, LOS, ward — non-PHI operational data only

Матрица политик RBAC

Роль

Клинические заметки EHR

Лекарства EHR

Коды диагнозов EHR

Аптека

Биллинг

discharge_coordinator

billing_agent

✗ ЗАБЛОКИРОВАНО

✗ ЗАБЛОКИРОВАНО

Только цена

pharmacy_agent

✗ ЗАБЛОКИРОВАНО

clinical_agent

Проверка наличия

✗ ЗАБЛОКИРОВАНО

Каждый вызов инструмента проверяет роль вызывающего объекта перед возвратом данных. Неавторизованные вызовы вызывают RBACError и регистрируются в ленте телеметрии.


Быстрый старт

Шаг 1: Установка зависимостей

pip install -r requirements.txt

Шаг 2: Генерация данных

cd data/
python generate_dataset.py

Шаг 3: Запуск серверов

HTTP-серверы FastMCP (производственного типа, требуются для асинхронного агента MCP):

# Terminal 1:
python src/servers/mcp_servers.py --server ehr

# Terminal 2:
python src/servers/mcp_servers.py --server pharmacy

# Terminal 3:
python src/servers/mcp_servers.py --server billing

Или запустите все три в одном процессе (запускает 3 фоновых потока):

python src/servers/mcp_servers.py --all

Прямой Python (без HTTP, только для обучения):

from src.servers.ehr_server import EHRServer

ehr = EHRServer()
meds = ehr.get_discharge_medications("PAT-001", role="discharge_coordinator")

Шаг 4: Запуск агента выписки

python src/agents/discharge_agent.py PAT-001
python src/agents/discharge_agent.py PAT-003

Шаг 5: Полная демонстрация

python demo/demo.py               # Runs 4 scenarios
python demo/demo.py --scenario 3  # RBAC violation only

Чат-интерфейс (React)

Этот репозиторий включает простой фронтенд чата на React, который вызывает легкий шлюз FastAPI, который, в свою очередь, вызывает серверы MCP.

1) Запуск серверов MCP (SSE)

python src/servers/mcp_servers.py --all

2) Запуск API шлюза чата (порт 8000)

copy .env.example .env   # then fill in Azure OpenAI settings (optional)
python -m uvicorn src.gateway.chat_gateway:app --reload --port 8000

3) Запуск сервера разработки React (порт 5173)

cd frontend
npm install
npm run dev

Шаг 6: Оценка

cd evaluation/
python eval_dashboard.py

Примечание: для оценки требуется запуск серверов MCP (шаг 3), так как она вызывает асинхронного агента MCP через SSE.


Структура проекта

mcpdischarge/
├── data/
│   ├── generate_dataset.py          ← Run this first
│   ├── ehr_patients.json            ← 6 patient records with discharge medications
│   ├── pharmacy_inventory.json      ← 17 drugs (4 out of stock, aliases table)
│   ├── billing_rate_cards.json      ← 15 charge codes
│   ├── insurance_contracts.json     ← 2 insurer contracts
│   ├── patient_insurance_map.json   ← Patient → insurer mappings
│   ├── icd10_billing_codes.json     ← ICD-10 → DRG billing mappings
│   └── rbac_policies.json           ← RBAC matrix (role → server → tools)
│
├── src/
│   ├── servers/
│   │   └── mcp_servers.py           ← EHRServer, PharmacyServer, BillingServer + FastMCP wrappers
│   └── agents/
│       └── discharge_agent.py       ← DischargeCoordinationAgent + WorkflowMetrics
│
├── evaluation/
│   ├── eval_dashboard.py
│   ├── 01_manual_vs_mcp.png
│   ├── 02_rbac_telemetry.png
│   └── 03_data_integrity.png
│
├── demo/
│   └── demo.py                      ← 4 scenarios + 2 limitations
│
├── configs/
│   ├── fastmcp_deployment.md        ← FastMCP HTTP server setup
│   ├── azure_foundry_mcp.md         ← Azure AI Foundry MCP integration
│   └── rbac_design.md               ← RBAC policy design guide
│
└── README.md

Внедренные шаблоны задач

Шаблон

Пациент

Препарат

Внедренная проблема

[NAME_MISMATCH]

PAT-001

Дапаглифлозин/Форсига

EHR использует бренд; Аптека хранит дженерик

[OUT_OF_STOCK]

PAT-001

Фуросемид 40 мг

Наличие=0; MCP предлагает Торасемид как альтернативу

[OUT_OF_STOCK]

PAT-003

Хумира/Адалимумаб

Бренд отсутствует; найден биоаналог Exemptia

[OUT_OF_STOCK]

PAT-004

Тафамидис/Виндамакс

Препарат от редкого заболевания — нет альтернативы; эскалация

[OUT_OF_STOCK]

PAT-005

Осимертиниб/Тагриссо

Специализированный препарат — заказ в центральной аптеке

[DATA_DRIFT]

PAT-002

Семаглутид 0.5 мг

Поддерживающая доза EHR против стартовой дозы 0.25 мг по формуляру

[SCOPE_VIOLATION]

PAT-006

Модафинил (Список H)

Биллинг НЕ должен видеть детали контролируемых веществ

[PHI_BOUNDARY]

Все

5 полей PHI заблокированы перед счетом на оплату


Три сервера MCP (подробно)

Сервер EHR

Инструменты, чувствительные к PHI (только клинические роли):

get_patient_discharge_summary(patient_id, caller_role)  # full clinical note
get_discharge_medications(patient_id, caller_role)       # medication list

Инструменты, безопасные для PHI (все роли, включая биллинг):

get_diagnosis_codes(patient_id, caller_role)             # ICD-10 only
get_admission_info(patient_id, caller_role)              # LOS, ward, dates
get_billing_safe_summary(patient_id, caller_role)        # strips PHI fields

Удаление PHI (что блокируется для биллинга):

PHI_FIELDS = {"name", "dob", "mrn", "discharge_note", "attending_physician"}
# Billing receives: patient_id, ward, admission_date, discharge_date, los_days, diagnosis_icd10

Сервер аптеки

Семантическое разрешение имен:

# EHR says "Dapagliflozin" → Pharmacy stores as "Farxiga"
# MCP alias table: {"farxiga": "PH-001", "dapa": "PH-001", "sglt2 inhibitor": "PH-001"}
drug = _find_drug_by_name("Dapagliflozin")  # → PH-001 (Dapagliflozin)
drug = _find_drug_by_name("Humira")          # → PH-008 (Adalimumab, branded)

Обнаружение конфликтов дозировки:

# EHR prescribes Semaglutide 0.5mg, formulary standard is 0.25mg starter
if queried_dose not in formulary_dose:
    dose_conflict = True  # triggers clinical review alert

Оценка семантического соответствия:

# score = word overlap / max(len(ehr_words), len(pharm_words))
# score < 0.85 → NAME_MISMATCH alert even if drug found
semantic_drug_match_score("Humira", "Adalimumab")  # → 0.0 (no word overlap)
semantic_drug_match_score("Furosemide", "Furosemide")  # → 1.0 (exact)

Сервер биллинга

Генерация счета (защита PHI):

def generate_invoice(patient_id, billing_safe_ehr, drug_costs, ...):
    # Verify PHI is stripped
    for phi_field in PHI_FIELDS:
        if phi_field in billing_safe_ehr:
            raise PermissionError(f"PHI field '{phi_field}' in billing payload")
    # Process invoice using only: ICD-10 + LOS + ward + drug prices

Сравнение MCP и традиционного API

Возможность

Традиционные REST API

Протокол MCP

Обнаружение схемы

Статические документы Swagger

Динамические манифесты инструментов

Межведомственные вызовы

Хрупкие «точка-точка»

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

Принудительное применение RBAC

Уровень приложения (непоследовательно)

Уровень протокола (гарантировано)

Граница PHI

Ручная политика

Принудительно для каждого инструмента

Разрешение названий лекарств

Жестко закодированное сопоставление

Таблица семантических псевдонимов

Обработка отсутствия на складе

Ручной обратный вызов аптеки

Автоматический поиск альтернатив

Телеметрия

Пользовательское логирование

Встроенная трассировка вызовов инструментов

Подключение нового отдела

Новая интеграция API

Регистрация нового сервера MCP


Результаты оценки (6 выписок пациентов)

Пациент

Вызовы MCP

Успех

Оповещения

PHI заблокировано

PAT-001 HFrEF

16

100%

1

5 полей

PAT-002 AKI

11

100%

1

5 полей

PAT-003 RA

13

100%

2

5 полей

PAT-004 ATTR

14

100%

2

5 полей

PAT-005 NSCLC

9

100%

1

5 полей

PAT-006 MS

9

100%

1

5 полей

Итого: 72 вызова инструментов MCP | 100% успех | 15 ручных передач заменено на выписку | ~45 минут сэкономлено на каждом случае


Развертывание FastMCP HTTP

См. configs/fastmcp_deployment.md. Ключевой шаблон:

from fastmcp import FastMCP

ehr_mcp = FastMCP("EHR-Server")

@ehr_mcp.tool()
def get_discharge_medications(patient_id: str, caller_role: str) -> dict:
    """Get discharge medication list from EHR."""
    return EHRServer().get_discharge_medications(patient_id, caller_role)

# Run as HTTP SSE server
ehr_mcp.run(transport="sse", host="0.0.0.0", port=8001)

Агент подключается как клиент MCP:

from mcp import ClientSession, StdioServerParameters
from mcp.client.sse import sse_client

async with sse_client("http://localhost:8001/sse") as (read, write):
    async with ClientSession(read, write) as session:
        result = await session.call_tool(
            "get_discharge_medications",
            {"patient_id": "PAT-001", "caller_role": "discharge_coordinator"}
        )

Интеграция с Azure AI Foundry

См. configs/azure_foundry_mcp.md. Серверы MCP регистрируются как инструменты Foundry:

from azure.ai.projects.models import McpToolDefinition

mcp_tools = [
    McpToolDefinition(server_url="http://ehr-server:8001/sse", name="ehr-server"),
    McpToolDefinition(server_url="http://pharmacy-server:8002/sse", name="pharmacy-server"),
    McpToolDefinition(server_url="http://billing-server:8003/sse", name="billing-server"),
]

agent = client.agents.create_agent(
    model="gpt-4o",
    name="DischargeCoordinationAgent",
    instructions=DISCHARGE_AGENT_SYSTEM_PROMPT,
    tools=[t.as_tool_definition() for t in mcp_tools],
)

CitiusTech Gen AI & Agentic AI Training Program — Проект 5 из 5

Related MCP Connectors

Related MCP Servers