2025年MCP协议发展及其在xiaozhi-esp32中的落地实践分析

1、一个开源协议,一个开源落地项目

MCP (Model Context Protocol) is an open-source standard for connecting AI applications to external systems.

MCP(模型上下文协议)是用于将AI应用连接到外部系统的开源标准。

作为一个去年(2024年)11月份发布的开源协议,它在短短一年时间里飞速发展成为AI领域极其重要的协议。

xiaozhi-esp32是由开发者"虾哥"于2024年底在GitHub上开源的面向嵌入式 AI应用的软硬件一体化项目,旨在将大语言模型(LLM)的能力通过Model Context Protocol(MCP)安全、高效地延伸至资源受限的 ESP32 系列芯片上,打造可语音交互、可自主控制物理世界的智能终端。截至写稿时,它在github上拥有了22.7k的Star,也是一个妥妥的明星项目。

本文根据作者使用MCP协议和xiaozhi-esp32的工作经历,解读和分析MCP协议在具体项目的落地实践。

2、MCP协议2025年关键发展回顾

2.1 MCP解决了什么痛点?

在大模型(LLM)应用爆发式增长的背景下,"如何让LLM可靠地调用外部工具"成为工程落地的核心瓶颈。2023--2024年间,各框架各自寻找解决方案:LangChain使用自定义Tool Schema,Ollama依赖函数描述字符串。这种碎片化导致工具无法跨平台复用、上下文状态管理混乱以及边缘设备难以解析非结构化指令等问题。

为了解决这一问题,MCP于2024年中由Anthropic、LangChain社区及多位开源贡献者联合发起,旨在提供一个语言无关、轻量、可扩展的标准化协议,用于描述LLM与外部工具之间的交互上下文。进入2025年以后,MCP从概念验证快速走向生态构建,成为AI工程化基础设施的重要一环。

2.2 2025年MCP核心版本演进

2025年是MCP协议迅速发展的关键一年。全年共发布三个重要规范版本,逐步完善协议语义与工程适配能力。

