Skip to content

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 ClientJoyInside 服务端,负责发起 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版的设备端接入。

lite版

以下按交互时序中的步骤节点逐一说明,每个节点均包含上行/下行事件协议结构及JSON示例。

7.4.4.1 MCP 功能开关 ​

设备通过 CLIENT_VOICE_CHAT_UPDATE上行事件上报 features.mcp.deviceMcp = true 开启设备端 MCP 工具拉取。缺省或 false 均视为关闭,服务端不会下发 initialize 握手。

上行事件(设备 → 服务端)

上行参见: 更新语音对话配置(CLIENT_VOICE_CHAT_UPDATE)

在基础字段之上,需额外传入:

字段名称类型必选说明
content.eventData.featuresobject否拓展功能配置
content.eventData.features.mcpobject否设备端 MCP 功能配置
content.eventData.features.mcp.deviceMcpbool否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.featuresobject否拓展功能配置
content.eventData.features.mcpobject否设备端 MCP 功能配置
content.eventData.features.mcp.deviceMcpbool否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.idInteger是固定为 1,标识 initialize 请求
content.methodstring是固定为 initialize
content.paramsobject是初始化参数
content.params.protocolVersionstring是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.idInteger是固定为 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.idInteger是固定为 2,标识 tools/list 请求
content.methodstring是固定为 tools/list
content.paramsobject否分页参数。首次拉取省略;续拉时必填
content.params.cursorstring否分页游标,取上一批响应返回的 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.idInteger是固定为 2,与下行 tools/list 请求 id 一致
content.resultobject是工具列表结果
content.result.toolsarray是工具数组
content.result.tools[].namestring是工具名称
content.result.tools[].descriptionstring否工具描述
content.result.tools[].inputSchemaobject否工具入参 Schema(JSON Schema 格式)
content.result.nextCursorstring否分页游标。非空表示还有下一批,为空/缺省表示拉取完成
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.idInteger是从 3 起步,依次递增,标识本次调用
content.methodstring是固定为 tools/call
content.paramsobject是调用参数
content.params.namestring是要调用的工具名称
content.params.argumentsobject是工具入参,结构与工具 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.idInteger是与下行 tools/call 请求 id 一致
content.resultobject是调用结果封装
content.result.contentarray是结果内容数组
content.result.content[].typestring是内容类型,如 text
content.result.content[].textstring是结果文本(可能为 JSON 字符串)
content.result.isErrorbool是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.idInteger是与下行 tools/call 请求 id 一致
content.errorobject是错误信息封装
content.error.messagestring是错误描述信息
JSON示例(点击展开)
json
{
  "mid": "24279824-8def-48c6-8d1c-ea8ec3aa50ac",
  "contentType": "MCP",
  "content": {
    "id": 3,
    "error": {
      "message": "method not found"
    }
  }
}

下一步 ​