Skip to main content
Glama
trionnemesis

Healthcare OpenData MCP

by trionnemesis

healthcare-opendata-mcp 🏥

官方開放資料 → 可查詢 MCP 介面,讓 AI agent 不必直接處理分散的政府資料來源。

Self-hosted MCP server that syncs Taiwan government procurement (PCC) and National Health Insurance (NHI) open data into a local SQLite database, then exposes it through read-only MCP tools. A SELECT-only query guard (syntax allowlist plus a read-only SQLite authorizer) keeps the Twinkle-compatible query_rows interface safe for agent-driven querying.

CI Python 3.11+ FastMCP License: MIT

healthcare-opendata-mcp(命令名稱:hcmcp)是一個自建、可部署的 MCP server:把政府電子採購網與健保署開放資料同步到 SQLite,再以穩定的 MCP tools 提供給 Claude 或其他 agent 查詢。

專案保留 Twinkle Hub query_rows 的 SQL 式查詢模式,但資料來源、同步流程與儲存層都由本專案自行掌握,不依賴第三方聚合服務。

GitHub Pages 導覽 · PCC 靜態資料看板 · GitHub repository

Contents

Related MCP server: M3

Why

AI agent 要查政府資料時,真正的摩擦通常不在模型,而在資料入口:來源分散、格式不同、欄位缺漏,而且外部聚合服務的政策或可用性可能改變。

Problem

What hcmcp does

政府電子採購網與健保資料各自分散

SourceAdapter 統一 discover → fetch → normalize → upsert 流程

半月 XML、CSV API、標案明細頁格式不同

正規化成可查詢的 dataset 與 schema

標案 open data 缺少截標、開標、預算

get_tender_detail 按需讀取官方明細頁補足資訊

第三方資料入口不可控

自行同步、儲存與提供 MCP 介面

How it works

flowchart LR
    PCC[政府電子採購網<br/>半月 XML] --> SYNC[hcmcp-sync<br/>fetch / normalize / upsert]
    NHI[健保署開放平台<br/>CSV API] --> SYNC
    SYNC --> DB[(SQLite<br/>~/.hcmcp/hcmcp.db)]
    DB --> SERVER[hcmcp<br/>唯讀 MCP server]
    SERVER --> AGENT[Claude / Agent]
    AGENT -->|按需補查| DETAIL[get_tender_detail]
    DETAIL --> PCCDETAIL[政府採購網<br/>標案明細頁]

The project keeps ingestion and querying separate:

  1. hcmcp-sync pulls official sources and writes the shared SQLite database.

  2. hcmcp opens the same database in the query path and exposes MCP tools.

  3. list_datasetsget_datasetquery_rows is the recommended discovery flow.

  4. get_tender_detail performs an on-demand lookup when a tender needs deadline, opening time, or budget details.

Static data dashboard

GitHub Pages 資料看板 是 SQLite 的可重建、唯讀 projection。它讀取 build-time 產生的 docs/data/current.json,提供 PCC 摘要、搜尋、公告類型/機關/日期篩選、日期/預算/決標金額排序與固定 20 筆分頁;現有 landing page 仍保留為專案導覽。

資料邊界:

  • 「資料集概覽」矩陣由 catalog(已啟用者)與資料庫產生,不硬編碼資料集名單:啟用一個新資料集後,重新 export 即會出現在頁面上,不需要改 HTML、schema 或部署閘門。停用的候選不發布 —— 它們尚未實查,列出來會讓訪客誤以為已涵蓋。

  • 矩陣中筆數顯示「—」代表該資料集從未同步成功(物化表尚未建立),與「同步成功但 0 筆」不同。

  • 新鮮度逐資料集判定,門檻由 catalog 的 update_cadence 推導(2 倍更新週期):每日更新的資料集停更 3 天就是過期,年度更新的資料集停更 3 天完全正常。顯示「無契約」代表該資料集未登錄更新頻率,沒有判定依據,不等於資料是最新的;這類資料集不會把整頁狀態拉成過期。判定基準是 last_fetched_at(per-dataset),不是 ingestion_runs.finished_at(per-source)—— 一個成功的 run 底下可能有某個資料集根本沒更新到。

  • generated_at 是 UTC 快照產生時間;status.source_max_date 才是 PCC 官方資料中的最新日期。

  • 狀態明確區分 freshstaledegradedempty;JSON 無法載入或格式錯誤時,頁面顯示失敗而不呈現假成功。

  • P0 只發布 allowlist 中的 PCC 欄位。NHI 僅顯示筆數與最後同步 metadata,不發布電話、地址或全院所目錄。

  • 金額缺值維持 null,不轉為 0。完整 projection 先量測;超過 5 MiB 時只縮限明細列,仍保留全量聚合與明確的 export strategy。

  • 部署流程會嘗試重新同步官方來源並重建快照;同步失敗時部署上一份有效快照(last-known-good)並讓 workflow 轉紅,不會部署空資料,也不會靜默成功。新快照必須通過驗證且非空,才准取代舊的。

  • 每日排程尚未啟用schedule:pages.yml 中維持註解):runner 能否穩定完成官方同步尚未實測,且每日全量重抓 12 個月的 PCC 半月檔對政府站台是持續性負載。以 workflow_dispatch 跑過一次確認後即可啟用。

  • 頁面載入時會以「現在」重新檢查快照年紀 —— 若自動同步停擺,舊快照不會繼續自稱最新。

  • 完整、即時或任意條件查詢仍使用 MCP/SQLite。

