跳到主要内容

MCP 服务端

MCP(Model Context Protocol,模型上下文协议)让设备端 AI 不再只是"说话",而是把设备自身的函数当作工具来调用——读取传感器、修改设置、触发执行器。ai_mcp 就是运行在设备端的 MCP 服务端:你把函数注册成工具,AI 需要时由服务端来执行它们。

每个工具都有名称、供 AI 判断何时调用的描述、带类型的输入属性(参数),以及回传给 AI 的返回值。服务端使用 JSON-RPC 2.0,遵循 Model Context Protocol2024-11-05 版本。

备注

本页讲的是运行在固件内部的设备端 MCP 服务端。云端 MCP 服务端在 AI 智能体平台的 MCP 管理中单独配置。本服务端暴露的是你眼前这台设备的硬件。

为什么需要它

没有 MCP,AI 只能用语言回答。有了 MCP,同一个 AI 就能对设备做动作:因为你说太吵就调低音量、听到访客就拍张照、被问到时报出固件版本。你只写一次函数,AI 会根据你给的描述判断何时调用。

整体协作关系

生命周期

ai_mcp_init 是应用唯一需要发起的调用。它订阅 MQTT 连接事件,连接成功后启动服务端并注册内置工具。底层的 ai_mcp_server_* 系列则用于手动构建服务端或添加你自己的工具。

函数参数作用
ai_mcp_init订阅 MQTT 连接事件;连接后启动服务端并注册内置工具。
ai_mcp_deinit销毁服务端并释放其资源。
ai_mcp_server_initnameversion初始化服务端。name 为服务端(开发板)名称,version 为版本字符串。
ai_mcp_server_destroy销毁服务端及所有已注册工具。
ai_mcp_server_add_tooltool——MCP_TOOL_T *向服务端添加一个工具,所有权移交给服务端。
ai_mcp_server_find_toolname返回该名称对应的已注册工具,找不到则返回 NULL

头文件:ai_mcp.hai_mcp_init / ai_mcp_deinit)与 ai_mcp_server.h(其余全部)。所有可能失败的函数都返回 OPERATE_RET(成功为 OPRT_OK)。

注意

只能在 MQTT 连接成功后初始化服务端。ai_mcp_init 通过订阅 EVENT_MQTT_CONNECTED 已经替你做了这件事。仅当你自行管理该事件时,才需要直接调用 ai_mcp_server_init

工具

一个工具把回调与名称、描述绑定在一起。AI 调用工具时回调即运行:读取输入属性、完成工作、填好返回值。

typedef OPERATE_RET (*MCP_TOOL_CALLBACK)(const MCP_PROPERTY_LIST_T *properties,
MCP_RETURN_VALUE_T *ret_val,
void *user_data);

MCP_TOOL_T 保存工具的 namedescriptionpropertiescallbackuser_data

函数参数作用
ai_mcp_tool_registernamedescriptioncallbackuser_data...一次调用即创建工具并注册到服务端。可变参数为 MCP_PROPERTY_DEF_T * 属性定义,以 NULL 结尾。
ai_mcp_tool_createnamedescriptioncallbackuser_data仅创建工具而不注册。返回 MCP_TOOL_T *NULL
ai_mcp_tool_add_propertytoolprop向你创建的工具添加一个属性。
ai_mcp_tool_destroytool释放你创建但未交给服务端的工具。
提示

用点号命名工具,例如 device.audio.set_volume,并把描述写得像向一个人解释这个工具一样——AI 会读它来决定何时、如何调用工具。

便捷宏 AI_MCP_TOOL_ADD(name, description, callback, user_data, ...) 会调用 ai_mcp_tool_register 并替你补上 NULL 结尾,因此可直接传入属性定义。用 MCP_PROP_* 系列宏来构造这些定义:

定义
MCP_PROP_INT(name, desc)整数参数。
MCP_PROP_INT_DEF(name, desc, def)带默认值的整数。
MCP_PROP_INT_RANGE(name, desc, min, max)限定在 [min, max] 的整数。
MCP_PROP_INT_DEF_RANGE(name, desc, def, min, max)带默认值与范围的整数。
MCP_PROP_BOOL(name, desc) / MCP_PROP_BOOL_DEF(name, desc, def)布尔参数。
MCP_PROP_STR(name, desc) / MCP_PROP_STR_DEF(name, desc, def)字符串参数。

