夜雨聆风学习资料网

ARTICLE · 1278777

ESP-Claw源码剖析-10_MCP通信体系

ESP-Claw源码剖析-10_MCP通信体系

当一块几美元的 ESP32 既能让大模型"指挥"邻居设备,又能被桌面上的任意 MCP 客户端当成工具调用,它就成了 Agent 生态的一等公民。本章精读 cap_mcp_client、cap_mcp_server 与 mcp_mdns 源码,看 MCP 双端如何在固件落地:三个元工具探索局域网、一张定义表暴露设备能力、mDNS 零配置发现。

目录

  1. 为什么几美元的设备要实现标准协议双端1.1 MCP:Agent 世界的工具插座标准1.2 双端定位:既消费工具也提供工具1.3 人机通道与机机通道的分工

  2. 组件全景与传输栈2.1 四个角色的分工2.2 传输栈:mcp-c-sdk 上的 HTTP 与 JSON-RPC

  3. cap_mcp_client:三个元工具驱动的客户端3.1 能力描述符:discover、list、call 三件套3.2 会话模型:一次调用一个生命周期3.3 三层错误模型与结果回填3.4 完整调用时序

  4. cap_mcp_server:把设备变成局域网上的 MCP 端点4.1 工具定义模型:与 claw_cap 解耦的 def 表4.2 生命周期与 HTTP 服务端的资源策略4.3 从 def 到 JSON-RPC 消息

  5. mcp_mdns:零配置的局域网服务发现5.1 服务标识与 TXT 记录5.2 查询流程与自身补录

  6. mcp_server_point:最小 MCP 服务端示例6.1 启动序列与能力裁剪6.2 六个 lua 工具与异步任务模型

  7. 设计权衡与边界7.1 元工具与全量注入之辨7.2 无状态会话的得与失7.3 与能力系统、IM 通道的边界7.4 安全边界:局域网信任模型参考文献

1. 为什么几美元的设备要实现标准协议双端

1.1 MCP:Agent 世界的工具插座标准

Model Context Protocol(MCP)是 Anthropic 于 2024 年底发布的开放协议,目标是统一"模型应用"与"外部工具、数据源"之间的连接层:消息层建立在 JSON-RPC 2.0 之上,核心原语是 tools(可调用工具)、resources(可读资源)与 prompts(提示模板),传输层则独立演进(本地 stdio、远程 HTTP 等)。在 MCP 出现之前,每个 Agent 宿主要为每类工具写一份私有胶水;MCP 之后,工具提供方只需实现一次服务端,就能被任意 MCP 客户端发现和调用——Claude Desktop、各类桌面 IDE Agent 都是现成的消费者。对硬件开发者而言,这意味着设备能力一旦讲 MCP 这门"普通话",就自动接入了整个正在膨胀的 Agent 生态。

1.2 双端定位:既消费工具也提供工具

ESP-Claw 在协议两端都下了注。作为客户端(cap_mcp_client),固件里的 LLM 可以发现并调用局域网内任何标准 MCP 设备的工具——例如让另一块板子上的传感器读数、屏幕显示为本次对话所用;作为服务端(cap_mcp_server),设备把自己的能力注册成 MCP 工具,供桌面 Agent 或其他 ESP-Claw 设备消费。两端合起来,多台 ESP-Claw 可以互为客户端与服务端,构成一个去中心的分布式 Agent 网络:没有谁是天然的主机,任何一台都能在对话中"雇用"另一台。这正是第2章《项目架构总览》里"智能在云、行动在端"的延伸——行动能力不仅在端上,还能跨端组合。

1.3 人机通道与机机通道的分工

