* ✨ Improve MCP server logging
Log only fingerprints of user tokens
* ✨ Add Loki transport support to MCP server logger
Loki logging is enabled iff PENPOT_LOGGERS_LOKI_URI is non-empty.
File logging is now enabled iff PENPOT_MCP_LOG_DIR is set to a non-empty value
(previously defaulted to the "logs" directory when unset).
GitHub #9415
SSE sessions were never included in the periodic inactivity timeout
checker, so a stale connection whose TCP close event never fired would
retain its SSEServerTransport and McpServer indefinitely.
Changes:
- Add lastActiveTime: number to the sseTransports entry type
- Initialise lastActiveTime at SSE session creation (GET /sse)
- Refresh lastActiveTime on every incoming message (POST /messages)
- Extend startSessionTimeoutChecker() to sweep and forcibly close SSE
sessions idle for more than SESSION_TIMEOUT_MINUTES, mirroring the
existing Streamable HTTP logic
- Update the checker log to count both transport maps
The existing res.on('close') cleanup path is preserved unchanged:
it remains the primary cleanup for normal disconnections; the timer
is a safety net for zombie sessions only.
Closes#9432
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
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.
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.
The ping interval was stored in a single variable shared across all
WebSocket connections, so each new connection overwrote the previous
handle and leaked the prior interval.
Move the interval onto ClientConnection as a per-connection field,
and centralize teardown in a new removeConnection(ws) method used
by the close, error and duplicate token rejection paths.
Resolves#9430
The ReplServer Express app was calling `app.listen(port)` with no host
argument, causing Node/Express to default to binding on all interfaces
(0.0.0.0). Combined with the unauthenticated /execute endpoint, any
network peer could POST arbitrary JS and get it run inside the MCP
process.
Fix: add a `host` parameter (default "localhost") to the ReplServer
constructor and pass it to `app.listen`. The call site in
PenpotMcpServer now forwards `this.host` (sourced from
PENPOT_MCP_SERVER_HOST env var, default "localhost"), so environment-
variable overrides continue to work.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Change isVariant() return type from boolean to 'this is LibraryVariantComponent',
enabling TypeScript users to directly access variants, variantProps, and
variantError after a type-narrowing check. Update MCP instructions with
improved variant navigation guidance.
Closes#9185
Co-authored-by: Claude (Anthropic) <noreply@anthropic.com>
* 📚 Update description of mcp-remote usage
* Use Streamable HTTP endpoint instead of SSE
* Remove redundant global installation
* 📚 Add recommendations on model selection
* 📚 Use new tags in npx launch commands
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.
The root lock file not being present causes issues, because
the sub-project dependencies are managed by it.
The lack of inclusion of pnpm-lock.yaml was the root cause of #8829.
A dependency was updated incompatibly, breaking the release.
Since npm pack has a hard exclusion rule for pnpm-lock.yaml,
we include it under a different name and restore it at runtime.
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.
Add support for 'page' as a special shapeId value in the MCP export_shape
tool. It resolves to penpot.root, exporting the entire current page as a
PNG or SVG snapshot.
Previously only 'selection' and explicit shape IDs were supported. The new
'page' shortcut is useful for AI agents needing a bird's-eye view of the
design without having to know a specific shape ID.
Closes https://github.com/penpot/penpot/issues/8689
Signed-off-by: Abhishek Mittal <abhishekmittaloffice@gmail.com>
Update instructions and API documentation to account for
* updated token property names; resolves#8512
* improved variant container creation; resolves#8564
* ✨ Reduce instructions transferred at MCP connection to a minimum
Force on-demand loading of the 'Penpot High-Level Overview',
which was previously transferred in the MCP server's instructions.
This greatly reduces the number of tokens for users who will
not actually interact with Penpot, allowing the MCP server to
remain enabled for such users without wasting too many tokens.
Resolves#8647
* 📎 Update Serena project