pulp
Enables AI agents to collaboratively design and edit web pages on an infinite canvas using real HTML/CSS, with tools for reading, mutating, and exporting documents.
README
pulp
The local-first, agent-driven design canvas. pulp is an infinite canvas where drawings are real HTML and CSS, humans and coding agents edit the same document, and the browser remains the source of truth. Ask Claude or Codex to design a page and watch it appear live, refine it by hand, save the editable drawing as a file, or export it as PNG, SVG, HTML, a portable deck, or PDF.
- Real DOM, not pictures. Every frame is real flexbox layout rendered by the browser. Canvas rendering and export share the same code generator, so HTML export is lossless.
- Local first. Named workspaces autosave independently in browser storage.
Editable
.pulp.jsonfiles provide explicit backups and handoff; no cloud account or database is required. - Agents first. Eighteen MCP tools let Claude, Codex, and other clients read and mutate the document. Multiple agents can share one live workspace or use isolated named sessions without fighting over the bridge port.
- Review is a loop, not a screenshot. Pin human feedback to a layer or canvas position; agents read the workspace inbox over MCP, make the change, and resolve it with a visible response. The MCP console keeps a searchable, workspace-persistent audit of every received request and response.
- Rust core, WASM delivery. The scene graph, command API, undo/redo, validation, and HTML codegen are a Rust crate compiled to WebAssembly. The app itself is static and deploys to GitHub Pages.
Browser demo
The 45-second browser-only walkthrough shows the live canvas, presentation mode, the MCP request/response history, and the multi-format export menu.
Quick start
Prerequisites: Node 20+, Rust via rustup with the WASM target, and wasm-pack:
rustup target add wasm32-unknown-unknown
cargo install wasm-pack --locked
Then:
npm install
npm run build:engine # rerun after Rust changes
npm run build:mcp # rerun after MCP changes
npm run dev # http://localhost:5173
Open http://localhost:5173 for the default workspace, or name one in the
URL:
http://localhost:5173/?workspace=design-notes
The workspace chip can switch among drawings already saved by that browser.
Names are normalized to lowercase letters, numbers, _, and -; use the name
shown in the chip when configuring an agent.
Connect agents
The repo's .mcp.json automatically registers Pulp for Claude Code launched
inside this directory. For a global registration:
claude mcp add pulp -- node /absolute/path/to/pulp/mcp/dist/index.js
codex mcp add pulp -- node /absolute/path/to/pulp/mcp/dist/index.js
With the default editor open, both clients join the default session and can
work on the same canvas. The first local MCP process becomes the lightweight
broker; later processes join it automatically, so EADDRINUSE is not a user
workflow or an error to work around.
For a named browser workspace, set the MCP session to the same normalized name:
claude mcp add pulp-design-notes -- \
env PULP_SESSION=design-notes PULP_CLIENT_NAME=claude \
node /absolute/path/to/pulp/mcp/dist/index.js
codex mcp add pulp-design-notes -- \
env PULP_SESSION=design-notes PULP_CLIENT_NAME=codex \
node /absolute/path/to/pulp/mcp/dist/index.js
PULP_SESSION controls routing; PULP_CLIENT_NAME is the label shown in logs
and the editor's live agent-activity cards. Agents using the same session share
the corresponding editor. Give agents different sessions, with one browser tab
per named workspace, to isolate their documents. All normal sessions share
port 7857 through the broker.
Advanced local experiments can pair PULP_PORT=7867 with ?port=7867, or use
an explicit browser bridge such as ?bridge=ws://127.0.0.1:7867. A hosted HTTPS
page requires an explicitly supplied wss:// bridge; this repository does not
deploy one.
Once the toolbar says agent live, ask an agent something like:
Design a SaaS landing page in pulp: dark hero, three feature cards, footer.
The agent reads a compact document outline, applies JSON commands, and every change renders immediately. Human edits use the same engine and undo history, so the next agent read sees them. The canvas shows the four most recent agent operations with pending, success, or error state; click a completed targeted operation to reveal its layer. Open MCP for the full searchable request and response history, including source agent, session, target, timing, filters, and copyable JSON.
Press C or open Review to attach feedback to the selected layer or place
a pin on the canvas. Open items are stored per workspace. Agents consume them
with pulp_list_feedback and close the loop with pulp_resolve_feedback; their
name and response appear on the feedback card. Because MCP stdio is
client-initiated, a pin is a durable inbox item for the agent's next read—it
does not wake an idle Claude/Codex process by itself.
Agent onboarding is in AGENTS.md. Claude and Codex also receive
the matching pulp-design skill from .claude/skills/ and .agents/skills/.
Local-first files and workspaces
Each ?workspace=<name> has its own autosave key,
pulp.doc.<normalized-name>, on the current browser origin. Autosave is
debounced after document mutations and its state appears in the toolbar.
- Save downloads the complete editable document as
<title>.pulp.json. - Open validates a
.pulp.jsonfile, replaces the current drawing, and autosaves it into the current workspace. - PNG / SVG / HTML export the selected frame as a 2x raster image, scalable wrapper, or lossless standalone DOM page.
- Presentation HTML turns every root frame into a portable keyboard-driven deck. Print / PDF opens the same frames one per print page.
Browser storage is convenient working state, not a durable backup. Save a
.pulp.json file before clearing site data, changing browsers, or handing a
drawing to another machine. Invalid or structurally broken document files are
rejected without replacing the current canvas.
How it fits together
browser workspace (?workspace=design-notes)
editor UI -> Rust/WASM document engine -> local autosave / .pulp.json / HTML
^
| session "design-notes" over ws://127.0.0.1:7857
|
shared MCP broker
^ ^
| stdio | stdio
Claude Code Codex (and other MCP clients)
Every mutation goes through the same engine command API. The document is plain JSON; a patch looks like:
{ "cmd": "set_props", "id": "n4",
"props": { "style": { "fill": "#0b0b10", "shadow": null } } }
Patches deep-merge: only fields present in the command change, and null
clears a field.
Editor controls
| Action | Input |
|---|---|
| Pan | Space+drag, middle-drag, or scroll |
| Zoom | Pinch or ⌘/Ctrl+scroll |
| Actual size | 0 |
| Fit selection / fit all | 1 / 2 |
| Create frame | F |
| Create rectangle / ellipse | R / O |
| Create sticky note / text | N / T |
| Select | Click; Shift+click for multi-select |
| Move / resize | Drag a frame or absolute child; drag selection handles |
| Undo / redo | ⌘/Ctrl+Z / Shift+⌘/Ctrl+Z |
| Duplicate / delete | ⌘/Ctrl+D / Delete |
| Clear selection | Escape |
| Toggle Layers / Inspector | [ / ] |
| Focus mode | \ |
| Pin review feedback | C; submit with ⌘/Ctrl+Enter |
| Present frames as slides | P; then arrows/space/Page Up/Page Down; Esc exits |
| Rename | Double-click in the Layers panel |
| Reveal on canvas | Alt+double-click in the Layers panel |
On initial load, Pulp fits all frames into view. Creation shortcuts place new objects in the active frame, creating a frame first when necessary.
Verification
npm run test:engine # native Rust engine tests
npm run test:mcp-multi # shared broker, isolation, failover, timeout tests
npm run test:mcp-tools # MCP tool registration, including feedback tools
npm run test:feedback # workspace feedback persistence and isolation
npm test # both suites
npm run build # WASM engine + app + MCP builds
npm run check # tests, then the full production build
With a local editor open on the matching session, the destructive end-to-end smoke test exercises the MCP tools and creates a sample frame:
node mcp/smoke.mjs
Deploy
Pushing to main runs the test suites, builds the engine and app, and deploys
app/dist to GitHub Pages. The hosted editor, named workspaces, local autosave,
file Open/Save, all export formats, and presentation mode work without a backend. Agent control defaults
to local development because a hosted HTTPS page cannot open an insecure local
WebSocket; an explicit wss:// bridge is an opt-in advanced configuration. On
HTTPS, the app also registers its manifest and offline shell; after the assets
have been cached by an online visit, it can reopen without the network.
Layout
engine/ Rust/WASM document model, commands, validation, history, codegen
app/ Vite/TypeScript editor, canvas UI, workspaces, local files, bridge client
mcp/ MCP tools, shared multi-session WebSocket broker, smoke tests
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。
