HumansXドキュメントセンター 公式サイトへ ↗

MCP(Model Context Protocol)統合ガイド

MCP の概要

MCP(Model Context Protocol)は、AI アシスタントと外部ツールやデータソースを接続するためのオープン標準プロトコルです。本プロジェクトでは統一された MCP 統合ソリューションを採用し、すべての MCP 機能を 1 つのクライアントファイルに統合することで、軽量ながら全機能を備えた MCP サポートを実現しています。

️ アーキテクチャ設計

アーキテクチャ概要

┌─────────────────────────────────────────────────────────────┐
│                ASR-LLM-TTS 主系统                            │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────────┐                                        │
│  │ Turn Manager    │ ◄─── 用户输入                           │
│  └─────────┬───────┘                                        │
│            │                                                │
│            ▼                                                │
│  ┌─────────────────┐      ┌─────────────────┐               │
│  │ Function Call   │────► │ MCPClient       │               │
│  │ Registry        │      │ (统一客户端)    │               │
│  └─────────────────┘      └─────────┬───────┘               │
│            │                        │                       │
│            ▼                        ▼                       │
│  ┌─────────────────┐      ┌─────────────────┐               │
│  │ Local Functions │      │ MCP Connections │               │
│  │ (本地工具)      │      │ (远程MCP工具)   │               │
│  └─────────────────┘      └─────────┬───────┘               │
└─────────────────────────────────────┼───────────────────────┘
                                      │
                    ┌─────────────────┼─────────────────┐
                    │                 │                 │
                    ▼                 ▼                 ▼
          ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
          │ MCP Server 1    │ │ MCP Server 2    │ │ MCP Server N    │
          │ (vision-arm)    │ │ (face-tracker)  │ │ (amap-maps)     │
          └─────────────────┘ └─────────────────┘ └─────────────────┘

コアコンポーネント

コンポーネント ファイルパス 役割
MCPClient utils/mcp/client/mcp_client.py すべての機能を統合した統一 MCP クライアント
MCPConnection utils/mcp/client/mcp_client.py 個々の MCP サーバーとの接続管理
MCP Configuration utils/mcp/config/mcp_servers.json MCP サーバーの設定ファイル

MCPClient の詳細

主な特長

  1. 単一ファイルへの統合:すべての MCP 機能を 1 つのファイルに統合
  2. バックグラウンド監視:自動的な接続管理とヘルスチェック
  3. ツールの統一:ローカルツールと MCP ツールを統一インターフェースで扱う
  4. 指数バックオフ:頻繁なリトライを避けるインテリジェントな再接続の仕組み
  5. 非同期優先:完全な非同期実装で、メインサービスをブロックしない

主要クラスの定義

@dataclass
class MCPTool:
    """MCP工具描述"""
    name: str
    parameters: Dict[str, Any]
    description: str
    server_name: str
    active: bool = True

@dataclass
class MCPServerState:
    """MCP服务器状态"""
    name: str
    config: dict
    status: str = "disconnected"  # disconnected, connecting, connected, failed
    last_health_check: Optional[datetime] = None
    retry_count: int = 0
    next_retry_time: Optional[datetime] = None
    tools: List[MCPTool] = None
    error_message: Optional[str] = None

    def should_retry_now(self) -> bool:
        """检查是否应该现在重试连接"""
        if self.status == "connected":
            return False
        if self.next_retry_time is None:
            return True
        return datetime.now() >= self.next_retry_time

接続管理

class MCPConnection:
    """单个MCP连接封装"""

    def __init__(self, name: str, config: dict):
        self.name = name
        self.config = config
        self.session: Optional[mcp.ClientSession] = None
        self.exit_stack = AsyncExitStack()
        self.tools: List[mcp.Tool] = []

    async def health_check(self, timeout: float = 3.0) -> bool:
        """快速健康检查,验证服务器可达性"""
        if "url" not in self.config:
            return True  # stdio连接无法网络检查

        try:
            parsed_url = urlparse(self.config["url"])
            host = parsed_url.hostname
            port = parsed_url.port or (443 if parsed_url.scheme == "https" else 80)

            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout)
            result = sock.connect_ex((host, port))
            sock.close()

            return result == 0
        except Exception:
            return False

バックグラウンド監視の仕組み

async def _background_monitor(self) -> None:
    """后台监控任务"""
    while not self.shutdown_event.is_set():
        try:
            # 尝试连接或重连服务器
            for name, server in self.servers.items():
                if server.should_retry_now():
                    await self._attempt_connection(name, server)

            # 健康检查已连接的服务器
            await self._health_check_all()

            # 等待30秒
            await asyncio.wait_for(self.shutdown_event.wait(), timeout=30.0)

        except asyncio.TimeoutError:
            continue
        except Exception as e:
            logger.error(f"后台监控异常: {e}")
            await asyncio.sleep(10)

設定管理

MCP サーバーの設定

設定ファイル:utils/mcp/config/mcp_servers.json

