主题
7.4 MCP 接入
JoyInside 支持 MCP(Model Context Protocol)协议,让智能体在对话过程中动态调用设备端能力(如获取设备状态、控制音量等),实现"边说边控"的自然交互。
本章重点讲解「设备端 MCP 接入」——服务端通过 WebSocket 通道向设备发起握手、拉取工具列表、调用工具的完整协议对接, 且目前仅支持JoyInside产品类型为lite版的设备端接入。
7.4.1 MCP 是什么?
MCP 模型上下文协议 是一种用于在大语言模型(LLM)与外部工具/资源之间建立标准化通信的开放协议。它定义了一套统一的请求/响应格式,使 LLM 能够:
- 能力提供:MCP服务(无论是标准服务还是设备端MCP)提供一组可被调用的工具(tool)或接口。
- 模型决策:模型在处理用户请求时,会根据输入的提示词和提供的工具列表,决定调用哪一个工具来完成任务。
- 能力调用:模型将决策结果(包含工具名及参数)返回给服务端。服务端再根据决策,通过HTTP请求调用标准MCP服务,或通过WebSocket事件通知设备端来执行相应操作。
MCP 可视作为一种“通用插座”或“Type-C 接口”,让大模型能够用同一种方式连接各种数据和工具。所有工具按同一套接口标准开发,大模型插上这个“插座”,就能直接调用各种能力。
7.4.2 MCP 两种实现方式
JoyInside MCP 支持两种实现方式,根据工具能力部署位置的不同进行选择:
方式一:服务端 MCP(Server-side MCP)
标准MCP服务: 一种业界通用的标准能力。开发者在服务端部署一个MCP服务,通过HTTP(S)协议暴露接口。客户端(如模型)通过调用MCP网关,获取到服务端提供的接口列表(tool list),并由模型决策使用哪个接口及参数,从而实现对服务端能力的调用。
即:服务端侧集成外部 MCP Server(如第三方 API 封装),工具逻辑运行在云端,无需设备端参与,服务端自行管理工具的拉取与调用。MCP服务如何注册与接入,具体参见JoyInside 运营平台产品说明 MCP服务。
方式二:设备端 MCP(Device MCP)
设备端MCP: 一种为特定设备(如采用“小智”开源框架的方案商设备)设计的定制化方案。设备端通过WebSocket与服务端通信,设备端通过上行事件将MCP能力(tool list)推送给服务端。当模型决策需要调用某个设备能力时,服务端会通过下行事件通知设备端执行相应操作。
即:设备端作为 MCP Server,直接暴露本机工具(如传感器读取、硬件控制)。服务端通过 WebSocket 向设备发起握手与调用。本文重点讲解此接入方式。
在 JoyInside 场景中,MCP 用于让云端智能体调用设备端本地能力。设备作为 MCP Server 暴露工具,服务端作为 MCP Client 发起调用,从而实现语音控制硬件、读取传感器数据等场景。
| 角色 | 说明 |
|---|---|
| MCP Client | JoyInside 服务端,负责发起 initialize 握手、拉取工具列表、调用工具 |
| MCP Server | 设备端,负责响应握手、返回工具清单、执行工具调用并回传结果 |
| 传输通道 | 复用语音对话 WebSocket 连接,contentType 为 MCP 的消息即 MCP 协议帧 |
7.4.3 设备端 MCP 交互时序
设备端 MCP交互时序主要包含三个阶段:初始化握手 → 分页拉取工具列表 → 工具调用。整个过程复用语音对话 WebSocket 连接,在 CLIENT_VOICE_CHAT_UPDATE 上行事件中开启 MCP 功能开关后触发。
7.4.4 设备端 MCP 接入
特别说明:设备端 MCP 接入,目前仅支持JoyInside 产品类型为lite版的设备端接入。