從已成功同步的真實 DB 重建 snapshot 與預先渲染摘要:

.venv/bin/python scripts/export_board_data.py \
  --db /path/to/hcmcp.db \
  --out docs/data/current.json \
  --template scripts/templates/dashboard.html \
  --dashboard-out docs/dashboard/index.html

.venv/bin/python scripts/verify_dashboard.py --site docs

快照契約見 docs/data/schema-v1.json,設計與 P0/P1/P2 邊界見 docs/superpowers/specs/2026-09-01-pages-dashboard-p0.md;資料集矩陣改由 catalog 驅動的決策見 docs/superpowers/specs/2026-09-07-pages-catalog-driven-datasets.md,過期門檻的推導規則見 docs/superpowers/specs/2026-09-09-freshness-from-cadence.md,自動同步與 last-known-good 的設計見 docs/superpowers/specs/2026-09-10-pages-auto-sync.md

Install

git clone https://github.com/trionnemesis/healthcare-opendata-mcp.git
cd healthcare-opendata-mcp

python3.11 -m venv .venv
.venv/bin/python -m pip install -e .

# 建立或更新預設 DB:~/.hcmcp/hcmcp.db
.venv/bin/hcmcp-sync

加入 Claude Code:

claude mcp add hcmcp -- /absolute/path/to/healthcare-opendata-mcp/.venv/bin/hcmcp

Sync options

.venv/bin/hcmcp-sync --db /path/to/hcmcp.db --tender-months 12 --award-months 12

Flag

Default

Purpose

--db

HCMCP_DB~/.hcmcp/hcmcp.db

寫入的 SQLite 路徑

--tender-months

12

招標回溯月數(PCC 站上實際可回溯約 6 個月)

--award-months

12

決標回溯月數

Environment variables

Variable

Default

Used by

Purpose

HCMCP_DB

~/.hcmcp/hcmcp.db

sync + server

SQLite 路徑;兩個 process 必須一致

HCMCP_TRANSPORT

stdio

server

stdio / http / sse(僅相容既有部署)

HCMCP_HOST

0.0.0.0

server(http/sse)

監聽位址;server 無 authentication,公開網段請改綁內網位址並在前方配置存取控制

HCMCP_PORT

8000

server(http/sse)

監聽 port

hcmcp-synchcmcp server 共用同一個預設 DB:~/.hcmcp/hcmcp.db。如果要改路徑,兩個 process 都必須使用相同的 HCMCP_DB;sync 也可以使用 --db

HCMCP_DB=/path/to/hcmcp.db .venv/bin/hcmcp-sync
HCMCP_DB=/path/to/hcmcp.db .venv/bin/hcmcp

否則可能出現「同步成功,但 server 查不到資料」的路徑漂移問題。server 啟動時若 DB 沒有任何資料集會直接以錯誤訊息結束,提醒先跑 hcmcp-sync

What it provides

Datasets

目前 CLI 預設同步兩個資料集:

Dataset

Scope

Official source

Update path

pcc-tender

衛生福利部轄下機關的資訊勞務相關標案

政府電子採購網

半月 XML;明細欄位按需 enrich

nhi-clinic

健保特約醫事機構-診所

健保署資料開放平台

CSV API,每日更新

候選(catalog 中 enabled=false尚未同步,待對官方端點實查後啟用):

