feat: 重构 MCP 配置管理,移除旧配置文件,更新为使用全局配置
This commit is contained in:
@@ -4,9 +4,10 @@ MCP (Model Context Protocol) 客户端包。
|
||||
提供 MCPManager 用于管理 MCP 服务器连接、发现工具、调用工具。
|
||||
|
||||
用法:
|
||||
from src.config.config import global_config
|
||||
from .manager import MCPManager
|
||||
|
||||
manager = await MCPManager.from_config("config/mcp_config.json")
|
||||
manager = await MCPManager.from_app_config(global_config.mcp)
|
||||
if manager:
|
||||
tools = manager.get_openai_tools() # 获取 OpenAI 格式工具列表
|
||||
result = await manager.call_tool(name, args) # 调用工具
|
||||
|
||||
@@ -1,56 +1,36 @@
|
||||
"""
|
||||
MCP 配置加载与验证。
|
||||
从 config/mcp_config.json 读取 MCP 服务器定义,解析为结构化配置对象。
|
||||
"""MCP 运行时配置转换。
|
||||
|
||||
配置格式示例:
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "C:/Users"],
|
||||
"env": {}
|
||||
},
|
||||
"remote-api": {
|
||||
"url": "http://localhost:8080/sse",
|
||||
"headers": {"Authorization": "Bearer xxx"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- command + args: Stdio 传输(启动子进程)
|
||||
- url: SSE 传输(连接远程服务器)
|
||||
负责将主程序官方配置中的 MCP 配置转换为运行时使用的结构化对象。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import json
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from src.cli.console import console
|
||||
if TYPE_CHECKING:
|
||||
from src.config.official_configs import MCPConfig
|
||||
|
||||
|
||||
DEFAULT_MCP_CONFIG_PATH = Path(__file__).resolve().parents[2] / "config" / "mcp_config.json"
|
||||
|
||||
|
||||
@dataclass
|
||||
class MCPServerConfig:
|
||||
"""单个 MCP 服务器配置。"""
|
||||
@dataclass(slots=True)
|
||||
class MCPServerRuntimeConfig:
|
||||
"""单个 MCP 服务器的运行时配置。"""
|
||||
|
||||
name: str
|
||||
|
||||
# ── Stdio 传输 ──
|
||||
command: Optional[str] = None
|
||||
command: str = ""
|
||||
args: list[str] = field(default_factory=list)
|
||||
env: Optional[dict[str, str]] = None
|
||||
|
||||
# ── SSE 传输 ──
|
||||
url: Optional[str] = None
|
||||
env: dict[str, str] = field(default_factory=dict)
|
||||
url: str = ""
|
||||
headers: dict[str, str] = field(default_factory=dict)
|
||||
|
||||
@property
|
||||
def transport_type(self) -> str:
|
||||
"""返回传输类型: 'stdio' / 'sse' / 'unknown'。"""
|
||||
"""返回当前服务器的传输类型。
|
||||
|
||||
Returns:
|
||||
str: ``stdio``、``sse`` 或 ``unknown``。
|
||||
"""
|
||||
|
||||
if self.command:
|
||||
return "stdio"
|
||||
if self.url:
|
||||
@@ -58,50 +38,33 @@ class MCPServerConfig:
|
||||
return "unknown"
|
||||
|
||||
|
||||
def load_mcp_config(config_path: str = str(DEFAULT_MCP_CONFIG_PATH)) -> list[MCPServerConfig]:
|
||||
"""
|
||||
从配置文件加载 MCP 服务器列表。
|
||||
def build_mcp_server_runtime_configs(mcp_config: "MCPConfig") -> list[MCPServerRuntimeConfig]:
|
||||
"""将官方 MCP 配置转换为运行时配置列表。
|
||||
|
||||
Args:
|
||||
config_path: 配置文件路径
|
||||
mcp_config: 主程序中的 MCP 官方配置对象。
|
||||
|
||||
Returns:
|
||||
解析后的 MCPServerConfig 列表;文件不存在或为空时返回空列表。
|
||||
list[MCPServerRuntimeConfig]: 启用且配置完整的 MCP 服务器列表。
|
||||
"""
|
||||
if not os.path.isfile(config_path):
|
||||
|
||||
if not mcp_config.enable:
|
||||
return []
|
||||
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except (json.JSONDecodeError, OSError) as e:
|
||||
console.print(f"[warning]⚠️ 读取 MCP 配置失败: {e}[/warning]")
|
||||
return []
|
||||
|
||||
mcp_servers = data.get("mcpServers", {})
|
||||
if not isinstance(mcp_servers, dict):
|
||||
console.print("[warning]⚠️ MCP 配置中的 mcpServers 格式无效[/warning]")
|
||||
return []
|
||||
|
||||
configs: list[MCPServerConfig] = []
|
||||
for name, cfg in mcp_servers.items():
|
||||
if not isinstance(cfg, dict):
|
||||
console.print(f"[warning]⚠️ MCP 服务器 '{name}' 配置格式无效,已跳过[/warning]")
|
||||
runtime_configs: list[MCPServerRuntimeConfig] = []
|
||||
for server in mcp_config.servers:
|
||||
if not server.enabled:
|
||||
continue
|
||||
|
||||
server = MCPServerConfig(
|
||||
name=name,
|
||||
command=cfg.get("command"),
|
||||
args=cfg.get("args", []),
|
||||
env=cfg.get("env"),
|
||||
url=cfg.get("url"),
|
||||
headers=cfg.get("headers", {}),
|
||||
runtime_configs.append(
|
||||
MCPServerRuntimeConfig(
|
||||
name=server.name.strip(),
|
||||
command=server.command.strip(),
|
||||
args=[str(argument) for argument in server.args],
|
||||
env={str(key): str(value) for key, value in server.env.items()},
|
||||
url=server.url.strip(),
|
||||
headers={str(key): str(value) for key, value in server.headers.items()},
|
||||
)
|
||||
)
|
||||
|
||||
if server.transport_type == "unknown":
|
||||
console.print(f"[warning]⚠️ MCP 服务器 '{name}' 缺少 command 或 url,已跳过[/warning]")
|
||||
continue
|
||||
|
||||
configs.append(server)
|
||||
|
||||
return configs
|
||||
return runtime_configs
|
||||
|
||||
@@ -10,12 +10,12 @@ from src.core.tooling import ToolExecutionResult
|
||||
|
||||
from src.cli.console import console
|
||||
|
||||
from .config import MCPServerConfig
|
||||
from .config import MCPServerRuntimeConfig
|
||||
|
||||
# ──────────────────── MCP SDK 可选导入 ────────────────────
|
||||
#
|
||||
# mcp 是可选依赖。如果未安装,MCP_AVAILABLE = False,
|
||||
# MCPManager.from_config() 会检测到并返回 None,不影响主程序运行。
|
||||
# MCPManager.from_app_config() 会检测到并返回 None,不影响主程序运行。
|
||||
|
||||
try:
|
||||
from mcp import ClientSession
|
||||
@@ -44,15 +44,20 @@ except ImportError:
|
||||
|
||||
|
||||
class MCPConnection:
|
||||
"""
|
||||
管理单个 MCP 服务器的连接生命周期。
|
||||
"""管理单个 MCP 服务器的连接生命周期。
|
||||
|
||||
支持两种传输方式:
|
||||
- Stdio: 启动子进程,通过 stdin/stdout 通信
|
||||
- SSE: 连接远程 HTTP SSE 端点
|
||||
"""
|
||||
|
||||
def __init__(self, config: MCPServerConfig):
|
||||
def __init__(self, config: MCPServerRuntimeConfig) -> None:
|
||||
"""初始化单个 MCP 连接。
|
||||
|
||||
Args:
|
||||
config: 当前服务器的运行时配置。
|
||||
"""
|
||||
|
||||
self.config = config
|
||||
self.session: Optional[Any] = None # mcp.ClientSession
|
||||
self.tools: list = [] # mcp Tool objects
|
||||
|
||||
@@ -3,7 +3,7 @@ MaiSaka - MCP 管理器
|
||||
管理所有 MCP 服务器连接,提供统一的工具发现与调用接口。
|
||||
"""
|
||||
|
||||
from typing import Any, Optional
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
from src.cli.console import console
|
||||
from src.core.tooling import (
|
||||
@@ -13,9 +13,12 @@ from src.core.tooling import (
|
||||
build_tool_detailed_description,
|
||||
)
|
||||
|
||||
from .config import DEFAULT_MCP_CONFIG_PATH, MCPServerConfig, load_mcp_config
|
||||
from .config import MCPServerRuntimeConfig, build_mcp_server_runtime_configs
|
||||
from .connection import MCPConnection, MCP_AVAILABLE
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.config.official_configs import MCPConfig
|
||||
|
||||
# 内置工具名称集合 —— MCP 工具不允许与这些名称冲突
|
||||
BUILTIN_TOOL_NAMES = frozenset(
|
||||
{
|
||||
@@ -31,37 +34,38 @@ BUILTIN_TOOL_NAMES = frozenset(
|
||||
|
||||
|
||||
class MCPManager:
|
||||
"""
|
||||
MCP 服务器连接管理器。
|
||||
"""MCP 服务器连接管理器。
|
||||
|
||||
职责:
|
||||
- 根据配置文件连接所有 MCP 服务器
|
||||
- 根据主程序官方配置连接所有 MCP 服务器
|
||||
- 将 MCP 工具转换为 OpenAI function calling 格式
|
||||
- 路由工具调用到正确的 MCP 服务器
|
||||
- 统一管理连接生命周期
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
"""初始化 MCP 管理器。"""
|
||||
|
||||
self._connections: dict[str, MCPConnection] = {} # server_name → connection
|
||||
self._tool_to_server: dict[str, str] = {} # tool_name → server_name
|
||||
|
||||
# ──────── 工厂方法 ────────
|
||||
|
||||
@classmethod
|
||||
async def from_config(
|
||||
async def from_app_config(
|
||||
cls,
|
||||
config_path: str = str(DEFAULT_MCP_CONFIG_PATH),
|
||||
mcp_config: "MCPConfig",
|
||||
) -> Optional["MCPManager"]:
|
||||
"""
|
||||
从配置文件创建并初始化 MCPManager。
|
||||
从官方配置创建并初始化 MCPManager。
|
||||
|
||||
Args:
|
||||
config_path: mcp_config.json 文件路径
|
||||
mcp_config: 主程序中的 MCP 配置对象。
|
||||
|
||||
Returns:
|
||||
初始化完成的 MCPManager;无配置或全部连接失败时返回 None。
|
||||
初始化完成的 MCPManager;无可用配置或全部连接失败时返回 None。
|
||||
"""
|
||||
configs = load_mcp_config(config_path)
|
||||
configs = build_mcp_server_runtime_configs(mcp_config)
|
||||
if not configs:
|
||||
return None
|
||||
|
||||
@@ -80,7 +84,7 @@ class MCPManager:
|
||||
|
||||
# ──────── 连接管理 ────────
|
||||
|
||||
async def _connect_all(self, configs: list[MCPServerConfig]) -> None:
|
||||
async def _connect_all(self, configs: list[MCPServerRuntimeConfig]) -> None:
|
||||
"""连接所有配置的 MCP 服务器,跳过失败的连接。"""
|
||||
for cfg in configs:
|
||||
conn = MCPConnection(cfg)
|
||||
|
||||
Reference in New Issue
Block a user