Skip to content

MCP Server ​

4B Pen includes an MCP (Model Context Protocol) server that lets AI coding tools — Claude Code, Cursor, Windsurf, etc. — read and modify designs through the running app.

Two transports: stdio for MCP clients, and Streamable HTTP for browser extensions and scripts. On macOS and Linux, local clients prefer a private Unix domain socket; Windows and unavailable sockets fall back to localhost TCP.

Tool definitions own native Valibot input schemas, execution/mutation metadata, capabilities, and optional interface exposure exclusions. Tools are included by default; exposure: { mcp: false, ai: false, webmcp: false } can exclude them independently from each adapter. Exposure does not bypass execution support or user permissions: WebMCP still requires supported execution and explicit Off, Inspect, or Edit access. AI and MCP consume the same schema through Standard Schema; WebMCP derives its JSON Schema from that input. Numeric strings are accepted consistently across adapters, while non-finite values are rejected. Programmatic integrations use MCP SDK v2; custom tools replace the former params/ParamDef contract with input and execution metadata.

Tool access settings ​

Use Settings → Tool access (select Local MCP) to search and toggle the local server's tools, individually or by read-only/side-effect group. Group switches affect all group members, even during search. Restore defaults enables the configurable MCP tools again. Existing MCP preferences are preserved separately from the Built-in AI settings.

Restart the MCP server, then reconnect stdio clients, to apply changes. For an externally managed server, restart its owning process. The list shows the tools the server offers, also while it is stopped; disabling a dedicated tool does not prevent an enabled script tool from performing the same operation. These switches are not a sandbox and do not configure remote MCP servers or WebMCP.

Share only the selection ​

Turn on Share only the selection in Settings → MCP → Local server to let MCP clients read only the layers you select. Clients then get get_selection, get_node, get_page_tree, describe, and export_image, and no tools that edit, open files, list documents, or change settings. Every node a call names must be a selected layer or inside one; describe and export_image read the selection when given no IDs, and get_page_tree needs a root_id from the selection. export_image returns the image but cannot write it to a file. With nothing selected, calls fail and ask the user to select layers.

The server enforces this on every call it sends to the app, including POST /rpc and stdio clients, so a client cannot widen it. Restart the MCP server to apply the change. For a server you start yourself, set OPENPENCIL_MCP_SCOPE=selection; a stdio client can also set it to limit itself while the server shares the whole document.

Browser-native WebMCP (experimental) ​

WebMCP is off by default. Open Settings → MCP → WebMCP and choose Inspect for read-only access or Edit to also allow scoped, undoable changes. Off unregisters all browser tools; changing modes revokes the previous registrations immediately. This preference is independent of local MCP authentication, tool switches, and outbound connections.

For local testing, use a Chrome version exposing document.modelContext, enable chrome://flags/#enable-webmcp-testing, and relaunch the browser. Open a document, enable access in Settings, and connect a WebMCP-capable browser agent or the Model Context Tool Inspector. Settings shows browser support and registration status. See the Chrome WebMCP guide for current availability.

In supported browsers, 4B Pen registers the selected reviewed set of tools directly in the workspace. Browser agents can inspect nodes, JSX, variables, components, and design patterns, and edit existing layer properties and variable values without installing or connecting an MCP server.

Tools target the document and page active when the call starts. Switching tabs does not redirect an in-flight call. Closing the workspace unregisters the tools. Tool inputs are validated and large inspection results require a narrower query. Oversized editing results are omitted with a committed-edit notice rather than reporting a successful edit as failed.

Edits to geometry, paints, layout, text, and variable bindings/values commit synchronously as individual undoable operations. Failed edits roll back, and undo targets the original document/page even after a page switch. Cancellation prevents an edit from starting; cancellation after commit does not reverse it. Font loading finishes separately without holding a mutation transaction open. Atomic editing currently requires a document with at most 10,000 nodes and variables combined; this shared limit also applies when the same editing tools run through app AI/MCP.

