Skip to content

1. MCP 服务简介

MCP 是专门为 AI 平台设计的,用于处理 AI 平台发送的指令,并返回处理结果的服务。MCP 服务需要与 AI 平台进行通信,这个过程已经在 SDK 中实现,开发者只需要如何添加MCP功能,实现MCP控制与查询即可。

▫️MCP 参考资料

2. MCP 工具开发

工具 是模型上下文协议(MCP)中的基础功能单元(原语),用于让服务器向客户端暴露可执行功能。通过工具,LLM 可以与外部系统交互(如控制设备、获取状态)。工具由 AI 模型自动调用(可配置人工审批)。

简单来说:AI 模型想要控制一些设备或者获取状态,就是通过调用 工具 来实现。

▫️MCP 工具开发流程

    1. 创建工具,并设置工具的名称、描述、参数、回调函数等。
    1. 工具 注册到 MCP 服务中。
    1. 编写 工具 的回调函数,实现 工具 的功能。

▫️MCP 工具开发示例

🔹步骤1:创建工具

创建 工具 需要通过 mcp_server_tool_t 结构体来实现,结构体定义如下:

c
/**
 * @brief 工具结构体
 *  用于创建 MCP 工具和描述 MCP 工具的输入参数
 */
typedef struct {
    char *name;                   // 工具名称
    char *description;            // 工具描述
	void (*setRequestHandler)(void *);	 // 工具设置回调函数
	void (*checkRequestHandler)(void *); // 工具查询回调函数
	inputSchema_t inputSchema;			 // 输入参数
} mcp_server_tool_t;
c

/**
 * @brief 输入参数结构体
 *  用于描述工具的输入参数,告诉 AI 平台,该工具提供了哪些参数,参数的用途是什么。
 */

typedef struct
{
	properties_t properties[MCP_SERVER_TOOL_PROPERTIES_NUM]; // 属性
	methods_t methods[MCP_SERVER_TOOL_METHODS_NUM];			 // 方法
} inputSchema_t;
c
/**
 * @brief 属性结构体
 *  用于描述工具的属性,告诉 AI 平台,该工具提供了哪些可以读取的属性,比如:查询当前音量
 */
typedef struct
{
	char *name;					 // 属性名称
	char *description;			 // 属性描述
	mcp_server_tool_type_t type; // 属性类型
} properties_t;
c
/**
 * @brief 方法结构体
 *  用于描述工具的方法,告诉 AI 平台,该工具提供了哪些可以调用的方法,比如:设置音量
 */
typedef struct
{
	char *name;														 // 方法名称
	char *description;												 // 参数描述
	parameters_t parameters[MCP_SERVER_TOOL_METHODS_PARAMETERS_NUM]; // 方法参数
} methods_t;
c
/**
 * @brief 参数结构体
 *  用于 方法的输入参数,告诉 AI 平台, 需要下发什么数据,才能实现控制,比如:设置音量时需要下发的关键字和值的范围
 */
typedef struct
{
	char *name;					 // 参数名称
	char *description;			 // 参数描述
	mcp_server_tool_type_t type; // 参数类型
} parameters_t;
结构体说明:
  • name:工具名称,用于标识工具,工具的唯一标识符。
  • description:工具描述,用于描述工具的功能。
  • setRequestHandler:工具设置回调函数,用于处理工具的设置请求。
  • checkRequestHandler:工具查询回调函数,用于处理工具的查询请求。
  • inputSchema:输入参数,用于描述工具的输入参数,MCP 查询工具时,会通过该参数获取工具的输入参数。如:当前音量
    • properties:属性,用于描述工具的属性。
      • name:属性名称,用于标识属性。
      • description:属性描述,用于描述属性的用途。
      • type:属性类型,用于描述属性的值类型。
    • methods:方法,用于描述工具的方法。需要实现MCP控制时,需要实现该参数。如:设置音量
      • name:方法名称,用于标识方法。
      • description:方法描述,用于描述方法的用途。
      • parameters:方法参数,用于描述方法的输入参数。
        • name:参数名称,用于标识参数。
        • description:参数描述,用于描述参数的用途。描述参数的具体功能,比如:音量的设置范围,亮度的设置范围等
        • type:参数类型,用于描述参数的值类型。
  • 创建示例(LED 工具):