Dataset

Scope

Official source

Update path

nhi-healthcare-facility

部立/地方政府醫院、健保特約診所與衛生所

健保署資料開放平台

CSV API,每日更新

Dataset catalog

要同步哪些政府開放資料,由 src/health_opendata_mcp/catalog.py 這份宣告式目錄決定 —— 不是散落在程式流程裡。每筆 entry 除了 resource id/URL,還攜帶出處與驗證狀態:更新頻率、官方說明頁、verified_at(何時對官方端點實查過)、verified_note(實查當下觀察到的事實)與 enabled

目錄在 import 時就會被驗證,違反即失敗(不靜默降級):

  • 驗證閘門enabled=true 必須有 verified_atverified_note。未實查的候選可以留在目錄裡,但只能是 enabled=false;目錄有能力誠實表達「還沒驗證」。

  • 官方網域白名單 — 下載與說明頁 URL 的 host 必須在 OFFICIAL_HOSTS。entry 是資料;不設限等於「新增一筆資料 = 新增一個對任意主機發請求的能力」。

  • 欄位一致性與 r_id 字元集r_id 會被插值進 query string,限制字元集才能保證它不會挾帶 & / ? 改寫 URL 的其他參數。

因此擴充資料範圍的流程是:

1. 加一筆 CatalogEntry,enabled=False、verified_at=None
2. 對官方端點實查,把觀察到的事實寫進 verified_note,填上 verified_at
3. 改 enabled=True

目前目錄中的候選(尚未實查,不會被同步):nhi-hospital-district(健保特約醫事機構-地區醫院)、nhi-hospital-bed-ratio(全民健保特約醫院之保險病床比率)。兩者的 rId 取自本 repository 既有測試,沒有實查日期紀錄,因此維持停用。

第 2 步的實查由 scripts/verify_catalog_sources.py 執行 —— 它只輸出可觀察的事實(HTTP status、列數、欄位名、natural key 是否存在與是否唯一),並產生可貼進 catalog.pyverified_at / verified_note

.venv/bin/python scripts/verify_catalog_sources.py --only nhi-clinic
[OK  ] nhi-clinic  (enabled=True)
       url: https://info.nhi.gov.tw/api/iode0000s01/Dataset?rId=A21030000I-D21004-009
       HTTP 200 · text/csv; charset=utf-8 · ... bytes · 24695 列 · 29 欄
       natural key 醫事機構代碼 · 相異 24695 · 無鍵列 0
       建議填入 catalog.py:
           verified_at="2026-09-07",
           verified_note="實查 2026-09-07:HTTP 200;...",

啟用一個資料集之後不需要再改別的地方:hcmcp-sync 會同步它,list_datasets 會列出它,重新 export 後 資料看板 的資料集矩陣也會出現它。

該 script 只讀:不會修改 catalog.py,也不會翻 enabled;啟用仍是人工 review 後的 PR 編輯。--enabled-only 可作為上游漂移檢查(欄位或 natural key 不再成立時離開碼非零)。若本機不便連外,可用 GitHub Actions 的 Verify catalog sources workflow 手動觸發(workflow_dispatch,未排程)。

MCP tools

Tool

Purpose

list_sources

列出資料來源、取得策略與最後抓取時間

list_datasets