This surface does not expose structural creation/deletion, arbitrary JavaScript/JSX execution, image loading, filesystem operations, or credentials. Those tools retain their existing AI/MCP paths. WebMCP is an evolving browser proposal, not universally available; unsupported browsers continue to use 4B Pen normally. The stdio and HTTP integrations below remain independent.

Install ​

sh
npm install -g @open-pencil/mcp

Stdio (Claude Code, Cursor, etc.) ​

The stdio server discovers the running 4B Pen app automatically. It prefers the app's Unix domain socket on macOS and Linux and falls back to localhost TCP when needed. Make sure the desktop app is open with a document loaded.

Claude Code ​

Install the MCP package and register it with Claude Code:

sh
npm install -g @open-pencil/mcp
claude mcp add --scope user open-pencil -- openpencil-mcp

Check the connection:

sh
claude mcp list

Claude Code asks before using each MCP tool unless you allow the server's tools. To auto-approve 4B Pen tools only, add this to ~/.claude/settings.json:

json
{
  "permissions": {
    "allow": ["mcp__open-pencil__*"]
  }
}

This is narrower than --permission-mode bypassPermissions, which skips prompts for every tool. You can also approve tools interactively from Claude's prompt by choosing “Yes, and don't ask again”.

Example prompt:

text
Use the open-pencil MCP server to inspect the current page and create a small hero section on the canvas.

Other MCP clients ​

Add to your MCP config (for example .cursor/mcp.json):

json
{
  "mcpServers": {
    "open-pencil": {
      "command": "openpencil-mcp"
    }
  }
}

Or run from source without installing:

json
{
  "mcpServers": {
    "open-pencil": {
      "command": "bun",
      "args": ["/path/to/open-pencil/packages/mcp/src/stdio.ts"]
    }
  }
}
json
{
  "mcpServers": {
    "open-pencil": {
      "command": "npx",
      "args": ["tsx", "/path/to/open-pencil/packages/mcp/src/stdio.ts"]
    }
  }
}

HTTP ​

For browser extensions, scripts, CI, or any HTTP client:

sh
openpencil-mcp-http

Or from source: bun packages/mcp/src/index.ts / npx tsx packages/mcp/src/index.ts

Security defaults:

  • Unix socket and discovery files are created with owner-only permissions on macOS and Linux.
  • TCP binds to 127.0.0.1 and uses port 7600 by default.
  • Authentication is enabled by default with a generated token stored in the private discovery file.
  • eval is disabled.
  • File operations are limited to OPENPENCIL_MCP_ROOT (defaults to the current working directory) and reject symlink escapes.
  • Only the desktop app's own origin (tauri://localhost and its http(s)://tauri.localhost variants) is allowed by default, so a server you start yourself works from the app without extra configuration. Set OPENPENCIL_MCP_CORS_ORIGIN to a comma-separated list to allow other origins, such as a worktree dev server.

Set PORT=0 to disable TCP on macOS and Linux. Windows requires TCP. Set OPENPENCIL_MCP_SOCKET to override the Unix socket path, or OPENPENCIL_MCP_DISCOVERY_PATH to override the discovery file location. To provide a stable token, set OPENPENCIL_MCP_AUTH_TOKEN; an explicitly empty value disables authentication and should only be used with a trusted local socket.

Endpoints are available over both active transports:

  • GET /health — server and app connection status; never returns the auth token.
  • POST /rpc — authenticated live-app automation.
  • POST /mcp — MCP Streamable HTTP. Sessions use the mcp-session-id header.

Workflow ​

  1. Discover targets — call list_documents first when more than one document or page may be open. It returns stable document_id and page IDs.
  2. Open — open_file to load an existing .fig, or new_document for a blank canvas. These return target metadata for the opened or created document.
  3. Read — get_page_tree, find_nodes, get_node, list_pages
  4. Create — create_shape, render (JSX)
  5. Modify — set_fill, set_stroke, set_layout, update_node, set_effects
  6. Structure — reparent_node, group_nodes, clone_node, delete_node
  7. Save — save_file to write back to .fig
  8. Close — close_file to close an open document tab. With unsaved changes it fails unless unsaved is "save" or "discard"; it never prompts in the app.