第9章《IM通信集成》解决的"用户在哪,Agent 就在哪":微信、Telegram 是人机通道,消息是给人读的自然语言,异步、可靠投递优先,容忍分钟级延迟。MCP 则是机机通道:消息是给模型或程序消费的结构化 JSON-RPC,请求—响应式,毫秒级往返,参数与返回值都有类型契约。两者互补而非替代——用户在 IM 里说"把客厅那块板子的屏幕换成时钟",Agent 循环(第4章《Agent核心设计》)经由 MCP 把指令变成对另一台设备的 tools/call,再把结果用人话回复到 IM。通道选择由消息的"读者是谁"决定,这是本章与第9章最清晰的边界。

2. 组件全景与传输栈

2.1 四个角色的分工

MCP 体系由两个能力组件、一个公共组件和一个 SDK 依赖拼成,职责如下表:

组件
位置
职责
cap_mcp_clientcomponents/claw_capabilities/cap_mcp_client
注册 mcp_discover/mcp_list_tools/mcp_call_tool 三个能力,代理远端调用
cap_mcp_servercomponents/claw_capabilities/cap_mcp_server
创建 MCP 引擎与 HTTP 服务端,注册并分发本地 MCP 工具
mcp_mdnscomponents/common/mcp_mdns
mDNS 服务广播、查询与本地服务配置(端口、端点、主机名)
espressif/mcp-c-sdk
托管组件(idf_component.yml 声明 ^2.0.0)
提供 esp_mcp_engine/esp_mcp_mgr 引擎、传输与 JSON-RPC 编解码

两个能力组件的 idf_component.yml 完全一致:路径依赖 mcp_mdns,版本依赖 espressif/mcp-c-sdk。也就是说,发现与协议被刻意拆到独立层——mcp_mdns 不知道 MCP 消息长什么样,mcp-c-sdk 不知道 mDNS 存在,两端只通过 mcp_mdns_config_t 这份共享配置握手。

2.2 传输栈:mcp-c-sdk 上的 HTTP 与 JSON-RPC

源码中传输层的接线只有两处:客户端在 cap_mcp_client_core.c 里填 esp_mcp_mgr_config_t.transport = esp_mcp_transport_http_client,配置体是 esp_http_client_config_t(20 秒超时、4096 字节缓冲、keep-alive);服务端在 cap_mcp_server.c 里填 esp_mcp_transport_http_server,配置体是 httpd_config_t。两端都是 HTTP 请求—响应模型,SDK 之上则是 JSON-RPC:响应回调 cap_mcp_mgr_response_cb 的签名里赫然有 jsonrpc_request_id 参数,三个请求函数 esp_mcp_mgr_post_info_init、esp_mcp_mgr_post_tools_list、esp_mcp_mgr_post_tools_call 分别对应 MCP 规范的 initialize、tools/list、tools/call 方法;解析代码中出现的 tools、nextCursor、content、isError 也都是规范字段。需要说明的是,官方文档提到 SSE 传输与"自动映射 claw_cap 能力",但当前源码中两端均为普通 HTTP 收发、工具为显式注册,本章一律以源码为准。

3. cap_mcp_client:三个元工具驱动的客户端

3.1 能力描述符:discover、list、call 三件套

cap_mcp_client.c 没有为每个远端工具生成一个本地工具,而是只注册三个"元工具",以标准能力描述符(第5章《能力系统》)的形式挂进注册表:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
{    .id = "mcp_call_tool",    .name = "mcp_call_tool",    .family = "mcp",    .description = "Call a tool on a remote MCP server.",    .kind = CLAW_CAP_KIND_CALLABLE,    .cap_flags = CLAW_CAP_FLAG_CALLABLE_BY_LLM,    .input_schema_json = "{\"type\":\"object\",\"properties\":{\"server_url\":{\"type\":\"string\"},\"endpoint\":{\"type\":\"string\"},\"tool_name\":{\"type\":\"string\"},\"arguments\":{\"type\":\"object\"}},\"required\":[\"server_url\",\"tool_name\"]}",    .execute = cap_mcp_call_execute,},

