内容
# holst-mcp
MCP服务器,用于通过Claude Code或任何其他MCP客户端与Holst.so的俄罗斯替代品Miro/FigJam的板进行交互。
由于Holst没有公共API,因此集成绕过 - 通过**本地运行的桌面客户端Holst.app**,并启用Chrome DevTools协议。服务器连接到本地端口,通过用户实时会话读取和写入Yjs文档的板。
## 功能
### 读取
| 工具 | 描述 |
|---|---|
| `holst_whoami` | 检查连接和授权。返回已登录的用户。 |
| `holst_list_boards` | 所有工作区中所有板的列表。 |
| `holst_get_board_content` | 板的完整原始Yjs转储:所有对象+所有文档。 |
| `holst_get_board_summary` | 结构化markdown:按最近的shape标题分组的贴纸。 |
| `holst_search_text` | 板上所有对象的文本搜索。 |
### 创建对象
| 工具 | 类型 | 描述 |
|---|---|---|
| `holst_add_sticker` | sticker | 黄色贴纸 192×192 |
| `holst_add_shape` | shape | 带文本的图形 - 正方形,椭圆形,圆角矩形,基本星形等。 |
| `holst_add_simple_text` | simple-text | 没有背景的简单文本标签。 |
| `holst_add_arrow` | arrow | 两个点或对象之间的箭头。 |
| `holst_add_frame` | frame | 带标题的大容器(放在内容下)。 |
| `holst_add_stamp` | stamp | 表情符号/邮票 60×60 |
### 编辑
| 工具 | 描述 |
|---|---|
| `holst_update_text` | 重写贴纸/shape/simple-text中的文本。 |
| `holst_move_object` | 移动对象。 |
| `holst_resize_object` | 更改大小(适用于shape,sticker)。 |
| `holst_set_color` | 设置fillColor/strokeColor。 |
| `holst_delete_object` | 删除对象。 |
所有更改自动与连接的其他客户端(Yjs CRDT)同步。
### v0.2.0不支持的功能
- **绘图(P)** - 手绘。包含复杂的路径作为坐标字符串(`"0,0,8.2,-11.7,..."`),贝塞尔曲线,枢轴。需要路径生成器。可以添加到v0.3如果需要。
- **任务卡/看板/投票/反应邮票** - 小众类型,不需要。
- **将对象附加到parentId** (嵌入框架) - 通过`move_object`添加,但没有自动嵌入。
## 要求
- macOS / Linux / Windows(在macOS上测试)
- Node.js ≥ 20
- Holst.app已安装并**使用标志`--remote-debugging-port=9222`启动**
- 用户已登录Holst桌面版
## 安装
```bash
cd ~/Developer/holst-mcp
npm install
chmod +x bin/start-holst.sh
```
在`~/.claude/settings.json`中注册:
```json
{
"mcpServers": {
"holst": {
"command": "node",
"args": ["/Users/andrej/Developer/holst-mcp/src/server.js"]
}
}
}
```
## 以CDP模式启动Holst
在使用MCP之前**必须**使用调试端口启动Holst:
```bash
~/Developer/holst-mcp/bin/start-holst.sh
```
脚本:
1. 从应用程序中删除隔离(否则macOS会阻止CDP端口)
2. 如果Holst已经使用CDP启动 - 什么也不做
3. 如果已启动但没有CDP - 重新启动
4. 如果未启动 - 重新启动
5. 保存已登录用户的会话(user-data-dir不被触及)
首次启动后,您需要通过Google/Yandex/email登录一次 - 然后会话将保留在`~/Library/Application Support/Holst/`并在重启后继续。
## 技术实现
### 架构
```
Claude Code
│ (stdio)
▼
holst-mcp服务器(Node.js)
│ (CDP WebSocket)
▼
Holst.app:9222(Electron,本地)
│ (HTTPS + WSS,已授权会话)
▼
api.holst.so / hud.holst.so
```
MCP服务器只是Claude和正在运行的Holst浏览器之间的中介。所有请求都通过**真实用户会话**:cookie,令牌,权限 - 与普通应用程序操作相同。
### 读取板
1. 连接到`127.0.0.1:9222`,找到包含`app.holst.so/board/{id}`的选项卡。如果没有 - 取任意Holst选项卡并导航到所需板。
2. 通过CDP `Runtime.evaluate`在页面上下文中执行:
```js
window.ds.yPersistence.yDoc.toJSON()
```
3. 获取JSON,包含:
- `documents`: `{ docId: "文本" }` - 文本内容
- `objects`: `{ oid: { type, position, documentId, ... } }` - 视觉对象
- `task-card-statusesStore`,`kanban-*Store`,`reactionsStore` - 其他子系统
### 在板上写入
Holst使用 **Yjs CRDT** (`window.Yjs`全局导出)。数据模型:
- `yDoc.getMap('objects')` - Y.Map,键=对象UUID,值=Y.Map,包含字段(type,position,documentId,width,height,...)。
- `yDoc.getMap('documents')` - Y.Map,键=documentId,值=Y.Text。
要添加贴纸:
```js
const yObjects = yDoc.getMap('objects');
const yDocs = yDoc.getMap('documents');
const stickerMap = new Yjs.Map();
stickerMap.set('type', 'sticker');
stickerMap.set('documentId', newDocId);
stickerMap.set('position', { x, y });
// ... 其他字段
const yText = new Yjs.Text();
yText.insert(0, '贴纸文本');
yDoc.transact(() => {
yDocs.set(newDocId, yText);
yObjects.set(newId, stickerMap);
});
```
Yjs自动:
- 通过WebSocket提供程序将更改同步到Holst服务器
- 将更改传递给其他连接的客户端
- 在并发编辑时解决冲突
### 授权
Holst在`Authorization`标头中使用Bearer令牌(不在cookie中)。令牌位于`localStorage['social-auth-store'].token`。MCP不知道令牌本身 - 它在页面上下文中执行,其中本地存储已可用。
## 工具 - 详细信息
### `holst_get_board_content`
```json
{ "boardId": "372ea1bf-8bac-481b-94f2-ddd9088461ca" }
```
返回:
```json
{
"meta": { "id": "...", "name": "娜斯佳", "workspace_id": "...", ... },
"content": {
"documents": { "bb3c0ac3-...": "价值观", ... },
"objects": { "04a54183-...": { "type": "shape", "position": {...}, ... } },
...
}
}
```
### `holst_add_sticker`
```json
{
"boardId": "...",
"text": "新贴纸",
"x": 100,
"y": 200,
"width": 192,
"height": 192
}
```
返回 `{ ok: true, id, documentId }` - 新贴纸的ID及其文档。
### `holst_update_text`
```json
{ "boardId": "...", "objectId": "...", "text": "新文本" }
```
更改现有对象(贴纸/部分/简单文本)中的文本。内部 - `Y.Text.delete + insert`,不重新创建对象。
## 陷阱
- **Holst更新可能会破坏模式。** 已验证版本:Holst 0.0.2(Electron 29.4.6)。如果新版本更改共享密钥的名称(`documents`,`objects`),Yjs用例或全局变量(`window.ds`,`window.Yjs`)- 需要修复`src/holst.js`。
- **Bearer令牌在注销时旋转。** 如果用户注销 - MCP将返回错误,需要运行`start-holst.sh`并再次登录。
- **CDP端口必须保持打开。** 如果Holst崩溃或在没有标志的情况下重新启动 - MCP将无法连接。`start-holst.sh`可以解决这个问题。
- **没有阻止并发编辑** - 如果您通过MCP编写,同时有人在UI中编辑同一板,Yjs将合并。但是,如果对象刚刚被某人删除,MCP尝试更新它 - 将出现`object not found`错误。
## 故障排除
| 症状 | 原因 | 解决方法 |
|---|---|---|
| `No app.holst.so tab found` | Holst未运行或没有CDP标志 | `~/Developer/holst-mcp/bin/start-holst.sh` |
| `Holst API 401: unauthorized` | 会话丢失/用户注销 | 在Holst桌面版上登录,然后重试 |
| `JS exception: TypeError: e.toJSON is not a function` | 记录了普通对象而不是Y.Map | 可能与Holst的新版本不兼容 - 检查`holst.js` |
| `JS exception: Cannot read properties of undefined (reading 'yDoc')` | 板尚未加载 | 等待5秒,然后重试 |
## 开发
本地Smoke测试(Holst必须运行):
```bash
cd ~/Developer/holst-mcp
node -e "import('./src/holst.js').then(m => m.whoAmI()).then(console.log)"
```
结构:
```
src/
├── server.js # MCP服务器(stdio)
├── cdp.js # CDP客户端在WebSocket上
└── holst.js # Holst特定(yDoc,gapi,工具)
bin/
└── start-holst.sh # 使用调试标志启动Holst.app
```
## 许可证
MIT。