Skip to main content
Glama

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.json files 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

Watch the browser-only Pulp 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.json file, 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
-
license - not tested
-
quality - not tested
-
maintenance - not tested

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nipunbatra/pulp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server