Files
awoooi/docs/adr/ADR-015-mcp-modular-architecture.md
OG T 8a163609bf docs(adr): 更新 ADR-006/009/015 狀態
ADR-015: 標記為「已實作」 (Phase 16 R1 完成)
ADR-009: 標記為「已實作」 (Phase 9.1-9.5 全部完成)
ADR-006: 新增智能路由整合章節 (Phase 13.3)

首席架構師 ADR 審計 P0/P1 完成

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 10:45:29 +08:00

4.1 KiB
Raw Blame History

ADR-015: MCP 模組化架構重構

欄位
狀態 已實作
決策日期 2026-03-26
實作完成 2026-03-26 (Phase 16 R1)
決策者 統帥 + 首席架構師
觸發原因 Code Review 發現嚴重模組化違規

背景

Phase 13.2 實作 MCP Tool 整合時,為了快速交付,在 mcp_bridge.py 直接 import services 層,違反了 leWOOOgo 積木化原則。

違規清單

位置 違規
mcp_bridge.py:570 from src.services.executor import get_executor
mcp_bridge.py:655 from src.services.signoz_client import get_signoz_client
mcp_bridge.py:734 from src.services.approval_db import ...
mcp_bridge.py:738 from src.services.incident_service import get_incident_service

違反的鐵律

  1. Interface 先行 - 無 ABC 定義
  2. 禁止跨模組非法引用 - plugins → services 直接 import
  3. 模組間透過 Public API 溝通 - 直接呼叫具體實作

決策

架構重構

apps/api/src/plugins/mcp/
├── __init__.py
├── interfaces.py          # MCPToolProvider ABC (新增)
├── mcp_bridge.py          # 透過 DI 注入 providers (重構)
├── registry.py            # Provider 註冊中心 (新增)
└── providers/             # 具體實作 (新增)
    ├── __init__.py
    ├── k8s_provider.py
    ├── signoz_provider.py
    └── database_provider.py

Interface 定義

from abc import ABC, abstractmethod
from typing import Any

class MCPToolProvider(ABC):
    """MCP Tool Provider 抽象介面"""

    @property
    @abstractmethod
    def name(self) -> str:
        """Provider 名稱 (如 'kubernetes', 'signoz')"""
        pass

    @abstractmethod
    async def list_tools(self) -> list[dict]:
        """列出可用工具"""
        pass

    @abstractmethod
    async def execute(self, tool_name: str, parameters: dict) -> Any:
        """執行工具"""
        pass

DI 注入模式

# registry.py
class ProviderRegistry:
    _providers: dict[str, MCPToolProvider] = {}

    @classmethod
    def register(cls, provider: MCPToolProvider) -> None:
        cls._providers[provider.name] = provider

    @classmethod
    def get(cls, name: str) -> MCPToolProvider | None:
        return cls._providers.get(name)

# mcp_bridge.py (重構後)
async def call_tool(self, server_name: str, tool_name: str, parameters: dict):
    provider = ProviderRegistry.get(server_name)
    if not provider:
        raise ValueError(f"Unknown provider: {server_name}")
    return await provider.execute(tool_name, parameters)

優點

  1. 符合 leWOOOgo 積木化 - Interface 先行DI 注入
  2. 可測試性 - 可輕鬆 Mock Provider
  3. 可擴展性 - 新增 Provider 無需修改 mcp_bridge.py
  4. 單一職責 - 每個 Provider 只負責一個領域

缺點

  1. 重構工時 - 需要 2-3 天
  2. 檔案數增加 - 從 1 個變 6+ 個
  3. 學習曲線 - 新成員需了解 DI 模式

實作計畫

步驟 內容 工時
1 建立 interfaces.py 30min
2 建立 registry.py 30min
3 建立 providers/ 目錄 15min
4 實作 k8s_provider.py 1h
5 實作 signoz_provider.py 1h
6 實作 database_provider.py 1h
7 重構 mcp_bridge.py 2h
8 更新測試 1h
9 Code Review 1h
總計 8-9h

實作驗收 (2026-03-26)

項目 狀態
interfaces.py 已建立 MCPToolProvider ABC
registry.py 已建立 ProviderRegistry
k8s_provider.py 已實作
signoz_provider.py 已實作
database_provider.py 已實作
mcp_bridge.py 重構 透過 DI 注入
測試驗證 28/28 通過

驗收人: 首席架構師 (Phase 16 R1.3 審查)


相關文件