三个描述符构成一个探索闭环:mcp_discover 用 mDNS 找出局域网里的 MCP 设备,mcp_list_tools 拉取某台设备的工具清单,mcp_call_tool 携带 tool_name 与 arguments 执行调用。它们经 cap_mcp_client_register_group() 以组 id cap_mcp_client 注册(幂等,重复调用先查 claw_cap_group_exists)。值得注意的是开关策略:Kconfig 里 CONFIG_APP_CLAW_CAP_MCP_CLIENT 默认开启(组会注册),但 app_capabilities.c 的组登记表中它的 llm_visible_by_default 为 false——默认不进 LLM 可见组,需要应用在 llm_visible_cap_groups 配置中显式点名后才经 claw_cap_set_llm_visible_groups() 对模型放出。工具常备、门默认关,这给系统提示词留下了按需开门的余地。

3.2 会话模型:一次调用一个生命周期

客户端不维护任何长连接状态。每次 tools/list 或 tools/call 都现场搭建一个完整的 MCP 会话,用完即毁:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
staticesp_err_tcap_mcp_mgr_create(constchar *server_url,constchar *endpoint,esp_mcp_mgr_handle_t *mgr_out){esp_http_client_config_t http_config = {        .url = server_url,                     /* 目标地址,如 http://192.168.1.20:18791 */        .timeout_ms = CAP_MCP_HTTP_TIMEOUT_MS, /* 20 秒:容忍远端慢工具 */        .buffer_size = 4096,        .keep_alive_enable = true,    };esp_mcp_mgr_config_t mgr_config = {        .transport = esp_mcp_transport_http_client,  /* HTTP 客户端传输 */        .config = &http_config,    };/* 依次 esp_mcp_mgr_init → start → register_endpoint,       任一步失败立即 stop+deinit 回滚(略) */return cap_mcp_init_remote_session(*mgr_out, endpoint); /* initialize 握手 */}

cap_mcp_init_remote_session 发出 initialize 请求,客户端自报家门为 name="esp-claw"、version="1.0.0"、title="ESP-Claw"——正是 MCP initialize 请求中的客户端信息字段。请求完成后 cap_mcp_mgr_destroy 立即 esp_mcp_mgr_stop 加 esp_mcp_mgr_deinit,HTTP 连接、请求缓冲、JSON 解析树全部归还。对一台只有几百 KB 可用 RAM 的设备,"没有会话状态"本身就是最可靠的资源策略:任何一台远端设备崩溃、超时或返回垃圾,最坏影响也只是这一次调用。

3.3 三层错误模型与结果回填

cap_mcp_call_execute 把远端世界的混乱收敛成 LLM 能读懂的文本,错误被明确分成三层:

层级
判定依据
处理方式
传输层
cap_mcp_call_remote_tool
 返回非 ESP_OK
写入 "Error: MCP request failed (...)",能力调用本身报错
协议层
响应对象含 error_message 字段
文本回填输出,返回 ESP_OK(调用链正常,错误内容给模型看)
应用层
结果 isError 为真或 content 为空
输出 "Tool returned application error" 或 "(empty)"

成功路径上,cap_mcp_extract_content_text 遍历 content 数组,把所有 type=="text" 条目的 text 拼接成一段文本——多段文本内容被摊平成 LLM 友好的纯字符串。tools/list 侧的对应处理是把每个工具渲染成 - name: description 行,并透传 MCP 的分页游标 nextCursor,模型可以拿着它继续翻页。协议层的错误码也有一套翻译:回调里 error_code<0 视为协议错误,直接把 resp_json 作为 error_message;其他正值翻译成 esp_err_to_name 文本。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
err = cap_mcp_call_remote_tool(input_json, &result);if (err != ESP_OK) { /* 第一层:传输失败,能力本身报错 */snprintf(output, output_size, "Error: MCP request failed (%s)", esp_err_to_name(err));return err;}/* 第二层:协议错误,以 error_message 回填给 LLM */error_message = cJSON_GetStringValue(cJSON_GetObjectItem(result, "error_message"));if (error_message && error_message[0]) {snprintf(output, output_size, "Error: %s", error_message);    cJSON_Delete(result);return ESP_OK;}/* 第三层:应用结果——拼接 content 数组中的 text 片段 */cap_mcp_extract_content_text(cJSON_GetObjectItem(result, "content"), output, output_size);

