198 Commits

Author SHA1 Message Date
Dr. Dominik Jain
6031f6c318
⬆️ Upgrade MCP SDK to v2, removing SSE support and HTTP session management (#11841)
* ⬆️ Upgrade MCP SDK to v2 and remove HTTP sessions

MCP's per-request protocol removes the need to retain HTTP sessions.
Use the v2 handler to manage each request's transport and lifecycle,
so requests can reach any server instance without session affinity
or the workaround that adopts sessions through private SDK fields.

Keep legacy SSE support and the shared plugin and Redis bridges.
Remove the shared expiry checker, including legacy SSE idle expiry;
SSE connections now remain until disconnection or server shutdown.
Verify stateless requests, token isolation, and legacy compatibility.

Resolves #11827

AI-assisted-by: gpt-6-astra

* 🔥 Remove legacy MCP SSE support

Use Streamable HTTP as the sole MCP client transport so the server no
longer needs a separate SSE connection registry or lifecycle.

Remove /sse and /messages, their nginx routes, and the server-legacy
dependency. Legacy SSE clients must switch to /mcp; older Streamable
HTTP clients remain supported. Document the migration and verify that
the removed endpoints return 404.

Resolves #11846

AI-assisted-by: gpt-6-astra
2026-09-23 15:07:19 +02:00
David Barragán Merino
091fe456ad 🐳 Migrate media-processor image to DHI and wire up its build"
Migrate docker/images/Dockerfile.media-processor from ubuntu:26.04 to
dhi.io/node (Debian 13/trixie), which also drops the manual Node tarball
download since the base image ships it. The -dev tag stays as the final
image: fontforge, woff2 and the graphics libraries are needed at runtime.

scripts/build now assembles the release bundle under target/ (dist/ plus
the manifests and a generated setup script), the way the other modules do,
since esbuild leaves the runtime dependencies external. manage.sh gains
build-media-processor-bundle and build-media-processor-docker-image, both
wired into build-bundle and build-docker-images.

The CI workflows are intentionally left untouched: the module is still work
in progress and its images are not published yet, so this only enables
local builds."

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-22 18:52:05 +02:00
Andrey Antukh
476e25e371 Merge remote-tracking branch 'origin/staging' into develop 2026-09-22 13:44:56 +02:00
Andrey Antukh
b9bb62b497
🐛 Block MCP REPL server in multi-user mode and decouple its host (#11632)
The unauthenticated REPL server started in --multi-user mode whenever DEVENV or REPL_ENABLE was set, unlike developer tools which are already blocked there. Gate its creation behind shouldStartReplServer, mirroring the developer tools policy, so it never starts with --multi-user. Give it a dedicated PENPOT_MCP_REPL_HOST knob defaulting to localhost so it no longer inherits the main server 0.0.0.0 bind in Docker, keep the devenv script on 0.0.0.0, and document and test the new behavior. Closes #11631 AI-assisted-by: muse-spark-1.3-contributor
2026-09-22 12:17:59 +02:00
Andrey Antukh
b5274a4476
🐛 Bind MCP PluginBridge WebSocket to configured host (#11605)
The plugin WebSocket bridge ignored PENPOT_MCP_SERVER_HOST and bound all interfaces, 
exposing unauthenticated task dispatch to the network in single-user mode. Pass mcpServer.host 
into WebSocketServer, mirroring the ReplServer fix, so the bridge binds localhost by default and 
0.0.0.0 only on explicit opt-in. 

Closes #11603. 

AI-assisted-by: muse-spark-1.3-contributor
2026-09-22 11:53:23 +02:00
Andrey Antukh
117c8db0bb Merge remote-tracking branch 'origin/staging' into develop 2026-09-22 10:24:30 +02:00
Andrey Antukh
d68531b783
⬆️ Update devenv dependencies (#11790)
* ⬆️ Update devenv dependencies

Update Node.js, OpenCode, clj-kondo, Babashka, Pixi, GitHub CLI, uv,
and Serena to their current stable releases.

AI-assisted-by: gpt-5.6-sol

* ⬆️ Update devenv to Java 27

Use Zulu JDK 27 in the development image for compatibility testing.
Update the official checksums for both supported architectures.

AI-assisted-by: gpt-5.6-sol

* 🐳 Replace MinIO with RustFS in devenv

Run RustFS as the development S3 service and wait for its health check.
Install a pinned AWS CLI with checksums and use it to create the bucket
idempotently from each backend entry point.

Keep the old MinIO volume untouched and use a new RustFS volume.

AI-assisted-by: gpt-5.6-sol

* 🐳 Replace MailCatcher with persistent Mailpit

Run Mailpit as the devenv SMTP sink while preserving mailer:1025 and the
localhost:1080 UI.

Store its SQLite inbox in a named volume and wait for the readiness
endpoint before starting runtime containers. Bind the web UI to loopback so
development emails stay local.

AI-assisted-by: gpt-5.6-sol

* ⬆️ Update Node.js to 24.21.0

Align the host NVM version with the Node.js version used by devenv.

AI-assisted-by: gpt-5.6-sol

* ⬆️ Update devenv to PostgreSQL 18.6

Run PostgreSQL 18 with its versioned volume layout and a TCP readiness
check that ignores the temporary initialization server.

Install the matching client, create penpot_nexus, and preserve the old
PostgreSQL 16 volume for rollback or logical migration.

AI-assisted-by: gpt-5.6-sol

* 🐳 Expose RustFS ports in devenv

Publish the RustFS S3 API and management console on localhost port 9000
and 9001.

Keep both bindings on loopback so object storage is not exposed to the local
network.

AI-assisted-by: gpt-5.6-sol

* 🐳 Install standalone pnpm in devenv

Install pnpm 12.5.0 from architecture-specific release archives and
verify their published checksums.

Remove the Corepack setup while allowing pnpm to honor the project
packageManager pins.

AI-assisted-by: gpt-5.6-sol

* 🔥 Remove corepack, use system pnpm everywhere

Corepack is gone from Node 25+, so every `corepack enable` call
fails. pnpm now ships as a system binary (devenv, CI runners and
Docker images install it directly) and auto-downloads the version
pinned in `packageManager` on mismatch.

Scripts, workflows and Dockerfiles call `pnpm` straight away; the
three deploy workflows use a single `pnpm/setup@v2` step; and the
new `scripts/sync-pnpm-version` stamps all 35 `packageManager`
fields from the system pnpm, replacing the `corepack use` sweep.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Fix exporter watch missing render-wasm build step

The exporter watch compiled CLJS requiring the generated
src/app/wasm/shared.js, which only render-wasm/build export
produces. Without it shadow-cljs failed with a cryptic missing
./shared.js dependency. Run build:wasm before watching, as
the frontend watch:app and exporter scripts/build already do.

AI-assisted-by: muse-spark-1.3-contributor

* 🔧 Add opencode V2 support and adapt plugins

Register the penpot tools for both opencode V1 (server())
and V2 (setup() with JSON Schema inputs) from a single
dependency-free plugin file, sharing the psql and
paren-repair runners between both paths.

Install the opencode2 binary side-by-side with V1 in the
devenv image and document the dual registration in the
paren-repair and psql memories.

AI-assisted-by: muse-spark-1.3-contributor

* ⬆️ Update pnpm and opencode
2026-09-22 10:22:31 +02:00
Andrey Antukh
c1bd3cb9f0 Merge remote-tracking branch 'origin/staging' into develop 2026-09-09 10:31:41 +02:00
Andrey Antukh
5c474939ac 🔧 Pin all pnpm workspaces to one shared pnpm store
Set storeDir in every pnpm-workspace.yaml: `.pnpm-store` at the repo
root and `../.pnpm-store` in the ten module workspaces, so all of them
resolve to <repo>/.pnpm-store. pnpm resolves the value against the
workspace root, and nested workspaces do not inherit settings, which
had left the root workspace and the modules on two different stores.

Add scripts/clean-node-modules: removes every workspace node_modules
in one pass (ignores external/ and .opencode/), keeps the shared store
unless --store removes it too.

Verified: every workspace resolves the same store path; reinstalls
after a full clean reuse the cache with zero downloads;
frozen-lockfile installs pass in all 11 workspaces with no lockfile
changes; the frontend storybook suite stays green.

AI-assisted-by: omen-alpha
2026-09-09 08:04:11 +02:00
Andrey Antukh
e4d1816117 ⬆️ Update pnpm to 12.3.4 across all workspaces
Run `corepack use pnpm@next-12` (resolved to 12.3.4) on every directory
with a package.json: the repo root, the 11 module workspaces, and all
submodules. Every packageManager field now carries the same pinned
version and hash; the root and backend move off 11.20.0.

Fix the composable-test-suite workspace config (esbuild allowBuilds
placeholder left by pnpm 12) so its install passes, and add the missing
packageManager fields to frontend/packages/ui and mcp/packages/plugin,
since corepack only updates existing fields.

Document the canonical update procedure in
.serena/memories/workflow/updating-pnpm.md.

AI-assisted-by: omen-alpha
2026-09-09 08:04:11 +02:00
Andrey Antukh
f695553469 Merge remote-tracking branch 'origin/staging' into develop 2026-09-08 21:15:58 +02:00
Andrey Antukh
acc078064b
🐛 Do not register developer tools in multi-user MCP mode (#11310)
Prevent developer tools from being exposed when the MCP server runs in multi-user mode. Keep them available for local devenv usage and document the mode restriction. Add regression coverage for the registration policy.

Closes #11291

AI-assisted-by: gpt-5.6-luna

Co-authored-by: niwinz <843689+niwinz@users.noreply.github.com>
2026-09-07 13:19:32 +02:00
Andrey Antukh
f5aad7b1ae Merge remote-tracking branch 'origin/staging' into develop 2026-09-01 12:45:17 +02:00
Andrey Antukh
a1079cf788
⬆️ Update JVM, pnpm and node dependencies (#11404)
* ⬆️ Update pnpm and its deps

* ⬆️ Update JVM dependencies in backend and common

Update several JVM dependencies across backend and common:

- passay 1.6.6 -> 2.0.0 (package reorg, ctor-based rules)
- siphash 2.0.0 -> 3.0.0 (SipHasher* renamed to SipHash*)
- lettuce-core, guava, sqlite-jdbc, jsoup, lz4-java, markdown-clj,
  awssdk s3/sts, selmer, jackson-core/databind, shadow-cljs

Adapt passay validation to the new API (moved packages, constructor
configuration) and siphash to the renamed classes. Add tests for
password validation and UUID advisory-lock hashing.

AI-assisted-by: deepseek-v4-flash

* ⬆️ Update node on docker images

* 📎 Minor fixes related to pnpm12 compatibility
2026-09-01 12:01:35 +02:00
Andrey Antukh
23869f3b1b
🐛 Gate MCP REPL server behind isDevEnv check (#11282)
* 🐛 Gate MCP REPL server behind isDevEnv check

The ReplServer was starting unconditionally on every MCP server
instance, regardless of configuration. This exposed an
unauthenticated POST /execute endpoint that forwarded arbitrary
JavaScript to connected Penpot plugins.

Gate ReplServer creation, startup, and shutdown behind isDevEnv(),
consistent with how CljsReplTool and other dev tools are already
protected. Log an info message when the REPL server is disabled.

Consolidate the dev-env check into a single static isDevEnvEnabled()
method that isDevEnv() delegates to, avoiding duplicate logic. Add
PluginBridge.close() for proper WebSocket server cleanup on shutdown.

Add regression tests that construct PenpotMcpServer and verify
hasReplServer() returns the correct value based on the dev-env flag.

AI-assisted-by: mimo-v2.5-pro

*  Add PENPOT_MCP_REPL_ENABLE env var for explicit REPL control

Allow the REPL server to be enabled independently of the devenv
setting via a new PENPOT_MCP_REPL_ENABLE environment variable.
When set to "true", the REPL server starts regardless of
PENPOT_MCP_DEVENV; when set to any other value, it is disabled.
When unset, the previous isDevEnv fallback applies.

Addresses review feedback on PR #11282.

AI-assisted-by: mimo-v2.5-pro
2026-09-01 08:50:05 +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
Andrey Antukh
3865e29b65 ⬆️ Update pnpm dependencies across all modules 2026-08-04 14:15:35 +02:00
Andrey Antukh
e741313add Merge remote-tracking branch 'origin/staging' into develop 2026-07-31 19:21:07 +02:00
Andrey Antukh
07efd8fb99
⬆️ Update dependencies (#10982)
* ⬆️ Update root pnpm depdendencies

* ⬆️ Update common pnpm dependencies

* ⬆️ Upgrade docs pnpm dependencies

* ⬆️ Upgrade pnpm deps on library

* ⬆️ Upgrade plugins pnpm dependencies

* ⬆️ Update pnpm dependencies on mcp

* ⬆️ Update pnpm dependencies on frontend

* ⬆️ Update exporter pnpm dependencies

* ⬆️ Update jvm/clojure deps

* ⬆️ Update docker dependencies (jvm and node)

* 📎 Add minor fixes
2026-07-31 18:53:05 +02:00
Dominik Jain
30943f1074 Make MCP tool call timeout configurable, raising default
The timeout for tool calls (which is trictly relevant for plugin tasks only)
is now configurable via env. var PENPOT_MCP_TOOL_TIMEOUT_S.

The default was raised from 30 to 120, because 30 seconds was not enough for
some calls, especially in larger Penpot files. #10953
2026-07-30 14:21:42 +02:00
Dominik Jain
b4659df5b2 🐛 Preserve established plugin connection when rejecting a duplicate #10961
In multi-user mode, rejecting a second plugin WebSocket connection for an
already-registered user token performed the full removeConnection cleanup
for the newcomer. Since the token-keyed cleanup is keyed by token rather
than by socket, this deleted the clientsByToken entry and the Redis
request-channel subscription of the established, healthy connection. That
connection then remained open and heartbeating but was unroutable, so every
subsequent MCP tool call for the user failed although a valid plugin
connection existed.

removeConnection now performs the token-keyed cleanup only if the removed
connection actually owns the token registration, so rejecting a duplicate
releases only the resources the newcomer itself registered.

AI-assisted-by: claude-fable-5
2026-07-30 14:21:42 +02:00
Dominik Jain
1ae9334064 🐛 Fail fast on Redis task dispatch when no instance is connected #10958
In multi-user mode, plugin task requests are published to a Redis channel
keyed by user token. When no MCP server instance held a plugin connection
for that token (e.g. after the user navigated away from the workspace),
the publish reached zero subscribers and the request was silently dropped,
so every tool call stalled until the 30-second task timeout instead of
failing with a meaningful error.

RedisBridge.sendTaskRequest now returns the PUBLISH receiver count and
releases its response-channel subscription when the request reached no
receiver (or publishing failed), since no response can arrive. PluginBridge
uses the count to reject the pending task immediately with the multi-user
connection error message; publish failures likewise reject the task instead
of surfacing as an unhandled rejection followed by a timeout. The pending-
task settlement logic shared with the timeout handler is extracted into a
rejectPendingTask helper.

AI-assisted-by: claude-fable-5
2026-07-30 14:21:42 +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
Dr. Dominik Jain
d4e87ec59d
⬆️ Update Serena to 1.6.1 in agentic devenv (#10770)
Update project files accordingly
2026-07-22 09:35:50 +02:00
Dr. Dominik Jain
51c0129485
Simplify MCP client setup, adding client-setup command (#10604)
Add a `client-setup` command to the published `@penpot/mcp` package.
When invoked as `penpot-mcp client-setup`, the bin delegates to the `add-mcp`
CLI against the local MCP server URL (from `PENPOT_MCP_SERVER_PORT`, default
4401), run via npx.

Update docs on MCP client configuration.
Recommend calling `add-mcp` directly for now, since the `client-setup` command
only becomes available once a new `@penpot/mcp` release is published to npm.

AI-assisted-by: Claude

Co-authored-by: Michael Panchenko <michael.panchenko@oraios-ai.de>
2026-07-20 13:08:51 +02:00
Dr. Dominik Jain
d9511db585
🔧 Auto-confirm pnpm modules purge in MCP bootstrap #10680 (#10681)
pnpm occasionally detects an incompatible node_modules directory (e.g.
after a store location or pnpm major version change) and interactively
asks whether to remove and recreate it, blocking the MCP bootstrap in
the devenv tmux pane. Set confirmModulesPurge: false in
mcp/pnpm-workspace.yaml so the purge is auto-confirmed; this file is
included in the npm pack tarball (unlike .npmrc) and applies to all
install invocations from a single place.

AI-assisted-by: claude-fable-5
2026-07-16 17:00:20 +02:00
Andrey Antukh
a004f7cd84 Merge remote-tracking branch 'origin/staging' into develop 2026-07-08 15:44:37 +02:00
alonso.torres
88186eaec1 ⬆️ Release MCP 2.17.0 2026-07-08 12:57:45 +02:00
Dominik Jain
93b5baa9fc 🐛 Relocate MCP run directory out of the npm-managed install directory
For the npm MCP package, adjust the launcher to specifically prepare
the runtime directory: The package contents are copied once per version
to a runtime directory owned by the launcher.

The npm-owned package directory cannot be used. While installing there
worked for older npm/npx version, we have to assume it is read-only.

Fixes #9947
2026-07-08 11:28:17 +02:00
Dominik Jain
aee555ff6e 📚 Update API types for MCP server for new release 2026-07-08 11:27:46 +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
Alejandro Alonso
b82ab0c830 Merge remote-tracking branch 'origin/staging' into develop 2026-07-02 13:52:25 +02:00
Andrey Antukh
6cc2c403c4 ⬆️ Update dependencies 2026-07-01 14:45:41 +02:00
Andrey Antukh
c4e72fd7f9 Merge remote-tracking branch 'origin/staging' into develop 2026-06-30 16:23:23 +02:00
Andrey Antukh
5bae6bd078 📎 Fix mcp deprecation warning 2026-06-30 16:15:30 +02:00
Andrey Antukh
e2b52d88fc ⬆️ Update dependencies 2026-06-30 16:15:30 +02:00
Alonso Torres
28f3b8048a
Improve MCP handling when tab is frozen in the browser (#10392) 2026-06-25 08:27:33 +02:00
Dr. Dominik Jain
0270c2a90f
🐛 Fix description of TokenCatalog.addTheme in high-level overview (#10359)
Fixes #10074
2026-06-24 10:27:28 +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
Andrey Antukh
203817fe6a Merge remote-tracking branch 'origin/staging' into develop 2026-06-18 09:44:10 +02:00
Andrey Antukh
71f5c11a11 Qualify MCP Redis channel names with tenant prefix
Read PENPOT_TENANT env var (defaulting to "default") and embed it in
Redis Pub/Sub channel names as penpot.mcp.<tenant>.task.{req,res}.<id>.

This prevents cross-tenant interference when multiple environments share
a Redis instance, matching the backend convention
(e.g. penpot.rlimit.<tenant>.window.<name> in app.rpc.rlimit).

Co-authored-by: deepseek-v4-flash <deepseek-v4-flash@penpot.app>
2026-06-18 09:22:29 +02:00
Andrey Antukh
03341ed857
⬆️ Update npm deps and pnpm on all subpackages (#10183) 2026-06-15 13:57:29 +02:00
Andrey Antukh
d44c6250ea Merge remote-tracking branch 'origin/staging' into develop 2026-06-15 13:04:19 +02:00
Alonso Torres
d6c973e269
🐛 Improved error handling on a tool error (#10148) 2026-06-12 08:59:05 +02:00
Andrey Antukh
715bd1c09e 📎 Update mcp server api_types.yml file 2026-06-11 14:51:02 +02:00
Andrey Antukh
6827c4554b 📎 Update version on mcp package.json 2026-06-11 14:49:48 +02:00
Andrey Antukh
2d843da2cd 📎 Update mcp server api_types.yml 2026-06-11 13:31:34 +02:00
martrend33
5dea5db685
📎 Add license metadata for MCP package (#10089)
Add the repository license SPDX identifier to the published @penpot/mcp package metadata.

Signed-off-by: martrend33 <145278472+martrend33@users.noreply.github.com>
2026-06-10 11:17:07 +02:00