版本 关键特性
2024-11-05 定义基础消息结构:ToolRequest / ToolResponse,支持 JSON Schema 描述工具参数,无认证机制
2025-03-26 授权升级至 OAuth 2.1;工具注解系统,JSON-RPC批处理;引入 Streamable HTTP(单端点),支持双向通信与断线恢复,简化架构
2025-06-18 新增 Elicitation 协议,支持主动询问用户确认;工具注解扩展为 结构化输出;移除 JSON-RPC 批处理,简化协议;增强断点续传机制
2025-11-25 授权与安全增强:OpenID Connect Discovery 1.0(PR #797)支持标准化授权服务器发现、增量范围授权(Incremental Scope Consent);元数据与可视化;交互与协议扩展

2.3 主流框架支持与官方SDK的双向奔赴

2025年,MCP迅速获得主流AI框架和平台的官方或社区级支持,简直就是遍地开花。比如LangChain 很早就开始对MCP提供支持,目前可以通过langchain-mcp-adapters库来使用在MCP服务器上定义的工具。入下图所示:

同时,MCP协议也提供了多种开发语言的SDK供开发者使用:

其中MCP Python SDK是使用人数最多的,也印证了python是AI时代最合适的语言这个说法。

2.4 小结

2025年,MCP协议完成了从"理念"到"基础设施"的关键跨越。其核心价值不仅在于统一了工具调用格式,更在于为LLM与物理世界之间架起了一座可编程的桥梁。

3、xiaozhi-esp32项目概览

3.1 项目简介

xiaozhi-esp32 是由开发者"虾哥"于2024年12月在GitHub上开源的AIoT项目,目标是打造一个低成本、可语音交互、能控制物理设备的边缘AI助手终端

到了今天,项目的描述被精炼概括为:一个基于MCP的聊天机器人|An MCP-based chatbot。

作为语音交互入口,小智AI聊天机器人利用Qwen/DeepSeek等大型模型的AI功能,通过MCP协议实现多终端控制。

该项目已经具备了非常完善的AI对话硬件应该具备的功能,包含:

  • 基于 ESP-IDF 的嵌入式固件;
  • 配套的云端服务参考实现;
  • 详细的硬件接线与烧录教程;
  • 面向普通用户的开箱即用体验(支持免开发环境烧录)。

这是让我惊喜异常的,因为我在2024年初开始搞AI音响,当时可参考的项目非常稀少。我还记得当时采用HTTP通信,为了调通协议头而费劲了心思。现在,我可以更新到小智方案了。

3.2 项目整体架构

xiaozhi-esp32 采用典型的"云-边协同"架构,将计算密集型任务(如语音识别、大模型推理)交由云端处理,而指令解析与物理控制则在本地 ESP32 设备完成,兼顾响应速度与功能完整性。

端到端流程如下图:

3.3 核心功能与硬件支持

3.3.1 主要功能特性

  • 语音交互全流程支持

    支持唤醒词检测(默认"你好小智")、流式语音上传、TTS 语音播报,形成完整对话闭环。

  • MCP驱动的设备控制

    通过MCP协议调用预定义工具,例如控制底盘前进:

    json 复制代码
      {
      "jsonrpc": "2.0",
      "method": "tools/call",
      "params": {
          "name": "self.chassis.go_forward",
          "arguments": {}
      },
      "id": 2
      }
  • 多模态反馈

    • OLED / LCD 屏幕显示表情或状态(如"思考中..."、"执行成功");
    • LED呼吸灯随对话状态变化颜色;
    • 语音 + 视觉双重反馈提升用户体验。
  • 低功耗与离线能力

    • 支持深度睡眠模式,待机电流 < 10μA;
    • 唤醒词识别完全本地运行,不依赖网络;
    • 断网时可缓存指令,恢复后重试。

3.3.2 硬件平台兼容性

项目适配多种主流 ESP32 开发板,包括:

平台 特点 典型应用场景
ESP32-S3-BOX 内置麦克风、喇叭、屏幕 桌面语音助手
M5Stack CoreS3 彩色触摸屏 + 扬声器 教学演示、智能家居面板
LILYGO T-Circle-S3 圆形AMOLED + 小巧机身 可穿戴AI吊坠
ESP32-S3开发板 市面上成本非常低的方案 超低成本IoT控制器

此外,项目还支持通过 红外发射管 控制传统家电(如空调、电视),极大扩展了物理控制范围。

3.4 通信与协议栈

xiaozhi-esp32 支持两种主要通信模式,适应不同部署场景:

模式 协议 适用场景
WebSocket模式 WebSocket既传文本又可以传音频 高频交互、低延迟要求(如实时对话)
MQTT + UDP混合模式 MQTT用于控制信令,UDP用于音频流 弱网环境、4G移动网络

所有来自LLM的工具调用指令均封装为 MCP 格式的 JSON 对象,通过上述通道下发至设备。设备端不关心LLM如何生成该消息,只负责"按规执行"即可。

3.5 项目选择MCP带来的好处

引入MCP后,项目获得三大优势:

  1. 标准化:工具描述符合社区规范,便于与其他MCP服务互通;
  2. 可扩展:新增设备只需注册新tool,无需修改通信层;
  3. 安全性 :通过schema限制参数范围,防止非法指令(如 brightness=9999)。

4、源码解析:MCP是如何在ESP32上运行的?

4.1 相关源文件简介

由于MCP消息是封装在基础通信协议(如 WebSocket 或 MQTT)的消息体中的,今天的源码分析基于WebSocket通信,所以我们只关心如下几个源文件:

  • main/application.cc
    它是项目的核心主控逻辑模块,协调硬件、网络、音频、协议通信与用户交互,管理设备全生命周期状态,并提供MCP能力集成。
  • main/mcp_server.h, main/mcp_server.cc
    提供了MCP服务端相关的工具类,包括:
    ImageContent :封装一张图片的内容,用于 MCP 工具返回图像结果。
    Property :描述一个MCP工具的输入参数(属性)。
    PropertyList :管理一组Property,代表一个工具的所有输入参数。
    McpTool :一个可被LLM调用的本地工具。
    McpServer:MCP服务的全局入口,负责协议解析与调度。它是一个单例。
  • main/protocol/protocol.cc, main/protocol/websocket_protocol.cc
    protocol是一个通信协议抽象类,websocket_protocol是实现类。

4.2 整体交互图

MCP在xiaozhi-esp32上的整体交互流程如下图:

4.3 协议格式

消息结构格式如下所示:

json 复制代码
{
  "session_id": "...", // 会话 ID
  "type": "mcp",       // 消息类型,固定为 "mcp"
  "payload": {         // JSON-RPC 2.0 负载
    "jsonrpc": "2.0",
    "method": "...",   // 方法名 (如 "initialize", "tools/list", "tools/call")
    "params": { ... }, // 方法参数 (对于 request)
    "id": ...,         // 请求 ID (对于 request 和 response)
    "result": { ... }, // 方法执行结果 (对于 success response)
    "error": { ... }   // 错误信息 (对于 error response)
  }
}

其中,payload部分是标准的JSON-RPC 2.0消息:

jsonrpc: 固定的字符串 "2.0"。

method: 要调用的方法名称 (对于 Request)。

params: 方法的参数,一个结构化值,通常为对象 (对于 Request)。

id: 请求的标识符,客户端发送请求时提供,服务器响应时原样返回。用于匹配请求和响应。

result: 方法成功执行时的结果 (对于 Success Response)。

error: 方法执行失败时的错误信息 (对于 Error Response)。

4.4 交互流程

MCP的交互主要是围绕客户端(后台 API)发现和调用设备上的"工具"(Tool)进行的。后台API相当于MCP的客户端,ESP32设备其实是MCP的服务器。

(1) 设备端初始化

esp32设备启动后,首先设备上电、初始化 Application,创建并初始化实现 Protocol 接口的WebSocket协议实例(WebsocketProtocol

参考源码application.cc的start函数:

cpp 复制代码
void Application::Start() {
    // ...
    if (ota.HasMqttConfig()) {
        protocol_ = std::make_unique<MqttProtocol>();
    } else if (ota.HasWebsocketConfig()) {
        protocol_ = std::make_unique<WebsocketProtocol>();
    }
    // ...
}

(2) 建立WebSocket连接并发送"hello"消息

当设备需要开始语音会话时(如被用户唤醒),调用 OpenAudioChannel()函数,它会根据配置获取 WebSocket URL、设置若干请求头(Authorization, Protocol-Version, Device-Id, Client-Id)以及调用 Connect() 与服务器建立 WebSocket 连接。

参考源码websocket_protocol.cc的OpenAudioCHannel函数:

cpp 复制代码
bool WebsocketProtocol::OpenAudioChannel() {
    // ...
    websocket_->SetHeader("Protocol-Version", std::to_string(version_).c_str());
    websocket_->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
    websocket_->SetHeader("Client-Id", Board::GetInstance().GetUuid().c_str());

    websocket_->OnData([this](const char* data, size_t len, bool binary) {
        // 接收数据处理
    }

        ESP_LOGI(TAG, "Connecting to websocket server: %s with version: %d", url.c_str(), version_);
    if (!websocket_->Connect(url.c_str())) {
        ESP_LOGE(TAG, "Failed to connect to websocket server");
        SetError(Lang::Strings::SERVER_NOT_CONNECTED);
        return false;
    }
    // 连接成功,发送hello消息
    // Send hello message to describe the client
    auto message = GetHelloMessage();
    if (!SendText(message)) {
        return false;
    }
    // ...
}

hello消息的格式参考:

json 复制代码
{
    "type": "hello",
    "version": 1,
    "features": {
    "mcp": true
    },
    "transport": "websocket",
    "audio_params": {
    "format": "opus",
    "sample_rate": 16000,
    "channels": 1,
    "frame_duration": 60
    }
}
  • 其中 features 中的"mcp": true 表示支持 MCP 协议。
  • frame_duration 的值对应 OPUS_FRAME_DURATION_MS(例如 60ms)。

(3) 服务器回复 "hello"及初始化MCP

服务端收到hello后,会回复一个hello。此时通信正式建立。

同时,服务端确认设备支持MCP后, 会发送initialize初始化MCP会话。

服务器返回hello的消息格式如下:

json 复制代码
{
    "type": "hello",
    "transport": "websocket",
    "session_id": "xxx",
    "audio_params": {
    "format": "opus",
    "sample_rate": 24000,
    "channels": 1,
    "frame_duration": 60
    }
}

初始化MCP会话消息格式如下:

json 复制代码
{
  "jsonrpc": "2.0",
  "method": "initialize",
  "params": {
    "capabilities": {
      // 客户端能力,可选

      // 摄像头视觉相关
      "vision": {
        "url": "...", //摄像头: 图片处理地址(必须是http地址, 不是websocket地址)
        "token": "..." // url token
      }

      // ... 其他客户端能力
    }
  },
  "id": 1 // 请求 ID
}

解析MCP消息的方法在mcp_server.cc的ParseMessage,截取片段如下:

cpp 复制代码
void McpServer::ParseMessage(const cJSON* json) {
    // ...
    if (method_str == "initialize") {
        if (cJSON_IsObject(params)) {
            auto capabilities = cJSON_GetObjectItem(params, "capabilities");
            if (cJSON_IsObject(capabilities)) {
                ParseCapabilities(capabilities);
            }
        }
        auto app_desc = esp_app_get_description();
        std::string message = "{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{\"tools\":{}},\"serverInfo\":{\"name\":\"" BOARD_NAME "\",\"version\":\"";
        message += app_desc->version;
        message += "\"}}";
        ReplyResult(id_int, message);
    } 
    // ...
}

如上面代码所示,在收到MCP初始化消息后,设备端立即做成响应。

(4)发现设备工具列表

接着,服务端会发送"tools/list"请求,消息格式如下:

json 复制代码
{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "params": {
    "cursor": "" // 用于分页,首次请求为空字符串
  },
  "id": 2 // 请求 ID
}

同样的,也是在ParseMessage方法中解析:

cpp 复制代码
void McpServer::ParseMessage(const cJSON* json) {
    // ...
if (method_str == "tools/list") {
        std::string cursor_str = "";
        bool list_user_only_tools = false;
        if (params != nullptr) {
            auto cursor = cJSON_GetObjectItem(params, "cursor");
            if (cJSON_IsString(cursor)) {
                cursor_str = std::string(cursor->valuestring);
            }
            auto with_user_tools = cJSON_GetObjectItem(params, "withUserTools");
            if (cJSON_IsBool(with_user_tools)) {
                list_user_only_tools = with_user_tools->valueint == 1;
            }
        }
        GetToolsList(id_int, cursor_str, list_user_only_tools);
    } 
    // ...
}

设备端在获取工具列表后,返回结果。细节参考GetToolsList方法,以及McpTool类。当然,想要能够发现设备工具,前提是要将工具加入到列表中。请自行参考并调查如下函数:

cpp 复制代码
void McpServer::AddTool(const std::string& name, const std::string& description, const PropertyList& properties, std::function<ReturnValue(const PropertyList&)> callback) {
    AddTool(new McpTool(name, description, properties, callback));
}

响应消息的格式如下:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": 2, // 匹配请求 ID
  "result": {
    "tools": [ // 工具对象列表
      {
        "name": "self.get_device_status",
        "description": "...",
        "inputSchema": { ... } // 参数 schema
      },
      {
        "name": "self.audio_speaker.set_volume",
        "description": "...",
        "inputSchema": { ... } // 参数 schema
      }
      // ... 更多工具
    ],
    "nextCursor": "..." // 如果列表很大需要分页,这里会包含下一个请求的 cursor 值
  }
}

(5) 调用设备工具

当后台需要执行设备上的某个具体功能时,就会调用tools/call方法来调用设备工具。

消息格式为:

json 复制代码
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "self.audio_speaker.set_volume", // 要调用的工具名称
    "arguments": {
      // 工具参数,对象格式
      "volume": 50 // 参数名及其值
    }
  },
  "id": 3 // 请求 ID
}

