看板娘 KanbanNiang
Tauri 2 + Live2D 桌面看板娘,内置 MCP 服务(Streamable HTTP)。 Agent 通过 11 个 MCP 工具操作她的动作、表情、台词、窗口位置与模型;本仓库同时是一个 DSH 插件包,安装后 DSH 启动时自动拉起看板娘,并把工具注入为 mcp__kanbanniang__*。
In English: a Tauri 2 + Live2D desktop mascot with a built-in MCP server (11 tools, Streamable HTTP). It is also packaged as a DSH (DeepSeek Harness) plugin: DSH auto-launches the mascot and exposes mcp__kanbanniang__* tools to any MCP client.
给其他人的一键安装(DSH 插件)
git clone https://github.com/Evil-Wideplain/kanbanniang.git && cd kanbanniang && npm install && npm run plugin:install
前置:DSH 桌面端 0.2.x、Node 18+、Rust 工具链已就绪。该命令会构建应用、编译宿主插件并注册到 DSH desktop profile;手动步骤与验证见下文「快速开始」与「作为 DSH 插件安装」。
特性
| 能力 | 说明 |
|---|---|
| MCP 单端点 | POST http://127.0.0.1:7432/mcp,Streamable HTTP 单传输;GET /health 供插件探活 |
| Agent 即插即用 | DSH、Claude Desktop、MCP Inspector 等任意支持 Streamable HTTP 的客户端 |
| DSH 插件 | cordis.patch.yml 插入宿主插件与官方 MCP 客户端两行,工具注册为 mcp__kanbanniang__<tool> |
| 11 个工具 | 动作序列 / 表情 / 说话 / 窗口移动 / 窗口内站位 / 待机 / 列举动作 / 列举表情 / 状态 / 换模型 / 列举模型 |
| 拖拽导入模型 | 把 .zip 或解压后的模型文件夹拖到角色身上,解析配置、落盘、热切换 |
| 鼠标穿透 | 只有角色像素接收鼠标事件,空白区域事件落到下层窗口 |
| 模型热切换 | 运行时 set_model 切换,失败回滚原模型;语义别名屏蔽不同模型的命名差异 |
| 窗口形态 | 透明、无边框、置顶、不占任务栏;主窗口 640×800,角色固定按 360×480 渲染 |
架构
DSH(插件系统 + MCP 客户端) Claude Desktop / Inspector / 自研脚本
│ Streamable HTTP │ Streamable HTTP
▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 看板娘进程(Tauri 2 / Rust,127.0.0.1:7432) │
│ axum:POST /mcp · GET /health(Streamable HTTP 唯一传输) │
│ JSON-RPC 2.0 路由 → 工具清单 src/mcp/tools.json → tools/call │
└───────────────────────────────┬─────────────────────────────────────┘
│ Tauri 事件 mascot:action
▼
┌─────────────────────────────────────────────────────────────────────┐
│ WebView 前端(TypeScript + pixi.js + pixi-live2d-display) │
│ dispatcher → Live2D 控制器 → 动作 / 表情 / 气泡 │
│ 需回传结果的工具经 mascot:result 事件返回(oneshot,5s 超时) │
└─────────────────────────────────────────────────────────────────────┘
- 传输:单端点
POST /mcp。initialize的protocolVersion按客户端请求回显,受支持版本为2025-06-18/2025-03-26/2024-11-05;未声明或未知版本回退2025-06-18。旧版 HTTP+SSE 的/mcp/sse已下线。 - 动作链路:
tools/call→ Rust 发射 Tauri 事件mascot:action→ 前端 dispatcher 驱动 Live2D 控制器执行。 - 查询链路:
src/mcp/tools.json中query: true的工具(play_motion、set_expression、move、list_motions、list_expressions、get_status、set_model、list_models)由前端经mascot:result回传,Rust 侧 oneshot 等待 5s;speak、move_in_window、set_idle发射后直接返回。 - 导入链路:拖放到角色 → Tauri 原生拖放事件 → Rust 解析
*.model3.json并落盘到应用数据目录 → 前端注册运行时档案并热切换;模型资源经只读协议mascot-model://提供给 WebView(Windows 上为http://mascot-model.localhost/...)。 - DSH 链路:DSH 启动 → 宿主插件读设置解析端点、探活
/health、未运行则拉起 exe → 官方 MCP 客户端连同一端点 → 工具注入会话。
前置依赖
| 依赖 | 说明 |
|---|---|
| Node.js ≥ 18 | 前端构建与 Tauri CLI |
Rust stable(推荐 windows-msvc 目标;windows-gnu 亦可) |
https://rustup.rs/ |
| WebView2 Runtime | Windows 10/11 一般已预装 |
| VS Build Tools | 勾选「使用 C++ 的桌面开发」;详见 https://v2.tauri.app/start/prerequisites/ |
| Live2D 模型 | 需自备 Cubism 3–5.3 的模型(.model3.json),见 models/README.md |
Cubism Core 运行时 live2dcubismcore.min.js(6.0.1,支持 Cubism 3–5.3 / moc3 v1–v6)已内置于 src/frontend/mascot/render/live2d/ 并随仓库提交,无需手动获取。
快速开始
# 1. 安装依赖
npm install
# 2. 准备模型(任选其一,详见 models/README.md)
# a) 构建期静态资源:把模型目录放进 models/<模型名>/
# b) 运行时导入:先跳过本步,启动应用后把 .zip 或模型文件夹拖到角色身上
# 3. 启动桌面应用(Vite + 编译 Rust + 打开看板娘窗口)
npm run tauri:dev
# 4. 另开终端验证 MCP 链路(需应用已启动)
node cmd/client/test-streamable.mjs
冒烟脚本按顺序覆盖:/health → initialize(校验协议与 Mcp-Session-Id)→ notifications/initialized → tools/list(≥ 11 个工具)→ get_status → speak → play_motion。执行时应看到看板娘显示气泡并播放两个动作,终端逐项打印 ✅。
作为 DSH 插件安装
前置:DSH 桌面端 0.2.x 已启动过一次(完成 profile 初始化),本机已具备 Rust 工具链。
npm install
npm run plugin:install # 构建应用本体 + 编译插件宿主半侧 + 注册到 desktop profile
npm run plugin:install -- --no-build # 跳过构建,沿用 target/release 现有产物
脚本调用桌面端自带的 dsh.cmd(resources/runtime/cli/bin/dsh.cmd)执行 plugin --profile desktop add link:<仓库绝对路径>;PATH 上的 npm dsh 会拒绝操作 desktop profile。找不到桌面端 CLI 时退化为直接修改 profile 清单,并打印需要手工执行的 pnpm 安装命令。
它做了什么
package.json的dsh.bundle.patch指向cordis.patch.yml,安装后自动成为一层 bundle;cordis.patch.yml按顺序插入两行插件:kanbanniang宿主插件(src/plugin/→lib/host.js):从看板娘settings.json的mcpEnabled/mcpPort推导 MCP 端点(默认 7432),发布mascotStartup服务;探测/health,未运行则拉起target/release/kanbanniang.exe(缺省回退target/debug/),就绪后再用 MCPget_status做一次前端链路探活;@deepseek-ai/dsh-mcp-client:inject: [mascotStartup],以streamable-http连接同一端点,把工具注册为mcp__kanbanniang__<tool>。
插件以
link:方式安装,运行时直接拉起仓库里的target/release产物,不打包、不复制 exe,因此安装脚本会先执行tauri build --no-bundle。不要用cargo build --release代替:那样编出的二进制带cfg(dev),前端走devUrl,DSH 拉起后是白屏且查询超时。
安装后验证
- 完全退出并重启 DSH,新建会话,工具列表应出现
mcp__kanbanniang__get_status/speak/play_motion等; - 对模型说「让看板娘挥挥手,并说欢迎回来」,应看到动作与气泡;
- 宿主插件日志:
<DSH_HOME>/kanbanniang/plugin.log(Windows 默认%USERPROFILE%\.dsh)。
配置
| 位置 | 字段 | 说明 |
|---|---|---|
| 看板娘设置页 | MCP 开关 / 端口 | 端口唯一来源,写入 settings.json 的 mcpEnabled / mcpPort,插件自动读取 |
cordis.patch.yml(宿主行) |
autoLaunch |
是否由插件拉起应用,默认 true |
cordis.patch.yml(宿主行) |
mcpUrl |
强制固定端点(覆盖设置文件),一般不需要 |
cordis.patch.yml(宿主行) |
repoRoot / launchCommand |
仓库位置 / 自定义启动命令,一般不需要 |
cordis.patch.yml(MCP 行) |
toolCallTimeoutMs |
单次工具调用超时,默认 15000ms |
cordis.patch.yml(MCP 行) |
reconnect.maxAttempts |
连续重连上限,耗尽后工具注销,需重载配置 |
图形界面安装(等价):DSH 侧栏「插件」→「安装第三方插件」→ 填本仓库绝对路径 → 安装并启用。
卸载
npm run plugin:uninstall # dsh plugin --profile desktop remove kanbanniang
安装/卸载后需完全退出并重启 DSH 生效;看板娘应用与仓库文件不受影响,仍可直接运行 target/release/kanbanniang.exe。
DSH 插件市场
现状:不能直接从 DSH 插件市场安装,请使用上一节的本地 link: 安装。 结论是「协议与工具链层面支持,本仓库打包形态尚不支持」:
- 机制本身可用(DSH 0.2.1-rc.3 实测):可选组合包
@deepseek-ai/dsh-experimental-plugin-market-bundle提供「发现」页;索引格式 1 有随装校验器;安装 spec 由marketInstallTarget生成(npm 为name@version,git 为git+<url>#<commit>),交给pluginManager.installBundle。 - 入口缺失:
lib/(宿主插件编译产物)在.gitignore中,git 形态的包没有main入口;改用安装期prepare构建也走不通——pnpm 会拦截 git 依赖的构建脚本,要求精确到包名@git+url#commit的allowBuildskey,而市场安装的授权只记录包名。 - 应用 exe 缺失:安装目录里没有
target/release/kanbanniang.exe;宿主插件的findRepoRoot()要求包内同时存在package.json与src/tauri/Cargo.toml,但 pnpm 按files打包,src/与target/都不会进包,实测plugin.log报「未找到看板娘可执行文件」。 - 没有 npm 版本:
package.json为private: true,且这是含 Rust 源码的应用仓库,市场只能走 git source;DSH 目前也没有官方市场,索引源需自建自托管、由用户手动添加。 - 模型不入库:市场安装无法附带 Live2D 模型,用户仍需自备并拖拽导入。
完整的验证过程(索引校验、隔离 profile 实测、plugin.log 证据)、阻塞项清单与上架前启用清单见 docs/market-distribution.md。在补齐这些之前,请使用本地安装方式。
让 Agent 接入(MCP 客户端)
端点:http://127.0.0.1:7432/mcp(Streamable HTTP,唯一传输)。 端口可在看板娘设置页修改;旧版 HTTP+SSE(2024-11-05)的 /mcp/sse 已下线。
- DSH:按上一节安装插件,无需手工配置。
- Claude Desktop / 其他客户端:编辑客户端的 MCP 配置(Claude Desktop 为
claude_desktop_config.json,位置见其文档),重启客户端后确认kanbanniang已连接:
{
"mcpServers": {
"kanbanniang": {
"url": "http://127.0.0.1:7432/mcp"
}
}
}
- 可视化调试:官方
@modelcontextprotocol/inspector,填入上述 URL 即可。 - 仅支持旧版 HTTP+SSE 的客户端需先升级(Claude Desktop、DSH 及现代客户端均已支持 Streamable HTTP)。
工具清单(MCP)
| 工具 | 参数 | 说明 | 返回 |
|---|---|---|---|
play_motion |
steps: {motion, index?, holdMs?, expression?, text?}[]、idle?: boolean |
串行连播动作,每步等播完再进下一步;holdMs 为播完后的额外停留(0–5000ms);expression / text 在本步开始时同步生效且不打断动作;idle=true 整段播完回待机;新调用打断旧队列 |
每步解析结果数组 |
set_expression |
expression: string |
换表情,接受真实名或语义名(忽略大小写与下划线),未命中回退默认表情;播放中可用且不打断动作 | {applied, fallback} |
speak |
text: string |
显示气泡并做表情反馈(当前无真实口型同步) | — |
move |
x: number、y: number(0–1) |
平滑移动整个窗口到工作区归一化坐标,约 0.6s 到位,窗口保证完整可见 | {x, y, physicalX, physicalY, clamped, superseded} |
move_in_window |
x: number、y: number(0–1) |
调整角色在窗口内的站位,不移动窗口本身 | — |
set_idle |
— | 清除表情与动作,回到待机 | — |
list_motions |
— | 列出当前模型可用的动作组名 | 组名列表 |
list_expressions |
— | 列出当前模型可用的表情名 | 表情名列表 |
get_status |
— | 当前状态快照(模式 / 模型 / 表情 / 位置 / 是否在说话) | 状态对象 |
set_model |
model: string |
运行时热切换模型档案,加载失败时原模型保持不变 | {ok:true, model, mode, modelFile} / {ok:false, error} |
list_models |
— | 列出模型档案:id / 名称 / 渲染模式 / 主文件 / 是否默认 / 当前使用中 | 档案列表 |
工具定义的单一来源是
src/mcp/tools.json(Rust 与 TS 同源加载),本表为文档镜像,冲突以清单为准。可用动作名与表情名取决于模型自带文件,先调list_motions/list_expressions查询。
接入真实 Live2D
方式 A:拖拽导入(不写入仓库)
把 .zip 压缩包或解压后的模型文件夹拖到角色身上,松开即导入:解析 moc / 贴图 / 动作组 / 表情,校验必需文件存在后复制到 <app_data_dir>/models/imported-<名字>/(Windows 一般在 %APPDATA% 下)。同名模型重复导入整体覆盖;不同名模型 id 撞车时分配 -2 保留,不会互相删除。导入成功后立即热切换,并出现在设置页芯片与 list_models 中(id 形如 imported-haru)。移除:退出应用后删除对应目录。
方式 B:构建期静态资源(可随仓库分发,但模型目录不入库)
- 把模型目录放到
models/<模型名>/(含*.model3.json及motions/、expressions/等); models/经 VitepublicDir作为静态资源根,models/Haru/…会以/Haru/…提供;npm run dev/npm run build前由scripts/gen-models.mjs自动扫描并生成模型清单,无需手工登记;- 需要展示名、缩放 / 锚点或语义别名时,编辑
src/frontend/shared/domain/profiles.ts的MODEL_OVERRIDES(键为models/下的目录名)。
语义别名:不同模型的动作 / 表情命名不一致(官方系多为 Idle / TapBody,表情可能是 F01 起)。档案可为 idle、speak、neutral、happy 等语义名配置映射,Agent 即可用同一套调用名操作任意模型。批量发现与命名可运行 node cmd/client/preview.mjs(需应用已启动),逐个播放并让你在终端标注。
运行时切换:list_models 查档案 → set_model 热切换;切换会重置表情与位置(跨模型名称不同),同 id 重复切换返回 {ok:true, unchanged:true}。完整说明(含排查)见 models/README.md。
目录结构
pet/
├─ src/
│ ├─ frontend/ 前端(TS + Vite)
│ │ ├─ app/ 三个窗口入口与组合根
│ │ ├─ shared/ 领域契约 / IPC / 表现工具
│ │ ├─ mascot/ 看板娘域:dispatcher、模型热切换、Live2D 渲染、气泡、拖放浮层
│ │ ├─ settings/ 设置页
│ │ ├─ debug/ MCP 调试面板(仅开发构建)
│ │ └─ styles/
│ ├─ plugin/ DSH 宿主插件半侧(TS → lib/)
│ ├─ mcp/tools.json MCP 工具清单(单一来源)
│ └─ tauri/ Rust 后端:Tauri 装配、axum MCP 服务、模型导入
├─ cmd/client/ MCP 客户端与脚本:client.mjs / test-streamable.mjs / preview.mjs
├─ scripts/ gen-models.mjs(构建前扫描模型)、install-dsh-plugin.mjs(安装到 DSH)
├─ models/ 静态资源根;模型目录不入库,接入文档见 models/README.md
├─ assets/icon.png 插件管理页图标
├─ docs/ 补充文档(如 market-distribution.md)
├─ .github/workflows/ CI
├─ .agents/skills/ 内置开发规约(按需加载)
├─ cordis.patch.yml DSH bundle patch:宿主插件 + MCP 客户端两行
├─ Makefile 常用命令入口
├─ package.json 前端依赖、脚本与 DSH 插件元数据
├─ AGENTS.md Agent 开发入口与硬性规则
├─ LICENSE MIT
├─ .gitattributes / .gitignore
└─ README.md
开发与验证
| 命令 | 作用 |
|---|---|
make check |
前端门禁:tsc --noEmit + Vite 构建(前端改动必过) |
make unit |
单元测试:npm run test + npm run test:plugin(改领域层 / 纯函数 / 插件逻辑必过) |
make lint |
Rust 门禁:clippy --all-targets -- -D warnings + fmt --check(Rust 改动必过) |
make verify |
提交前全量门禁(= check + unit + lint) |
make score |
Rust CRAP 复杂度打分(cargo-crap) |
make dev / make build |
npm run tauri:dev / npm run tauri:build |
npm run plugin:build |
编译插件宿主半侧到 lib/ |
node cmd/client/test-streamable.mjs |
MCP 冒烟测试(需应用已启动;协议 / 工具改动必跑) |
提交前至少执行 make verify;改动协议、工具清单或动作后补跑冒烟测试;改动 DSH 装配后用非 desktop profile(如复制 web 模板)验证层叠与端到端工具调用。
CI(GitHub Actions,.github/workflows/ci.yml)在 windows-latest 上跑同一组门禁:前端 npm ci → npm run build → npm run test → npm run test:plugin,Rust 侧 cargo fmt --check + cargo clippy --all-targets -- -D warnings(先构建 dist/,不跑 tauri build)。
GNU 工具链 + MSYS / Git Bash 下若
make lint报 windresUnable to create temp file,是该 shell 把TEMP置为/tmp导致资源编译找不到合法临时目录:改用 PowerShell / cmd 运行,或先把TEMP、TMP设为 Windows 路径(如C:\...\Temp);make check/make unit不受影响。
编码规约、分层约束与完整检查清单见 AGENTS.md 与 .agents/skills/,本 README 不重复搬运。
常见问题
| 现象 | 排查方向 |
|---|---|
| DSH 拉起后白屏、工具不可用 | 构建方式错(用了 cargo build --release):产物带 cfg(dev),前端加载 devUrl。看日志里的 [boot] 当前为开发构建…,用 npm run plugin:install 重新做 release 构建;plugin.log 里 get_status 探活失败也指向这里 |
| 改了端口,插件仍连旧端口 | 端口唯一来源是看板娘设置页写入的 settings.json;插件每次启动重读,改完重启 DSH 即可。不要在 cordis.patch.yml 里另写一份端口 |
| 插件没激活 / 工具没出现 | 查 <DSH_HOME>/kanbanniang/plugin.log;确认 profile 依赖里有 kanbanniang、cordis.patch.yml 未被覆盖,然后完全重启 DSH |
| 查询型工具 5s 超时 | 前端不在线(窗口关闭或白屏)。/health 健康只证明 Rust 在监听,不代表前端活着;用 get_status 判断 |
| 拖模型没反应 | 拖到了空白区域:鼠标穿透已把事件交给下层窗口,需把文件放到角色身上;仅支持 .model3.json(Cubism 3+) |
| 模型加载失败 | 读 set_model 返回的 {ok:false, error}。常见原因是 moc3 版本高于内置 Core 上限(6.0.1 最高支持 Cubism 5.3),需替换 live2dcubismcore.min.js |
| 导入后设置页没有新模型 | 设置窗口重新聚焦时刷新;列表来自后端仓库,仍不出现请重启应用 |
已知限制
- 无鉴权:MCP 服务仅监听
127.0.0.1,未做 token / 证书,请勿直接暴露到公网。 - 平台:Windows 为主要验证平台;macOS / Linux 的自动拉起路径(可执行文件名)未适配。
- 口型:
speak仅显示气泡并做表情反馈,真实口型同步需接入 AudioEngine + Phoneme。 - 前端依赖:查询型工具要求前端在线,窗口关闭即 5s 超时。
- 模型:
models/*/不入库,需自备模型并遵守各自许可;Cubism 2 的.model.json会被拒绝。 - 市场分发:DSH 市场机制本身可用,但当前打包形态(
lib/不入库、包内无 exe)装完起不来,只能本地安装;详见「DSH 插件市场」与docs/market-distribution.md。 - 鼠标穿透:按角色像素命中动态开关,空白区域收不到任何鼠标消息;前端有 fail-safe(交互层停止、光标源失效、穿透态 1s 无采样主动恢复可交互),不会卡死。
- 窗口适配:启动时按当前显示器工作区收缩窗口(只收缩不放大);角色缩放以 360×480 设计基准计算,窗口变小只是减少四周留白,不改变角色像素尺寸。
贡献与致谢
- 提交前跑
make verify;commit message 遵循 Conventional Commits(type(scope): 中文 subject),细则见.agents/skills/commit。 - 文档分工:README 面向使用者,
AGENTS.md与.agents/skills/面向开发规约;补充文档放docs/。 - 致谢:Tauri、axum、Live2D Cubism SDK、pixi-live2d-display、pixi.js、Model Context Protocol、DeepSeek Harness。
- 仓库不附带模型;演示用的示例模型来自 Live2D Cubism SDK(遵循 Live2D 开放许可),商用请自行确认授权。
No comments yet. Be the first to write one.