跳到正文
dsh-market 浏览插件 GitHub EN

toddpan/dsh-xiaozhi

把小智语音助手接入 DSH Web:DSH 作为 MCP 工具提供方,把 35 个 DSH Web 接口封装成 16 个语音友好工具,覆盖工作区、会话、对话、模型、设置与文件;默认出站 WebSocket 连到小智 MCP 接入点(无需公网 IP 和端口转发),可同时绑定多台设备,并自带实时刷新状态的 DSH 设置页。

Star 数 ★ 0 分类 语音与音频 收录于 2026-09-29

安装

在 DeepSeek Harness 里通过 dsh-market 安装

dsh plugin --profile web add dshmarket

或使用命令行

dsh plugin --profile web add github:toddpan/dsh-xiaozhi

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络。请先审阅源码,并尽量锁定 commit(github:owner/repo#sha)。

截图

README

把小智(Xiaozhi)语音助手接入 DeepSeek Harness(DSH)Web:DSH 作为 MCP 工具提供方, 通过 WebSocket 上的 JSON-RPC 2.0 把工作区、会话、模型、设置、文件等能力封装成工具,供小智语音调用。

中文 · English · 安装与验证 · 工具清单

把小智(Xiaozhi)语音助手接入 DSH Web:DSH 作为 MCP 工具提供方,把 35 个 DSH Web 接口封装成 16 个 语音友好工具,自带 DSH Web 设置页。 Connect the Xiaozhi voice assistant to DSH Web as an MCP tool provider — 35 endpoints, 16 voice-friendly tools.

实施前的设计提案(架构 ADR、v2 复核、设置页 UX 走查)归档在 docs/design/,其中与实际交付不一致的地方已逐条注明。


1. 它解决什么问题

DSH Web 的能力都在 HTTP REST 接口上,而小智只认 MCP(Model Context Protocol)。 本插件坐在两者中间:

 你说一句话
     │
     ▼
┌─────────────┐   MCP(JSON-RPC 2.0 / WebSocket)   ┌──────────────────────────┐
│  小智助手    │ ◄───────────────────────────────► │  dsh-xiaozhi (Host 半边)  │
│ (App/硬件)   │   initialize / tools/list / call   │  ├ MCP 会话与工具注册表    │
└─────────────┘                                    │  ├ 能力→REST 路由映射      │
                                                   │  └ LocalInvoker(进程内调用)│
                                                   └───────────┬──────────────┘
                                                               │ 不走网络,直接调用
                                                               ▼
                                                   ┌──────────────────────────┐
                                                   │  DSH Web REST 路由(内置副本)│
                                                   └──────────────────────────┘

三个关键设计决定:

  1. DSH 永远是 MCP 的“服务端/工具提供方”。 两种传输方式下都由 DSH 应答 initialize / ping / tools/list / tools/call,从不主动发起这些请求。
  2. 默认主动外连(endpoint 模式)。 DSH 作为 WebSocket 客户端连到小智官方 MCP 接入点,所以不需要公网 IP、端口映射或反向代理。
  3. 进程内调用,而不是回环 HTTP。 工具调用通过 LocalInvoker 直接打到内置的 DSH REST 路由,无需猜测 DSH 自身的 host/port/鉴权,也不依赖外部服务。

2. 快速开始(3 步)

前提:DSH Web 已经在跑(dsh web,默认 http://127.0.0.1:3080);你有小智账号并能打开它的 「MCP 接入点」页面。

  1. 安装插件(在本仓库目录下执行):

    dsh plugin add https://github.com/toddpan/dsh-xiaozhi
    

    也可以在 DSH Web 的「设置 → 插件」里用「安装本地目录」选择该目录。

  2. 拿到接入点地址并添加设备:打开 DSH Web →「设置 → 小智接入」→「接入配置」, 点「添加设备」,把小智后台的 MCP 接入点 WebSocket 地址(形如 wss://api.xiaozhi.me/mcp/?token=…)粘贴进去,点「保存并重载」。 可以重复添加,同时绑定多台小智设备/智能体,每台独立连接、独立显示状态。

  3. 看状态:回到「状态」页签,连接状态应为 connected;状态页每 3 秒自动刷新, 点「立即重连」或某台设备的「重连」后,无需手动刷新即可看到状态变化。 点一次「测试连接」可以看到真实握手结果。

    然后在手机上对小智说:“用 dsh 看一下我的会话列表” 或 “让 DSH 汇报一下运行状态”。

接入点地址含 token,属于敏感信息。设置页读回时只显示 token=***, 保存时也会被识别为「未修改」,不会把掩码写进配置。详见 §7。


3. 两种传输方式

endpoint(默认,推荐) server(自建服务端)
谁发起连接 DSH 主动外连小智接入点 小智服务端连到 DSH
需要公网可达吗 不需要 需要(或反向代理 / 内网同段)
主要配置 endpoints(多设备列表)、endpointHeaders serverPath、serverPort、serverToken
适用场景 小智官方 MCP 接入点、家用/办公本机 自建 xiaozhi-esp32-server、内网统一网关

两种方式可以同时开启:mode 决定主通道,serverPort > 0 时会额外在 0.0.0.0 上 监听一个独立端口。

多设备绑定:endpoint 模式支持同时绑定多台小智设备(多个智能体的 MCP 接入点)。 每台设备一条独立的 WebSocket 连接,各自带重连退避与心跳;设置页按设备显示状态、 支持单台「重连 / 测试」,删除设备也只影响该设备。设备列表保存在 endpoints (见 §8);旧版单设备配置(endpointUrl)仍然生效,设置页读回时会显示为一台设备, 首次保存后自动迁移为列表形态。

断线重连:endpoint 模式带指数退避(reconnectMinMs → reconnectMaxMs,含 ±20% 抖动) 和 heartbeatMs 心跳;「状态」页的「重连次数」和日志可以看到全部过程。


4. 工具暴露方式:grouped(默认)还是 flat

小智的工具名会被清洗成 [A-Za-z0-9_\-中文],本插件的所有工具名都是该规则的不动点 (例如 dsh_session_history),因此不会在平台侧被改名。

模式 工具数 说明
grouped(默认) 16(关闭工具组后更少) 按能力域合并,用 action 参数选择具体动作
flat 35 每个接口一个工具,名字与接口一一对应

默认选 grouped 的原因:语音模型在 35 个工具里挑一个的准确率明显低于在 16 个里挑。 超过 24 个工具时设置页会给出提示。完整对照表见 docs/TOOLS.md。

工具组开关:可在「接入配置 → 工具组开关」里关掉暂时不用的域(例如 docs、files)。 关闭整组会让对应工具(或分组工具里的对应动作)直接不可用。

写入开关:allowWriteTools = false 时,创建/修改/删除/发送类动作会被拒绝并返回一句 可直接朗读的中文说明,只读动作照常可用(即使它们和写动作合并在同一个 grouped 工具里)。


5. 能力覆盖

35 个接口全部可达,两种工具模式下都覆盖 35/35:

能力域 能力数 对应接口
系统状态 1 GET /system/status
工作区 6 /workspaces、/workspaces/:id、/workspaces/:id/sessions
会话 13 /sessions、/sessions/:id、history、stats、todos、skills、questions、answers、cancel、events
文件 3 /sessions/:id/files、/files/download
对话 3 /sessions/:id/prompt、/prompt-stream、/chat/completions
模型与预设 5 /models、/models/default、/providers、/presets
系统设置 2 /settings、/settings/:namespace
接口文档 2 /docs、/openapi.json

其中 4 个能力在 MCP 语义下做了降级(不是缺失,但仍需你知情),详见下一节。


6. MCP 语义降级(务必阅读)

MCP 的 tools/call 是一问一答的,没有增量流式通道,而原 REST 接口里有几个是流式的。 本插件选择「尽量保住语义、并如实告知」而不是假装支持:

能力 原本形态 在 MCP 上的行为 你需要知道
conversation.promptStream(dsh_say / dsh_conversation_promptstream) text/event-stream,增量推送 DSH 在服务端收集完整个流后一次性返回结果文本 语音端不会逐步流式;promptTimeoutMs 决定等待上限,超时返回「已提交、仍在运行」而不是错误
sessions.events(dsh_session_watch) 常驻 SSE 事件流 只在有限时间窗内(1–30 秒)收集事件后返回 只能当“看一眼最近的动静”,不能当实时监听;需要持续监听请用 sessions.stats 轮询
files.download 二进制文件流 文本文件回传正文(截断到 maxVoiceChars);二进制只回传摘要(大小、类型、路径) 语音播报二进制内容本来也没有意义;需要真文件请走 DSH Web 界面或内置 REST 层
docs.openapi 完整 OpenAPI JSON 返回结构摘要(openapi 版本、title、路径数、最多 100 条路径、字节数、原始 URL) 完整规范请直接访问 apiBase/openapi.json

另外两点:

  • dsh_say(wait=false) 用于「把话转给会话、不等结果」:它在约 1.5 秒预算内提交 prompt-stream,超时就静默返回「已提交」并附上会话当前状态,不会让你干等。
  • 所有工具结果都会按 maxVoiceChars 截断成单个 text 块,避免语音播报冗长。

7. 安全模型(请按自己的部署范围核对)

面 默认 保护
DSH Web 设置页 API /dsh-xiaozhi/admin 仅本机可访问(DSH 默认绑定 127.0.0.1) ①跨站 Origin 拒绝 ②sec-fetch-site: cross-site 拒绝 ③每个请求(含读取)都必须带 x-dsh-xiaozhi-admin: 1 自定义头;跨站表单/图片无法设置自定义头,跨域 fetch 会触发预检而本路由 cors: false 从不放行 ④若宿主存在 connection 服务,先由它做浏览器 cookie + Host/Origin 判定(401/403)
内置 DSH REST 层 /dsh-xiaozhi/api/v1 默认开启 设置 apiKey 后需 Authorization: Bearer … 或 X-API-Key;未设置密钥时会给出警告
MCP 工具(对外) 默认开启,默认允许写 allowWriteTools=false 关闭全部写操作;disabledGroups 缩小攻击面
server 模式独立端口 默认关闭(serverPort=0) 填端口会在 0.0.0.0 监听,必须设置 serverToken,否则设置页会警告

密钥掩码:设置页读回配置时,apiKey、serverToken、接入点 URL 里的 token=(含 endpoints 里每台设备的 URL)以及 endpointHeaders / 设备 headers 的所有值 都被替换成掩码(•••••• / ***),但会保留 header 名字。 保存时掩码会被识别为“未修改”并按存储值还原,不会用掩码覆盖真实密钥。

endpointHeaders(全局兜底)只能新增/覆盖,不能通过设置页删除(底层是合并写入); 要删掉某个全局请求头,手工编辑 settings.json。设备级 headers 则以整行替换的方式保存, 在设备卡片里删掉对应行并保存即可删除该键。


8. 配置项

配置分三层,优先级从低到高:

  1. 代码默认值(src/config.ts 的 DEFAULTS)
  2. 插件行的 config(profile 的 cordis.patch.yml)
  3. 设置页保存的覆盖(<homeDir>/settings.json)
配置 默认 说明
enabled true 关闭后小智无法调用任何工具
mode endpoint endpoint / server
endpoints [] 小智 MCP 设备列表({id?, name?, url, headers?}),非空时优先生效;设置页「添加设备」写的就是这个键
endpointUrl '' (旧单设备)小智 MCP 接入点;endpoints 非空时被忽略
endpointHeaders {} 接入点附加请求头(所有设备的兜底,设备级 headers 覆盖同名键)
serverPath /mcp/xiaozhi server 模式的路径(须含 /mcp/)
serverPort 0 0 复用 DSH Web 服务器;>0 额外监听 0.0.0.0
serverToken '' serverPort>0 时强烈建议设置
toolMode grouped grouped / flat
disabledGroups [] 关闭的能力域
allowWriteTools true 是否允许写操作
promptTimeoutMs 120000 语音指令等待上限(须小于内置 REST 层的 180000)
maxVoiceChars 700 单条回复截断长度
listLimit 10 列表类结果条数
heartbeatMs 30000 心跳间隔
reconnectMinMs / reconnectMaxMs 1000 / 30000 重连退避区间
apiPathPrefix /dsh-xiaozhi/api 内置 REST 层前缀(设置页 API 固定在 /dsh-xiaozhi/admin)
exposeDshApi true 是否挂载内置 DSH REST 层
apiKey '' 内置 REST 层鉴权密钥
cors false 内置 REST 层是否允许跨域
defaultCwd '' 创建会话的默认目录
maxUploadBytes 104857600 上传上限
homeDir '' 只能在插件行 config 里设置(见下)
logToolCalls true 记录每次工具调用
sendInitializedNotification true MCP 握手后发送 notifications/initialized
serverName DSH 对外声明的服务名

为什么 homeDir 不在设置页里? 它决定设定文件本身放在哪,如果允许从该文件里读, 就会出现“设置页显示新目录、覆盖却仍写旧目录”的自相矛盾。因此 homeDir 固定只从插件行 config 读取,设置页只做只读展示。环境变量 DSH_XIAOZHI_HOME 亦可指定。


9. 设置页

DSH Web →「设置 → 小智接入」,共 5 个页签:

  • 状态:连接状态徽标、传输方式、接入点(已掩码)、每台已绑定设备的状态行、 已连接客户端数、重连次数、最近错误、需要注意的警告、对外地址、工具/能力计数、 各工具组开关状态;可「测试连接」「立即重连」「刷新」。状态每 3 秒自动刷新, 重连后无需手动点刷新。
  • 接入配置:基础项 + 小智 MCP 设备列表(添加/重命名/删除,每台可单独「重连」「测试」)
    • 工具组开关 + 折叠的高级项;「保存并重载」会写覆盖文件并重启运行时, 「恢复默认」清空全部覆盖。
  • 工具清单:当前实际暴露的工具、读写属性、覆盖的能力数。
  • 能力清单:35 个能力按域列出,含方法与路径。
  • 日志:插件环形日志(默认 300 条),可打开 5 秒自动刷新。

页面只用 DSH 主题 token(--dsw-alias-*)着色,不依赖 dsh-client-ui-primitives, 因此明暗主题都跟随宿主,样式不会和宿主冲突。


10. 开发与验证

cd dsh-xiaozhi
bash scripts/build.sh                     # 需要 DSH 源码 checkout 提供 tsc(自动探测)
node --test --test-timeout=30000 "test/*.test.mjs"

测试覆盖(127 个用例):

文件 覆盖内容
test/protocol.test.mjs MCP 报文、工具名清洗不动点、信封解析
test/ws.test.mjs RFC 6455 分帧、掩码方向、分片、关闭握手
test/config.test.mjs 三层配置合并、密钥掩码、homeDir 不可覆盖
test/endpoints.test.mjs 多设备:endpoints 归一化与旧键兼容、设备掩码还原、每台设备独立拨号与状态
test/coverage.test.mjs 35 个接口逐条钉住;两种工具编织都全覆盖;工具名是清洗不动点
test/dispatcher.test.mjs 进程内调用:JSON、查询串、请求体、流式响应、404、超时 504
test/mcp-session.test.mjs 真实 socket 上的握手 → tools/list → tools/call,含并发与协议错误
test/routes.test.mjs 用真实路由表验证 35 个能力都命中已注册路由;分组工具端到端
test/client.test.mjs 浏览器半边的常量一致性、双语字典完整性、helpers、react-dom/server 渲染
test/admin.test.mjs 设置页 API:页面调用的每个路由都必须以正确方法可达;三层守卫;密钥掩码剥离
test/docs.test.mjs 文档与代码一致性:工具名/能力/计数不得漂移

src/dshapi/ 是上游 @dsh-external/dsh-web-service v0.1.11 的逐字拷贝(BSD-3-Clause), 唯一的新文件是把它组装成单个路由的 src/dshapi/service.ts,这样上游更新时仍是干净的三方 diff。 详见 NOTICE。


11. 常见问题

现象 原因与处理
状态一直是 disconnected 设备的接入点地址没填或填错(必须是 ws:///wss:// 且含 /mcp/,且不能含 key/call 字样);看「日志」页首个错误;多台设备时看「状态」页对应设备行的错误说明
小智能看到工具但调用失败 检查 allowWriteTools;写入类动作被关闭时会返回明确的中文说明
小智看不到任何工具 enabled=false,或所有工具组都被关闭
语音里会话/工作区 id 说不清 grouped 工具返回的 id 都做了缩短(如 sess-123);也可以用名称调用
局域网里的自建小智连不上 server 模式且 serverPort=0 时只监听 DSH 服务器(默认仅本机);填端口并设置 serverToken
修改 apiPathPrefix 后设置页没变 符合预期:设置页 API 固定在 /dsh-xiaozhi/admin,apiPathPrefix 只影响内置 REST 层

12. 许可

BSD-3-Clause。派生自 @dsh-external/dsh-web-service v0.1.11(Copyright © 2026 toddpan 潘祖继), 同一许可。见 LICENSE 与 NOTICE。

内容来自项目 README(GitHub)↗

评论

评论存放在 GitHub Discussions。用 GitHub 账号登录后可发表评论或点表情。