Skip to main content
Glama
salvadorsru

open-pencil-headless-mcp

by salvadorsru

OpenPencil headless MCP

Inspect and export OpenPencil .fig and .pen files from any MCP client without opening OpenPencil Desktop and without installing the openpencil CLI.

The server embeds the engine from salvadorsru/open-pencil in dist/engine.mjs. npx only installs Node dependencies from the public npm registry (@modelcontextprotocol/server, zod, canvaskit-wasm, css-tree). No npm login. No GitHub Packages.

This is not @open-pencil/mcp. That package is a bridge to a running desktop app. This one reads files on disk.

Requirements

  • Node.js 20+ (npx comes with npm)

Raster export (png, jpg, webp, pdf) downloads canvaskit-wasm the first time npx runs. Nothing else is required on PATH.

Related MCP server: penpot-headless

Install

From the project root:

npx -y --prefer-online github:salvadorsru/open-pencil-headless-mcp#main -- install

-- keeps install as an argument to this package, not to npm. The command detects the MCP client and writes the right file in that client's JSON shape:

Detects

Writes

Shape

Cursor (env or .cursor/)

.cursor/mcp.json

mcpServers

VS Code (env or .vscode/)

.vscode/mcp.json

servers + type: stdio

Claude Code (env or .mcp.json)

.mcp.json

mcpServers

Claude Desktop

OS user config

mcpServers

Windsurf

~/.codeium/windsurf/mcp_config.json

mcpServers

If nothing matches, it prints the snippet and does not write. If several project clients are present and the host is unclear, it also prints instead of guessing. Override with --client cursor|vscode|claude|claude-code|windsurf or --out FILE.

Point the sandbox at another folder with --root:

npx -y --prefer-online github:salvadorsru/open-pencil-headless-mcp#main -- install --root /absolute/path/to/your/designs

Give each project its own root. A single user-level config can only see one designs folder. Restart the MCP server in your client afterwards.

The server key is pencil. MCP instructions tell the client to use these tools when the user asks to consult Figma or a .fig, before a Figma cloud/API/desktop MCP, unless they paste a figma.com URL.

The client starts the server with npx. -y skips the npm prompt. --prefer-online refreshes main without deleting the npx cache. Progress goes to stderr (starting, engine loaded, ready, plus the package version).

Local clone

Skip npx and run the repo you already have:

{
  "mcpServers": {
    "pencil": {
      "command": "node",
      "args": ["/absolute/path/to/open-pencil-headless-mcp/server.mjs"],
      "env": {
        "OPENPENCIL_MCP_ROOT": "/absolute/path/to/your/designs"
      }
    }
  }
}

Paths

OPENPENCIL_MCP_ROOT is the only directory the tools can read or write. Point it at the folder that contains your documents. Tool arguments are relative to that root:

Root

file argument

/absolute/path/to/your/designs

project/file.fig

/absolute/path/to/your/designs/project

file.fig

Absolute paths or ../ that escape the root are rejected.

Inspect cache

The first inspect of a .fig still parses the file (~2 s for a large document). After that, the server writes an index under the designs root:

your-designs/.cache/pencil/project/file.fig.json

That folder is your designs root (OPENPENCIL_MCP_ROOT), not the npx cache. Anyone using npx with the same root reuses it.

The index (v3) is for navigation: TEXT plus named frames, components, instances, groups, and sections. Generic Frame 2147… / Container / vectors are omitted from find/tree.

  • pencil_find, pencil_tree, pencil_pages, pencil_info read the index.

  • pencil_node and pencil_section use the live graph for style. After ready, the server warms the graph in the background so the first style query is usually already in memory. If you query before that finishes, only the requested page is populated.

  • Changing the .fig (new mtime) or an older index version rebuilds the cache.

  • Export, lint, convert, XPath, and analyze always parse the .fig.

Delete .cache/pencil to force a rebuild.

Tools

Responses are JSON text. File-scoped tools take file relative to the root.

These match the headless CLI. App-only commands (documents, selection, eval) are omitted: they need OpenPencil Desktop.

pencil_info

Document metadata: page count, node counts by type, fonts.

file: project/file.fig