3.4 完整调用时序

流程执行说明:

  • 发现阶段(步骤 1-7)

    • LLM 发起 mcp_discover 工具调用,claw_core 经 claw_cap 分发到 cap_mcp_discover_execute;

    • mcp_mdns_query_devices() 组播查询 _mcp._tcp,对端应答 SRV/TXT 记录(端口与 endpoint),本机补录自身后返回 devices JSON;

    • LLM 从清单中选出目标,记下 server_url(如 http://192.168.1.20:18791)。

  • 枚举阶段(步骤 8-14)

    • LLM 调 mcp_list_tools(server_url),cap_mcp_list_remote_tools() 现场建会话:HTTP 连接、initialize 握手、tools/list 请求一次完成;

    • 远端返回 tools 数组与 nextCursor,本端渲染成 - name: description 清单;

    • cap_mcp_mgr_destroy 销毁会话,随后 LLM 结合清单规划调用。

  • 调用阶段(步骤 15-21,循环内可重复)

    • LLM 调 mcp_call_tool(server_url, tool_name, arguments),cap_mcp_call_remote_tool() 再建一个全新会话(再次 initialize);

    • esp_mcp_mgr_post_tools_call 发出 tools/call,对端执行工具回调并返回 content 数组或 isError;

    • cap_mcp_extract_content_text 提取文本回填,会话销毁,结果回到 LLM,模型生成最终回答。

关键规则/最佳实践:

  • 三个元工具是一个探索闭环,LLM 应遵循 discover → list → call 的顺序,server_url 贯穿后两步;

  • 每次 list/call 都是独立会话,跨调用不共享状态,重试即重建;

  • 分页靠 cursor/nextCursor 传递,工具多时需多轮 list。

4. cap_mcp_server:把设备变成局域网上的 MCP 端点

4.1 工具定义模型:与 claw_cap 解耦的 def 表

服务端的核心是一张极简的工具定义表。整个 cap_mcp_server.h 没有 include 任何 claw_cap 头文件——它自成一套暴露路径:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
typedefstruct {constchar *name;              /* 工具名,如 "lua.run_script" */constchar *description;       /* 广播给 MCP 客户端的工具说明 */esp_mcp_value_t (*callback)(constesp_mcp_property_list_t *properties);constchar *property_names[6]; /* 最多 6 个字符串属性 */size_t property_count;} cap_mcp_server_tool_def_t;

cap_mcp_server_add_tool() 把每个 def 翻译成 SDK 对象:esp_mcp_tool_create(name, description, callback) 建工具,esp_mcp_property_create_with_string(property_name, "") 为每个参数建字符串属性,esp_mcp_tool_add_property 挂到工具上,最后 esp_mcp_add_tool(s_mcp, tool) 注册进引擎。属性全部是字符串类型、上限 6 个——这是有意的收敛:MCP 属性系统足够表达大多数设备参数(路径、名称、超时),而完全不需要 JSON Schema 解析器的开销。与第5章的关系因此非常清晰:claw_cap 是面向本机 LLM 的内部注册表,cap_mcp_server 是面向外部 MCP 客户端的第二暴露路径,两套注册互不知晓;哪些能力走哪条路,由集成者显式决定。

4.2 生命周期与 HTTP 服务端的资源策略

生命周期是经典的五段式:cap_mcp_server_init(esp_mcp_create 建 s_mcp 引擎)→ cap_mcp_server_add_tool(注册工具)→ cap_mcp_server_start(拉起 HTTP 服务并广播)→ cap_mcp_server_stop(摘除 mDNS、停管理器)→ cap_mcp_server_deinit(esp_mcp_destroy 销毁引擎)。start 的资源策略值得细看:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
httpd_config_t http_config = HTTPD_DEFAULT_CONFIG();http_config.server_port = info.server_port;  /* 18791:MCP HTTP 端口 */http_config.ctrl_port = info.ctrl_port;      /* 18792:esp_http_server 控制端口 */http_config.max_uri_handlers = 4;http_config.stack_size = 8192;uint32_t task_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;if (heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM) >= http_config.stack_size) {    task_caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT; /* 栈挪去 PSRAM,省内部 RAM */}http_config.task_caps = task_caps;config.transport = esp_mcp_transport_http_server;config.instance = s_mcp;                    /* 绑定工具所在的引擎实例 */

