mirror of
https://github.com/penpot/penpot.git
synced 2026-10-03 09:16:15 +00:00
* 🎉 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>
359 lines
19 KiB
Markdown
359 lines
19 KiB
Markdown

|
||
|
||
# Penpot's Official MCP Server
|
||
|
||
Penpot integrates a LLM layer built on the Model Context Protocol
|
||
(MCP) via Penpot's Plugin API to interact with a Penpot design
|
||
file. Penpot's MCP server enables LLMs to perform data queries,
|
||
transformation and creation operations.
|
||
|
||
Penpot's MCP Server is unlike any other you've seen. You get
|
||
design-to- design, code-to-design and design-code supercharged
|
||
workflows.
|
||
|
||
|
||
[](https://www.youtube.com/playlist?list=PLgcCPfOv5v57SKMuw1NmS0-lkAXevpn10)
|
||
|
||
|
||
## Architecture
|
||
|
||
The **Penpot MCP Server** exposes tools to AI clients (LLMs), which
|
||
support the retrieval of design data as well as the modification and
|
||
creation of design elements. The MCP server communicates with Penpot
|
||
via the dedicated **Penpot MCP Plugin**,
|
||
which connects to the MCP server via WebSocket.
|
||
This enables the LLM to carry out tasks in the context of a design file by
|
||
executing code that leverages the Penpot Plugin API.
|
||
The LLM is free to write and execute arbitrary code snippets
|
||
within the Penpot Plugin environment to accomplish its tasks.
|
||
|
||

|
||
|
||
This repository thus contains not only the MCP server implementation itself
|
||
but also the supporting Penpot MCP Plugin
|
||
(see section [Repository Structure](#repository-structure) below).
|
||
|
||
## Demonstration
|
||
|
||
[](https://v32155.1blu.de/penpot/PenpotFest2025.mp4)
|
||
|
||
|
||
## Usage
|
||
|
||
> [!IMPORTANT]
|
||
> **These instructions are for local MCP server usage only!**
|
||
>
|
||
> The instructions below are for users who want to run the server locally,
|
||
> allowing the server to have access to local files, which can be
|
||
> relevant for development/coding tasks.
|
||
>
|
||
> If you need to work only on designs, the centrally hosted **remote MCP server**
|
||
> should be considered instead.
|
||
> Please refer to the [MCP usage instructions in the help center](https://help.penpot.app/mcp/).
|
||
|
||
To run the Penpot MCP server locally, you must
|
||
* run the MCP server and connect your AI client to it,
|
||
* run the web server providing the Penpot MCP plugin, and
|
||
* open the Penpot MCP plugin in Penpot and connect it to the MCP server.
|
||
|
||
Follow the steps below to enable the integration.
|
||
|
||
### Prerequisites
|
||
|
||
The project requires [Node.js](https://nodejs.org/) 20 or later (tested with v22.x).
|
||
|
||
### 1. Starting the MCP Server and the Plugin Server
|
||
|
||
#### Running a Released Version via npx
|
||
|
||
The easiest way to launch the servers is to use `npx` to run the appropriate
|
||
version that matches your Penpot version.
|
||
|
||
If you are using the latest Penpot release, e.g. as served on [design.penpot.app](https://design.penpot.app), run:
|
||
```shell
|
||
npx -y @penpot/mcp@latest
|
||
```
|
||
|
||
Once the servers are running, continue with step 2.
|
||
|
||
#### Running the Source Version from the Repository
|
||
|
||
The tools `pnpm` and `npx` should be available in your terminal.
|
||
|
||
On Windows, use the Git Bash terminal to ensure compatibility with the provided scripts.
|
||
|
||
##### Clone the Appropriate Branch of the Repository
|
||
|
||
Clone the Penpot repository, using the proper branch/tag depending on the
|
||
version of Penpot you want to use the MCP server with.
|
||
For instance, to target the latest development version, use the `develop` branch:
|
||
|
||
```shell
|
||
git clone https://github.com/penpot/penpot.git --branch develop --depth 1
|
||
```
|
||
|
||
Then change into the `mcp` directory:
|
||
|
||
```shell
|
||
cd penpot/mcp
|
||
```
|
||
|
||
##### Build & Launch the MCP Server and the Plugin Server
|
||
|
||
If it's your first execution, install the required dependencies.
|
||
(If you are using the Penpot devenv, this step is not necessary, as dependencies are already installed.)
|
||
|
||
```shell
|
||
./scripts/setup
|
||
```
|
||
|
||
Then build all components and start the two servers:
|
||
|
||
```shell
|
||
pnpm run bootstrap
|
||
```
|
||
|
||
This bootstrap command will:
|
||
|
||
* install dependencies for all components
|
||
* build all components
|
||
* start all components
|
||
|
||
### 2. Load the Plugin in Penpot and Establish the Connection
|
||
|
||
> [!NOTE]
|
||
> **Browser Connectivity Restrictions**
|
||
>
|
||
> Starting with Chromium version 142, the private network access (PNA) restrictions have been hardened,
|
||
> and when connecting to `localhost` from a web application served from a different origin
|
||
> (such as https://design.penpot.app), the connection must explicitly be allowed.
|
||
>
|
||
> Most Chromium-based browsers (e.g. Chrome, Vivaldi) will display a popup requesting permission
|
||
> to access the local network. Be sure to approve the request to allow the connection.
|
||
>
|
||
> Some browsers take additional security measures, and you may need to disable them.
|
||
> For example, in Brave, disable the "Shield" for the Penpot website to allow local network access.
|
||
>
|
||
> If your browser refuses to connect to the locally served plugin, check its configuration or
|
||
> try a different browser (e.g. Firefox) that does not enforce these restrictions.
|
||
|
||
1. Open Penpot in your browser
|
||
2. Navigate to a design file
|
||
3. Open the Plugins menu
|
||
4. Load the plugin using the development URL (`http://localhost:4400/manifest.json` by default)
|
||
5. Open the plugin UI
|
||
6. In the plugin UI, click "Connect to MCP server".
|
||
The connection status should change from "Not connected" to "Connected".
|
||
(Check the browser's developer console for WebSocket connection logs.
|
||
Check the MCP server terminal for WebSocket connection messages.)
|
||
|
||
> [!IMPORTANT]
|
||
> Do not close the plugin's UI while using the MCP server, as this will close the connection.
|
||
> Also keep the Penpot tab active during long MCP sessions. Browsers may freeze, suspend,
|
||
> or unload inactive tabs to save resources; when that happens, the MCP server will reject
|
||
> tasks until the tab wakes up or reconnects. In Chrome, add your Penpot site to
|
||
> **Settings → Performance → Always keep these sites active** or pin the tab to reduce
|
||
> tab deactivation.
|
||
|
||
### 3. Connect an MCP Client
|
||
|
||
> [!IMPORTANT]
|
||
> **Use an appropriate model.**
|
||
>
|
||
> We recommend that you ...
|
||
> * use the most capable model at your disposal.
|
||
> You will achieve the best results with frontier models,
|
||
> especially when dealing with more complex tasks.
|
||
> Weaker models, including most locally hosted ones,
|
||
> are unlikely to produce usable results for anything beyond simple tasks.
|
||
> * use a vision language model (VLM), as many design tasks necessitate visual
|
||
> inspection.
|
||
> (If you are using a standard commercial model, it almost certainly supports vision already.)
|
||
|
||
By default, the server provides a Streamable HTTP endpoint at `http://localhost:4401/mcp`.
|
||
Simply configure the client to connect the MCP server by providing the respective URL.
|
||
|
||
The legacy `/sse` and `/messages` endpoints are no longer supported.
|
||
Clients using the legacy SSE transport must switch to Streamable HTTP at `/mcp`.
|
||
|
||
You can change the port by setting the `PENPOT_MCP_SERVER_PORT` environment variable
|
||
before starting the server. This endpoint can be used directly by MCP clients that support Streamable HTTP.
|
||
|
||
#### Automatically configuring your client
|
||
|
||
You can configure your client with the [add-mcp](https://github.com/neon-solutions/add-mcp) helper.
|
||
Simply call
|
||
|
||
npx -y add-mcp -g -n penpot http://localhost:4401/mcp
|
||
|
||
and follow the interactive dialogue to configure the clients of your choice.
|
||
The config entry name is `penpot` (override it with `-n <name>`) and the URL points to the local http
|
||
endpoint (adjust the port if you changed `PENPOT_MCP_SERVER_PORT`).
|
||
|
||
When using a client that only supports stdio transport like **Claude Desktop**,
|
||
a proxy like [mcp-remote](https://github.com/geelen/mcp-remote) is required.
|
||
More information on connecting your client follows below.
|
||
|
||
#### Using a Proxy for stdio Transport
|
||
|
||
The `mcp-remote` package can proxy stdio transport to Streamable HTTP,
|
||
allowing clients that support only stdio to connect to the MCP server indirectly.
|
||
Use it to provide the launch command for your MCP client as follows:
|
||
|
||
npx -y mcp-remote http://localhost:4401/mcp --allow-http
|
||
|
||
#### Example: Claude Desktop
|
||
|
||
For Windows and macOS, there is the official [Claude Desktop app](https://claude.ai/download), which you can use as an MCP client.
|
||
For Linux, there is an [unofficial community version](https://github.com/aaddrick/claude-desktop-debian).
|
||
|
||
Since Claude Desktop natively supports only stdio transport, you will need to use a proxy like `mcp-remote`.
|
||
Install it as described above.
|
||
|
||
To add the server to Claude Desktop's configuration, locate the configuration file (or find it via Menu / File / Settings / Developer):
|
||
|
||
- **Windows**: `%APPDATA%/Claude/claude_desktop_config.json`
|
||
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
||
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
|
||
|
||
Add a `penpot` entry under `mcpServers` with the following content:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"penpot": {
|
||
"command": "npx",
|
||
"args": ["-y", "mcp-remote", "http://localhost:4401/mcp", "--allow-http"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
After updating the configuration file, restart Claude Desktop completely for the changes to take effect.
|
||
|
||
> [!IMPORTANT]
|
||
> Be sure to fully quit the app for the changes to take effect; closing the window is *not* sufficient.
|
||
> To fully terminate the app, choose Menu / File / Quit.
|
||
|
||
After the restart, you should see the MCP server listed when clicking on the "Search and tools" icon at the bottom
|
||
of the prompt input area.
|
||
|
||
### Working with Multiple Files
|
||
|
||
Connect the files you want to use:
|
||
|
||
* Open each file in a separate Penpot tab, run the plugin, and connect it to the same MCP server.
|
||
* Each connection has its own short session ID, shown in the plugin UI with a **Copy** button.
|
||
* Multiple tabs can connect to the same file. Connecting or disconnecting one tab does not disconnect the others.
|
||
|
||
Choose a session for your agent:
|
||
|
||
* With exactly one connected session, the server selects it automatically when the agent omits the session ID.
|
||
* With several connected sessions, the server lists them and asks the agent to have you select one.
|
||
* You can also copy an ID from the plugin and include it in your prompt, for example:
|
||
"Use Penpot session `o37vgcqsvt` and list the pages in that file."
|
||
|
||
For integrated remote MCP connection controls and session behavior, see the
|
||
[Help Center guide](../docs/mcp/index.md#working-with-multiple-files).
|
||
|
||
## Repository Structure
|
||
|
||
This repository is a monorepo containing four main components:
|
||
|
||
1. **Common Types** (`packages/common/`):
|
||
- Shared TypeScript definitions for request/response protocol
|
||
- Ensures type safety across server and plugin components
|
||
|
||
2. **Penpot MCP Server** (`packages/server/`):
|
||
- Provides MCP tools to LLMs for Penpot interaction
|
||
- Runs a WebSocket server accepting connections from the Penpot MCP plugin
|
||
- Implements request/response correlation with unique task IDs
|
||
- Handles task timeouts and proper error reporting
|
||
|
||
3. **Penpot MCP Plugin** (`packages/plugin/`):
|
||
- Connects to the MCP server via WebSocket
|
||
- Executes tasks in Penpot using the Plugin API
|
||
- Sends structured responses back to the server#
|
||
|
||
4. **Types Generator** (`types-generator/`):
|
||
- Generates data on API types for the MCP server (development use)
|
||
|
||
The core components are written in TypeScript, rendering interactions with the
|
||
Penpot Plugin API both natural and type-safe.
|
||
|
||
## Configuration
|
||
|
||
The Penpot MCP server can be configured using environment variables.
|
||
|
||
### Server Configuration
|
||
|
||
| Environment Variable | Description | Default |
|
||
|--------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|
|
||
| `PENPOT_MCP_SERVER_HOST` | Address on which the MCP server listens (binds to) | `localhost` |
|
||
| `PENPOT_MCP_SERVER_PORT` | Port for the MCP server | `4401` |
|
||
| `PENPOT_MCP_WEBSOCKET_PORT` | Port for the WebSocket server (plugin connection) | `4402` |
|
||
| `PENPOT_MCP_REPL_PORT` | Port for the REPL server (development/debugging) | `4403` |
|
||
| `PENPOT_MCP_REPL_HOST` | Address on which the REPL server listens (binds to) | `localhost` |
|
||
| `PENPOT_MCP_REPL_ENABLE` | Explicitly enable/disable the REPL server. Set to `true` to enable. When unset, defaults to the value of `PENPOT_MCP_DEVENV`. The REPL server never starts in multi-user mode. | (unset) |
|
||
| `PENPOT_MCP_REMOTE_MODE` | Enable remote mode (disables file system access). Set to `true` to enable. | `false` |
|
||
| `PENPOT_MCP_DEVENV` | Enable Penpot development environment tools in local single-user mode. Set to `true` to enable. | `false` |
|
||
| `PENPOT_MCP_TOOL_TIMEOUT_S` | Timeout, in seconds, for tool calls dispatched to the Penpot plugin | `120` |
|
||
| `PENPOT_MCP_EXPORT_SHAPE_MAX_PARALLEL_REQUESTS` | Maximum number of parallel export shape requests (multi-user mode only). | `0` (no limit) |
|
||
| `PENPOT_MCP_REDIS_URI` | Redis connection URI (e.g. `redis://host:6379`) enabling multi-instance horizontal scaling via Redis pub/sub task routing (multi-user mode only). When unset, the server runs in single-instance mode, requiring the plugin and MCP client to connect to the same instance. | (unset) |
|
||
|
||
### Logging Configuration
|
||
|
||
| Environment Variable | Description | Default |
|
||
|------------------------|------------------------------------------------------|----------|
|
||
| `PENPOT_MCP_LOG_LEVEL` | Log level: `trace`, `debug`, `info`, `warn`, `error` | `info` |
|
||
| `PENPOT_MCP_LOG_DIR` | Directory for log files; file logging is enabled iff this is set to a non-empty value | (unset) |
|
||
|
||
### Plugin Server Configuration
|
||
|
||
| Environment Variable | Description | Default |
|
||
|-------------------------------------------|-----------------------------------------------------------------------------------------|--------------|
|
||
| `PENPOT_MCP_PLUGIN_SERVER_HOST` | Address on which the plugin web server listens (single address or comma-separated list) | (local only) |
|
||
|
||
## The Server's Modes of Operation
|
||
|
||
The above instructions describe how to run the MCP server and plugin server locally,
|
||
for a single user – its simplest mode of operation.
|
||
|
||
The server supports additional modes of operation, which are intended for
|
||
non-local usage (and which are relevant to hosted deployments only).
|
||
Some of the aforementioned configuration options control these modes.
|
||
|
||
* **remote mode**:
|
||
In remote mode, the server is not assumed to be accessed by a local user on the same machine,
|
||
with corresponding limitations being enforced (tools offering local file system access are restricted/disabled).
|
||
|
||
* **multi-user mode**:
|
||
In multi-user mode, the server can be accessed by multiple users simultaneously.
|
||
This mode always implies *remote mode*.
|
||
User tokens are passed to the server both when connecting an MCP client and when establishing
|
||
plugin connections, associating each connection with the respective user and allowing tasks to be routed to the correct Penpot instance.
|
||
This mode is intended for hosted deployments using the integrated version of the Penpot MCP plugin,
|
||
which automatically sends the user token to the server when establishing WebSocket connections.
|
||
It is enabled by running the server with the `--multi-user` flag.
|
||
|
||
* **multi-instance mode**:
|
||
In multi-instance mode, multiple instances of the MCP server can be run in parallel (load balancing).
|
||
This can result in connections from the same user being owned by different server instances,
|
||
making it necessary to dispatch tasks to the correct instance. This is handled through Redis,
|
||
and the mode is thus enabled by setting the environment variable providing the Redis connection URI.
|
||
|
||
## Development
|
||
|
||
* The [contribution guidelines for Penpot](../CONTRIBUTING.md) apply
|
||
* Auto-formatting: Use `pnpm run fmt`
|
||
* Generating API type data: See [types-generator/README.md](types-generator/README.md)
|
||
* Versioning: Use `bash scripts/set-version` to set the version for the MCP package (in `package.json`).
|
||
- Ensure that at least the major, minor and patch components of the version are always up-to-date.
|
||
- The MCP plugin assumes that a mismatch between the MCP version and the Penpot version (as returned by the API)
|
||
indicates incompatibility, resulting in the display of a warning message in the plugin UI.
|
||
* Packaging and publishing:
|
||
1. Ensure that the API type data is up-to-date (see above).
|
||
2. Ensure release version is set correctly in package.json (call `bash scripts/set-version` to update it automatically)
|
||
3. Create npm package: `bash scripts/pack` (creates `penpot-mcp-<version>.tgz` for publishing)
|
||
4. Publish to npm: `npm publish penpot-mcp-<version>.tgz --access public`
|