以下按交互时序中的步骤节点逐一说明,每个节点均包含上行/下行事件协议结构及JSON示例。
7.4.4.1 MCP 功能开关
设备通过 CLIENT_VOICE_CHAT_UPDATE上行事件上报 features.mcp.deviceMcp = true 开启设备端 MCP 工具拉取。缺省或 false 均视为关闭,服务端不会下发 initialize 握手。
上行事件(设备 → 服务端)
上行参见: 更新语音对话配置(CLIENT_VOICE_CHAT_UPDATE)
在基础字段之上,需额外传入:
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| content.eventData.features | object | 否 | 拓展功能配置 |
| content.eventData.features.mcp | object | 否 | 设备端 MCP 功能配置 |
| content.eventData.features.mcp.deviceMcp | bool | 否 | true 开启设备端 MCP 功能;false/缺省为关闭 |
JSON示例(点击展开)
json
{
"mid": "24279824-8def-48c6-8d1c-ea8ec3aa50ac",
"contentType": "EVENT",
"content": {
"eventType": "CLIENT_VOICE_CHAT_UPDATE",
"eventData": {
"features": {
"mcp": {
"deviceMcp": true
}
}
}
}
}下行响应(服务端 → 设备)
服务端通过 SERVER_VOICE_CHAT_UPDATED 下行事件回送配置更新结果,表示 MCP 功能开关已生效。
下行参见: 更新配置成功响应(SERVER_VOICE_CHAT_UPDATED)
在基础字段之上,另额外下发:
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| content.eventData.features | object | 否 | 拓展功能配置 |
| content.eventData.features.mcp | object | 否 | 设备端 MCP 功能配置 |
| content.eventData.features.mcp.deviceMcp | bool | 否 | true 开启设备端 MCP 功能;false/缺省为关闭 |
JSON示例(点击展开)
json
{
"code": 200,
"msg": "Event success",
"requestId": "222b0306-4e95-4163-9683-94f8c29609bf",
"mid": "24279824-8def-48c6-8d1c-ea8ec3aa50ac",
"contentType": "EVENT",
"content": {
"eventType": "SERVER_VOICE_CHAT_UPDATED",
"eventData": {
"features": {
"mcp": {
"deviceMcp": true
}
}
}
},
"t": 1752724083510
}7.4.4.2 工具就绪
MCP 功能开启后,服务端依次发起 initialize 握手事件与 tools/list 拉取事件,完成标记工具就绪。
(1)初始化握手
服务端下发 initialize 事件请求,设备端回复响应表示 MCP 通道建立成功。
下行请求(服务端 → 设备)
下行参见: 设备端MCP事件 - 下行通用消息结构
在基础字段之上,content.params 独有配置如下:
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| content.id | Integer | 是 | 固定为 1,标识 initialize 请求 |
| content.method | string | 是 | 固定为 initialize |
| content.params | object | 是 | 初始化参数 |
| content.params.protocolVersion | string | 是 | MCP 协议版本号 |
JSON示例(点击展开)
json
{
"code": 200,
"msg": "mcp event",
"requestId": "会话 requestId",
"contentType": "MCP",
"content": {
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2026-06-25"
}
},
"t": 1750000000000
}上行响应(设备 → 服务端)
设备回复 content.id = 1,与下行 initialize 的 id 一致,表示握手成功。
上行参见: 设备端MCP事件 - 上行通用消息结构
本次响应仅需回传 content.id,无额外 result / error 字段:
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| content.id | Integer | 是 | 固定为 1,与下行 initialize 请求 id 一致 |
JSON示例(点击展开)
json
{
"mid": "24279824-8def-48c6-8d1c-ea8ec3aa50ac",
"contentType": "MCP",
"content": {
"id": 1
}
}(2)分页拉取工具列表
握手成功后,服务端发起 tools/list 事件请求拉取设备可用工具。支持分页:首次拉取不携带 cursor,后续续拉携带上一批响应返回的 nextCursor,直到 nextCursor 为空表示拉取完成。
下行请求(服务端 → 设备)
下行参见: 设备端MCP事件 - 下行通用消息结构
在基础字段之上,content.params 独有配置如下:
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| content.id | Integer | 是 | 固定为 2,标识 tools/list 请求 |
| content.method | string | 是 | 固定为 tools/list |
| content.params | object | 否 | 分页参数。首次拉取省略;续拉时必填 |
| content.params.cursor | string | 否 | 分页游标,取上一批响应返回的 nextCursor |
首次拉取请求示例:
JSON示例(点击展开)
json
{
"code": 200,
"msg": "mcp event",
"requestId": "会话 requestId",
"contentType": "MCP",
"content": {
"id": 2,
"method": "tools/list"
},
"t": 1750000000000
}续拉请求示例(携带 cursor):
JSON示例(点击展开)
json
{
"code": 200,
"msg": "mcp event",
"requestId": "会话 requestId",
"contentType": "MCP",
"content": {
"id": 2,
"method": "tools/list",
"params": {
"cursor": "上一批响应返回的 nextCursor"
}
},
"t": 1750000000000
}上行响应(设备 → 服务端)
设备回复 content.id = 2,返回工具列表及分页游标。nextCursor 非空表示还有下一批,为空(或缺省)表示拉取完成。
上行参见: 设备端MCP事件 - 上行通用消息结构
在基础字段之上,content.result 独有配置如下:
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| content.id | Integer | 是 | 固定为 2,与下行 tools/list 请求 id 一致 |
| content.result | object | 是 | 工具列表结果 |
| content.result.tools | array | 是 | 工具数组 |
| content.result.tools[].name | string | 是 | 工具名称 |
| content.result.tools[].description | string | 否 | 工具描述 |
| content.result.tools[].inputSchema | object | 否 | 工具入参 Schema(JSON Schema 格式) |
| content.result.nextCursor | string | 否 | 分页游标。非空表示还有下一批,为空/缺省表示拉取完成 |
JSON示例(点击展开)
json
{
"mid": "24279824-8def-48c6-8d1c-ea8ec3aa50ac",
"contentType": "MCP",
"content": {
"id": 2,
"result": {
"tools": [
{
"name": "self.get_device_status",
"description": "Provides the real-time information of the device...",
"inputSchema": {
"type": "object",
"properties": {}
}
},
{
"name": "self.audio_speaker.set_volume",
"description": "Set the volume of the audio speaker...",
"inputSchema": {
"type": "object",
"properties": {
"volume": {
"type": "integer",
"minimum": 0,
"maximum": 100
}
},
"required": ["volume"]
}
}
],
"nextCursor": ""
}
}
}7.4.4.3 工具调用
工具就绪后,当智能体对话过程中需要调用设备能力时,服务端通过 tools/call 事件向设备发起调用请求。content.id 从 3 起步依次递增。
下行请求(服务端 → 设备)
下行参见: 设备端MCP事件 - 下行通用消息结构
在基础字段之上,content.params 独有配置如下:
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| content.id | Integer | 是 | 从 3 起步,依次递增,标识本次调用 |
| content.method | string | 是 | 固定为 tools/call |
| content.params | object | 是 | 调用参数 |
| content.params.name | string | 是 | 要调用的工具名称 |
| content.params.arguments | object | 是 | 工具入参,结构与工具 inputSchema 对应 |
JSON示例(点击展开)
json
{
"code": 200,
"msg": "mcp event",
"requestId": "会话 requestId",
"contentType": "MCP",
"content": {
"id": 3,
"method": "tools/call",
"params": {
"name": "self.get_device_status",
"arguments": {}
}
},
"t": 1750000000000
}上行响应(设备 → 服务端)—— 调用成功
设备回复 content.id 与请求一致,content.result 为结果封装。isError = false 表示成功,结果取 content[0].text。
上行参见: 设备端MCP事件 - 上行通用消息结构
在基础字段之上,content.result 独有配置如下:
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| content.id | Integer | 是 | 与下行 tools/call 请求 id 一致 |
| content.result | object | 是 | 调用结果封装 |
| content.result.content | array | 是 | 结果内容数组 |
| content.result.content[].type | string | 是 | 内容类型,如 text |
| content.result.content[].text | string | 是 | 结果文本(可能为 JSON 字符串) |
| content.result.isError | bool | 是 | false 表示成功,true 表示业务出错 |
JSON示例(点击展开)
json
{
"mid": "24279824-8def-48c6-8d1c-ea8ec3aa50ac",
"contentType": "MCP",
"content": {
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"success\": true,\n \"result\": 7\n}"
}
],
"isError": false
}
}
}上行响应(设备 → 服务端)—— 业务出错
isError = true 表示业务执行失败,结果取 content[0].text 获取错误信息。
JSON示例(点击展开)
json
{
"mid": "24279824-8def-48c6-8d1c-ea8ec3aa50ac",
"contentType": "MCP",
"content": {
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "tool execution failed: division by zero"
}
],
"isError": true
}
}
}上行响应(设备 → 服务端)—— 协议错误
设备以 JSON-RPC error 字段回复(而非 result),表示协议层错误(如方法不存在)。
上行参见: 设备端MCP事件 - 上行通用消息结构
在基础字段之上,content.error 独有配置如下:
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| content.id | Integer | 是 | 与下行 tools/call 请求 id 一致 |
| content.error | object | 是 | 错误信息封装 |
| content.error.message | string | 是 | 错误描述信息 |
JSON示例(点击展开)
json
{
"mid": "24279824-8def-48c6-8d1c-ea8ec3aa50ac",
"contentType": "MCP",
"content": {
"id": 3,
"error": {
"message": "method not found"
}
}
}下一步
- 语音对话协议全集:读 B.WebSocket 协议参考 —— WebSocket 建连、音频上下行、TTS 事件等基础协议
- 外部智能体接入:读 7.1 外部智能体接入 —— 通过 A2A 协议接入企业自有智能体
- 外部媒体资源接入:读 7.2 外部媒体资源接入 —— 有声书/音乐内容源对接