Skip to main content
Glama
ianchu32123

事件知識圖譜 MCP Server

by ianchu32123
README.md
# 事件知識圖譜 MCP Server(Incident Knowledge Graph)

以 **MCP(stdio)** 對外提供的、以事件(incident)為中心的知識圖譜,可供任何相容
MCP 的 client 使用(本專案以 **Codex CLI** 與 **OpenCode** 為目標)。它是整個
SRE × MCP 架構中「可寫入、可完整測試」的核心:負責保存那些 beever-atlas MCP 介面
本身無法保存的觀測訊號與 **Atlas 永久座標**,讓上層 agent 在查詢時能把事件與對話
內容關聯起來。

> 設計原則:**不把 Atlas 內容複製進來。** 我們只存**指標(pointer)**
> (`channel_id` + 永久 `message_id` + `url`),避免產生重複的真實來源。

本 repo **只**實作 KG server(見 spec 第 0 節)。真實的 Atlas / Prometheus / DB2
client、signature 的內容(門檻)、JSON 以外的儲存後端、MCP gateway 等,皆為整合
擴充點(spec 第 10 節),不在此實作。

## 文件導覽

| 文件 | 內容 |
| ---- | ---- |
| [SPEC_knowledge_graph_mcp.md](SPEC_knowledge_graph_mcp.md) | **需求規格**:資料模型、工具規格、驗證規則、驗收標準 |
| [NEXT_STEPS.md](NEXT_STEPS.md) | 安裝說明、後續待確認事項、注意事項 |
| [skills/mmdb-troubleshooting/SKILL.md](skills/mmdb-troubleshooting/SKILL.md) | MMDB activelog / busy-ratio 排障手冊(規則) |
| 本檔 README | 安裝、設定、工具清單、專案結構 |

## 環境需求

- Python 3.11+

## 安裝

```bash
# 於專案根目錄
python -m venv .venv
# Windows PowerShell:  .venv\Scripts\Activate.ps1
# macOS/Linux:         source .venv/bin/activate
pip install -e ".[dev]"
```

## 設定

所有設定皆由環境變數驅動(見 `.env.example`),程式碼與 repo 內不寫死任何真實環境資訊。

| 變數           | 預設值               | 意義                     |
| -------------- | -------------------- | ------------------------ |
| `KG_DATA_PATH` | `./data/graph.json`  | JSON 圖譜的持久化路徑    |

資料檔於首次寫入時建立,採用原子寫入(temp 檔 + rename),寫入中途崩潰也不會毀損
既有資料。

## 執行

```bash
kg-mcp-server          # console 進入點(以 stdio 提供服務)
# 或
python -m kg_server.server
```

此 server 走 stdio,設計上由 MCP host 啟動,而非互動式使用。

## 註冊到 MCP client

本 server 走標準 MCP stdio 協定,任何相容 client 都能啟動它。建議直接指定 venv 的
Python(當 `kg-mcp-server` 不在 `PATH` 時最穩)。

### Codex CLI

加入 `~/.codex/config.toml`:

```toml
[mcp_servers.incident-knowledge-graph]
command = "C:\\path\\to\\tsmc\\.venv\\Scripts\\python.exe"
args = ["-m", "kg_server.server"]
env = { KG_DATA_PATH = "C:\\path\\to\\tsmc\\data\\graph.json" }
```

(若 `kg-mcp-server` 已在 `PATH`,也可用 `command = "kg-mcp-server"`、`args = []`。)

### OpenCode

