From b4e815e78747ca8a03b6d4616a96add992bc45ad Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Thu, 12 Feb 2026 12:03:31 +0100 Subject: [PATCH 01/12] :paperclip: Fix spelling errors --- mcp/packages/server/data/initial_instructions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mcp/packages/server/data/initial_instructions.md b/mcp/packages/server/data/initial_instructions.md index 8aa60b1a1b..ad163c151c 100644 --- a/mcp/packages/server/data/initial_instructions.md +++ b/mcp/packages/server/data/initial_instructions.md @@ -99,11 +99,11 @@ Boards can have layout systems that automatically control the positioning and sp - `dir`: "row" | "column" | "row-reverse" | "column-reverse" - Padding: `topPadding`, `rightPadding`, `bottomPadding`, `leftPadding`, or combined `verticalPadding`, `horizontalPadding` - To modify spacing: adjust `rowGap` and `columnGap` properties, not individual child positions. - Optionally, adjust indivudual child margins via `child.layoutChild`. + Optionally, adjust individual child margins via `child.layoutChild`. - When a board has flex layout, - child positions are controlled by the layout system, not by individual x/y coordinates (unless `child.layoutChild.absolute` is true); appending or inserting children automatically positions them according to the layout rules. - - CRITICAL: For for dir="column" or dir="row", the order of the `children` array is reversed relative to the visual order! + - CRITICAL: For dir="column" or dir="row", the order of the `children` array is reversed relative to the visual order! Therefore, the element that appears first in the array, appears visually at the end (bottom/right) and vice versa. ALWAYS BEAR IN MIND THAT THE CHILDREN ARRAY ORDER IS REVERSED FOR dir="column" OR dir="row"! - CRITICAL: The FlexLayout method `board.flex.appendChild` is BROKEN. To append children to a flex layout board such that From bac04f8a7318f65b41f2c6c24bd184559ab2b6d2 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Thu, 12 Feb 2026 12:37:24 +0100 Subject: [PATCH 02/12] :construction: Temporary workaround for sizing options not working Add instructions explaining that FlexLayout sizing options do not work. Relates to https://github.com/penpot/penpot-mcp/issues/39 --- mcp/packages/server/data/initial_instructions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/mcp/packages/server/data/initial_instructions.md b/mcp/packages/server/data/initial_instructions.md index ad163c151c..6105be7090 100644 --- a/mcp/packages/server/data/initial_instructions.md +++ b/mcp/packages/server/data/initial_instructions.md @@ -100,6 +100,7 @@ Boards can have layout systems that automatically control the positioning and sp - Padding: `topPadding`, `rightPadding`, `bottomPadding`, `leftPadding`, or combined `verticalPadding`, `horizontalPadding` - To modify spacing: adjust `rowGap` and `columnGap` properties, not individual child positions. Optionally, adjust individual child margins via `child.layoutChild`. + - Sizing: `verticalSizing` and `horizontalSizing` are NOT functional. You need to size manually for the time being. - When a board has flex layout, - child positions are controlled by the layout system, not by individual x/y coordinates (unless `child.layoutChild.absolute` is true); appending or inserting children automatically positions them according to the layout rules. From 926d573d3ec4a69f3d7cb56ab8b6ef79d119dfc6 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Thu, 12 Feb 2026 17:24:44 +0100 Subject: [PATCH 03/12] :construction: Temporary workaround for Token resolvedValue not working Instruct LLM to not use this property. To be reverted once #8341 is fixed. --- mcp/packages/server/data/initial_instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp/packages/server/data/initial_instructions.md b/mcp/packages/server/data/initial_instructions.md index 6105be7090..6f746d1012 100644 --- a/mcp/packages/server/data/initial_instructions.md +++ b/mcp/packages/server/data/initial_instructions.md @@ -290,7 +290,7 @@ The token library: `penpot.library.local.tokens` (type: `TokenCatalog`) `Token`: * `name: string` - Token name (may include group path like "color.base.white") * `value: string | TokenValueString` - Raw value (may be direct value or reference to another token like "{color.primary}") - * `resolvedValue` - Computed final value (follows references) + * `resolvedValue` - Computed final value (follows references) - currently NOT working, do not use! * `type: TokenType` Discovering tokens: From 08fc6fe917006e01b9e2412811357167698c37b9 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Thu, 12 Feb 2026 17:44:52 +0100 Subject: [PATCH 04/12] :sparkles: Improve description of token values --- mcp/packages/server/data/initial_instructions.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mcp/packages/server/data/initial_instructions.md b/mcp/packages/server/data/initial_instructions.md index 6f746d1012..720a34eee5 100644 --- a/mcp/packages/server/data/initial_instructions.md +++ b/mcp/packages/server/data/initial_instructions.md @@ -283,13 +283,14 @@ The token library: `penpot.library.local.tokens` (type: `TokenCatalog`) * `tokens: Token[]` - All tokens in set * `addToken(type: TokenType, name: string, value: TokenValueString): Token` - Creates a token, adding it to the set. - `TokenType`: "color" | "dimension" | "spacing" | "typography" | "shadow" | "opacity" | "borderRadius" | "borderWidth" | "fontWeights" | "fontSizes" | "fontFamilies" | "letterSpacing" | "textDecoration" | "textCase" + - `value`: depends on the type of token (inspect `Token` and related types) - Examples: const token = set.addToken("color", "color.primary", "#0066FF"); // direct value const token2 = set.addToken("color", "color.accent", "{color.primary}"); // reference to another token -`Token`: - * `name: string` - Token name (may include group path like "color.base.white") - * `value: string | TokenValueString` - Raw value (may be direct value or reference to another token like "{color.primary}") +`Token`: union type encompassing various token types, with common properties: + * `name: string` - Token name (typically structured, e.g. "color.base.white") + * `value` - Raw value (direct value or reference to another token like "{color.primary}") * `resolvedValue` - Computed final value (follows references) - currently NOT working, do not use! * `type: TokenType` From 7a525508895be2e4c00f6a0347b75f2a9fe6dc57 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Sat, 14 Feb 2026 01:43:25 +0100 Subject: [PATCH 05/12] :sparkles: Make clear that ExecuteCodeTool serialises automatically LLMs sometimes decide to apply serialisation themselves, which is unnecessary, and which this seeks to prevent. --- mcp/packages/server/src/tools/ExecuteCodeTool.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mcp/packages/server/src/tools/ExecuteCodeTool.ts b/mcp/packages/server/src/tools/ExecuteCodeTool.ts index adcb6339fa..6a514e0cf4 100644 --- a/mcp/packages/server/src/tools/ExecuteCodeTool.ts +++ b/mcp/packages/server/src/tools/ExecuteCodeTool.ts @@ -53,9 +53,10 @@ export class ExecuteCodeTool extends Tool { "could come in handy later should be stored in `storage` instead of just a fleeting variable; " + "you can also store functions and thus build up a library).\n" + "Think of the code being executed as the body of a function: " + - "The tool call returns whatever you return in the applicable `return` statement, if any.\n" + + "The tool call returns whatever you return in the applicable `return` statement, if any. " + + "You can return arbitrary JS objects; no need to apply JSON.stringify.\n" + "If an exception occurs, the exception's message will be returned to you.\n" + - "Any output that you generate via the `console` object will be returned to you separately; so you may use it" + + "Any output that you generate via the `console` object will be returned to you separately; so you may use it " + "to track what your code is doing, but you should *only* do so only if there is an ACTUAL NEED for this! " + "VERY IMPORTANT: Don't use logging prematurely! NEVER log the data you are returning, as you will otherwise receive it twice!\n" + "VERY IMPORTANT: In general, try a simple approach first, and only if it fails, try more complex code that involves " + From 141847585e79c629cd8734759baa8defa1c4bce3 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Sat, 14 Feb 2026 01:58:22 +0100 Subject: [PATCH 06/12] :construction: Temporary workaround for fills/strokes being read-only Add instructions to make the limintations. Once #8357 is resolved, this can be reverted. --- mcp/packages/server/data/initial_instructions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mcp/packages/server/data/initial_instructions.md b/mcp/packages/server/data/initial_instructions.md index 720a34eee5..eedb5f3804 100644 --- a/mcp/packages/server/data/initial_instructions.md +++ b/mcp/packages/server/data/initial_instructions.md @@ -48,6 +48,8 @@ Actual low-level shape types are `Rectangle`, `Path`, `Text`, `Ellipse`, `Image` **Other Writable Properties**: * `name` - Shape name * `fills`, `strokes` - Styling properties + IMPORTANT: The contents of the arrays are read-only. You cannot modify individual fills/strokes; you need to replace the entire array to change them, e.g. + `shape.fills = [{ fillColor: "#FF0000", fillOpacity: 1 }]` to set a single red fill. * `rotation`, `opacity`, `blocked`, `hidden`, `visible` **Z-Order**: From 6a49b5df8c2d33e4f95ebdbf1015a08ea893ee20 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Tue, 17 Feb 2026 13:16:21 +0100 Subject: [PATCH 07/12] :recycle: Move high-level instructions to the end In this way, they can reasonably reference the more low-level concepts --- .../server/data/initial_instructions.md | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/mcp/packages/server/data/initial_instructions.md b/mcp/packages/server/data/initial_instructions.md index eedb5f3804..573f8e2f2f 100644 --- a/mcp/packages/server/data/initial_instructions.md +++ b/mcp/packages/server/data/initial_instructions.md @@ -1,10 +1,6 @@ You have access to Penpot tools in order to interact with a Penpot design project directly. As a precondition, the user must connect the Penpot design project to the MCP server using the Penpot MCP Plugin. -IMPORTANT: When transferring styles from a Penpot design to code, make sure that you strictly adhere to the design. - NEVER make assumptions about missing values and don't get overly creative (e.g. don't pick your own colours and stick to - non-creative defaults such as white/black if you are lacking information). - # Executing Code One of your key tools is the `execute_code` tool, which allows you to run JavaScript code using the Penpot Plugin API @@ -213,19 +209,6 @@ Common tasks - Quick Reference (ALWAYS use penpotUtils for these): }); Always validate against the root container that is supposed to contain the shapes. -# Visual Inspection of Designs - -For many tasks, it can be critical to visually inspect the design. Remember to use the `export_shape` tool for this purpose! - -# Revising Designs - -* Before applying design changes, ask: "Would a designer consider this appropriate?" -* When dealing with containment issues, ask: Is the parent too small OR is the child too large? - Container sizes are usually intentional, check content first. -* Check for reasonable font sizes and typefaces -* The use of flex layouts is encouraged for cases where elements are arranged in rows or columns with consistent spacing/positioning. - Consider converting boards to flex layout when appropriate. - # Asset Libraries Libraries in Penpot are collections of reusable design assets (components, colors, and typographies) that can be shared across files. @@ -333,5 +316,24 @@ Removing tokens: Simply set the respective property directly - token binding is automatically removed, e.g. shape.fills = [{ fillColor: "#000000", fillOpacity: 1 }]; // Removes fill token +# Visual Inspection of Designs + +For many tasks, it can be critical to visually inspect the design. Remember to use the `export_shape` tool for this purpose! + +# Creating and Translating Designs + +* When transferring styles from a Penpot design to code, make sure that you strictly adhere to the design. + NEVER make assumptions about missing values and don't get overly creative (e.g. don't pick your own colours and stick to + non-creative defaults such as white/black if you are lacking information). + +# Revising Designs + +* Before applying design changes, ask: "Would a designer consider this appropriate?" +* When dealing with containment issues, ask: Is the parent too small OR is the child too large? + Container sizes are usually intentional, check content first. +* Check for reasonable font sizes and typefaces +* The use of flex layouts is encouraged for cases where elements are arranged in rows or columns with consistent spacing/positioning. + Consider converting boards to flex layout when appropriate. + -- You have hereby read the 'Penpot High-Level Overview' and need not use a tool to read it again. From 79d3469f365144fa3817a837119f369ebd733b13 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 18 Feb 2026 10:51:18 +0100 Subject: [PATCH 08/12] :books: Add instructions on cloning and the branch to use --- mcp/README.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/mcp/README.md b/mcp/README.md index 9e9821c065..b0a943b5f1 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -68,13 +68,38 @@ dependencies are already present and correctly setup. But nothing prevents you execute this outside of devenv if you satisfy the specified dependencies. +### 0. Clone the Appropriate Branch of the Repository + +> [!IMPORTANT] +> The branches are subject to change in the future. +> Be sure to check the instructions for the latest information on which branch to use. + +Clone the Penpot repository, using the proper branch depending on the +version of Penpot you want to use the MCP server with. + + * For released versions of Penpot, use the `mcp-prod` branch: + + ```shell + git clone https://github.com/penpot/penpot.git --branch mcp-prod --depth 1 + ``` + + * For the latest development version of Penpot, 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 +``` ### 1. Build & Launch the MCP Server and the Plugin Server If it's your first execution, install the required dependencies: ```shell -cd mcp/ ./scripts/setup ``` From ebdae2cf65db9c64d60aec1ad308dac5136a02cb Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 18 Feb 2026 11:09:19 +0100 Subject: [PATCH 09/12] :books: Revise instructions on prerequisites * Do not state that pnpm must be available after Node.js installation (it is installed by corepack) * Do not state that caddy is required; it is required only when rebuilding the API documentation for the server, which is not a task relevant to regular users. * Do not strongly suggest that MCP users should be using the devenv. * Windows: Add pointer to use Git Bash --- mcp/README.md | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/mcp/README.md b/mcp/README.md index b0a943b5f1..32d21e566b 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -50,23 +50,11 @@ Follow the steps below to enable the integration. ### Prerequisites -The project requires [Node.js](https://nodejs.org/) (tested with v22.x -with corepack). +The project requires [Node.js](https://nodejs.org/) (tested with v22.x). +Following the installation of Node.js, the tools `corepack` and `npx` +should be available in your terminal. -Following the installation of Node.js, the tools `pnpm` and `npx` -should be available in your terminal. For ensure corepack installed -and enabled correctly, just execute the `./scripts/setup`. - -It is also required to have `caddy` executeable in the path, it is -used for start a local server for generate types documentation from -the current branch. If you want to run it outside devenv where all -dependencies are already provided, please download caddy from -[here](https://caddyserver.com/download). - -You should probably be using penpot devenv, where all this -dependencies are already present and correctly setup. But nothing -prevents you execute this outside of devenv if you satisfy the -specified dependencies. +On Windows, use the Git Bash terminal to ensure compatibility with the provided scripts. ### 0. Clone the Appropriate Branch of the Repository @@ -97,7 +85,8 @@ cd penpot/mcp ### 1. Build & Launch the MCP Server and the Plugin Server -If it's your first execution, install the required dependencies: +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 From f8dd02169c765520270a0a956c4adeb3a0feff16 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 18 Feb 2026 11:14:21 +0100 Subject: [PATCH 10/12] :books: Remove unnecessary details on what the boostrap script does --- mcp/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mcp/README.md b/mcp/README.md index 32d21e566b..4e668309dc 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -100,9 +100,9 @@ pnpm run bootstrap This bootstrap command will: - * install dependencies for all components (`pnpm -r run install`) - * build all components (`pnpm -r run build`) - * start all components (`pnpm -r --parallel run start`) + * install dependencies for all components + * build all components + * start all components ### 2. Load the Plugin in Penpot and Establish the Connection From ea4c6c39980351ac1d4c57dfec1c2b9f5a35bb40 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 18 Feb 2026 20:17:05 +0100 Subject: [PATCH 11/12] :books: Update information on repository structure --- mcp/README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mcp/README.md b/mcp/README.md index 4e668309dc..3e26d2a20a 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -209,31 +209,30 @@ To add the Penpot MCP server to a Claude Code project, issue the command This repository is a monorepo containing four main components: -1. **Common Types** (`common/`): +1. **Common Types** (`packages/common/`): - Shared TypeScript definitions for request/response protocol - Ensures type safety across server and plugin components -2. **Penpot MCP Server** (`mcp-server/`): +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** (`penpot-plugin/`): +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. **Helper Scripts** (`python-scripts/`): - - Python scripts that prepare data for the MCP server (development use) +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. All configuration -options use the `PENPOT_MCP_` prefix for consistency. +The Penpot MCP server can be configured using environment variables. ### Server Configuration From 7cf88359fa7a3f489a8dd444ed2c44828c789290 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 18 Feb 2026 20:22:34 +0100 Subject: [PATCH 12/12] :books: Add section on 'Development' to README --- mcp/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mcp/README.md b/mcp/README.md index 3e26d2a20a..0d482aeb00 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -276,3 +276,9 @@ you may set the following environment variables to configure the two servers * `PENPOT_MCP_SERVER_ADDRESS=`: This sets the hostname or IP address where the MCP server can be reached. The Penpot MCP Plugin uses this to construct the WebSocket URL as `ws://:` (default port: `4402`). + +## 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)