From 30943f1074515a7184f20d29e9f9e40b787e5025 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Thu, 30 Jul 2026 10:38:29 +0200 Subject: [PATCH] :sparkles: 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 --- mcp/README.md | 1 + mcp/packages/server/src/PenpotMcpServer.ts | 3 ++- mcp/packages/server/src/PluginBridge.ts | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mcp/README.md b/mcp/README.md index 3efc6255e7..846ee4d128 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -272,6 +272,7 @@ The Penpot MCP server can be configured using environment variables. | `PENPOT_MCP_REPL_PORT` | Port for the REPL server (development/debugging) | `4403` | | `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. 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) | diff --git a/mcp/packages/server/src/PenpotMcpServer.ts b/mcp/packages/server/src/PenpotMcpServer.ts index 8a4ee30f25..bd992ec108 100644 --- a/mcp/packages/server/src/PenpotMcpServer.ts +++ b/mcp/packages/server/src/PenpotMcpServer.ts @@ -127,6 +127,7 @@ export class PenpotMcpServer { this.webSocketPort = parseInt(process.env.PENPOT_MCP_WEBSOCKET_PORT ?? "4402", 10); this.replPort = parseInt(process.env.PENPOT_MCP_REPL_PORT ?? "4403", 10); this.tenant = process.env.PENPOT_TENANT ?? "default"; + const toolTimeoutSecs = parseInt(process.env.PENPOT_MCP_TOOL_TIMEOUT_S ?? "120", 10); this.configLoader = new ConfigurationLoader(process.cwd()); this.apiDocs = new ApiDocs(); @@ -147,7 +148,7 @@ export class PenpotMcpServer { this.redisBridge = new RedisBridge(redisUri, this.tenant); } - this.pluginBridge = new PluginBridge(this, this.webSocketPort, this.redisBridge); + this.pluginBridge = new PluginBridge(this, this.webSocketPort, toolTimeoutSecs, this.redisBridge); this.replServer = new ReplServer(this.pluginBridge, this.replPort, this.host); } diff --git a/mcp/packages/server/src/PluginBridge.ts b/mcp/packages/server/src/PluginBridge.ts index e8dda27ac3..ac7a2c005c 100644 --- a/mcp/packages/server/src/PluginBridge.ts +++ b/mcp/packages/server/src/PluginBridge.ts @@ -24,6 +24,7 @@ export class PluginBridge { private readonly logger = createLogger("PluginBridge"); private readonly wsServer: WebSocketServer; + private readonly connectedClients: Map = new Map(); private readonly clientsByToken: Map = new Map(); private readonly pendingTasks: Map> = new Map(); @@ -39,12 +40,13 @@ export class PluginBridge { * holding the relevant plugin's WebSocket connection (which may be this same * instance) via Redis, rather than dispatched directly over a local socket. * @param taskTimeoutSecs - Timeout, in seconds, for plugin task execution + * (defaults to {@link DEFAULT_TASK_TIMEOUT_SECS}) */ constructor( public readonly mcpServer: PenpotMcpServer, private port: number, - private readonly redisBridge?: RedisBridge, - private taskTimeoutSecs: number = 30 + private readonly taskTimeoutSecs: number, + private readonly redisBridge?: RedisBridge ) { this.wsServer = new WebSocketServer({ port: port }); this.setupWebSocketHandlers();