同样的,也是在ParseMessage方法中解析:

cpp 复制代码
void McpServer::ParseMessage(const cJSON* json) {
    // ...
    if (method_str == "tools/call") {
        if (!cJSON_IsObject(params)) {
            ESP_LOGE(TAG, "tools/call: Missing params");
            ReplyError(id_int, "Missing params");
            return;
        }
        auto tool_name = cJSON_GetObjectItem(params, "name");
        if (!cJSON_IsString(tool_name)) {
            ESP_LOGE(TAG, "tools/call: Missing name");
            ReplyError(id_int, "Missing name");
            return;
        }
        auto tool_arguments = cJSON_GetObjectItem(params, "arguments");
        if (tool_arguments != nullptr && !cJSON_IsObject(tool_arguments)) {
            ESP_LOGE(TAG, "tools/call: Invalid arguments");
            ReplyError(id_int, "Invalid arguments");
            return;
        }
        // 执行具体任务,比如上文中调整音量为50
        DoToolCall(id_int, std::string(tool_name->valuestring), tool_arguments);
    }
    // ...
}

返回消息结构为:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": 3, // 匹配请求 ID
  "result": {
    "content": [
      // 工具执行结果内容
      { "type": "text", "text": "true" } // 示例:set_volume 返回 bool
    ],
    "isError": false // 表示成功
  }
}

