41 Commits

Author SHA1 Message Date
Alonso Torres
286a6d5c9a
✨ Add new dashboard methods to the API (#12169)
* ✨ Add new dashboard methods to the API

* 🐛 Fix some issues with the new methods

* 🐛 Fix problem with mcp test
2026-10-14 15:24:53 +02:00
breken
fa5e5b6176
🐛 Log errors readably in MCP execute_code console output (#11930)
* 🐛 Log errors readably in MCP execute_code console output

The console captured during execute_code rendered every object argument
with JSON.stringify. An Error has no enumerable fields, so logging a
caught error printed "{}" and the agent lost the message. A circular
object or a BigInt made JSON.stringify throw, so a console.log call
aborted the executed code with an unrelated serialization error.

Format console arguments in a small pure module: errors by their string
form (name and message), other objects as indented JSON, falling back to
the string form when JSON cannot serialize the value.

AI-assisted-by: claude-opus-5.5
Signed-off-by: breken-ai <312387581+breken-ai@users.noreply.github.com>

* ♻️ Keep console formatting within ExecuteCodeTaskConsole

---------

Signed-off-by: breken-ai <312387581+breken-ai@users.noreply.github.com>
Co-authored-by: breken-ai <312387581+breken-ai@users.noreply.github.com>
2026-10-09 19:39:40 +02:00
alonso.torres
9517f2ac94 ♻️ Type the MCP plugin with the repo plugin types
Link the MCP plugin to plugins/libs/plugin-types instead of the
published 1.5.0 package, which lacks PenpotMgmt. The repo types
declare the penpotMgmt global, so the inline import in index.d.ts
goes away and the plugin checks against the API it runs with.

Type Context.management as PenpotMgmt without on/off, so new
management methods need no extra listing.

Mark the background plugin mock in load-plugin.spec.ts as
NonNullable: createPlugin can return undefined, and the build
type-check failed on it.
2026-10-07 15:21:16 +02:00
alonso.torres
a125977fce 🐛 Keep the MCP plugin working without penpotMgmt
Penpot only exposes penpotMgmt to plugins running with global
scope. Without it, the MCP plugin failed on load when registering
its workspacechange listener.

When penpotMgmt is missing, the plugin now builds the workspace
metadata from the open file and skips the listener. execute_code
receives penpotMgmt as null and waits for layout updates as before.
The tool description and instructions tell agents that only the
open file is available when penpotMgmt is null.
2026-10-07 14:58:32 +02:00
Dominik Jain
be17fabc6a 🎉 Introduce global plugins and move MCP to application scope
Global plugin lifecycle:
- Add optional manifest scope "global" for plugins that remain active
  across dashboard and workspace navigation within one browser tab.
  Existing plugins retain workspace scope by default.
- Preserve global sandboxes when a workspace ends or another plugin
  loads, including when that other plugin fails to initialize.
- Avoid duplicate instances of an already-running global plugin.
- Close global plugins on explicit closure or logout, with idempotent
  cleanup of listeners, timers and UI resources. Global scope does not
  imply automatic startup or persistence across a browser reload.

Plugin API and navigation:
- Keep a permanently live penpot facade whose getters follow the current
  workspace, while user, theme and UI facilities remain available on
  the dashboard.
- Expose penpotMgmt to global plugins with workspace metadata,
  none/loading/ready status and workspacechange subscriptions.
- Add awaitable openFile(fileId, {teamId}) navigation in the current tab,
  defaulting to the current team and resolving only once the requested
  file and its active page are ready.
- Reject invalid identifiers, failed or superseded navigation and waits
  exceeding 30 seconds; release navigation watchers on completion.
- Guard workspace-bound operations while no workspace is ready and
  return null from root, currentFile and currentPage in that state.
- Require content:read for the initial management API. Callers must
  reacquire file, page and shape references after changing files.

MCP lifecycle and connection controls:
- Make MCP a global plugin and initialize its integrated controller per
  authenticated application session instead of per workspace.
- Load the enabled integration with a valid token on the dashboard as
  well as in a file, retaining its existing hidden-iframe implementation.
- Keep enabling and connecting separate: an enabled plugin starts idle,
  and each tab connects explicitly through dashboard or workspace
  controls.
- Preserve the iframe, WebSocket, heartbeat and reconnect machinery
  during navigation; disconnect on request and close on disable/logout.

MCP sessions and execution:
- Derive the short MCP session ID solely from currentUser.sessionId,
  excluding the file ID. Retain the SHA-256-based, 50-bit Base32 encoding
  to produce a stable, copyable 10-character identifier.
- Allow sessions without an open file and update workspace metadata over
  the existing connection without replacing its routing identity.
- Refresh context after the connection handshake so initialization does
  not leave stale file metadata.
- Expose penpotMgmt to execute_code, preserve execution storage across
  navigation and skip layout waits when no workspace is ready.
- Update API types, tool guidance, documentation and the changelog.

Validation:
- Cover authenticated startup, workspace survival, logout cleanup,
  readiness, navigation failure, duplicate loads and session routing.
- Verify that short IDs remain stable across files and reconnects and
  that disconnect cancels pending asynchronous ID initialization.
- Pass 16 focused frontend tests, 73 runtime tests, 30 MCP plugin tests,
  builds, TypeScript checks and frontend/runtime lint.
- Pass all 73 MCP server tests with --test-force-exit; the normal runner
  still stays alive after assertions finish.
- Verify a live dashboard-to-file-to-dashboard round trip without a
  document or iframe reload, preserving facade identity and storage.

AI-assisted-by: gpt-6
2026-10-07 14:58:32 +02:00
Dr. Dominik Jain
8efbd9b5d3
🎉 Allow MCP to work with multiple files in parallel (#11927)
* 🎉 Add multi-file session routing to the MCP server

Session routing:
- Track each user's Penpot connections by session ID.
- Dispatch explicit sessions directly; otherwise discover the sole session.
- Return a retryable error when discovery fails or is incomplete.

Plugin and tools:
- Initialize connections with a fresh session ID and file metadata.
- Share the optional sessionId schema and place it last in tool inputs.

Validation:
- 84 tests, server type checking, and MCP formatting checks pass.
- Disable test logging to avoid the logging worker shutdown hang.

AI-assisted-by: gpt-6

* 🎉 Support independent MCP connections

- Require explicit connection intent for each integrated workspace.
- Derive short session IDs from the Penpot app instance and file.
- Show and copy session IDs in the MCP menu and standalone plugin UI.
- Stop stale callbacks and reconnect attempts after disconnect.

Validate with frontend and plugin tests, type checking, live multi-tab
checks, and standalone UI checks with a simulated connection. Server
tests pass with --test-force-exit; the normal runner can hang on shutdown.

AI-assisted-by: gpt-6

* 📚 Document multi-file MCP sessions

- Explain independent connections, session selection, and copying IDs.
- Describe session ID lifetimes for integrated and standalone plugins.
- Correct browser focus, connection, and recovery guidance.

Validate with the documentation site build and diff checks.

AI-assisted-by: gpt-6

* ✨ Change behavior on session ID duplication

* ✨ Open MCP toolbar menu on hover and match menu style

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
Co-authored-by: elhombretecla <delacruzgarciajuan@gmail.com>
2026-10-02 14:22:52 +02:00
Andrey Antukh
dad6026132
🐛 Test and document MCP plugin terminal-close handling (#11906)
Extract the WebSocket close-code decision into a pure ReconnectPolicy
module and cover it with node:test. The plugin now names the 1008
policy-violation code instead of using a magic number, and the terminal
behavior of a 1008 close is pinned by a regression test so a future
refactor cannot reintroduce the reconnect storm.

Document the contract in mem:mcp/core: 1008 is terminal, it comes from a
duplicate user token or a missing token in multi-user mode, and recovery
is manual via "Connect here".

Closes #11510

AI-assisted-by: deepseek-v4.1-flash
2026-09-24 15:57:23 +02:00
breken
9b3594748c
🐛 Fix MCP plugin reconnect loop on duplicate connection (#11520)
The MCP server closes duplicate plugin connections with 1008 (policy
violation). The plugin treated every close as retryable: shouldReconnect
stayed true, scheduleReconnect re-armed, and each brief onopen reset the
backoff - looping at ~1s until the browser froze. Treat 1008 closes as
terminal and stay disconnected until the user reconnects explicitly.
2026-09-24 15:55:09 +02:00
Dr. Dominik Jain
36cf4f39e1
✨ Apply method Penpot.waitForLayoutUpdate, removing workarounds (#11433)
* MCP server: Update instruction manual to point to method instead of sleep
* MCP plugin: Call waitForLayoutUpdate prior to every code execution
* Composable test suite: Use waitForLayoutUpdate instead of 200ms delay in tests
2026-08-31 14:10:28 +02:00
Dr. Dominik Jain
a749763fb1
⬆️ Upgrade plugin-types and plugin-styles to v1.5.0 in MCP plugin (#10830)
The upgrade allows ts-ignore annotations and manually declared type information
to be removed.
2026-07-27 09:10:04 +02:00
Andrey Antukh
0e25f7d8ad Merge remote-tracking branch 'origin/staging' into develop 2026-07-08 10:25:25 +02:00
Alonso Torres
7655f854a4
✨ Improved api test suite (#10533)
* 🐛 Fix plugins api issues

* 🐛 Fix problem with text weight

* 🐛 Fix api problems

* ✨ Updated plugins test suite

* 🐛 Referential integrity tests

* ✨ Add resetOverrides method

* 🐛 Fix plugins components structure poluting
2026-07-07 16:12:12 +02:00
Alonso Torres
28f3b8048a
✨ Improve MCP handling when tab is frozen in the browser (#10392) 2026-06-25 08:27:33 +02:00
alonso.torres
f81d4af05e 🐛 Changes after review 2026-06-18 16:35:07 +02:00
alonso.torres
823f2107cd 🐛 Add create variant util function 2026-06-18 16:35:07 +02:00
Alonso Torres
d6c973e269
🐛 Improved error handling on a tool error (#10148) 2026-06-12 08:59:05 +02:00
Dr. Dominik Jain
14b53ecfec
✨ Bound MCP memory consumption by limiting parallel exports & response size (#9748)
* ✨ Bound the size of plugin task responses

When using the integrated remote MCP server, bound response size.
All responses are passed to LLMs, which themselves impose bounds.
This is a measure to bound memory usage in the centrally provided
MCP server.

GitHub #9493

* ✨ Bound parallelism in ExportShapeTool

Use an integer semaphore to bound parallel requests to this
memory-intensive tool, thus bounding memory usage.

GitHub #9493

* ✨ Add (manual) integration test script for ExportShapeTool parallelism

Add dependency tsx to facilitate executions.

GitHub #9493

* ✨ Make number of parallel export requests configurable in ExportShapeTool

Use env var PENPOT_MCP_EXPORT_SHAPE_MAX_PARALLEL_REQUESTS to configure
the maximum number of requests in multi-user mode (default 0, no limit).
2026-05-19 19:37:29 +02:00
Dominik Jain
7c42a1f9ac ✨ Catch serialisation issues in penpot.ui.sendMessage
This prevents timeouts in the MCP server in cases where there is an
issue with the serialisation.

GitHub #9634
2026-05-14 22:19:25 +02:00
Dr. Dominik Jain
c3743930c2
🚑 Use base64 envelope for Uint8Array task results to avoid JSON expansion (#9431)
Resolves #9420 (critical memory usage issue in PROD deployment)

When the plugin's ExecuteCodeTaskHandler returns a Uint8Array (e.g. from penpotUtils.exportImage),
JSON.stringify previously serialized it as an object with numeric string keys,
causing ~10x payload expansion and large peak heap usage on the server side.

The plugin now wraps a top-level Uint8Array result in a tagged envelope
{ __type: "base64", data: <base64> }, and ImageContent.byteData decodes this envelope
on the server. The legacy numeric-keyed-object path is retained as a fallback for
compatibility with older plugin builds.
2026-05-07 23:50:20 +02:00
Dominik Jain
ad1e598efe ✨ Add wait time in exportImage to account for async updates #8836
This is a temporary workaround for penpot/penpot-mcp#27.
It adds a wait time before exports via the export_shape tool to account
for asynchronous updates in Penpot, increasing the likelihood of exporting
the fully updated state.
2026-03-31 17:20:02 +02:00
Dominik Jain
f3cc6d0d72 🎉 Add MCP version mismatch detection
If the MCP version (as given in mcp/package.json) does not match
the Penpot version (as given by penpot.version), display a warning
message in the plugin UI.

This is important for users running the local MCP server, as it
is a common failure mode to combine the MCP server with an
incompatible Penpot version.
2026-03-31 12:15:12 +02:00
Dominik Jain
905f4fa5dd ✨ Provide root package version as PENPOT_MCP_VERSION in plugin
Update current root package version using set-version script
2026-03-31 12:15:12 +02:00
alonso.torres
623608799a 🐛 Fix problem with mcp plugin theme 2026-03-30 10:02:11 +02:00
Dominik Jain
1f7afcebe3 ✨ Apply throwValidationErrors flag during MCP code executions #8682 2026-03-24 15:57:04 +01:00
alonso.torres
937032c790 ✨ Allow for reconnections to MCP server 2026-03-24 11:32:47 +01:00
Juan de la Cruz
884cdbbf8d
✨ Add new MCP plugin UI changes (#8699)
* ✨ Add new MCP plugin UI changes

* 📎 Fix tool status misleading
2026-03-23 11:20:37 +01:00
alonso.torres
93de83c427 🐛 Fix problem with error message 2026-03-19 16:19:27 +01:00
Andrey Antukh
6079ef4e22 ✨ Make mcp plugin always ready to be in multiuser 2026-03-17 15:18:22 +01:00
alonso.torres
0e0029bd56 🐛 Fix MCP keep alive messages 2026-03-12 12:34:22 +01:00
Luis de Dios
b5874b365b
Merge pull request #8414 from oraios/mcp-dev-latest
✨ Update MCP server to account for recent API changes & general improvements
2026-02-26 13:18:19 +01:00
alonso.torres
701443c3d7 ✨ Add disconnect to MCP plugin 2026-02-25 13:16:56 +01:00
Dominik Jain
40fb4edc4a ✨ PenpotUtils: Update isContainedIn to use textBounds, adding getBounds
Follow-up to https://github.com/penpot/penpot-mcp/issues/30
2026-02-23 17:27:25 +01:00
Dominik Jain
f43de05d3d ✨ Remove workaround for atob function being unavailable
Follow-up to https://github.com/penpot/penpot-mcp/issues/17
2026-02-19 17:06:36 +01:00
Dominik Jain
7fceb92673 ✨ Apply naturalChildOrdering, removing workarounds
Set the flag to true during code execution, resetting it to the
original value afterwards.

If the flag is unavailable, issue an error message, which is passed
on to the user via the LLM.

Remove instructions that served to work around the corresponding
issues:
 * https://github.com/penpot/penpot-mcp/issues/28
 * https://github.com/penpot/penpot-mcp/issues/32
2026-02-19 17:06:36 +01:00
alonso.torres
03d0c62de1 🐛 Send a keep alive message in websocket connection 2026-02-19 10:26:51 +01:00
Alonso Torres
c32a336c50
🎉 Add MCP plugin embedded execution (#8368)
* ✨ Add core changes for mcp server

* ✨ Changes to plugins-runtime to add mcp extensions

* ✨ Changes to MCP plugin

* ✨ Changes post-review and ci fixes
2026-02-17 09:18:46 +01:00
Dominik Jain
7eb9a207f5 ✨ Change PenpotUtils.findShapes to search on all pages by default
This matches the behaviour of findShape, more closely aligning with
the LLM's expectations (given the lack of concrete information in
the instructions)
2026-02-11 11:35:10 +01:00
Dominik Jain
f5afcde0de ✨ Improve shapeStructure
* Add information on component instance (component id, name; main instance id)
* Improve JSON result order (children should come last)
2026-02-11 10:45:22 +01:00
Dominik Jain
bb4d0322d8 🚧 Temporarily add ts-ignore statements
This shall be reverted once the new API types are published
2026-02-10 12:35:44 +01:00
Dominik Jain
56e369a1c0 ✨ Add helper functions for token exploration
Extend PenpotUtils with helper functions for token exploration/discovery
and describe them in the system prompt
2026-02-10 12:35:44 +01:00
Dominik Jain
880b9b61c4 🎉 Integrate mcp repository
Original repository: https://github.com/penpot/penpot-mcp
Imported commit: fcfa67e908fc54e23a3a3543dee432472dc90c5d
2026-02-04 12:22:36 +01:00