diff --git a/mcp/README.md b/mcp/README.md index 9e9821c065..0d482aeb00 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -50,31 +50,45 @@ 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`. +On Windows, use the Git Bash terminal to ensure compatibility with the provided scripts. -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). +### 0. Clone the Appropriate Branch of the Repository -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. +> [!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: +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 -cd mcp/ ./scripts/setup ``` @@ -86,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 @@ -195,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 @@ -263,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) diff --git a/mcp/packages/server/data/initial_instructions.md b/mcp/packages/server/data/initial_instructions.md index 8aa60b1a1b..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 @@ -48,6 +44,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**: @@ -99,11 +97,12 @@ 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`. + - 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. - - 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 @@ -210,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. @@ -282,14 +268,15 @@ 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}") - * `resolvedValue` - Computed final value (follows references) +`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` Discovering tokens: @@ -329,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. 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 " +