HTTP 服务任务栈 8 KB,若 PSRAM 有足够的连续块就把栈放进 PSRAM,把珍贵的内部 RAM 留给实时逻辑——这是第12章《板级适配与配置服务》中内存分层思想在协议栈上的直接应用。启动序列是线性回滚的范本:mcp_mdns_init → esp_mcp_mgr_init/start → esp_mcp_mgr_register_endpoint(s_mgr, "mcp", NULL) → cap_mcp_server_register_discovery_service,任何一步失败都回滚已启动的资源并把句柄清零;全部成功后置 mcp_mdns_set_started(true) 并打印 MCP server ready: http://esp-claw.local:18791/mcp。默认端口 18791 与控制端口 18792、端点路径 mcp 全部来自 mcp_mdns 的共享配置,服务端身份与发现广播由此保证一致。

4.3 从 def 到 JSON-RPC 消息

运行期的消息处理全部委托给 mcp-c-sdk:外部 MCP 客户端向 /mcp 端点 POST JSON-RPC 请求,SDK 解码后按方法分发——tools/list 返回注册表里所有 def 的名字、描述与属性名,tools/call 找到对应工具、把属性值整理成 esp_mcp_property_list_t 传给回调,回调返回的 esp_mcp_value_t 被编码为带 content 数组的响应。cap 层不做任何 JSON 手工拼装,只负责 def 的静态描述与回调实现,协议细节(请求 id 关联、错误码封装)沉在 SDK 里。这种"描述一次、引擎分发"的分工与第5章 claw_cap 的描述符思想同构——只是面向的读者从云端 LLM 换成了任意 MCP 客户端。

5. mcp_mdns:零配置的局域网服务发现

5.1 服务标识与 TXT 记录

发现层的全部身份信息浓缩在 mcp_mdns.h 的一组宏里:

宏
值
含义
MCP_MDNS_SERVICE_TYPE"_mcp"
服务类型(DNS-SD 自定义类型)
MCP_MDNS_SERVICE_PROTO"_tcp"
协议后缀,合称 _mcp._tcp
MCP_MDNS_DEFAULT_ENDPOINT"mcp"
HTTP 端点路径,发布在 TXT 记录里
MCP_MDNS_DEFAULT_HOSTNAME"esp-claw"
主机名(.local 之前的部分)
MCP_MDNS_DEFAULT_INSTANCE"ESP-Claw"
人可读的实例名
MCP_MDNS_DEFAULT_SERVER_PORT18791
MCP HTTP 服务端口
MCP_MDNS_DEFAULT_CTRL_PORT18792
esp_http_server 控制端口
MCP_MDNS_DEFAULT_TIMEOUT_MS3000
查询超时

广播侧只做三件事:初始化 mDNS、添加服务、写 TXT 记录:

  • 1
  • 2
  • 3
  • 4
  • 5
mdns_txt_item_t txt[] = {    {"endpoint", service ? service->endpoint : NULL}, /* TXT:endpoint=mcp */};mdns_service_add(service->instance_name, MCP_MDNS_SERVICE_TYPE,   /* "_mcp" */                 MCP_MDNS_SERVICE_PROTO, service->port, txt, 1);  /* "_tcp" */

