Skip to main content
Glama

MCPDischarge — 부서 간 MCP 상호 운용성

EHR × 약국 × 청구 | RBAC | PHI 경계 | FastMCP

CitiusTech 생성형 AI & 에이전트 AI 교육 — 프로젝트 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단계: 서버 실행

FastMCP HTTP 서버 (프로덕션 스타일, 비동기 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

채팅 UI (React)

이 저장소에는 경량 FastAPI 게이트웨이를 호출하는 간단한 React 채팅 프론트엔드가 포함되어 있으며, 이 게이트웨이는 다시 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

참고: 평가는 SSE를 통해 비동기 MCP 에이전트를 호출하므로 MCP 서버가 실행 중이어야 합니다 (3단계).


프로젝트 구조

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

Dapagliflozin/Farxiga

EHR은 브랜드명 사용; 약국은 제네릭 저장

[OUT_OF_STOCK]

PAT-001

Furosemide 40mg

재고=0; MCP가 대체제로 Torsemide 제시

[OUT_OF_STOCK]

PAT-003

Humira/Adalimumab

브랜드 품절; 바이오시밀러 Exemptia 발견

[OUT_OF_STOCK]

PAT-004

Tafamidis/Vyndamax

희귀 질환 약물 — 대체제 없음; 에스컬레이션

[OUT_OF_STOCK]

PAT-005

Osimertinib/Tagrisso

전문 의약품 — 중앙 약국 주문

[DATA_DRIFT]

PAT-002

Semaglutide 0.5mg

EHR 유지 용량 vs 처방집 시작 용량 0.25mg

[SCOPE_VIOLATION]

PAT-006

Modafinil Schedule 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 vs 기존 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 생성형 AI & 에이전트 AI 교육 프로그램 — 5개 프로젝트 중 5번째

Related MCP Connectors

Related MCP Servers