undo and redo step back through the agent's own changes, and activate_document brings a tab to the front when the user should see it.

Each MCP session shows in the app as an agent with a callsign, like the built-in chat: its cursor and outline sit on the layers each tool reads or changes, it rests after a quiet spell and leaves when the session ends, and people can follow it from their avatar. Collaborators in a shared room see it too.

Most tools accept optional document_id and page_id fields. Pass them explicitly for agent workflows instead of relying on the visible active tab/page. create_page only creates a page; call switch_page separately when the workflow should change the active page.

AI Agent Skill ​

Teach your AI coding agent to use 4B Pen tools:

sh
npx skills add open-pencil/open-pencil

Works with Claude Code, Cursor, Windsurf, Codex, and any agent that supports skills. The skill covers the CLI, MCP tools, JSX rendering, eval, and the running app's automation bridge.

Tools ​

4B Pen currently registers 100+ shared design tools, plus MCP-only document and prompt operations when applicable.

Document ​

ToolDescription
open_fileOpen a .fig file for editing
close_fileClose an open document tab; unsaved: "save" or "discard" decides what happens to unsaved changes
save_fileSave the current document to a .fig file
new_documentCreate a new empty document
list_documentsList open app documents/tabs and their pages
activate_documentBring a document tab to the front, optionally on a given page

History ​

ToolDescription
undoUndo the newest change made through MCP or the CLI
redoRedo the newest change undone through MCP or the CLI

The history is shared with the person in the editor. undo and redo refuse when the newest step was made in the editor, so an agent never reverts the user's work. Each editing tool call is one undo step. An eval script is recorded against its target page, so edits it makes after switching figma.currentPage are not undoable.

Settings ​

ToolDescription
get_settingsRead editor settings: appearance, snapping, canvas rendering, recovery, AI chat, and design check preferences
update_settingsChange settings with a partial object shaped like get_settings output; invalid keys and values are rejected

Settings tools never expose credentials, AI models, MCP connections, storage, or tool access. The available keys are listed in Controlling the App.

Read ​

ToolDescription
get_selectionGet the selected nodes, with their direct children by default; depth sets how many levels
get_page_treeGet the full node tree of the current page
get_current_pageGet the current page name and ID
get_nodeGet detailed properties of a node by ID
find_nodesFind nodes by name pattern and/or type
get_componentsList all components in the document
list_pagesList all pages
list_variablesList design variables
list_collectionsList variable collections
list_fontsList fonts used in the current page
list_available_fontsList font families the current host can render
get_font_statusReport requested faces, loaded sources, active substitutions, why an installed face could not be loaded, and affected nodes
page_boundsGet bounding box of all objects on the current page
node_boundsGet bounding box of a node
node_ancestorsGet ancestor chain of a node
node_childrenGet direct children of a node
node_treeGet the subtree rooted at a node
node_bindingsGet variable bindings on a node

Create ​

ToolDescription
create_shapeCreate a shape (FRAME, RECTANGLE, ELLIPSE, TEXT, LINE, STAR, POLYGON, SECTION)
create_vectorCreate a vector node from a path string
create_sliceCreate an export slice
create_pageCreate a new page
renderRender JSX to design nodes — create entire component trees in one call
create_componentConvert a frame/group into a component
create_instanceCreate an instance of a component
create_slotMake a frame inside a main component a slot
set_behaviourMake a component behave as a Reka UI control, by its property and slot names; null removes it
get_behaviourRead a component's behaviour and what it still misses; without an ID, list every kind
node_to_componentConvert an existing node into a component in-place

Modify ​