TXT 记录只有一条 endpoint——SRV 记录已经携带端口与主机名,A/AAAA 记录携带 IP,多出来的信息只有端点路径这一项,那就只发布这一项。_mcp._tcp 是 ESP-Claw 圈定的局域网约定(不在 RFC 6763 已注册服务类型之列),任何遵循同一约定的设备都能被互相发现。

5.2 查询流程与自身补录

查询侧 mcp_mdns_query_devices() 用 mdns_query_ptr("_mcp", "_tcp", timeout, 20, &results) 做一次指针记录查询(上限 20 台),对每条结果:mcp_mdns_find_txt_value 取 TXT 里的 endpoint(缺失则回退默认值),mcp_mdns_pick_ip_string 遍历地址链表挑一个可用 IP,然后拼出完整的 URL。最巧妙的是自身补录 mcp_mdns_append_self_device_if_needed:本机自己的 mDNS 应答常常收不到(组播回环不保证),所以当 include_self 为真且本地服务已启动时,直接读本机 WIFI_STA_DEF 接口的 IP,把自己追加进清单,并用 mcp_mdns_devices_has_match 按 hostname+port+endpoint 三元组去重。返回的 JSON 长这样:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
{"count":2,"devices":[{"instance":"ESP-Claw","hostname":"esp-claw","ip":"192.168.1.20","port":18791,"endpoint":"mcp","server_url":"http://192.168.1.20:18791","url":"http://192.168.1.20:18791/mcp"}]}

server_url 与 url 是为 LLM 预拼好的:模型拿到清单后无需自己组装地址,直接把 server_url 塞进 mcp_list_tools 即可。发现层输出的不是"mDNS 结果",而是"下一步调用的入参",这个接口视角的统一让三个元工具衔接得毫无摩擦。

6. mcp_server_point:最小 MCP 服务端示例

6.1 启动序列与能力裁剪

application/mcp_server_point 是仓库里的瘦身应用:不开 claw_core、Agent、记忆、IM、事件路由与 CLI,只保留 cap_lua 一个能力组。它的 main.c 在 app_claw_start 之后用三行代码把 MCP 服务端拉起:

  • 1
  • 2
  • 3
ESP_ERROR_CHECK(cap_mcp_server_init());     /* 创建 MCP 引擎 */ESP_ERROR_CHECK(cap_mcp_lua_tools_init());  /* 注册 lua.* 工具 def 表 */ESP_ERROR_CHECK(cap_mcp_server_start());    /* HTTP 服务 + mDNS 广播 */

注意 cap_mcp_server 不走 enabled_cap_groups 注册路径,由 main.c 显式初始化——这个细节反过来印证了第4.1节的解耦:MCP 服务端不依赖 Agent 栈存在。一个没有 LLM 的固件依然可以是一个完整的 MCP 服务端,这是"机机通道独立性"最直接的证明。

6.2 六个 lua 工具与异步任务模型

cap_mcp_lua.c 的 s_tool_defs 表注册了六个工具,全部基于第11章《Lua运行时与驱动体系》的脚本底座:

工具
属性
语义
lua.run_script
path, args, timeout_ms
同步执行,等待返回
lua.run_script_async
path, args, timeout_ms, name, exclusive, replace
异步执行,返回 job id;timeout_ms=0 表示跑到被取消
lua.list_async_jobs
status
按状态过滤列出任务
lua.get_async_job
job_id, name
查询单个任务的状态与摘要
lua.stop_async_job
job_id, name, wait_ms
协作式停止,默认等待 2000ms
lua.stop_all_async_jobs
exclusive, wait_ms
按互斥组清空任务

这张表回答了一个协议层的根本矛盾:HTTP 请求—响应是同步边界,而设备上的长任务(时钟动画、传感器巡检)一跑就是几分钟,塞在一次响应里必然超时。解法是把"启动"与"治理"拆开——run_script_async 秒回 job id,客户端靠 get/list 轮询、靠 stop 干预;exclusive 互斥组(如 display)让屏幕这类独占资源不会被打架的脚本撕碎,replace 提供抢占语义。更值得学习的是工具描述的写法:stop_async_job 的 description 明确写着"MUST be called whenever the user asks to stop…"——把行为契约写进广播给对端模型的说明里,让远端 Agent 也能正确履行"停掉它"的意图。

