openpencil/packages/pen-mcp
Fini 6d4bdb88da test(pen-mcp): support-chat composition scenario covering #63-#67 + v1
Extends element-tools-composition.test.ts with a 7th real-screen
scenario that exercises every tool added after the 62-tool mark:

  - add_top_nav_bar_v0         (existing anchor)
  - add_chat_bubble_v0         × 2  (left from-others + right from-self)
  - add_attachment_row_v0      (file on the self-message path)
  - add_upload_dropzone_v0     (drop area for screenshots)
  - add_chip_input_v0          (conversation tags)
  - add_action_menu_v0         (floating menu, open state)
  - add_modal_shell_v1         (theme="dark" confirm dialog)
  - add_otp_input_v0           (phone verification step)

8 tool calls, chained through the full MCP handler pipeline
(ensureParentExists → builder → assignIdsRecursively →
batch_design insert with rollback-on-failure → post-insert
landing check → save → re-read from disk). Asserts:

  - every call emits a nodeId (no silent no-ops)
  - final doc has exactly 9 root children
  - every tool's role marker survives round-trip save/load
  - modal v1 theme=dark → card fill #1E293B (not v0's #FFFFFF)
  - chat right-side bubble surface → #2563EB accent
  - OTP focused slot stroke → #2563EB accent

This is end-to-end at the DATA layer, not the visual layer.
Still not covered by any test:
  - Skia rendering (needs debug_screenshot against a live
    canvas)
  - Real LLM tool routing (needs ab-corpus harness with live
    API keys — route to 方舟 CP works but has not run since
    the routing fix)

Document-layer coverage is enough to rule out handler-pipeline
bugs (bad parent_id threading / cache-stale doc-state / silent
rollback swallowing); visual regressions require the next gate.
2026-04-22 22:31:49 +08:00
..
src test(pen-mcp): support-chat composition scenario covering #63-#67 + v1 2026-04-22 22:31:49 +08:00
CLAUDE.md V0.7.1 (#102) 2026-04-13 21:30:23 +08:00
LICENSE V0.7.1 (#102) 2026-04-13 21:30:23 +08:00
package.json fix(ai): dispatcher imports DSL executor via browser-safe subpath 2026-04-22 11:15:00 +08:00
README.md V0.7.1 (#102) 2026-04-13 21:30:23 +08:00
tsconfig.json V0.7.0 (#95) 2026-04-11 23:25:13 +08:00

@zseven-w/pen-mcp

MCP server for OpenPencil — enables Claude, GPT, Gemini, and other LLMs to read, create, and modify designs through a standard tool protocol.

Install

npm install @zseven-w/pen-mcp
# or
bun add @zseven-w/pen-mcp

Overview

pen-mcp exposes OpenPencil's full editing API as MCP tools. External AI agents can open documents, inspect the canvas, insert/update/delete nodes, and generate complete designs — all through structured tool calls.

Three workflows are supported:

Workflow Tools Best for
Single-shot insert_node, batch_design Quick edits, single components
Layered design_skeleton → design_content × N → design_refine Full-page designs with high fidelity
CRUD batch_get → update_node / delete_node Reading & modifying existing content

Quick Start

# Run as stdio MCP server (for Claude Desktop, Cursor, etc.)
npx @zseven-w/pen-mcp

# Or connect to a running OpenPencil instance
op mcp:dev

Claude Desktop Configuration

{
  "mcpServers": {
    "openpencil": {
      "command": "npx",
      "args": ["@zseven-w/pen-mcp"]
    }
  }
}

Tools

Document & Read Tools

Tool Description
open_document Open an .op file or connect to the live Electron canvas. Always call first.
batch_get Search and read nodes by type, name regex, or specific IDs. Controls read depth for nested content.
get_selection Get the currently selected nodes on the live canvas.
snapshot_layout Get a compact bounding-box layout tree — useful for spatial understanding.
find_empty_space Find available canvas space in a given direction for placing new content.
get_design_prompt Retrieve segmented design knowledge (schema, layout, roles, text, style, icons, examples).

Node CRUD Tools

Tool Description
insert_node Insert a new node with full PenNode data. Supports postProcess for auto-defaults.
update_node Shallow-merge properties into an existing node.
delete_node Delete a node and all its children.
move_node Reparent a node to a new container.
copy_node Deep-clone a node with new IDs under a target parent.
replace_node Replace a node entirely with new data at the same position.
import_svg Import a local SVG file as editable PenNodes.

Batch Design DSL

batch_design accepts a compact DSL — one operation per line:

root=I(null, { "type": "frame", "name": "Page", "width": 1200, ... })
header=I(root, { "type": "frame", "name": "Header", ... })
U(header, { "fill": [{ "type": "solid", "color": "#1A1A2E" }] })
logo=C("existing-logo", header, { "x": 24 })
M("floating-btn", header)
D("old-section")
Op Syntax Description
I binding=I(parent, { data }) Insert node
U U(path, { updates }) Update properties
C binding=C(source, parent, { overrides }) Copy node
R binding=R(path, { newData }) Replace node
M M(nodeId, parent, index?) Move node
D D(nodeId) Delete node

Layered Generation Workflow

For high-fidelity multi-section designs:

1. design_skeleton  → Create root frame + section placeholders
2. design_content   → Fill each section with content nodes (call per section)
3. design_refine    → Run full-tree validation and auto-fixes

Page Management

Tool Description
add_page Add a new page to the document
remove_page Remove a page (cannot remove the last one)
rename_page Rename a page
reorder_page Move a page to a new index
duplicate_page Deep-clone a page with new IDs

Post-Processing

All creation tools support postProcess=true for automatic:

  • Semantic role defaults (button padding, input height, card radius, etc.)
  • Icon name → SVG path resolution (Lucide icon set)
  • Card row equalization in horizontal layouts
  • Text height estimation
  • Frame height expansion when content overflows
  • clipContent auto-addition for frames with cornerRadius + images

Design Prompt Sections

get_design_prompt(section) returns focused subsets of design knowledge:

Section Content
schema PenNode type definitions and property reference
layout Flexbox layout engine rules (gap, padding, justify, align)
roles Semantic roles and their auto-defaults (button, input, card, navbar, ...)
text Typography rules, CJK support, copywriting guidelines
style Visual style policy (colors, fonts, aesthetic)
icons Feather/Lucide icon naming conventions
examples Complete design examples with DSL
guidelines Design tips (cards, inputs, phone mockups, hero sections)
planning Layered workflow guide with section decomposition rules

Live Canvas Sync

When connected to a running OpenPencil desktop app, changes made via MCP tools appear on the canvas in real-time. The sync is bidirectional — user edits on the canvas are reflected in subsequent batch_get / snapshot_layout calls.

Programmatic Usage

import { configureMcpHooks, MCP_DEFAULT_PORT } from '@zseven-w/pen-mcp';

// Configure custom hooks (optional)
configureMcpHooks({
  onDocumentOpen: (path) => console.log(`Opened: ${path}`),
  onNodeInsert: (node) => console.log(`Inserted: ${node.id}`),
});

License

MIT