至此,一个正常的MCP交互流程就完成了。请结合时序图和代码一并来看,效果更加。

5、 写在最后

Model Context Protocol(MCP)作为一种面向大语言模型(LLM)的标准化工具调用协议,为LLM与物理世界的交互提供了通用接口。本文基于ESP32嵌入式平台上的实际实现,深入解读了MCP的运行机制,我们可以看出它们配合的几个特点:
轻量的协议适配

该版本的xiaozhi-esp32项目实现了MCP v2024-11-05 规范的核心方法(initialize、tools/list、tools/call),采用 JSON-RPC 2.0 作为传输格式,与WebSocket无缝集成。
硬件能力抽象化

将音频、屏幕、摄像头、系统信息等本地硬件功能封装为具名工具(如 self.audio_speaker.set_volume),形成统一的"设备能力 API"。工具描述包含自然语言说明与结构化输入 Schema,便于LLM理解与调用。
权限管控

引入User-Only工具机制,将敏感操作(如重启、固件升级)与普通控制指令隔离,确保LLM无法自主触发高危行为。

短短的一年,技术发展飞快!

可以预见的是,未来随着协议演进与硬件升级,嵌入式设备将成为LLM在现实世界中的"眼睛、耳朵与双手",而且这些感官功能越来越丝滑,越来越强大。