7. 设计权衡与边界

7.1 元工具与全量注入之辨

理论上,客户端可以把所有远端工具逐个映射成本地 claw_cap 描述符,让 LLM 一次看到全部工具。ESP-Claw 没这么做,原因有三:其一,上下文经济——每台设备可能贡献十几个工具,三台设备就足以淹没系统提示词,而三个元工具的 schema 是恒定的;其二,拓扑动态——设备上线下线、工具增删在局域网里是常态,静态注入必然带着过期风险,元工具模式下的清单永远是最新的;其三,内存——为远端工具建描述符就要建 schema 字符串与注册槽位,RAM 账算不过来。代价是 LLM 必须多走几轮(discover → list → call),换来的是探索能力与零膨胀,这与第4章 ReAct 循环的本性正好合拍。

7.2 无状态会话的得与失

每次调用都重做 initialize 握手,看似浪费,实则是嵌入式环境里的最优解:不需要为每台远端设备维护连接状态机、重连逻辑与超时回收任务,异常恢复退化为"下一次调用重试";内存占用从"N 台设备 × 常驻缓冲"变成"峰值一次调用 × 4096 字节"。局域网内一次握手往返不到几毫秒,相比 20 秒的工具超时可忽略。这笔账的本质是:广域网上会话复用省的是 RTT,局域网上省的是内存——场景换了,最优解也换了。

7.3 与能力系统、IM 通道的边界

三通道各司其职:claw_cap(第5章)是面向本机 LLM 的内部能力注册表,描述符带 JSON Schema,走函数指针分发;cap_mcp_server 是面向外部 MCP 客户端的对外暴露路径,def 表只声明字符串属性,走 HTTP 分发;IM 通道(第9章)是面向人的自然语言通道,异步推送、可靠投递。一个能力想同时服务三类读者,就需要分别注册三处——重复不是缺陷,而是三种读者契约本就不同的诚实表达。本章工具的实现底座(Lua 脚本、异步任务、驱动访问)属于第11章;设备的启动裁剪与端口、分区等板级参数属于第12章《板级适配与配置服务》。

7.4 安全边界:局域网信任模型

必须直言:当前源码中未见任何鉴权——cap_mcp_server_start 未配置身份校验,mDNS TXT 里也没有令牌字段,HTTP 明文传输。这意味着信任边界完全等同于局域网边界:能组播到达的任何人都可以 tools/list 并调用 lua.run_script 在设备上执行脚本。这对原型与家庭实验网是务实取舍(零配置与安全鉴权天然冲突),但部署到不可信网络时必须外加防线——MCP 规范后续版本已引入授权(authorization)章节(基于 OAuth 2.1),在网关层终止 TLS 并注入令牌、或用防火墙隔离 _mcp._tcp 组播,都是比修改固件更便宜的补强路径。理解"这个设计默认了什么样的网络",与理解它如何工作同样重要。

参考文献

[1] Model Context Protocol 官方文档:https://modelcontextprotocol\.io/docs/getting\-started/intro

[2] Model Context Protocol 规范:https://modelcontextprotocol\.io/specification/

[3] JSON-RPC 2.0 Specification:https://www\.jsonrpc\.org/specification

[4] RFC 6762: Multicast DNS:https://www\.rfc\-editor\.org/rfc/rfc6762

[5] RFC 6763: DNS-Based Service Discovery:https://www\.rfc\-editor\.org/rfc/rfc6763

[6] espressif/mcp-c-sdk 组件主页:https://components\.espressif\.com/components/espressif/mcp\-c\-sdk

[7] ESP-Claw cap_mcp 参考文档:https://github\.com/espressif/esp\-claw/blob/main/docs/src/content/docs/en/reference\-cap/cap\-mcp\.mdx

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料