feat(agent): implement local Canvas Agent with MCP integration and HTTP server

This commit is contained in:
HouYunFei
2026-06-14 18:27:44 +08:00
parent 8aa3fad684
commit e422285c59
27 changed files with 2388 additions and 7 deletions
+1 -1
View File
@@ -8,6 +8,6 @@
"render",
"docker",
"third-party-prompt-repositories",
"[在线体验](https://infinite-canvas-cpco.onrender.com/)"
"[在线体验](https://canvas.best/)"
]
}
@@ -0,0 +1,248 @@
---
title: 本地 Agent 接入规划
description: 规划通过本地 Canvas Agent、MCP 和侧边栏助手连接 Codex / Claude Code 操作画布
---
# 本地 Agent 接入规划
本文档规划个人用户在访问线上画布网页时,如何连接自己电脑上的 Codex / Claude Code,并让 Agent 通过对话和工具调用操作当前画布。
## 目标
- 用户打开线上画布后,可以启动本机服务连接当前画布。
- 用户可以在 Codex 终端里通过 MCP 操作画布。
- 用户也可以在网页侧边栏里和本地 Codex / Claude Code 对话。
- 侧边栏需要展示普通消息、流式输出、工具调用、工具结果和错误提示。
- 线上服务不保存用户本地 Codex / Claude Code 登录态、API Key 或本地文件权限。
## 核心结论
浏览器页面不能直接启动本地进程,也不应该直接控制本机 Codex / Claude Code。推荐增加一个用户本机运行的 `canvas-agent` 服务:
```txt
线上画布网页 <-> 本机 canvas-agent <-> Codex / Claude Code
|
+-> MCP Server
+-> 画布连接
```
`canvas-agent` 是本地可信边界,负责启动或连接 Codex / Claude Code、暴露 MCP 工具、维护画布连接、转发侧边栏对话事件。
## 推荐目录
后续实现时建议在项目根目录新增独立 npm 包:
```txt
canvas-agent/
package.json
tsconfig.json
src/
index.ts
config.ts
http-server.ts
canvas-session.ts
mcp-server.ts
agents.ts
schemas.ts
tools.ts
types.ts
```
原因:
- 这是用户本机运行的 Node 服务,不属于线上 Go 后端,也不属于纯前端页面。
- 后续可以发布成 npm 包,用户通过 `npx` 或全局安装启动。
- Codex SDK、Codex MCP、Claude SDK 都更适合在本地 Node 进程中接入。
- HTTP 路由使用 ExpressMCP 协议层使用官方 `@modelcontextprotocol/sdk`,工具入参使用 `zod`,避免手写协议和松散 JSON。
## 本机启动方式
MVP 阶段优先提供 `npx`
```bash
npx -y canvas-agent
```
在本仓库内开发调试时可直接运行:
```bash
cd canvas-agent
npm install
npm run build
node dist/index.js
```
启动后输出:
```txt
Infinite Canvas Agent
Local URL: http://127.0.0.1:17371
Connect token: xxxxxx
```
网页侧边栏填写或自动发现 `http://127.0.0.1:17371` 和 token 后连接本机服务。
## 前端需要增加的能力
前端不直接暴露 HTTP 接口给本机服务,优先由网页主动连接本机 Canvas Agent。当前 MVP 使用 SSE 接收本机事件,HTTP POST 上报画布状态和工具结果:
```txt
网页 -> http://127.0.0.1:17371/events?token=xxx
网页 -> http://127.0.0.1:17371/canvas/state?token=xxx
网页 -> http://127.0.0.1:17371/canvas/result?token=xxx
```
前端需要提供一个画布 Agent 控制层,内部调用现有 store / hook,不直接让外部操作 React 组件:
```ts
canvasAgent.getState()
canvasAgent.getSelection()
canvasAgent.applyOps(ops)
canvasAgent.focusNodes(ids)
canvasAgent.exportSnapshot()
```
建议先支持最小操作集:
- `add_node`:新增图片、文本、音频、视频、生成配置节点。
- `update_node`:更新节点位置、尺寸、内容和配置。
- `delete_node`:删除节点。
- `connect_nodes`:连接两个节点。
- `set_viewport`:移动或缩放当前视口。
- `select_nodes`:选中节点。
工具入参应使用画布业务 JSON,不使用模拟鼠标点击或屏幕坐标自动化。
MCP 读取画布状态时默认返回摘要,不直接把完整图片、视频、音频或超长 base64 内容塞给 Agent。
## MCP 工具设计
`canvas-agent` 内置 MCP Server,让 Codex CLI 可以连接:
```txt
Codex CLI <-> canvas-agent MCP <-> 当前网页画布
```
建议首批 MCP 工具:
- `canvas_get_state`:读取当前画布节点、连线、选区和视口摘要。
- `canvas_apply_ops`:批量执行画布操作。
- `canvas_get_selection`:读取当前选中的节点。
- `canvas_export_snapshot`:导出当前画布快照,用于让 Agent 理解布局。
- `canvas_create_text_node`:快捷创建文本节点。
- `canvas_create_image_prompt_flow`:快捷创建提示词文本节点和图片生成配置节点。
用户本机 Codex 配置示例:
```bash
codex mcp add infinite-canvas -- npx -y canvas-agent mcp
```
本仓库调试时可使用,实际配置建议替换为本机绝对路径:
```bash
codex mcp add infinite-canvas -- node /path/to/infinite-canvas/canvas-agent/dist/index.js mcp
```
网页侧边栏助手另走 Canvas Agent 的对话通道。Canvas Agent 使用官方 `@openai/codex` CLI 的 `codex app-server --stdio` 启动并续用同一个 thread,启动时会注入 `infinite-canvas` MCP 配置并把 `default_tools_approval_mode` 设为 `approve`,避免 Codex 自己的 MCP 审批卡住侧边栏;真正修改画布前由网页侧边栏做二次确认。
侧边栏图片附件通过 HTTP 发送到本机 Canvas AgentCanvas Agent 临时写入本机文件后作为 app-server `localImage` 输入传给 Codex;MVP 会在输入区提示附件体积,单次请求体限制为 30MB。
## 侧边栏助手设计
侧边栏助手连接 `canvas-agent` 后,由 Canvas Agent 选择 Agent Adapter
```txt
Sidebar -> canvas-agent -> Codex app-server stdio
Sidebar -> canvas-agent -> Claude Code CLI / Claude Agent SDK
```
侧边栏优先展示 Codex app-server 原生结构化事件。前端只消费必要事件:
```ts
type AgentEvent =
| { type: "thread.started"; thread_id: string }
| { type: "turn.started" }
| { type: "item.started" | "item.updated" | "item.completed"; item: ThreadItem }
| { type: "turn.completed"; usage: Usage }
| { type: "turn.failed"; error: { message: string } };
```
Claude 后续接入时再单独做 Claude Adapter;当前前端默认只展示 Codex。
## Codex 接入优先级
优先使用官方 `@openai/codex` CLI 的 `codex app-server --stdio`。
- Codex app-server 会输出 `item/agentMessage/delta`Canvas Agent 转成 `item.updated` 后,前端用同一条消息做真实流式渲染。
- 图片附件使用 app-server `localImage` 输入,不手写多模态协议。
参考:
- https://github.com/openai/codex/tree/main/codex-rs/app-server
- https://developers.openai.com/codex/codex-manual.md
## Claude Code 接入优先级
Claude Code 侧当前 MVP 先调用本机 Claude Code CLI 的流式 JSON 输出,由 `canvas-agent` 统一转换事件和工具调用;后续可升级为 Claude Agent SDK。
如果希望 Claude Code 也能操作当前画布,需要给 Claude Code 配置同一个 MCP
```bash
claude mcp add --scope user --transport stdio infinite-canvas -- npx -y canvas-agent mcp
```
Canvas Agent 调用 Claude Code 时默认允许 `mcp__infinite-canvas__*`,避免 print 模式被工具审批卡住;真正修改画布仍由网页侧边栏确认。
参考:
- https://docs.anthropic.com/en/docs/claude-code/sdk
- https://code.claude.com/docs/en/agent-sdk/typescript
## 安全边界
- Canvas Agent 默认只监听 `127.0.0.1`。
- Canvas Agent 启动时生成 token,网页连接必须携带 token。
- Canvas Agent 只接受允许的 Origin,默认允许用户当前打开的画布域名。
- 画布操作默认先在侧边栏展示工具调用,`canvas_apply_ops` 默认需要用户确认后执行,并保留最近一次工具操作撤销入口。
- 不把 Codex / Claude 登录态、API Key、用户本地文件路径上传到线上服务。
- 线上网页只保存 Canvas Agent 地址和必要偏好,不保存本地密钥。
## 实现阶段
### 第一阶段:Codex 终端操作画布
1. 新增 `canvas-agent/` npm 包。
2. Canvas Agent 提供 SSE/HTTP 连接,网页连接并注册当前画布。
3. 前端新增 `canvasAgent` 控制层。
4. Canvas Agent 内置 MCP Server。
5. Codex 通过 MCP 调用 `canvas_get_state` 和 `canvas_apply_ops`。
### 第二阶段:网页侧边栏连接 Codex
1. 前端新增本地 Agent 侧边栏。
2. Canvas Agent 通过官方 `@openai/codex` CLI 的 `codex app-server --stdio` 接入 Codex,复用同一个 thread,注入 `infinite-canvas` MCP,并展示结构化事件流。
3. 运行日志只保留关键 Codex 事件,避免把流式中间更新刷满日志。
4. 侧边栏展示消息流、工具调用、工具结果、错误和图片附件大小提示。
### 第三阶段:接入 Claude Code
1. Canvas Agent 新增 Claude Code CLI Adapter。
2. 统一 Claude Code 的消息和工具调用事件。
3. 前端侧边栏增加 Agent 类型选择。
### 第四阶段:体验完善
1. 增加本机连接状态、重连和 token 更新。
2. 优化工具调用确认、撤销和批量工具队列体验。
3. 增加常用画布动作模板。
4. 增加 npm 发布和用户安装文档。
## MVP 验收标准
- 用户运行 `npx -y canvas-agent` 后,线上画布能显示已连接。
- 用户在 Codex CLI 中可以创建文本节点、移动节点、连接节点。
- 网页侧边栏能发送一条消息给本地 Codex,并展示流式回复。
- 网页侧边栏默认只展示本地 Codex,并展示结构化回复。
- 侧边栏能展示一次 `canvas_apply_ops` 工具调用和结果。
- 断开 Canvas Agent 后,网页能显示清晰的本机连接失败提示。
+1
View File
@@ -4,6 +4,7 @@
"defaultOpen": true,
"pages": [
"[更新日志](/docs/progress/changelog)",
"local-agent-integration-plan",
"pending-test",
"todo"
]
@@ -5,6 +5,7 @@ description: 当前版本已实现但仍需人工验证的变更项
# 待测试
- 本地 Agent 接入 MVP:新增 `canvas-agent/` 本机 Node 服务,源码使用 TypeScriptHTTP 路由使用 ExpressMCP 协议层使用官方 `@modelcontextprotocol/sdk`,工具入参使用 `zod`;支持仓库内 `cd canvas-agent && npm install && npm run build && node dist/index.js` 或发布后 `npx -y canvas-agent` 启动并输出本机地址和 token;根 README 已增加本地 Canvas Agent 入口;Canvas Agent 默认监听 `127.0.0.1`,使用 token 连接并记录允许的网页 Origin;画布右上角新增 `Agent` 面板,默认只展示 Codex,可拖拽调整宽度,并与右侧画布助手一致支持打开/收起宽度动画;Agent 面板首次打开后收起不卸载,网页侧对话、日志、连接配置、等待状态和待确认工具调用会放在 Zustand 内存 store 中保留,暂不持久化且暂不读取 Codex 本地历史;以对话气泡展示用户消息、Codex 文本、工具调用和错误,前端按 Codex `type/item/usage` 事件格式读取 `agent_message`、工具结果和 `turn.completed` 状态,收到真实 `item.updated` 时用同一个消息 ID 流式更新 assistant 文本,未收到 `item.updated` 时不做伪流式,原始事件 JSON 收进独立“运行日志”弹窗,可切换排查日志和原始 JSON,排查日志会合并本地 Agent 地址、连接状态、等待状态、pending tool、发送/接收关键节点,支持复制排查日志和最近错误,`working...` 旁可直接打开日志;连接成功和本轮结束不再写入对话区,状态留在面板头部,运行日志不再记录 item.updated 和 reasoning 噪声事件;对话区去掉单独背景色并融入面板背景,消息内容裸文本展示,不再显示 `Codex`/`You` 名称,Codex 消息保留透明背景 OpenAI 图标,用户消息保留透明背景用户头像或默认用户图标,回复前显示 `working...` 等待状态;工具调用和授权状态改为对话流里的轻量小卡片,`canvas_apply_ops` 在执行前通过内联确认卡片让用户取消或执行,工具卡片会合并执行结果并可展开查看输入、输出和授权详情;底部输入区改为一体式对话框,图片上传入口和发送按钮都在框内,支持选择或粘贴图片、显示附件体积、发送前限制约 30MB,并在用户消息中显示缩略图,图片会通过本机 Canvas Agent 临时写入文件后作为 app-server `localImage` 输入传给 CodexCanvas Agent 单次请求体限制为 30MBCanvas Agent 使用官方 `@openai/codex` CLI 的 `codex app-server --stdio` 事件流,复用同一个 Codex thread,并在 app-server 配置中注入 `infinite-canvas` MCP 和 `default_tools_approval_mode = "approve"`,侧边栏无需用户预先全局 `codex mcp add` 也可调用画布工具;Claude Code Adapter 代码暂保留但前端入口先隐藏;画布可响应 `canvas_apply_ops` 工具调用来新增/更新/删除/连接节点、选中节点和调整视口,执行后可撤销最近一次工具操作;Canvas Agent 内置 MCP stdio 模式并向 Codex 返回画布工具使用说明,Codex 终端仍可通过 `codex mcp add infinite-canvas -- node /path/to/infinite-canvas/canvas-agent/dist/index.js mcp` 或发布后的 `codex mcp add infinite-canvas -- npx -y canvas-agent mcp` 调用 `canvas_get_state`、`canvas_get_selection`、`canvas_export_snapshot`、`canvas_apply_ops`、`canvas_create_text_node` 和 `canvas_create_image_prompt_flow`Claude Code 可通过 `claude mcp add --scope user --transport stdio infinite-canvas -- npx -y canvas-agent mcp` 接入同一组工具;需要验证浏览器连接、侧边栏自动注入 MCP 后的 Codex 工具调用、终端 Codex MCP 工具调用、工具确认/取消/撤销、Codex thread 复用、运行日志弹窗复制、图片附件预览和断开重连提示。
- 视频创作台创建视频任务成功后会立即写入左侧生成记录,状态显示为“生成中”;刷新页面后会读取本地任务 ID 继续轮询,任务成功或失败后更新同一条记录,需要验证 OpenAI 视频接口和 Seedance 任务接口的刷新恢复。
- 修复画布文字节点编辑时双击进入编辑、全选文本出现重影和错位的问题;文字节点内容编辑框改为使用原生 textarea 文本渲染,仍保留 `@` 资源候选插入,需要验证编辑态选区、展示态文字换行和右上角“生图”按钮避让都正常。
- 配置弹窗新增 WebDAV 同步配置,可填写 WebDAV 地址、远程目录、用户名和密码/应用密码,并选择“前端直连”或“Next.js 转发”;远端会按 `canvas/`、`assets/`、`image-workbench/`、`video-workbench/` 四个业务目录分别写入 `manifest.json` 清单和 `files/` 媒体目录,会合并画布项目、我的素材、生图/视频生成记录和引用到的本地媒体文件,四个业务分区会并发同步,并在同步中显示读取远端、检查媒体、上传新增媒体、上传清单等阶段和文件计数进度条;WebDAV 请求会做超时提示,目录已存在但 `MKCOL` 返回 `423 Locked` 时会复查目录存在后继续同步,需要在支持 CORS 的 NAS/WebDAV 服务和不支持 CORS 的 Koofr 转发模式下验证首次上传、第二台设备拉取合并、再次同步和认证失败提示。
+2
View File
@@ -6,3 +6,5 @@ description: 当前项目后续值得处理的事项
# TODO
本文档用来记录当前项目后续比较值得处理的事项。
- 本地 Agent 接入后续:按[本地 Agent 接入规划](/docs/progress/local-agent-integration-plan)把 Claude Code CLI Adapter 升级为 Claude Agent SDK Adapter,完善 Codex SDK thread 恢复入口、工具队列体验和正式 npm 发布流程。
+1 -1
View File
@@ -3,7 +3,7 @@ import { ArrowUpRight, BookOpen, Rocket } from 'lucide-react';
import { appName, gitConfig } from '@/lib/shared';
const githubUrl = `https://github.com/${gitConfig.user}/${gitConfig.repo}`;
const demoUrl = 'https://infinite-canvas-cpco.onrender.com/';
const demoUrl = 'https://canvas.best/';
const starHistoryUrl = `https://www.star-history.com/?repos=${gitConfig.user}%2F${gitConfig.repo}&type=date`;
const starHistoryChart = `https://api.star-history.com/chart?repos=${gitConfig.user}/${gitConfig.repo}&type=date&transparent=true`;
const darkStarHistoryChart = `${starHistoryChart}&theme=dark`;
+1 -1
View File
@@ -28,7 +28,7 @@ export function baseOptions(): BaseLayoutProps {
<ArrowUpRight className="size-4" />
</span>
),
url: 'https://infinite-canvas-cpco.onrender.com/',
url: 'https://canvas.best/',
external: true,
on: 'nav',
},