mirror of
https://github.com/penpot/penpot.git
synced 2026-07-22 22:17:58 +00:00
✨ 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>
This commit is contained in:
parent
4c21923c4a
commit
51c0129485
@ -129,17 +129,18 @@ If you just want to try Penpot AI workflows quickly through the MCP, follow this
|
||||

|
||||
|
||||
4. #### Add the server to your MCP client
|
||||
In your MCP-aware IDE/agent (Cursor, Claude Code, etc.), add a new server pointing to that URL.
|
||||
**Example (generic JSON config):**
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"penpot": {
|
||||
"url": "https://<your-penpot-domain>/mcp/stream?userToken=YOUR_MCP_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
We recommend using the [add-mcp](https://github.com/neon-solutions/add-mcp) project for connecting your MCP client to the Penpot MCP server.
|
||||
With `npx` available, call
|
||||
|
||||
```shell
|
||||
npx -y add-mcp -g -n penpot <URL>
|
||||
```
|
||||
|
||||
and follow the interactive setup. Alternatively, follow your client's instructions for connecting
|
||||
to a remote MCP server. For Claude Desktop, we recommend adding the Penpot MCP Server as a
|
||||
custom [connector](http://claude.ai/customize/connectors).
|
||||
See the section **Connect your MCP client** for more details on how to connect.
|
||||
|
||||
5. #### Open a Penpot file and connect MCP
|
||||
In Penpot, open a design file and use **File → MCP Server → Connect** to connect the plugin to your current file.
|
||||
|
||||
@ -216,84 +217,21 @@ You can use Penpot MCP server in two main ways:
|
||||
|
||||
## Connect your MCP client
|
||||
|
||||
Use the same client setup flow for both modes. What changes is the server URL and authentication method.
|
||||
|
||||
### Connection values by mode
|
||||
|
||||
* **Remote MCP**
|
||||
* URL: `https://<your-penpot-domain>/mcp/stream?userToken=YOUR_MCP_KEY`
|
||||
* Auth: MCP key in `userToken`
|
||||
* URL (copy from your Penpot account overview): `https://<your-penpot-domain>/mcp/stream?userToken=YOUR_MCP_KEY`
|
||||
* Configure your MCP client with `npx -y add-mcp -g -n penpot <URL>` or by following your client's instructions for connecting to a remote MCP server.
|
||||
For Claude Desktop, the server can be added as a custom [connector](http://claude.ai/customize/connectors).
|
||||
* **Local MCP**
|
||||
* URL: `http://localhost:4401/mcp`
|
||||
* Auth: none (uses your active Penpot browser session)
|
||||
* Configure your MCP client with `npx -y add-mcp -g -n penpot http://localhost:4401/mcp` (adjust the port if you changed `PENPOT_MCP_SERVER_PORT`) or by following your client's instructions for connecting to an HTTP MCP server.
|
||||
|
||||
### Cursor
|
||||
|
||||
1. Open Cursor MCP/tool configuration.
|
||||
2. Add a Penpot MCP server entry:
|
||||
Note: For clients that do not support HTTP servers directly (like Claude Desktop), the local MCP server can be connected via [mcp-remote](https://github.com/geelen/mcp-remote) as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"penpot": {
|
||||
"url": "REMOTE_OR_LOCAL_URL",
|
||||
"type": "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace `REMOTE_OR_LOCAL_URL` with the URL for your mode.
|
||||
|
||||
### Claude Code
|
||||
|
||||
1. Open MCP configuration in Claude Code.
|
||||
2. Add a Penpot server with `http` transport and the URL for your mode.
|
||||
3. Restart Claude Code or reload tools.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"penpot": {
|
||||
"transport": "http",
|
||||
"url": "REMOTE_OR_LOCAL_URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### VS Code / Copilot
|
||||
|
||||
1. Open external MCP server configuration in your extension/settings.
|
||||
2. Add Penpot with the URL for your mode.
|
||||
3. Save and reload tools.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp.servers": {
|
||||
"penpot": {
|
||||
"transport": "http",
|
||||
"url": "REMOTE_OR_LOCAL_URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Codex / OpenCode etc
|
||||
|
||||
1. Use your client's "Add MCP server" flow.
|
||||
2. Set the URL for your mode.
|
||||
3. Reload tools and verify Penpot tools are available.
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"penpot": {
|
||||
"url": "REMOTE_OR_LOCAL_URL",
|
||||
"transport": {
|
||||
"type": "http"
|
||||
}
|
||||
"command": "npx",
|
||||
"args": ["-y", "mcp-remote", "<URL>", "--allow-http"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,9 +264,8 @@ Remote MCP is the easiest way to start using AI agents with Penpot. It's hosted
|
||||
<a id="connect-remote"></a>
|
||||
### Connect
|
||||
|
||||
For client-specific setup, use the shared section **Connect your MCP client**.
|
||||
|
||||
For remote mode, use the URL shown in **Your account → Integrations → MCP Server**, which includes your `userToken`.
|
||||
See the section **Connect your MCP client** for details on how to connect.
|
||||
|
||||
### Setup videos
|
||||
|
||||
@ -464,9 +401,7 @@ For advanced or repository-based workflows, see the [MCP README](https://github.
|
||||
<a id="connect-local"></a>
|
||||
### Connect
|
||||
|
||||
For client-specific setup, use the shared section **Connect your MCP client**.
|
||||
|
||||
For local mode, use `http://localhost:4401/mcp` with HTTP transport (no MCP key; authentication uses your active Penpot browser session).
|
||||
See the section **Connect your MCP client** for details on how to connect.
|
||||
|
||||
<a id="use-local"></a>
|
||||
### Use
|
||||
|
||||
@ -59,14 +59,10 @@ The project requires [Node.js](https://nodejs.org/) (tested with v22.x).
|
||||
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
|
||||
```
|
||||
* If you are participating in the MCP beta-test, which uses [test-mcp.penpot.dev](https://test-mcp.penpot.dev), run:
|
||||
```shell
|
||||
npx -y @penpot/mcp@beta
|
||||
```
|
||||
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.
|
||||
|
||||
@ -78,24 +74,13 @@ On Windows, use the Git Bash terminal to ensure compatibility with the provided
|
||||
|
||||
##### 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/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:
|
||||
|
||||
Clone the Penpot repository, using the proper branch depending on the
|
||||
version of Penpot you want to use the MCP server with.
|
||||
|
||||
* For the current Penpot release 2.14, use the `mcp-prod-2.14.1` branch:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/penpot/penpot.git --branch mcp-prod-2.14.1 --depth 1
|
||||
```
|
||||
|
||||
* For the MCP beta-test, use the `staging` branch:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/penpot/penpot.git --branch staging --depth 1
|
||||
```
|
||||
```shell
|
||||
git clone https://github.com/penpot/penpot.git --branch develop --depth 1
|
||||
```
|
||||
|
||||
Then change into the `mcp` directory:
|
||||
|
||||
@ -180,16 +165,27 @@ By default, the server runs on port 4401 and provides:
|
||||
- **Modern Streamable HTTP endpoint**: `http://localhost:4401/mcp`
|
||||
- **Legacy SSE endpoint**: `http://localhost:4401/sse`
|
||||
|
||||
These endpoints can be used directly by MCP clients that support them.
|
||||
You can change the port by setting the `PENPOT_MCP_SERVER_PORT` environment variable
|
||||
before starting the server. These endpoints can be used directly by MCP clients that support them.
|
||||
Simply configure the client to connect the MCP server by providing the respective URL.
|
||||
|
||||
When using a client that only supports stdio transport,
|
||||
a proxy like `mcp-remote` is required.
|
||||
#### 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
|
||||
|
||||
NOTE: only relevant if you are executing this outside of devenv
|
||||
|
||||
The `mcp-remote` package can proxy stdio transport to HTTP/SSE,
|
||||
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:
|
||||
@ -232,12 +228,6 @@ After updating the configuration file, restart Claude Desktop completely for the
|
||||
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.
|
||||
|
||||
#### Example: Claude Code
|
||||
|
||||
To add the Penpot MCP server to a Claude Code project, issue the command
|
||||
|
||||
claude mcp add penpot -t http http://localhost:4401/mcp
|
||||
|
||||
## Repository Structure
|
||||
|
||||
This repository is a monorepo containing four main components:
|
||||
@ -296,9 +286,9 @@ The Penpot MCP server can be configured using environment variables.
|
||||
## Beyond Local Execution
|
||||
|
||||
The above instructions describe how to run the MCP server and plugin server locally.
|
||||
We are working on enabling remote deployments of the MCP server, particularly
|
||||
in [multi-user mode](docs/multi-user-mode.md), where multiple Penpot users will
|
||||
be able to connect to the same MCP server instance.
|
||||
|
||||
The Penpot MCP server can also support multiple remote users simultaneously
|
||||
in [multi-user mode](docs/multi-user-mode.md).
|
||||
|
||||
To run the server remotely (even for a single user),
|
||||
you may set the following environment variables to configure the two servers
|
||||
|
||||
70
mcp/bin/client-setup.js
Normal file
70
mcp/bin/client-setup.js
Normal file
@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Client-setup entry point of the published @penpot/mcp package.
|
||||
// Configures an MCP client to talk to the local Penpot MCP server by delegating
|
||||
// to the add-mcp CLI. This launcher is deliberately independent of the server
|
||||
// build/bootstrap: it only needs npx to fetch add-mcp on demand, so it does not
|
||||
// require a source checkout, a prior install, or a running server.
|
||||
|
||||
const { spawnSync } = require("child_process");
|
||||
|
||||
/**
|
||||
* Runs the add-mcp CLI against the local Penpot MCP server URL.
|
||||
*
|
||||
* The server port is read from PENPOT_MCP_SERVER_PORT (default 4401), matching
|
||||
* the server's own default. add-mcp is fetched via npx using @latest on
|
||||
* purpose: MCP client config formats change frequently, so we always want the
|
||||
* newest add-mcp rather than a pinned version.
|
||||
*
|
||||
* forwardedArgs are passed straight through to add-mcp so callers can pick
|
||||
* agents, set headers, override the name or URL, etc. Two defaults are applied
|
||||
* conditionally so caller args always win: "penpot" as the server name (dropped
|
||||
* when the caller passes -n/--name) and the local server URL as the positional
|
||||
* target (dropped when the caller passes their own URL). Unlike a repeated
|
||||
* option, the URL is a positional, so add-mcp cannot "last-wins" two of them --
|
||||
* we must omit ours rather than append it. A caller URL is detected as an arg
|
||||
* that starts with a scheme (e.g. http://, https://); matching only at the start
|
||||
* avoids false positives from URLs embedded in --header/--env values.
|
||||
*
|
||||
* @param {string[]} forwardedArgs - additional arguments passed through to add-mcp
|
||||
*/
|
||||
function runClientSetup(forwardedArgs) {
|
||||
const port = process.env.PENPOT_MCP_SERVER_PORT || "4401";
|
||||
const defaultUrl = `http://localhost:${port}/mcp`;
|
||||
|
||||
const hasName = forwardedArgs.some(
|
||||
(arg) => arg === "-n" || arg === "--name" || arg.startsWith("--name="),
|
||||
);
|
||||
const nameArgs = hasName ? [] : ["-n", "penpot"];
|
||||
|
||||
const isUrl = (arg) => /^[a-zA-Z][\w+.-]*:\/\//.test(arg);
|
||||
const hasUrl = forwardedArgs.some(isUrl);
|
||||
const urlArgs = hasUrl ? [] : [defaultUrl];
|
||||
|
||||
const args = ["-y", "add-mcp@latest", "-g", ...nameArgs, ...forwardedArgs, ...urlArgs];
|
||||
|
||||
const result = spawnSync("npx", args, {
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
if (typeof result.status === "number" && result.status !== 0) {
|
||||
const error = new Error(`add-mcp exited with code ${result.status}`);
|
||||
error.status = result.status;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runClientSetup };
|
||||
|
||||
// run directly, e.g. via `pnpm run client-setup` or `node bin/client-setup.js`
|
||||
if (require.main === module) {
|
||||
try {
|
||||
runClientSetup(process.argv.slice(2));
|
||||
} catch (error) {
|
||||
process.exit(error.status ?? 1);
|
||||
}
|
||||
}
|
||||
@ -54,18 +54,37 @@ function prepareRuntimeRoot() {
|
||||
return runtimeRoot;
|
||||
}
|
||||
|
||||
const root = prepareRuntimeRoot();
|
||||
|
||||
// pnpm-lock.yaml is hard-excluded by npm pack; it is shipped as pnpm-lock.dist.yaml
|
||||
// and restored here before bootstrap runs.
|
||||
const distLock = path.join(root, "pnpm-lock.dist.yaml");
|
||||
const lock = path.join(root, "pnpm-lock.yaml");
|
||||
if (fs.existsSync(distLock)) {
|
||||
fs.copyFileSync(distLock, lock);
|
||||
// check arguments
|
||||
const cliArgs = process.argv.slice(2);
|
||||
if (cliArgs[0] === "client-setup") {
|
||||
// when invoked as `penpot-mcp client-setup [...]`, configure an MCP client
|
||||
// instead of bootstrapping/starting the server. This must run before the
|
||||
// runtime-directory copy and bootstrap, neither of which client setup needs.
|
||||
try {
|
||||
require("./client-setup.js").runClientSetup(cliArgs.slice(1));
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
process.exit(error.status ?? 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// regular case: run bootstrap, building and starting the servers
|
||||
|
||||
try {
|
||||
execSync(`npx -y pnpm@${pnpmVersion()} run bootstrap`, { cwd: root, stdio: "inherit" });
|
||||
} catch (error) {
|
||||
process.exit(error.status ?? 1);
|
||||
// get runtime root
|
||||
const root = prepareRuntimeRoot();
|
||||
|
||||
// pnpm-lock.yaml is hard-excluded by npm pack; it is shipped as pnpm-lock.dist.yaml
|
||||
// and restored here before bootstrap runs.
|
||||
const distLock = path.join(root, "pnpm-lock.dist.yaml");
|
||||
const lock = path.join(root, "pnpm-lock.yaml");
|
||||
if (fs.existsSync(distLock)) {
|
||||
fs.copyFileSync(distLock, lock);
|
||||
}
|
||||
|
||||
// run the bootstrap command
|
||||
try {
|
||||
execSync(`npx -y pnpm@${pnpmVersion()} run bootstrap`, { cwd: root, stdio: "inherit" });
|
||||
} catch (error) {
|
||||
process.exit(error.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"start:multi-user": "pnpm -r --parallel run start:multi-user",
|
||||
"bootstrap": "pnpm -r install && pnpm run build && pnpm run start",
|
||||
"bootstrap:multi-user": "pnpm -r install && pnpm run build && pnpm run start:multi-user",
|
||||
"client-setup": "node bin/client-setup.js",
|
||||
"fmt": "prettier --write packages/",
|
||||
"fmt:check": "prettier --check packages/",
|
||||
"test": "pnpm -r run test"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user