加入 OpenCode 設定檔(專案根目錄的 `opencode.json`,或全域的
`~/.config/opencode/opencode.json`):

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "incident-knowledge-graph": {
      "type": "local",
      "command": [
        "C:\\path\\to\\tsmc\\.venv\\Scripts\\python.exe",
        "-m",
        "kg_server.server"
      ],
      "environment": { "KG_DATA_PATH": "C:\\path\\to\\tsmc\\data\\graph.json" },
      "enabled": true
    }
  }
}
```

重啟 client 後,KG 工具就會出現在其 MCP 工具清單中。

## 多 MCP server 協作(方案 A)

本 server 是 MCP **server**,不會主動去呼叫別的 server。要整合 Grafana / MariaDB
等資料源,標準做法是**在 client 層並列掛載多個 server**,由 agent 自己編排跨源查詢:

```
Codex CLI / OpenCode(agent = MCP client)
   ├── incident-knowledge-graph(本專案)  寫入事件、查因果鏈、存 Teams 座標
   ├── grafana(官方 mcp-grafana)         跑 PromQL、查 Loki logs、dashboard
   └── mariadb(官方 MariaDB/mcp)         唯讀 SQL、查 schema
```

**分工原則:讀在別人,寫在我們。** Grafana / MariaDB 負責讀取現場資料;本 server 負責
把「判讀後的結論」沉澱成可累積、可回溯的知識圖譜。

可直接套用的設定範例:

- Codex CLI → [`examples/codex-config.toml`](examples/codex-config.toml)
- OpenCode → [`examples/opencode.json`](examples/opencode.json)

外部 server 安裝方式:

| Server | 安裝 | 需要的認證 |
| ------ | ---- | ---------- |
| [mcp-grafana](https://github.com/grafana/mcp-grafana) | `go install github.com/grafana/mcp-grafana/cmd/mcp-grafana@latest` 或 `docker pull grafana/mcp-grafana` | `GRAFANA_URL` + service account token |
| [MariaDB/mcp](https://github.com/mariadb/mcp) | 見其 README | 連線資訊(**請用唯讀帳號**) |

### 典型的跨 server 排障流程

agent 在一次對話中會這樣串接(括號內是它呼叫的 server):

1. 「正式機昨天下午怪怪的」→ `resolve_alias("正式機")` **(KG)**
   → 得知 canonical 是 `MMDB`、Prometheus instance 是 `mmdb-prod-01:9104`
2. `query_prometheus("mmdb_busy_ratio{...}")` **(Grafana)** → 撈出指標曲線
3. 判讀出異常區段 → `create_incident(system="MMDB", slug_hint="busy_ratio", ...)` **(KG)**
   → 自動建立 `AFFECTS` 關係
4. `record_signature_match(...)` **(KG)** → 掛上 `mmdb.busy_ratio.sustained`
5. `incident_time_window(...)` **(KG)** → 取得時間窗
6. 用該時間窗查 Teams/Atlas → `attach_atlas_reference(...)` **(KG)** 存永久座標
7. 需要細看資料庫狀態時 → 唯讀 SQL **(MariaDB)** → 結論寫回 `add_observation(...)` **(KG)**

下次再遇到類似狀況,`find_incidents` / `trace_incident_chain` 就能直接調出歷史情境,
不必再人工翻聊天紀錄。

## 工具(Tools)

寫入類:

- `upsert_system(name, aliases=[], labels={})` — 建立/合併 System(合併、去重)。
- `create_incident(system, occurred_at, ...)` — 建立事件,自動建立 `AFFECTS` 關係。
- `add_observation(entity_name, observation)` — 對實體追加自由備註。
- `link(from_entity, to_entity, relation_type)` — 建立關係,強制型別紀律。
- `record_signature_match(incident_id, signature_name)` — 建立/連結 Signature(`MATCHES`)。
- `attach_atlas_reference(incident_id, channel_id, message_id, url, fact_id=None)` — 附上 Atlas 永久座標。

查詢類:

- `get_incident(incident_id)` — 事件完整視圖 + 關係。
- `find_incidents(system=, signature=, severity=, since=, until=)` — 條件篩選摘要。
- `resolve_alias(term)` — canonical / alias / label → System。
- `incident_time_window(incident_id, pad_minutes=15)` — `{start_utc, end_utc}`。
- `trace_incident_chain(incident_id, direction="downstream")` — 沿 `CAUSED` 追鏈。

## 重要慣例

- **時間戳**一律為 ISO-8601 **UTC**(例:`2026-06-12T03:14:00Z`)。無時區或非 UTC
  的值會被拒絕——請先轉成 UTC 再送。這是對齊 Prometheus(UTC)與 Teams(本地時間)
  的前提。
- **incident_id** 格式為 `INC-YYYY-MM-DD-<slug>`;未指定時由 server 依 system slug
  自動產生。
- **System 的 canonical 名稱**採用團隊口語慣用的短名(以便與 Atlas 實體對齊);精確的
  ops 標籤(如 `prometheus_instance`)放進 `labels`。
- **Atlas 永久鍵**是 `message_id`;`fact_id` 僅為快取(Atlas 重新同步後可能改變)。

## 關係契約(強制)

| RelationType | from        | to          |
| ------------ | ----------- | ----------- |
| AFFECTS      | Incident    | System      |
| MATCHES      | Incident    | Signature   |
| BASED_ON     | Signature   | Metric      |
| HANDLED_BY   | Incident    | Person      |
| RESOLVED_BY  | Incident    | Remediation |
| CAUSED       | Incident    | Incident    |

連結端點型別錯誤時,會回傳明確錯誤。

## 測試

```bash
pytest
```

測試全程離線(mock Atlas client + 暫存檔儲存),不依賴任何外部服務或網路。

## 專案結構

```
src/kg_server/
  models.py       # pydantic 模型 + enums + 關係契約
  storage.py      # Storage protocol + JsonStorage(原子寫入) + MemoryStorage
  validation.py   # server 端驗證規則 + 型別化錯誤
  graph.py        # entity/relation CRUD、查詢、alias、追鏈、Atlas 配對
  atlas.py        # AtlasClient protocol + MockAtlasClient
  tools.py        # MCP 工具定義(薄封裝、結構化錯誤)
  server.py       # FastMCP 組裝 + stdio 進入點
  sources/        # 監控 / 資料庫來源整合(擴充點)
    prometheus.py # 功能完整:偵測門檻突破 -> 自動 create_incident
    db2.py        # 骨架 + mock:DB2 insight 報告增補
    mariadb.py    # 骨架 + mock:MariaDB(MMDB)狀態增補