{
  "mcpServers": {
    "vision-arm-control": {
      "active": true,
      "type": "streamable-http",
      "url": "http://192.168.1.9:8002/mcp/",
      "transport": "streamable_http",
      "timeout": 10,
      "health_check_timeout": 2,
      "sse_read_timeout": 300,
      "terminate_on_close": true,
      "headers": {
        "Accept": "application/json, text/event-stream",
        "Content-Type": "application/json"
      },
      "description": "智能视觉机械臂控制系统MCP服务器"
    },

    "face-tracker": {
      "active": true,
      "type": "streamable-http",
      "url": "http://192.168.1.9:8000/mcp/",
      "transport": "streamable_http",
      "timeout": 10,
      "health_check_timeout": 2,
      "sse_read_timeout": 300,
      "terminate_on_close": true,
      "headers": {
        "Accept": "application/json, text/event-stream",
        "Content-Type": "application/json"
      },
      "description": "智能人脸追踪系统MCP服务器"
    },

    "amap-maps": {
      "active": false,
      "type": "sse",
      "url": "https://mcp.api-inference.modelscope.net/14ffca24064c4d/sse",
      "description": "高德地图MCP服务器(第三方)"
    }
  }
}

対応する接続タイプ

1. Streamable HTTP 接続(推奨)

{
  "server-name": {
    "active": true,
    "type": "streamable-http",
    "url": "http://host:port/mcp/",
    "transport": "streamable_http",
    "timeout": 10,
    "health_check_timeout": 2,
    "sse_read_timeout": 300,
    "terminate_on_close": true,
    "headers": {
      "Accept": "application/json, text/event-stream",
      "Content-Type": "application/json"
    }
  }
}

2. SSE 接続

{
  "server-name": {
    "active": true,
    "type": "sse",
    "url": "https://mcp-server.example.com/sse",
    "transport": "sse",
    "timeout": 5,
    "sse_read_timeout": 300,
    "headers": {
      "Authorization": "Bearer your-token",
      "Accept": "text/event-stream"
    }
  }
}

3. stdio 接続(ローカルプロセス)

{
  "server-name": {
    "active": true,
    "type": "stdio",
    "command": "python",
    "args": ["-m", "mcp_server_module"],
    "cwd": "/path/to/working/directory",
    "env": {
      "API_KEY": "your-api-key"
    },
    "timeout": 30
  }
}

ツール呼び出しの流れ

1. 初期化の流れ

1. MCPClient.initialize()
2. 加载配置文件 mcp_servers.json
3. 创建MCPServerState对象
4. 启动后台监控任务 _background_monitor()
5. 异步连接MCP服务器
6. 获取工具列表并合并schema

2. ツールの検出と登録

async def get_all_tools_schema(self) -> List[Dict[str, Any]]:
    """获取所有工具的schema(本地+MCP)"""
    # 获取本地工具
    local_schemas = []
    if LOCAL_TOOLS_AVAILABLE:
        try:
            local_schemas = get_all_schemas()
        except Exception as e:
            logger.warning(f"无法获取本地工具: {e}")

    # 获取MCP工具
    mcp_schemas = []
    for server in self.servers.values():
        if server.status == "connected":
            for tool in server.tools:
                if tool.active:
                    schema = {
                        "type": "function",
                        "function": {
                            "name": tool.name,
                            "description": tool.description,
                            "parameters": tool.parameters
                        }
                    }
                    mcp_schemas.append(schema)

    logger.info(f"获取工具schema: 本地 {len(local_schemas)} 个, MCP {len(mcp_schemas)} 个")
    return local_schemas + mcp_schemas

3. ツール呼び出しの実行

async def call_tool(self, tool_name: str, **arguments) -> Any:
    """调用工具(自动判断本地或MCP)"""
    # 先检查是否为MCP工具
    for server in self.servers.values():
        if server.status == "connected":
            for tool in server.tools:
                if tool.name == tool_name and tool.active:
                    conn = self.connections.get(server.name)
                    if conn:
                        return await conn.call_tool(tool_name, arguments)

    # 尝试本地工具
    if LOCAL_TOOLS_AVAILABLE:
        try:
            return await FunctionRegistry.call(tool_name, **arguments)
        except Exception as e:
            logger.warning(f"本地工具调用失败: {e}")

    raise ValueError(f"工具 '{tool_name}' 不存在或不可用")

接続状態の管理

接続ステートマシン

再接続戦略

[disconnected] → [connecting] → [connected]
       ↑              ↓              ↓
       └── [failed] ←─────────────────┘
              ↓
         (指数退避等待)
              ↓
         [disconnected]
async def _attempt_connection(self, name: str, server: MCPServerState) -> None:
    """尝试连接服务器"""
    try:
        server.status = "connecting"
        server.retry_count += 1

        logger.info(f"尝试连接MCP服务器: {name} (第{server.retry_count}次)")

        # 建立新连接
        conn = MCPConnection(name, server.config)
        await conn.connect()

        # 连接成功
        self.connections[name] = conn
        server.status = "connected"
        server.retry_count = 0
        server.next_retry_time = None

        logger.info(f"MCP服务器 {name} 连接成功,工具: {[t.name for t in server.tools]}")

    except Exception as e:
        # 连接失败,计算下次重试时间(指数退避)
        server.status = "failed"
        server.error_message = str(e)

        delay = min(300, 10 * (2 ** min(server.retry_count - 1, 5)))
        delay *= random.uniform(0.5, 1.5)  # 添加抖动
        server.next_retry_time = datetime.now() + timedelta(seconds=delay)

        logger.warning(f"MCP服务器 {name} 连接失败: {e}, 下次重试: {server.next_retry_time.strftime('%H:%M:%S')}")

