- Add a repo-wide TypeScript AST duplicate shape check to the quality gate
- Extract shared named types for existing cross-file duplicates
- Keep same-file oxlint coverage for editor feedback
- Add AST-based lint checks for broad unknown object assertions and local JsonObject aliases
- Centralize JsonObject in core and package-local MCP RPC JSON typing
- Replace baseline Record<string, unknown> assertions with named shared/domain types
Stdio handshake:
Server now sends { type: register, token } to WebSocket clients on
connection, so the stdio bridge receives the token immediately instead
of deadlocking while both sides wait to receive.
Eval return values (REPL-style):
Add wrapEvalCode() that detects when the last non-empty line is a value
expression and promotes it to a return statement. Bare expressions like
JSON.stringify(result) now return the value instead of undefined.
Single implementation in packages/core/src/tools/analyze/eval-wrap.ts
used by both the tool and the automation bridge.
Co-authored-by: stiff <v.stiff@gmail.com>
Co-authored-by: Jais Pedersen <jais@pedersens.net>
- Configure oxfmt custom import groups for workspace, app, package, and test aliases
- Keep type imports grouped with their matching source category instead of one global tail group
- Expand the format script to cover formatter config, Vite files, and scripts
- open_file: opens a .fig/.pen from disk into a new tab via MCP
- new_document: creates an empty doc with optional save path
- export_image, export_svg, get_jsx: optional path param writes output
to disk instead of returning base64/string (avoids flooding AI context)
- OPENPENCIL_MCP_ROOT env var scopes all file paths (defaults to cwd)
- setPlannedFilePath + startWatchingCurrentFile on editor store
- Extract openFileFromPath helper in use-menu.ts
- Add fs:allow-mkdir and scoped fs:allow-watch Tauri capabilities
- Better 'app not connected' error in stdio — instructs agent to stop
Co-authored-by: Jais Pedersen <jais@pedersens.net>
tsgo with `include: ["src"]` sometimes skips standalone entry points
like stdio.ts that aren't imported by other files. This caused the
published npm package to be missing dist/stdio.js, breaking the
`openpencil-mcp` stdio binary.
Fixes#224 (npm publish issue)
JSX renderer: support position="absolute", top, and left props for
placing children inside auto-layout containers without breaking flow.
MCP server: send notifications/tools/list_changed when the desktop
app connects or disconnects, so MCP clients can refresh their tool
list instead of discovering disconnection per-call.
Addresses #208 (findings 2 and 5)
MCP clients often serialize numeric arguments as JSON strings.
Previously this caused validation errors on tools like node_move.
Now both the MCP server (zod) and AI adapter (valibot) coerce
string-encoded numbers to numbers, rejecting only genuinely
non-numeric values like 'abc'.
Fixes#207
- Add packages/mcp/src/stdio.ts — proper stdio MCP server that
connects to the running app via WebSocket
- openpencil-mcp bin now points to stdio entry (was HTTP)
- openpencil-mcp-http bin added for the HTTP server
- Extract registerTools() from server.ts to share between transports
- Move index.ts banner output to stderr (prevents stdout corruption)
- Update Tauri shell spawn to use openpencil-mcp-http
- Update all docs (EN + 6 translations): correct binary names, source
paths, port number
Fixes#194
- Add save_file MCP tool that proxies to the browser app via RPC
- Add save_file RPC handler in the automation server
- Rebuild dist/ to fix stale @open-pencil/core/* self-referencing imports
from before the domain module restructuring
Fixes#184Closes#185 (save_file added; open_file requires File System Access API
which is browser-interactive; new_document needs a store reset method)
Remove headless SceneGraph mode — the MCP server now always proxies
tool calls to the browser via WebSocket. One server, one architecture:
HTTP :7600 — /health, /rpc (CLI), /mcp (MCP Streamable HTTP)
WS :7601 — browser connects, executes tool calls against live editor
- Delete http.ts (merged into server.ts)
- Delete headless SceneGraph, open_file, save_file, new_document
- Delete canvaskit-wasm dependency (no headless rendering)
- Add ws dependency for WebSocket server
- Rewrite tests with mock browser over real HTTP+WebSocket
- Simplify bridge.ts to 6 lines (just imports and starts the server)
- Replace ssrLoadModule hacks with direct imports from @open-pencil/core
- Replace isBunRuntime() hack with @hono/node-server (works everywhere)
- Remove ViteServer type parameter from startAutomationBridge
- Upgrade @open-pencil/mcp zod dependency from v3 to v4
- Add @open-pencil/mcp as root devDependency for bridge imports
ACP agents now get an MCP server at http://127.0.0.1:7600/mcp that
proxies tool calls through the automation WebSocket to the browser,
where they execute against the live editor store. No separate
subprocess, no stale npm package — tools operate on the open canvas.
- Add /mcp endpoint to automation bridge (Streamable HTTP transport)
- Each MCP tool call → sendToBrowser({command:'tool'}) → browser executes
- Strip mcp__open-pencil__ prefix from tool names in chat UI
- Export paramToZod from MCP package for bridge reuse
Pass openpencil-mcp as a stdio MCP server to the ACP session so
agents get all design tools (create shapes, render JSX, export
images, etc.) instead of just plain conversation.
- Add makeFigma option to createServer for external FigmaAPI injection
- Skip file lifecycle tools (open/save/new) when using external FigmaAPI
- Pass MCP server config in ACP newSession mcpServers
- Add bun to Tauri shell scope for MCP server subprocess