skills/
  mmdb-troubleshooting/SKILL.md   # MMDB activelog / busy-ratio 排障手冊(規則)
tests/            # pytest 測試 + 合成 fixtures
```

## 來源整合(`sources/`)

每個來源都比照 `AtlasClient` 的樣式——一個 `Protocol` 介面加上離線的 `Mock*`——
讓偵測/增補流程無需真實基礎設施即可完整測試。換成真實 client 時,圖譜與工具邏輯
完全不需更動。

- **`prometheus.py`(功能完整):** `record_threshold_anomalies(graph, prom,
  system, rule, start, end)` 會查詢序列、偵測「持續超過門檻」的突破(`ThresholdRule`),
  並自動建立事件 + signature 連結。具冪等性(同一 system+start 不會重複建立)。這就是
  目標 (1)+(2) 的示範:一個指標來源自動餵入圖譜。
- **`db2.py` / `mariadb.py`(骨架):** `enrich_incident_with_*` 把報告 / 狀態快照
  併入既有事件(合併 metrics、findings 轉為 observation)。報告格式為待與團隊確認的
  起始形狀。

上述流程引用的 MMDB signatures(如 `mmdb.busy_ratio.sustained`)記錄於
[`skills/mmdb-troubleshooting/SKILL.md`](skills/mmdb-troubleshooting/SKILL.md)
——規則歸 Skill,事件實例歸圖譜。

## 擴充點(仍待處理)

- 真實 `AtlasClient`,由 Atlas MCP 支撐(接回 Teams 對話)。
- 真實 `PrometheusClient` / `Db2InsightClient` / `MariaDbClient`(替換 mock)。
- 截圖辨識(vision/OCR)——屬於上層另一層處理。
- `Signature` 門檻——與 ops 確認 SKILL.md 中的 placeholder 數值。
- TKMSAPI 研究(spec 凍結,待其文件釋出)。
- 額外 `Storage` 後端(`SqliteStorage`、`Neo4jStorage`)——protocol 已抽象化,工具
  邏輯不變。