MCP探究和聚合节点设计

ChainPray 发布于 10 天前 112 次阅读 2418 字 预计阅读时间: 11 分钟


本文旨在分析MCP协议原理,探究目前常见的MCP实现并且针对现有问题提出MCP聚合节点的解决方案

MCP引入

MCP是做什么的?简单来说就是一套协议,用来规范工具的使用说明;基于MCP协议实现的server相当于是在LLM和实际工具(浏览器,命令行等)之间的中间件,工具厂商只要实现MCP server,LLM厂商只需要在api中加入MCP负载入口,LLM不用在针对每个工具都要单独适配

系统架构

MCP有三个核心概念,主机(host),客户端(Client)和服务器(Server),下面是官网对于这三者的解释

  • MCP 主机:希望通过 MCP 访问数据的 Claude Desktop、IDE 或 AI 工具等程序
  • MCP 客户端:与服务器保持 1:1 连接的协议客户端
  • MCP 服务器:轻量级程序,每个程序都通过标准化的模型上下文协议公开特定功能

下面这张来自lilianweng博客的图片能很好的理解MCP server和MCP client所做的工作

Overview of a LLM-powered autonomous agent system.1

MCP Server在上图中充当一个提供工具(Tools)的server,左侧这些工具函数都是在MCP server中,而MCP Client则是接受LLM输出的工具调用(fuction call或者tool call)并向MCP server请求调用相应的工具,相当于图上的Action节点

交互流程

下面是MCP协议下Host向LLMs发起一次对话的标准流程

  1. 客户端从服务器获取可用工具的列表
  2. 用户的查询将与工具描述一起发送给 LLMs
  3. LLMs 决定使用哪些工具(如果有)
  4. 客户端通过服务器执行任何请求的工具调用
  5. 结果被寄回给 LLMs
  6. LLMs 提供自然语言响应
  7. 显示响应

注意第二步需要LLM的API有基于MCP协议的tools挂载点,例如下面官方示例代码中claude的api提供了名为mcp的key来传入tools2

"""Process a query using Claude and available tools"""
    messages = [
        {
            "role": "user",
            "content": query
        }
    ]

    response = await self.session.list_tools()
    available_tools = [{
        "name": tool.name,
        "description": tool.description,
        "input_schema": tool.inputSchema
    } for tool in response.tools]

    # Initial Claude API call
    response = self.anthropic.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1000,
        messages=messages,
        tools=available_tools
    )

连接生命周期

1. 初始化

官方架构中是由Client主动发起初始化请求,并由服务器响应请求,来实现协议握手

官方初始化流程
  1. Client 发送包含协议版本和能力的 initialize 请求
  2. Server 以其协议版本和能力响应
  3. Client 发送 initialized 通知作为确认
  4. 开始正常消息交换

2. 消息交换

初始化后,支持以下模式:

  • 请求-响应:客户端或服务器发送请求,另一方响应
  • 通知:任一方发送单向消息

3. 终止

任一方可以终止连接:

  • 通过 close() 进行干净关闭
  • 传输断开
  • 错误条件

传输层

传输层负责在client和server之间传输数据包,其使用 JSON-RPC 2.0 作为其传输格式,详细内容请到官网查看传输 - 模型上下文协议

这里笔者主要说一下传输层的实现,官方的SDK自带两个实现

  • stdio:标准输入输出,也就是命令行的stdin和stdout
  • Streamable HTTP:基于Http post,遵循请求-响应架构
  • 自实现传输层,比如小智AI使用websocket在后台API和本地硬件上的server之间交换MCP包,实现云端client对本地server的访问

注意官方在2025.6.18的更新中删除了以前的sse传输模式,并用Streamable HTTP取代

局限性和解决

MCP的这种CS架构存在一种问题,server会运行在本地环境中,这就意味着必须存在本地host才能使用这个MCP server,像coze这种云端Agent没法使用本地的MCP服务,这也是为什么必需要Claude Desktop才能执行官网demo所示的浏览器调用,命令行调用等一系列操作

最简单的方法是将本地server通过内网穿透工具暴露到公网,并在云平台比如coze和阿里百宝箱中添加插件并填写暴露后的公网ip+端口,利用隧道访问本地工具。但是这种方法只能临时应急,直接将其暴露在公网意味着任何人都能访问并通过MCP server控制你的电脑或者本地设备,极其不安全

那么解决方案就呼之欲出,那就是设计一个中间层来代理所有的请求:

  • client先通过中间件鉴权后才能使用工具
  • 所有server也首先接入中间件并上报自身工具

不过在设计这个中间层之前笔者先来探究一下小智AI的MCP接入原理

小智AI本身也是将LLM调用服务放在云端实现,并代理硬件发送的请求来实现对话功能,同样面临着无法使用本地MCP server的问题。对此小智AI提出的的解决方案是使用MCP接入点

小智AI官方文档所示的MCP接入点功能