ステータス監視

サーバーステータスの照会

def get_server_status(self) -> Dict[str, Any]:
    """获取服务器状态摘要"""
    connected_count = sum(1 for s in self.servers.values() if s.status == "connected")
    total_tools = sum(len(s.tools) for s in self.servers.values() if s.status == "connected")

    servers_info = []
    for server in self.servers.values():
        info = {
            "name": server.name,
            "status": server.status,
            "tools_count": len(server.tools),
            "last_health_check": server.last_health_check.isoformat() if server.last_health_check else None,
            "error_message": server.error_message
        }
        if server.next_retry_time:
            info["next_retry_time"] = server.next_retry_time.isoformat()
        servers_info.append(info)

    return {
        "mcp_available": MCP_AVAILABLE,
        "total_servers": len(self.servers),
        "connected_servers": connected_count,
        "total_tools": total_tools,
        "servers": servers_info
    }

グローバル API

便利な関数の使用

# 获取全局MCP客户端实例
async def get_mcp_client() -> MCPClient:
    """获取全局MCP客户端实例"""
    global _mcp_client
    if _mcp_client is None:
        _mcp_client = MCPClient()
        await _mcp_client.initialize()
    return _mcp_client

# 获取增强的工具schema
async def get_enhanced_function_schemas() -> List[Dict[str, Any]]:
    """获取增强的函数schema列表"""
    client = await get_mcp_client()
    return client.get_all_tools_schema()

# 增强的工具调用
async def enhanced_function_call(tool_name: str, **kwargs) -> Any:
    """增强的函数调用"""
    client = await get_mcp_client()
    return await client.call_tool(tool_name, **kwargs)

# 清理全局客户端
async def cleanup_mcp_client() -> None:
    """清理全局MCP客户端"""
    global _mcp_client
    if _mcp_client:
        await _mcp_client.cleanup()
        _mcp_client = None

開発ガイド

新しい MCP サーバーの追加

  1. 設定ファイルを更新します

  2. サービスを再起動します

{
  "mcpServers": {
    "my-custom-server": {
      "active": true,
      "type": "streamable-http",
      "url": "http://localhost:9000/mcp/",
      "transport": "streamable_http",
      "timeout": 10,
      "description": "我的自定义MCP服务器"
    }
  }
}
  1. MCP クライアントが新しいサーバーを自動検出して接続します

接続の確認

  • ログを確認し、接続状態とツールの読み込み状況を確認します

MCP 接続のデバッグ

️

# 获取MCP状态
import asyncio
from utils.mcp.client.mcp_client import get_mcp_client

async def debug_mcp():
    client = await get_mcp_client()
    status = client.get_server_status()

    print(f"MCP可用: {status['mcp_available']}")
    print(f"服务器总数: {status['total_servers']}")
    print(f"已连接: {status['connected_servers']}")
    print(f"工具总数: {status['total_tools']}")

    for server in status['servers']:
        print(f"\n服务器: {server['name']}")
        print(f"  状态: {server['status']}")
        print(f"  工具数: {server['tools_count']}")
        if server.get('error_message'):
            print(f"  错误: {server['error_message']}")

# 运行调试
asyncio.run(debug_mcp())

セキュリティ上の考慮事項

1. 接続のセキュリティ

  • HTTPS/TLS による暗号化接続に対応

  • API キーおよびトークンによる認証

  • ホワイトリストにより接続可能なサーバーを制限

2. ツール呼び出しの制限

  • パラメーターの検証と型チェック

  • 実行タイムアウトの制御(デフォルト 60 秒)

  • リソース使用量の監視

3. エラー処理

  • 機密情報のフィルタリング

  • 接続の異常がメインサービスに影響しない

  • 詳細なエラーログの記録

⚡ パフォーマンスの最適化

1. 接続管理

  • コネクションプールの再利用により頻繁な接続を回避

  • ヘルスチェックの仕組みで接続の問題を早期に発見

  • 指数バックオフによる再接続で無駄な試行を削減

2. 非同期処理

  • すべての操作を完全に非同期化

  • バックグラウンドタスクがメインプロセスをブロックしない

  • 複数の MCP ツールを同時に呼び出し可能

3. リソースのクリーンアップ

  • 切断された接続を自動的にクリーンアップ

  • メモリリソースを適時解放

  • サービスを適切に終了する処理

この MCP 統合ソリューションは、機能の完全性を保ちながら実装の複雑さを大幅に低減し、ASR-LLM-TTS システムに強力で信頼性の高いリモートツール呼び出し機能を提供します。