列出可查詢資料集、欄位與新鮮度(last_fetched_at / row_count

get_dataset

取得 dataset metadata、schema、新鮮度與可選的抽樣資料列

query_rows

對單一 dataset 做 SELECT-only 篩選、排序與聚合

search_records

跨資料集關鍵字搜尋

get_record

(dataset_id, natural_key) 取得單筆完整資料

get_vendor_stats

依得標次數與金額整理廠商排名

get_tender_detail

即時取得標案明細的截標、開標、預算與採購屬性

Querying

先看資料集與 schema,再執行查詢:

list_datasets()
get_dataset(dataset_id="pcc-tender", sample_rows=5)

兩者都會回 last_fetched_atrow_count,因此不需要額外查詢就能判斷資料是否過期。row_countnull 代表該資料集從未同步成功(物化表尚未建立),與「同步成功但 0 筆」不同 —— 缺值不會被折成 0。

query_rows 保留 Twinkle 相容的 SQL-style 查詢介面,支援欄位選取、WHEREGROUP BY、排序與聚合:

query_rows(
    dataset_id="pcc-tender",
    columns=[
        "agency",
        "COUNT(*) AS n",
        "SUM(CAST(award_price AS INTEGER)) AS total",
    ],
    where="announcement_type='決標公告' AND date >= '2025-01-01'",
    group_by=["agency"],
    order_by="total DESC",
    limit=50,
)

# nhi-healthcare-facility 目前為候選(catalog enabled=false),啟用後才有資料
query_rows(
    dataset_id="nhi-healthcare-facility",
    columns=[
        "醫事機構代碼",
        "醫事機構名稱",
        "facility_type",
        "governing_level",
        "classification_source",
        "is_active",
    ],
    where=(
        "facility_type='hospital' AND governing_level='mohw' "
        "AND is_active=1"
    ),
    order_by="醫事機構名稱",
    limit=50,
)

SQLite 使用 LIKE,不使用 PostgreSQL 的 ILIKE;金額欄位需要依資料內容使用 CAST(... AS INTEGER)

Trust & security

query_rows 接受 SQL 片段,因此實作了兩層防禦:

  • 語法層:只允許單一 SELECT;拒絕多語句、註解、PRAGMAATTACH、DML、DDL 與危險 keyword,並將 limit 硬上限設為 400。

  • 執行層:使用 SQLite read-only connection 與 authorizer allowlist,只允許讀取單一物化資料表;另有 VM 步數上限。

寫入路徑(sync)另有一層 ingestion 防禦:PCC 半月 XML 一律以 defusedxml 解析,DTD 與 entity 在 parser 層就被拒絕(CWE-611/776,不使用可被 padding 繞過的字串前綴檢查),並保留 20M 字元的輸入上限。

這是查詢執行安全邊界,不是使用者認證層。MCP server 本身沒有 authentication;HTTP/GKE 部署應放在內部網路,或在前方配置 IAP、service mesh mTLS 等存取控制。

HTTP & GKE

本機或容器可使用 MCP streamable HTTP:

HCMCP_TRANSPORT=http HCMCP_PORT=8000 .venv/bin/hcmcp

curl http://localhost:8000/healthz
# {"status":"ok"}

claude mcp add --transport http hcmcp http://<host>:8000/mcp

HCMCP_TRANSPORT=sse 僅保留給既有部署相容;新網路部署使用 http。預設監聽 0.0.0.0:8000,容器外執行時可用 HCMCP_HOST 收斂綁定位址。

GKE 架構、Workload Identity、CronJob、GCS DB artifact 與 Kubernetes manifests 請見 deploy/README.md。DB 以不可變唯讀 artifact 形式從 GCS 拉進各 pod 的 emptyDir,因此 replica 可自由水平擴展;manifests 依 Pod Security Standards restricted 設定 runAsNonRootallowPrivilegeEscalation: falsecapabilities.drop: ["ALL"] 與 seccomp RuntimeDefault

Development

.venv/bin/python -m pip install -e ".[dev]"
.venv/bin/python -m pytest
.venv/bin/python -m bandit -r src -ll        # SAST,與 CI 同門檻(Medium 以上失敗)
.venv/bin/python -m pip_audit --skip-editable # 依賴弱點掃描

CI(.github/workflows/ci.yml)在 push 與 pull request 跑相同三項:pytest(Python 3.11 / 3.12)、bandit、pip-audit。另有 verify-catalog.yml,僅手動觸發(workflow_dispatch),用於對官方端點實查;刻意未排程,避免對官方來源產生持續性流量。

主要程式分層如下:

src/health_opendata_mcp/
├── catalog.py     資料集目錄:要同步哪些開放資料(含出處與驗證閘門)
├── adapters/      官方來源 adapter 與 HTTP/CSV/PCC parser
├── domain/        query_guard 等純函式安全規則
├── ingestion/     discover → fetch → normalize → upsert pipeline
├── repository/    SQLite schema、物化表與唯讀 query executor
└── mcp_server/    FastMCP tools、transport 與 QueryService

新增資料集是在 catalog.py 加一筆 CatalogEntry(見上方 Dataset catalog),不必改 cli.pycli.pybuild_adapters() 只依目錄中 enabled 的 entry 組裝 adapter,某一種 adapter 沒有啟用項目時就不建立。新增資料來源(新的取得策略)才需要實作 SourceAdapterdiscoverfetchnormalize。標案的資訊勞務主題篩選由 cli.pyIT_INCLUDE / IT_EXCLUDE 關鍵字決定。

行為契約以 Gherkin 記錄在 spec/features/(ingestion、query-rows、query-tools、source-registration、headless-fallback),資料模型見 spec/erm.dbml

Maintenance scripts

Script

Purpose

scripts/enrich_bid_deadline.py

對近期、IT 類、尚未 enrich 且尚未決標的招標公告逐案補截標/開標/預算(限量 --limit + 節流 --throttle,被封鎖即停)

scripts/export_board_data.py

從真實 SQLite 原子匯出 versioned current.json 與預渲染看板摘要;超過 5 MiB 時縮限明細

scripts/verify_dashboard.py

在 Pages 上傳前驗證 snapshot schema、大小、安全 DOM 路徑、連結與 artifact 邊界

scripts/prune_local_db.py

清除超出目前同步範圍的舊資料(預設 dry-run,--apply 才寫入)

scripts/verify_catalog_sources.py

對 catalog 的官方端點實查,輸出 verified_at / verified_note 素材與上游漂移檢查(只讀,不改 catalog.py

enrich_bid_deadline.py 的候選條件為:announcement_type='招標公告'date 在區間內、bid_deadline 為空、標題屬 IT 類,且同 job_number 尚無決標公告。決標與招標是兩筆獨立 record,只看招標那筆看不出案子已結束,因此另行比對決標的 job_number 集合,避免已決標的舊案佔用有限的明細頁請求額度、排擠仍可投標的新案。

Scope & limits

  • 預設同步範圍刻意收斂為衛福部資訊勞務相關標案與健保診所,不是完整的政府採購或醫療資料目錄。

  • catalog.pyenabled=false 的候選資料集尚未對官方端點實查,不會被同步,也不應被視為已支援的資料範圍。

  • get_tender_detail 依賴政府電子採購網即時明細頁;舊案下架、網站維護或限流時,工具可能回傳錯誤,應稍後重試。

  • GitHub Pages 看板是提交時的靜態 snapshot,不等於 MCP/SQLite 即時查詢;自動同步與 last-known-good 發布屬後續 P1。

  • HTTP server 預設沒有 authentication;公開暴露前必須自行配置網路層存取控制。

  • nhi-healthcare-facility 目前在 catalog 中為 enabled=false:adapter 與分類規則已併入且有測試覆蓋,但尚未對官方端點實查,因此不會被同步。啟用方式見下方 Development。

  • 啟用後 nhi-clinicnhi-healthcare-facility 會有資料重疊(例如診所),這是正常行為;兩者各自保留原有用途。

  • nhi-healthcare-facility 僅納入 特約類別 1~3 且 權屬別名稱 在「部立及直轄市立醫院」與「縣市立醫院」,以及 特約類別 4(診所/衛生所)。

  • governing_levelmohw 由衛福部官方名冊與 NHI exact-name mapping 決定;衛生所與其餘市/縣立醫院標為 local_government,私人診所等無可靠主管層級者為 unknown

  • 資料依官方來源更新節奏而變動;repository 只提交經欄位 allowlist、大小門檻與驗證的 Pages snapshot,不提交 SQLite 或原始同步資料。

g0VMCP — 衛福部標案的生命週期與明細加值 MCP,處理招標 → 更正 → 決標狀態與深度標案情報。兩個專案刻意零耦合:本專案提供 Twinkle 相容的扁平列查詢,g0VMCP 提供深度標案資訊;PCC XML parser 以純函式方式 vendored 自 g0VMCP。

opendataCampus-MCP — 教育資源導航 MCP,以 TWCampus 為目錄入口路由至台灣官方教育平台。與本專案同屬「官方開放資料 → 可查詢 MCP 介面」系列,但服務網域為教育資源而非採購/健保。

License

MIT — 資料依政府資料開放授權條款使用。

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    MCP server for TDCC OpenData, enabling natural language query of Taiwan securities custody data, including shareholding distribution, stock custody changes, offshore fund NAVs, and e-voting information.
    8
    2
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables querying MIMIC-IV medical data using natural language through MCP clients, with support for local DuckDB and cloud BigQuery backends.
    29 PyPI
    77
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    An open-source MCP server that aggregates Taiwan public data sources (data.gov.tw, TWSE, MOEA, CWA, etc.) and exposes them through the Model Context Protocol, enabling AI agents to query Taiwan data with a single configuration line.
    1
    Apache 2.0