pencil_pages

Page list with node counts.

file: project/file.fig

pencil_tree

Node tree. Optional page (name) and depth.

file: project/file.fig
page: Home
depth: 2

Omitting page uses the first page. Large .fig files can be heavy without depth.

pencil_query

XPath over the document. Optional page and limit (max 10000). Returns id, name, type, and box. Use pencil_node for text and styles.

file: project/file.fig
selector: //SECTION[@name='Hero']
page: Home

Useful selectors:

//FRAME[@name='Header']
//COMPONENT[contains(@name,'Button')]
//TEXT[contains(@name,'Title')]
//*[@name='Hero']

Layer names must match the document exactly.

pencil_find

Find nodes by partial name and/or type (FRAME, TEXT, COMPONENT, …). Optional page and limit.

file: project/file.fig
name: Hero
type: FRAME

pencil_section

One-shot inspect. Resolves a named block and returns path, descendant copy, child names, and style (fills, padding, gap, layout) from the live graph. Prefer this over find + node + tree.

file: project/file.fig
name: Hero
page: Mobile
within: Homepage

page and within are substrings. If several layers share the name, frames and components win over text.

pencil_node

Full properties for one node from the live graph: fills, strokes, padding, gap (itemSpacing), layout, type, parent. Works for any id in the document, including layers omitted from the content index.

file: project/file.fig
id: 12:34

pencil_variables

Design variables and collections. Optional collection and type (COLOR, FLOAT, STRING, BOOLEAN).

pencil_fonts

Fonts used in the document and whether they resolve.

pencil_analyze

kind: colors, typography, spacing, clusters, overlaps.

file: project/file.fig
kind: colors
similar: true

pencil_formats

Supported read / write / export formats. No file.

pencil_lint

Quality and accessibility rules. Optional preset: recommended (default), strict, accessibility.

file: project/file.fig
preset: recommended

pencil_export

Write a derived file inside the root.

Argument

Notes

file

Source .fig / .pen

format

png, jpg, webp, svg, pdf, pptx, jsx, html, fig

output

Relative destination, e.g. project/exports/hero.png

page

Optional page name

scale

Optional, raster only

file: project/file.fig
format: png
output: project/exports/hero.png
page: Home
scale: 2

Missing fonts on raster/PDF are reported as a warning in the JSON (warn policy). HTML writes a fragment plus any sidecar assets next to output.

pencil_convert

Write a .fig copy.

file: project/file.fig
output: project/exports/file.fig

Environment

Variable

Default

When it applies

OPENPENCIL_MCP_ROOT

process working directory

Runtime. Sandbox for every tool.

OPENPENCIL_SRC

../open-pencil

bun run bundle only. Path to the fork checkout.

OPENPENCIL_SMOKE_FILE

first existing candidate

bun run test only.

There is no OPENPENCIL_CLI.

Develop

The published npx payload is server.mjs + dist/engine.mjs. The source of the engine is engine.mjs; it is bundled from a sibling clone of the fork.

git clone https://github.com/salvadorsru/open-pencil-headless-mcp.git
git clone https://github.com/salvadorsru/open-pencil.git   # sibling directory
cd open-pencil && bun install
cd ../open-pencil-headless-mcp && bun install
bun run bundle
bun run test

bun run test runs info on OPENPENCIL_SMOKE_FILE if set, otherwise the first existing local .fig or .pen fixture it finds.

After changing the fork, regenerate and commit dist/engine.mjs so GitHub npx picks it up:

bun run bundle
bun run test

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    A hybrid MCP server that proxies Pencil's native design tools and adds utilities for .pen file management, enabling design token extraction, layer trees, diffs, and batch export.
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    A portable MCP server for headless Penpot project/file/content management, using Penpot's RPC API directly. No browser, no Penpot plugin session.
    54
    12 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables agents to extract, inspect, search, and bundle design context from local Figma .fig files entirely offline, exposing frame summaries, node context, style tokens, assets, vector SVGs, and visual-match review via MCP.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP clients to read and modify the currently open Figma design file through a paired local Figma plugin, supporting operations like creating nodes, editing properties, and exporting PNG/SVG.
    1
    MIT