aipi-palchatv1/mcp_sercer/user_mcp_tools.cuser_cmp_creat_tools_examples 函数中定义工具。

c
int user_cmp_creat_tools_examples(cJSON *toolsList)
{
	if (toolsList == NULL)
	{
		return -1;
	}
	cJSON *json_toolsList = toolsList;
	mcp_server_tool_t led = {
			.name = "Light",
			.description = "控制是否打开灯光",
			.setRequestHandler = NULL, //后续实现的设置回调
			.checkRequestHandler = NULL,//后续实现的查询回调
			.inputSchema = {
				  // 设置属性,让小智AI读取当前LED状态
				.properties[0].name = "enabled",
				.properties[0].description = "当前灯光状态",
				.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN,
				  // 设置方法,让小智AI控制LED
				.methods[0].name = "SetEnabled",
				.methods[0].description = "设置是否打开灯光",
				  // 添加设置参数
				.methods[0].parameters[0].name = "enabled",
				.methods[0].parameters[0].description = "true 表示打开灯光,false 表示关闭灯光",
				.methods[0].parameters[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN,
			},
	};
}

🔹步骤2:注册工具

通过 mcp_server_add_tool_to_toolList 函数将工具添加到 MCP 服务的工具列表,使其被 AI 平台识别。

c
/**
 * @brief 添加工具到工具列表
 *
 * @param toolsList 工具列表
 * @param tool 工具对象
 * @return int int 0 表示成功,-32602 表示失败
 */
int mcp_server_add_tool_to_toolList(void *toolsList, mcp_server_tool_t *tool)
  • 示例(承接 LED 工具创建):
c
mcp_server_add_tool_to_toolList(json_toolsList, &led);

🔹步骤3:实现设置回调函数

  • (1)设置回调函数(处理控制请求)

当 AI 平台发送控制指令(如 “打开灯光”)时,MCP 服务会调用此函数。参数 value 为 AI 平台传入的控制值(类型与工具定义的 type 一致)。

  • setRequestHandler函数原型
c
typedef struct
{
	char *name;							 // 工具名称
	char *description;					 // 工具描述
	void (*setRequestHandler)(void *);	 // 工具回调函数
	void (*checkRequestHandler)(void *); // 工具回调函数
	inputSchema_t inputSchema;			 // 输入参数
} mcp_server_tool_t;

参数说明

  • void *(void * value):会自动输出为 AI 设置结果,比如:设置LED时,value为 int 型,当设置类型为stringtext时,value为char *型。

因此,你只需要在回调函数中使用 value 参数,作为控制的识别即可。

  • 设置回调示例,控制LED为例
c
mcp_server_tool_t led = {
			.name = "Light",
			.description = "控制是否打开灯光",
			.setRequestHandler = setLEDRequestHandler, //设置回调
			.checkRequestHandler = NULL,//暂不设置回调
			.inputSchema = {
				// 设置属性,让小智AI读取当前LED状态
				.properties[0].name = "enabled", 
				.properties[0].description = "当前灯光状态",
				.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN,
				// 设置方法,让小智AI控制LED
				.methods[0].name = "SetEnabled",
				.methods[0].description = "设置是否打开灯光",
				// 添加设置参数
				.methods[0].parameters[0].name = "enabled",
				.methods[0].parameters[0].description = "true 表示打开灯光,false 表示关闭灯光",
				.methods[0].parameters[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN,
			},
	};
c
/**
 * @brief Set the LED Request Handler object
 *
 * @param value
 */
static void setLEDRequestHandler(void *value)
{
	if (value == NULL)
	{
		return;
	}
	led_state = *(int *)value; //获取控制结果
	// 设置 LED 状态
	bl_gpio_output_set(GPIO_LED_PIN, led_state); //控制LED
}
  • (2)查询回调函数(处理状态查询

当 AI 平台查询状态(如 “灯光是否打开”)时,MCP 服务会调用此函数。需将查询结果写入 returnValues_t 结构体返回。

  • checkRequestHandler结构体定义
c
typedef struct
{
	char *name;							 // 工具名称
	char *description;					 // 工具描述
	void (*setRequestHandler)(void *);	 // 工具回调函数 
	void (*checkRequestHandler)(void *); // 工具回调函数
	inputSchema_t inputSchema;			 // 输入参数
} mcp_server_tool_t;

参数说明

  • void *(void * value):为需要返回的查询结果,但是它的类型为固定的 returnValues_t 结构体,结构体原型如下:
c
/** 
 * @brief MCP 服务器工具结构体
 *
 */
typedef struct
{
	int error_code; // 错误码
	char value[8];	// 内容
	char *type;		//  类型
} returnValues_t;

returnValues_t 参数说明

  • int error_code:错误码,0 表示成功,-32602 表示失败。
  • char value[8]:返回内容,
  • char *type:返回类型,固定为 text
  • 示例(查询 LED 状态):
c
mcp_server_tool_t led = {
			.name = "Light",
			.description = "控制是否打开灯光",
			.setRequestHandler = setLEDRequestHandler, //设置回调 
			.checkRequestHandler = checkLEDRequestHandler,//暂不设置回调
			.inputSchema = {
				// 设置属性,让小智AI读取当前LED状态
				.properties[0].name = "enabled", 
				.properties[0].description = "当前灯光状态",
				.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN,
				// 设置方法,让小智AI控制LED
				.methods[0].name = "SetEnabled",
				.methods[0].description = "设置是否打开灯光",
				// 添加设置参数
				.methods[0].parameters[0].name = "enabled",
				.methods[0].parameters[0].description = "true 表示打开灯光,false 表示关闭灯光",
				.methods[0].parameters[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN,
			},
	};
c
/**
 * @brief Set the LED Request Handler object
 *
 * @param value
 */
static void checkLEDRequestHandler(void *value)
{
	if (value == NULL)
	{
		return;
	}
	//读取 led_state 状态
	returnValues_t *returnValues = (returnValues_t *)value;
	//把状态值转化成字符转复制给 returnValues->value
	sprintf(returnValues->value, "%s", led_state ? "true" : "false");
}

▫️完整示例 (LED+音量控制)

点击展开查看
c
/**
 * @file user_mcp_tools.c
 * @author Seahi-Mo (seahi-mo@foxmail.com)
 * @brief
 * @version 0.1
 * @date 2025-08-25
 *
 * @copyright Ai-Thinker co.,ltd (c) 2025
 *
 */
#if 1
#include <FreeRTOS.h>
#include <string.h>
#include <task.h>
#include "timers.h"
#include "user_mcp_tools.h"
#include <bl_gpio.h>
#include "vb6824.h"
#include "ws_demo.h"
#include "easyflash_common.h"
#include "user_mcp_tools.h"

#define GPIO_LED_PIN 17

static bool led_state = false;
/**
 * @brief 初始化 MCP 设备
 *
 */
void user_cmp_tools_hw_init(void)
{
	// 初始化 LED GPIO
	bl_gpio_enable_output(GPIO_LED_PIN, 0, 0);
	if (mcp_tool_arry[0].name == NULL)
	{
		memset(mcp_tool_arry, 0, sizeof(mcp_server_tool_t) * MCP_SERVER_TOOL_NUMBLE_LEN);
	}
}
/**
 * @brief Set the Volume Request Handler object
 *
 * @param value
 */
static void setVolumeRequestHandler(void *value)
{
	int volume = *(int *)value;
	uint8_t vb_volume_value = volume * VB6824_MAX_VOLUME / 100;
	if (vb_volume_value < VB6824_MIN_VOLUME)
	{
		vb_volume_value = VB6824_MIN_VOLUME;
	}
	printf("[%s()-%d]cloud volume:%d set volume:%d\r\n", __func__, __LINE__, volume, vb_volume_value);
	vb6824_set_volume(vb_volume_value);
	ef_set_u8(volume_key, vb_volume_value);
}
/**
 * @brief Set the Volume Request Handler object
 *
 * @param value
 */
static void checkVolumeRequestHandler(void *value)
{
	int volume;
	returnValues_t *returnValues = (returnValues_t *)value;
	uint8_t vb_volume_value = 0;
	ef_get_u8(volume_key, &vb_volume_value);
	volume = vb_volume_value * 100 / VB6824_MAX_VOLUME;
	sprintf(returnValues->value, "%d", volume);
	printf("[%s()-%d]get volume:%s\r\n", __func__, __LINE__, returnValues->value);
}
/**
 * @brief Set the LED Request Handler object
 *
 * @param value
 */
static void setLEDRequestHandler(void *value)
{
	if (value == NULL)
	{
		return;
	}
	led_state = *(int *)value;
	// 设置 LED 状态
	bl_gpio_output_set(GPIO_LED_PIN, led_state);
}

static void checkLEDRequestHandler(void *value)
{
	if (value == NULL)
	{
		return;
	}
	returnValues_t *returnValues = (returnValues_t *)value;
	sprintf(returnValues->value, "%s", led_state ? "true" : "false");
}

/**
 * @brief MCP 服务器示例
 *
 */
int user_cmp_creat_tools_examples(cJSON *toolsList)
{
	if (toolsList == NULL)
	{
		return -1;
	}
	cJSON *json_toolsList = toolsList;
	// 添加一个扬声器工具
	mcp_server_tool_t speaker = {
		.name = "Speaker",
		.description = "扬声器",
		.inputSchema = {
			// 设置属性,让小智AI读取当前音量
			.properties = {
				{"volume", "当前音量值", MCP_SERVER_TOOL_TYPE_NUMBER},
			},
			// 设置方法,让小智AI控制音量
			.methods = {
				{"SetVolume", "设置音量", {{"volume", "音量值,0到100之间的整数", MCP_SERVER_TOOL_TYPE_NUMBER}}},
			},
		},
		.setRequestHandler = setVolumeRequestHandler,
		.checkRequestHandler = checkVolumeRequestHandler,
	};
	// 添加一个点灯工具
	mcp_server_tool_t led = {
		.name = "Light",
		.description = "控制是否打开灯光",
		.setRequestHandler = setLEDRequestHandler,
		.checkRequestHandler = checkLEDRequestHandler,
		.inputSchema = {
			// 设置属性,让小智AI读取当前LED状态
			.properties[0].name = "enabled",
			.properties[0].description = "当前灯光状态",
			.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN,
			// 设置方法,让小智AI控制LED
			.methods[0].name = "SetEnabled",
			.methods[0].description = "设置是否打开灯光",
			// 添加设置参数
			.methods[0].parameters[0].name = "enabled",
			.methods[0].parameters[0].description = "true 表示打开灯光,false 表示关闭灯光",
			.methods[0].parameters[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN,
		},
	};

	int ret = mcp_server_add_tool_to_toolList(json_toolsList, &speaker);
	ret = mcp_server_add_tool_to_toolList(json_toolsList, &led);

	return ret;
}
#endif
c
/**
 * @file user_mcp_tools.h
 * @author Seahi-Mo (seahi-mo@foxmail.com)
 * @brief
 * @version 0.1
 * @date 2025-08-25
 *
 * @copyright Ai-Thinker co.,ltd (c) 2025
 *
 */
#if 1
#ifndef USER_MCP_TOOLS_H
#define USER_MCP_TOOLS_H

#include "mcp_server.h"

/**
 * @brief 设备初始化
 *
 */
void user_cmp_tools_hw_init(void);

int user_cmp_creat_tools_examples(cJSON *toolsList);
#endif // USER_MCP_TOOLS_H
#endif

Released under the MIT License.