接入点的本质是个WebSocket服务器,运行python mcp_pipe.py calculator.py可以将calculate的工具接入小智后台,其中calculate.py代码如下

# server.py
from mcp.server.fastmcp import FastMCP
import logging
logger = logging.getLogger('test_mcp')

import math
import random

# Create an MCP server
mcp = FastMCP("Calculator")

# Add an addition tool
@mcp.tool()
def calculator(python_expression: str) -> dict:
    """For mathamatical calculation, always use this tool to calculate the result of a python expression. `math` and `random` are available."""
    result = eval(python_expression)
    logger.info(f"Calculating formula: {python_expression}, result: {result}")
    return {"success": True, "result": result}

# Start the server
if __name__ == "__main__":
    mcp.run(transport="stdio")

可以看见calculate.py是一个标准的基于stdio(命令行输入输出)实现的MCP server,mcp_pipe.py提供与接入点建立websocket连接并作为桥梁转发MCP server和接入点之间的通信;而MCP接入点本身又可以是一个标准的MCP server实现,供小智后台API的MCP client调用

除此以外,对于连接在小智AI硬件上的末端执行器(舵机,电机等),可以在小智AI的固件上写入MCP server的实现,并通过websocket与小智后台API连接,以解决本地server和云端API的持久连接和双向通信问题,实现云端对本地server的调用。该部分实现的部分流程图如下

可以看见其通信流程依然严格遵循MCP官方的核心架构,即由Client发起初始化请求;不过小智AI在server端先行发起握手请求(Hello Message),并包涵“mcp”:true从而通知后台API的client主动发起初始化请求

基于以上MCP接入点相关内容,笔者提出一种MCP聚合节点架构

MCP聚合节点

概述

MCP 聚合节点是一个通过 WebSocket 技术,将多个本地或云端的 MCP server(通过下游桥接器连接)整合到一起,对外暴露为一个标准 MCP server 的聚合层。

它的作用是「汇总」所有下游 MCP server 的 tools/call 和 tools/list 能力,并通过标准 MCP 协议对外服务。

关键特性

  • 本地 MCP server 通过 mcp_pipe.py 与聚合节点建立 WebSocket 连接
  • 聚合节点本身对外表现为一个 MCP server,遵循 MCP 官方协议
  • 聚合节点负责管理所有连接的下游 MCP server,汇总其可用工具
  • 客户端调用时,聚合节点会转发调用到正确的下游 server

架构图

   [客户端]
       |
  (标准 MCP 协议)
       |
 [聚合节点 MCP Server]
       |
  (WebSocket + MCP 协议)
       |
 [下游桥接器]
       |
 (本地 Stdio/Streamable HTTP)
       |
 [本地 MCP Server]

主要组件与角色

1️⃣ 聚合节点

  • 对外暴露为标准 MCP server(支持 Streamable HTTP transport)
  • 内部通过 WebSocket 与多个 mcp_pipe.py 连接
  • 维护所有下游 server 的 tools 列表
  • 根据调用路由请求到正确的下游 server

2️⃣ 下游桥接器

  • 运行在下游机器上
  • 通过 WebSocket 连接到聚合节点
  • 在本地通过 Stdio/Streamable HTTP 与本地 MCP server 通信
  • 只负责「转发」:将 WebSocket 上的请求转到本地 MCP server,将响应再转回 WebSocket

工作流程

下游接入

  • 下游桥接器建立 WebSocket 连接后,发送注册帧通知聚合节点
  • 聚合节点响应并发送标准 tools/list 请求
  • 下游桥接器 转发 tools/list 到本地 MCP server,并把结果返回
  • 聚合节点将该下游 server 的 tools 保存到内存中的 servers 列表中

下游断连

  • WebSocket 连接断开
  • 聚合节点会从内存中移除该 server 及其已注册的 tools

上游客户端调用

  1. 列出工具
    • 客户端调用聚合节点的 /tools/list
    • 聚合节点返回所有下游服务器汇总的工具列表(来自内存)
  2. 调用工具
    • 客户端调用 /tools/call 指定某个 tool
    • 聚合节点根据 tool 所属的下游 server,转发调用到对应的 WebSocket
    • 下游桥接器 转发到本地 MCP server
    • 本地返回结果经过 下游桥接器 → WebSocket → 聚合节点 → 返回给客户端

协议标准

对上游客户端

  • 使用标准 MCP 官方协议
  • Transport:Streamable HTTP

对下游桥接器

  • 使用 WebSocket
  • WebSocket 上走标准 MCP 协议帧
  • 下游桥接器 只转发,不做业务逻辑
  1. 17491549 {:WAAX6PEP} 1 apa 50 default 973 https://chainpray.top/wp-content/plugins/zotpress/
    ↩︎
  2. For Client Developers - Model Context Protocol ↩︎

深圳大学腾讯创新俱乐部的一名TICer,目前致力于成为全栈工程师
最后更新于 2025-07-06