ToolDescription
set_fillSet fill color (hex)
set_strokeSet stroke color, weight, alignment
set_effectsAdd shadow or blur effects
update_nodeUpdate position, size, opacity, corner radius, text, font
set_layoutSet auto-layout (flexbox) — direction, spacing, padding, alignment
set_constraintsSet resize constraints
set_rotationSet rotation angle in degrees
set_opacitySet opacity (0–1)
set_radiusSet corner radius (uniform or per-corner)
set_minmaxSet min/max width and height constraints
set_textSet text content of a TEXT node
set_fontSet font family and weight
set_font_rangeSet font properties on a character range
set_text_resizeSet text auto-resize mode (fixed/auto-width/auto-height)
set_visibleShow or hide a node
set_blendSet blend mode
set_lockedLock or unlock a node
set_stroke_alignSet stroke alignment (inside/center/outside)
set_text_propertiesSet text layout: alignment, auto-resize, text case, decoration, truncation
set_layout_childConfigure auto-layout child: sizing, grow, alignment, absolute positioning
node_moveMove a node to a new position
node_resizeResize a node
node_replace_withReplace a node with another node
arrangeAlign or distribute selected nodes

Structure ​

ToolDescription
delete_nodeDelete a node
clone_nodeDuplicate a node
rename_nodeRename a node
reparent_nodeMove a node into a different parent
select_nodesSelect nodes by ID
group_nodesGroup nodes
ungroup_nodeUngroup a group
flatten_nodesFlatten nodes into a single vector
boolean_unionBoolean union of two or more nodes
boolean_subtractBoolean subtraction
boolean_intersectBoolean intersection
boolean_excludeBoolean exclusion

Vector Path ​

ToolDescription
path_getGet the path data of a vector node
path_setSet the path data of a vector node
path_scaleScale a vector path
path_flipFlip a vector path horizontally or vertically
path_moveTranslate a vector path

Export ​

ToolDescription
export_imageExport nodes as PNG, JPG, or WEBP. Returns base64-encoded image data
export_svgExport nodes as SVG markup

Viewport ​

ToolDescription
viewport_getGet current viewport position and zoom level
viewport_setSet viewport position and zoom
viewport_zoom_to_fitZoom viewport to fit specified nodes

Variables ​

ToolDescription
get_variableGet a variable by ID or name
find_variablesFind variables by name pattern or type
create_variableCreate a new variable in a collection
set_variableSet a variable value in a mode
delete_variableDelete a variable
bind_variableBind a variable to a node property
get_collectionGet a variable collection by ID or name
create_collectionCreate a new variable collection
delete_collectionDelete a variable collection

Analyze ​

ToolDescription
analyze_colorsAnalyze color palette usage across the document
analyze_typographyAnalyze font/size/weight distribution
analyze_spacingAnalyze gap and padding values
analyze_clustersDetect repeated patterns (potential components)
lintCheck accessibility and consistency issues, with fixes and suggestions
lint_fixApply safe lint fixes, and optionally the first suggestion of each finding

Diff ​

ToolDescription
diff_createPatch that turns one node tree into another, as JSX attribute changes
diff_jsxStructural diff between two nodes as design JSX
diff_showPreview the patch that setting JSX attributes on a node would produce
diff_applyApply a patch after checking the nodes still match its old values
diff_visualPixel diff between two rendered nodes, returned as an image

Comments ​

ToolDescription
get_commentsRead the comments left on the canvas, open ones unless asked for resolved too, with their layer, page, and replies
add_commentPin a comment on a layer or at a point of a page, in Markdown
reply_to_commentAnswer a thread, which reopens it if it was resolved
resolve_commentResolve a thread once its feedback is addressed, or reopen it
ToolDescription
switch_pageSwitch to a page by name or ID

Escape Hatch ​

ToolDescription
evalExecute JavaScript with full Figma Plugin API access

Note: eval is available over stdio, but disabled in HTTP mode for security.

Released under the MIT License.