参考:

1、https://github.com/modelcontextprotocol

2、https://github.com/78/xiaozhi-esp32

3、https://github.com/modelcontextprotocol/python-sdk

4、https://modelcontextprotocol.io/specification/versioning

5、https://modelcontextprotocol.io/specification/2025-11-25/changelog

相关推荐
码农小白猿2 小时前
IACheck提升锅炉安装验收报告审核效率:智能化审核为安全合规保驾护航
运维·人工智能·ai·iacheck
king0073 小时前
Spring-AI 结合自定义 mcp server 实现飞书智能机器人
spring boot·ai·ai编程
come112343 小时前
Claude Code CLI 使用文档 (2025 最新版)
ai
村口曹大爷3 小时前
【深度】OpenAI 推理架构演进:GPT-5.2(Internal版)性能实测与开发者接入路径分析
gpt·ai·chatgpt·架构·gpt5.2
小真zzz4 小时前
Nano Banana Pro 深度解析与 AI PPT 工具全面评测报告
人工智能·ai·powerpoint·ppt·chatppt·banana pro
喂完待续5 小时前
【Big Data】2025年大数据技术演进与产业变革
大数据·ai·数据安全·big data·年度总结·微博之星
Elastic 中国社区官方博客5 小时前
使用 Elasticsearch 中的结构化输出创建可靠的 agents
大数据·人工智能·elk·elasticsearch·搜索引擎·ai·全文检索
Jerry Lau6 小时前
Nano Studio: 打造现代化的 AI 知识管理平台
人工智能·ai·rag
模型启动机6 小时前
阿里通义开源GUI智能体SOTA:2B到235B端云协同重新定义移动端GUI智能体
人工智能·ai·大模型