属性

属性是工具的一个带类型的输入参数。MCP_PROPERTY_T 保存属性的 nametypeMCP_PROPERTY_TYPE_E)、description、可选默认值,以及——针对整数——可选的 [min_val, max_val] 范围。一个 MCP_PROPERTY_LIST_T 最多容纳 MCP_MAX_PROPERTIES(16)个属性。

MCP_PROPERTY_TYPE_E 取值为 MCP_PROPERTY_TYPE_BOOLEANMCP_PROPERTY_TYPE_INTEGERMCP_PROPERTY_TYPE_STRING

当你不用 MCP_PROP_* 宏、而是手动构建属性列表时使用以下函数:

函数参数作用
ai_mcp_property_createnametypedescription创建属性。返回 MCP_PROPERTY_T *NULL
ai_mcp_property_set_default_boolpropvalue设置布尔默认值。
ai_mcp_property_set_default_intpropvalue设置整数默认值。
ai_mcp_property_set_default_strpropvalue设置字符串默认值。
ai_mcp_property_set_rangepropmin_valmax_val为整数属性限定范围。
ai_mcp_property_destroyprop释放一个属性。
ai_mcp_property_list_initlist初始化一个空属性列表。
ai_mcp_property_list_addlistprop向列表添加一个属性。
ai_mcp_property_list_findlistname按名称查找属性。
ai_mcp_property_list_destroylist释放列表中的所有属性。
备注

在回调内部,AI 为每个属性传入的值放在 prop->default_val 中——例如整数为 prop->default_val.int_val。按 name 匹配属性、检查 type,再读取对应的联合体字段。

返回值

返回值是工具回传给 AI 的内容。MCP_RETURN_VALUE_T 保存 typeMCP_RETURN_TYPE_E)及对应数据。

MCP_RETURN_TYPE_E 取值为 MCP_RETURN_TYPE_BOOLEANMCP_RETURN_TYPE_INTEGERMCP_RETURN_TYPE_STRINGMCP_RETURN_TYPE_JSONMCP_RETURN_TYPE_IMAGE

函数参数作用
ai_mcp_return_value_initret_valtype把返回值初始化为某种类型。
ai_mcp_return_value_set_boolret_valvalue返回一个布尔值。
ai_mcp_return_value_set_intret_valvalue返回一个整数。
ai_mcp_return_value_set_strret_valvalue返回一个字符串(内部会复制)。
ai_mcp_return_value_set_jsonret_valjson返回一个 cJSON 对象(所有权移交)。
ai_mcp_return_value_set_imageret_valmime_typedatadata_len返回一张图片,数据会经 Base64 编码用于传输。
ai_mcp_return_value_cleanupret_val释放返回值占用的内存。

图片的 MIME 类型传入 MCP_IMAGE_MIME_TYPE_JPEGMCP_IMAGE_MIME_TYPE_PNG

完整示例

定义一个上报门是否打开的工具,给它一个布尔输入属性和一个布尔返回值并注册,让 AI 可以调用。AI_MCP_TOOL_ADD 宏一次完成创建与注册:

#include "ai_mcp_server.h"

// AI 想知道门的状态时调用本回调。它读取可选的 "refresh" 属性,
// 采样传感器,返回一个布尔值。
static OPERATE_RET door_is_open_cb(const MCP_PROPERTY_LIST_T *properties,
MCP_RETURN_VALUE_T *ret_val,
void *user_data)
{
bool refresh = false;
for (int i = 0; i < properties->count; i++) {
MCP_PROPERTY_T *prop = properties->properties[i];
if (strcmp(prop->name, "refresh") == 0 && prop->type == MCP_PROPERTY_TYPE_BOOLEAN) {
refresh = prop->default_val.bool_val;
break;
}
}

bool open = read_door_sensor(refresh); // 你的硬件读取
ai_mcp_return_value_set_bool(ret_val, open);
return OPRT_OK;
}

OPERATE_RET register_door_tool(void)
{
return AI_MCP_TOOL_ADD(
"device.door.is_open",
"Report whether the door is currently open. Returns true if open.",
door_is_open_cb,
NULL,
MCP_PROP_BOOL_DEF("refresh", "Re-read the sensor instead of using the cached value.", false));
}

在服务端启动后调用 register_door_tool()——例如在你自己的 MQTT 连接处理函数里,与 ai_mcp_init() 一起。

相关文档