Taiwan-Health-MCP 完整教學
把台灣健保 / TFDA / 國際醫療編碼資料一次整合給 LLM agent 用的生產級 MCP 伺服器深度教學。
1. 專案定位
Taiwan-Health-MCP 是一個 Model Context Protocol (MCP) 伺服器,目的是讓 Claude / GPT 等 LLM agent 能透過標準工具呼叫介面,查詢以下台灣與國際醫療健康資料:
- 台灣本地:健保署 ICD-10-CM/PCS 多版本(2014/2021/2023)、TFDA 健康食品、台灣食品營養成分、TWCore IG、本地臨床指引
- 國際標準:SNOMED CT、LOINC、FHIR R4 Condition
與一般「把 OpenAPI 包成 MCP」的薄殼專案不同,這套系統做了三件實質工作:
- 資料模型在地化:使用台灣健保署官方版次的 ICD codes、繁中名稱優先、TFDA 字號搜尋。
- 臨床語意層:不只回傳代碼,還會做「鄰近碼」、「diagnosis × procedure 衝突檢查」、「lab 數值判讀」這類語意化結果。
- 隱私 / 合規架構:HIPAA-aware audit(只記 hash,永不存 raw PHI)、HTTPS / Docker 網路隔離、
/privacy與/dpa端點供 Anthropic Connectors 審核。
技術定位:生產可部署(Postgres 16 + pgBouncer + Redis + Prometheus + non-root container),而非概念驗證。
2. 安裝指南
2.1 系統需求
- Docker / Docker Compose v2
- 至少 4 GB RAM(含 Postgres + Redis + Ollama 嵌入 server)
- ~10–20 GB 磁碟(依載入哪些資料集)
- (選用)外部 Ollama server 跑
qwen3-embedding:0.6b模型;未設定OLLAMA_BASE_URL時自動降級為純關鍵字搜尋
2.2 快速啟動
1git clone https://github.com/healthymind-tech/Taiwan-Health-MCP.git
2cd Taiwan-Health-MCP
3
4# 1. 建立環境變數(必須至少改 POSTGRES_PASSWORD)
5cp .env.example .env
6$EDITOR .env
7
8# 2. 建立 dataset 設定
9cp config/datasets.example.yaml config/datasets.yaml
10
11# 3. 啟動主服務(postgres / pgbouncer / redis / mcp-server)
12docker compose up -d
13
14# 4. 載入資料集(耗時 10–30 分鐘)
15docker compose --profile loader run --rm data-loader --all
或依需求個別載入:
1docker compose --profile loader run --rm data-loader --icd
2docker compose --profile loader run --rm data-loader --loinc
3docker compose --profile loader run --rm data-loader --twcore
4docker compose --profile loader run --rm data-loader --guideline
5docker compose --profile loader run --rm data-loader --snomed
6docker compose --profile loader run --rm data-loader --health-food
7docker compose --profile loader run --rm data-loader --food-nutrition
2.3 驗證
1# 驗 HTTP MCP 端點(streamable-http 模式)
2curl http://localhost:8000/mcp -X POST -H 'Content-Type: application/json' \
3 -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
4
5# 驗 /privacy 與 /dpa 頁面(給 Anthropic Connectors 審核用)
6curl http://localhost:8000/privacy | head -20
7curl http://localhost:8000/dpa | head -20
8
9# 驗 metrics(只綁 127.0.0.1)
10curl http://127.0.0.1:9090/metrics | head -10
2.4 Claude Desktop 接入
於 Claude Desktop 設定加入:
1{
2 "mcpServers": {
3 "taiwan-health": {
4 "transport": { "type": "streamable-http", "url": "http://localhost:8000/mcp" }
5 }
6 }
7}
或用 stdio 模式(不需 Docker):先設定 DATABASE_URL / REDIS_URL 指向 host 上的服務,再用 python src/server.py 啟動。
3. 核心架構解析
整體採「MCP 入口 + Service 分層」設計,每個資料領域一個 service module,由 server.py 中的 @mcp.tool() 暴露對應工具。
flowchart TB
subgraph Client["LLM Agent Client"]
A1[Claude Desktop / API / 任意 MCP client]
end
subgraph Transport["MCP Transport"]
T1[stdio]
T2[streamable-http :8000/mcp]
T3[sse]
end
subgraph Core["server.py FastMCP Core"]
C1[lifespan init]
C2[tool registry x24]
C3[audited decorator
SHA-256 of params]
C4[PrivacyPageMiddleware
/privacy /dpa]
end
subgraph Services["Service Layer"]
S1[ICDService]
S2[SNOMEDService]
S3[LabService LOINC]
S4[ClinicalGuidelineService]
S5[HealthFoodService]
S6[FoodNutritionService]
S7[FHIRConditionService]
S8[TWCoreService]
end
subgraph Infra["Infrastructure"]
I1[(PostgreSQL 16
+ pgvector)]
I2[pgBouncer
transaction pool]
I3[(Redis 7
cache + TTL)]
I4[Ollama
qwen3-embedding]
I5[Prometheus
127.0.0.1:9090]
end
A1 --> T1 & T2 & T3
T1 & T2 & T3 --> C1
C1 --> C2 --> C3
C2 --> C4
C3 --> S1 & S2 & S3 & S4 & S5 & S6 & S7 & S8
S1 & S2 & S3 & S4 & S5 & S6 & S7 & S8 --> I2 --> I1
S1 & S2 & S3 & S4 & S5 & S6 & S7 & S8 -.-> I3
S1 & S2 & S3 -.-> I4
C3 --> I5
設計重點
- lifespan 單次初始化:FastMCP 的 streamable-http 模式會在每個 session 跑 lifespan,作者用
_init_lock+_initializedflag 確保 pool / cache 只建一次。 - Service 注入:每個 service 是 module-level 可選 instance(型別
XxxService | None),尚未載入資料時 tool 會回_svc_unavailable(...)而不是 raise,保持 LLM 端可讀錯誤。 - 動態工具啟用:
DatasetStatusManager會檢查 db 中是否有對應 schema 資料,沒有的工具不註冊,避免 LLM 拿到「會 fail 的工具」。 @audited裝飾器:每個 tool 都包,計時 + 記 SHA-256(params) + Prometheusrecord_tool_call。
資料夾結構
1src/
2├── server.py # 主入口 + tool registry(24 個 @mcp.tool)
3├── config.py # AppConfig.from_env()
4├── audit.py # HIPAA audited 裝飾器
5├── cache.py # Redis client wrapper
6├── database.py # asyncpg pool
7├── metrics.py # Prometheus counter / histogram
8├── embedding_service.py
9├── icd_service.py
10├── snomed_service.py
11├── lab_service.py
12├── food_nutrition_service.py
13├── health_food_service.py
14├── fhir_condition_service.py
15├── clinical_guideline_service.py
16└── twcore_service.py
17loader/
18├── main.py # data-loader CLI(--all / --icd / ...)
19├── dataset_config.py
20└── dataset_resolver.py
21db/schema.sql # postgres init schema
22docs/ # mkdocs 站台原始檔
4. 主要工具詳細用法
24 個 MCP tool 全部走 @audited 計時與審計。以下分七類列出。
4.1 ICD-10(5 個)
| Tool | 用途 | 範例 |
|---|---|---|
search_medical_codes(keyword, code_type='diagnosis', limit=10) | 語意+關鍵字 hybrid search | keyword="高血壓", code_type="diagnosis" |
infer_complications(code) | 由主診斷推併發症候選 | code="I10" |
get_nearby_codes(code) | 相鄰類別 | code="J18.9" |
check_medical_conflict(diagnosis_code, procedure_code) | 診斷×手術衝突檢查 | ("J18.9","0BH17EZ") |
browse_icd_category(category=None, limit=50) | 瀏覽 chapter / category | category="I" |
4.2 健康食品 / 食品營養(5 個)
| Tool | 用途 |
|---|---|
search_health_supplement(...) | TFDA 健康食品搜尋(依字號 / 功效 / 成分) |
query_food_nutrition(...) | 食品營養成分查詢(卡路里、蛋白質、鈉…) |
query_food_ingredient(...) | 食品成分(過敏原、添加物) |
search_foods_by_nutrient(nutrient, limit=20) | 找「高鈣」、「高鐵」食物 |
analyze_meal_nutrition(foods: list[str]) | 整餐營養估算 |
4.3 FHIR R4 Condition(2 個)
| Tool | 用途 |
|---|---|
query_fhir_condition(...) | 由關鍵字 / ICD code 產生 FHIR Condition resource |
validate_fhir_condition(condition_json) | 驗證 FHIR Condition 結構 |
4.4 LOINC 檢驗判讀(4 個)
| Tool | 用途 |
|---|---|
search_loinc(...) | LOINC code 搜尋 |
query_loinc(...) | 單一 LOINC 詳細資訊 + 參考區間 |
interpret_lab_result(loinc_code, value, unit, sex, age) | 單項檢驗值判讀 |
batch_interpret_lab_results(results) | 批次判讀(一次傳一組) |
4.5 臨床指引(2 個)
| Tool | 用途 |
|---|---|
search_clinical_guideline(keyword, limit=3) | 指引摘要搜尋 |
query_guideline(...) | 指引分段內容 + 臨床路徑建議 |
4.6 TWCore IG(1 個)
| Tool | 用途 |
|---|---|
query_twcore_code(...) | 台灣健保署 CodeSystem 查詢(科別碼、專科醫師代碼…) |
4.7 SNOMED CT(4 個)
| Tool | 用途 |
|---|---|
search_snomed_concept(...) | 概念關鍵字搜尋 |
query_snomed_concept(...) | 概念詳細資料 |
get_snomed_relationships(...) | 父子 / IS-A / 屬性關係 |
query_snomed_mapping(...) | SNOMED ↔ ICD 對應 |
4.8 health_check(管理)
回傳當前 service / dataset / db 狀態,可用於 K8s liveness probe。
5. 應用場景
5.1 醫療 LLM Copilot
讓 Claude Desktop 直接幫醫師 / 衛教師查 ICD code、產 FHIR Condition、驗檢驗值。所有查詢經 audit log 留痕,但不留參數值,符合 HIPAA Minimum Necessary 原則。
5.2 健康 / 營養 chatbot
接 TFDA 健康食品 + 食品營養 → 給使用者「這款健康食品有沒有過合法字號?」、「便當熱量大概多少?」、「我血壓高想找低鈉食物」。
5.3 EHR / HIS 對外查詢介面
把 SNOMED↔ICD mapping、LOINC 判讀、TWCore 科別碼,用 MCP 標準化暴露給 internal LLM workflow,省掉 N 套 ad-hoc REST API。
5.4 醫療 AI 評估資料源
研究單位需要「真實的台灣編碼系統 ground truth」做模型評估時,可直接 query 而不必自己整理 TFDA / 健保署原始檔。
6. 資安掃描報告
整體評等:🟢 低風險(生產級設計,安全考量已落實在系統層)
6.1 加分項
| 項目 | 評等 | 說明 |
|---|---|---|
| Audit 不記 PHI | 🟢 | audit.py 只存 SHA-256(params)[:16],永不寫原始參數值 |
| 容器非 root | 🟢 | Dockerfile 建立 mcp user,USER mcp,/sbin/nologin |
| pgBouncer + SCRAM | 🟢 | AUTH_TYPE: scram-sha-256 |
| Redis 綁本機 | 🟢 | 127.0.0.1:6379:6379 |
| Metrics 綁本機 | 🟢 | /metrics 預設 127.0.0.1:9090 |
| Postgres password 強制 | 🟢 | ${POSTGRES_PASSWORD:?...} 沒設就 fail |
| 隱私文件齊全 | 🟢 | /privacy + /dpa 端點 + docs,符合 Anthropic Connectors Directory 審核要求 |
| 不存 cookie / token | 🟢 | server.py 第 396 行明示「不存 session token 或 user identifier」 |
| Append-only audit | 🟢 | audit.query_log 設計為只插入,無 UPDATE / DELETE 路徑 |
無 eval / os.system / pickle.loads | 🟢 | 全域 grep 0 命中 |
| 無 PII 處理 | 🟢 | grep 身分證 / 健保 / 實聯制 0 命中(系統設計本身不接觸個資) |
6.2 觀察點 / 部署者責任
| 項目 | 評等 | 說明 |
|---|---|---|
.env 中明文 password | 🟡 | 預期;正式部署應改用 docker secrets / vault |
| Postgres 對外開放 5432 | 🟡 | compose.yaml 預設 expose 5432:5432;正式部署應拿掉 host port 或加 firewall |
| MCP 端點預設無 auth | 🟡 | streamable-http :8000/mcp 預設沒有 token 驗證,須由前端 reverse proxy / Anthropic Connectors 認證層處理 |
| Ollama URL 寫死 IP | 🟡 | .env.example 預設 192.168.1.100:11434(內網),對外部署須改 / 設為 unset |
| LLM 端輸入仍可能含 PII | 🟡 | 系統不記 raw param,但呼叫端(LLM 上下文)仍可能傳病人姓名 / ID;應在 client 側做去識別化 |
| 資料來源合法性 | 🟢 | ICD/SNOMED/LOINC 為國際公開標準;TFDA / 健保署資料為政府公開資料;TWCore IG 為 HL7 Taiwan 公開 IG |
6.3 結論
內部 / 受信任網路部署:直接可用,🟢 低風險。對外公開時建議:
- 加 reverse proxy(Nginx / Cloudflare)+ JWT/OIDC auth
- 拿掉 compose 中 postgres
5432:5432對 host expose .env改用 secrets manager- 在 LLM client 側建立 PII redaction middleware
7. FAQ
Q1:為什麼 drug domain 被移除?
A:2026-05-28 commit 29de0bd 移除。觀察是「rxnorm→TFDA bridge」搭配信度不足,作者選擇縮邊界、不暴露半成品 tool 給 LLM。日後可能由獨立 server 重做。
Q2:沒設定 Ollama 會怎樣?
A:OLLAMA_BASE_URL 未設時,EmbeddingService 自動降級,所有 search 退到純 PostgreSQL to_tsvector 全文索引(GIN)。語意檢索失效但功能仍在。
Q3:能否只啟用部分資料集?
A:可以。data-loader 支援 --icd / --loinc / --twcore / --guideline / --snomed / --health-food / --food-nutrition。沒載的 service 對應 tool 不會被註冊(DatasetStatusManager 動態判斷)。
Q4:audit log 真的不存原始參數?
A:是。看 src/audit.py 第 70 行:params_hash = sha256(json.dumps(kwargs)).hexdigest()[:16],只取 hex 前 16 字。原始 kwargs 從未進 DB。
Q5:可以接內部 EHR 嗎?
A:可以,這是設計目標之一。建議方式:(1) 在 EHR 系統內部署一個 MCP client(Python mcp lib),(2) MCP server 部署於同一內網,(3) 用 stdio 或 unix socket 模式避免 HTTP port 暴露。
Q6:與 mcp-server-everything、smart-mcp 等通用工具比?
A:那些是「把任意 API 包成 MCP」的通用框架。本專案是台灣醫療領域專用、含資料庫 + 載入 pipeline 的完整 stack。等同 vertical SaaS vs PaaS 差別。
Q7:對台灣健保結算碼有支援嗎?
A:透過 query_twcore_code 查詢 TWCore IG 中的「臺灣健保署就醫科別」與「專科醫師代碼」CodeSystem;申報碼本身(如門急診 BAA02 之類)不在當前 scope,可後續擴充。
8. 進階技巧
8.1 自訂 dataset 目錄
不想用預設 /app/fhir-code/...?設 DATASETS_CONFIG=/app/config/datasets.yaml 並參考 config/datasets.example.yaml 寫自訂路徑。
8.2 嵌入模型換成 OpenAI
把 OLLAMA_BASE_URL 改成自架的 OpenAI-compatible endpoint,OLLAMA_EMBED_DIMENSIONS 改 1536(text-embedding-3-small),重跑 data-loader --embed。注意要同步改 db/schema.sql 的 vector 維度,否則 INSERT 會失敗。
8.3 用 Prometheus + Grafana 觀測
/metrics 已內建:mcp_tool_calls_total{tool,status} counter、mcp_tool_duration_seconds histogram、db_pool_size。直接 scrape 即可。
8.4 純 stdio 模式
不想跑 docker compose,可:
1export DATABASE_URL=postgresql://... # 自架 postgres
2export REDIS_URL=redis://localhost:6379/0
3export MCP_TRANSPORT=stdio
4python src/server.py
適合單機 dev / Claude Desktop subprocess 模式。
8.5 加額外 tool
仿造 src/icd_service.py 寫一個新 service → 在 server.py 的 lifespan 初始化 → 加 @mcp.tool() 包 @audited("your_tool")。
9. 整合進其他工作流
- MCP knowledge base:搭配
paper-search/gh-tutorial-qd等其他 MCP server,由 LLM 自由路由:醫療詞彙查 Taiwan-Health-MCP、文獻查 paper-search。 - 內部 RAG:用
query_fhir_condition把使用者問題轉成 FHIR resource 再丟向 EHR RAG。 - 教學/評估:固定一組 ICD/LOINC 查詢當作 LLM 醫療能力 benchmark(不需 OpenAI key、不外洩 PHI)。
- 政府開放資料 wrapper:作為「TFDA 健康食品 + 食品營養」這類資料的標準介面,避開直接 scrape gov 網站的脆弱性。
10. Checklist(投入生產前)
-
.env中POSTGRES_PASSWORD已改成強密碼,未進版控 -
compose.yaml中 postgres 不對 host 開 5432(或加 firewall) - MCP
/mcp端點前置 reverse proxy + auth -
/privacy與/dpa內容已客製化(聯絡信箱、管轄法) - Prometheus
/metrics確認只綁 127.0.0.1(預設已是) - Ollama URL 已更新成實際 endpoint(或 unset 走純關鍵字)
- data-loader 已跑過至少一個 dataset,
health_check回應正常 - LLM client 側已實作 PII redaction(呼叫端責任)
- Audit log 保留期符合所在地法規(程式預設 90 天,可調)
- Docker image 已掃描(trivy / grype)
11. 進一步閱讀
- 上游 README:
/tmp/Taiwan-Health-MCP/README.md - Privacy / DPA 設計:
docs/deployment/privacy.md、docs/deployment/dpa.md - MkDocs 完整文件:在 repo 根目錄執行
mkdocs serve - gh-save metadata 版:本專案
inbox/2026-06-01-github-healthymind-tech-Taiwan-Health-MCP.md - MCP 官方規格:https://modelcontextprotocol.io
- FHIR R4 Condition:http://hl7.org/fhir/R4/condition.html
- 台灣 TWCore IG:https://twcore.mohw.gov.tw
Comments