From 2d649bd53cb19ad820b152c747db5302bb7985af Mon Sep 17 00:00:00 2001 From: Robert <36516516+robertkodra@users.noreply.github.com> Date: Tue, 3 Nov 2026 17:08:54 +0200 Subject: [PATCH] :bug: Ignore patch versions in the MCP version compatibility check (#12009) The plugin compared the full major.minor.patch of the running Penpot version with the MCP package version. The MCP package version is only set per minor release (2.15.0, 2.16.0, 2.17.0), so every Penpot patch release (for example 2.16.2 or 2.17.2) showed the "Version mismatch detected" warning although nothing changed for the MCP. Compare only major.minor, move the check into a small module with unit tests, and update the README versioning note to match. AI-assisted-by: claude-opus-5-5 Signed-off-by: robertkodra <36516516+robertkodra@users.noreply.github.com> --- mcp/README.md | 7 +++-- .../plugin/src/VersionCompatibility.test.ts | 28 +++++++++++++++++++ .../plugin/src/VersionCompatibility.ts | 27 ++++++++++++++++++ mcp/packages/plugin/src/plugin.ts | 19 +++---------- 4 files changed, 63 insertions(+), 18 deletions(-) create mode 100644 mcp/packages/plugin/src/VersionCompatibility.test.ts create mode 100644 mcp/packages/plugin/src/VersionCompatibility.ts diff --git a/mcp/README.md b/mcp/README.md index bc13252c16..32530bc869 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -309,9 +309,10 @@ you may set the following environment variables to configure the two servers * 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. + - Ensure that at least the major and minor components of the version are always up-to-date. + - The MCP plugin assumes that a mismatch between the major.minor components of 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. Patch releases are treated as compatible. * Packaging and publishing: 1. Ensure release version is set correctly in package.json (call `bash scripts/set-version` to update it automatically) 2. Create npm package: `bash scripts/pack` (creates `penpot-mcp-.tgz` for publishing) diff --git a/mcp/packages/plugin/src/VersionCompatibility.test.ts b/mcp/packages/plugin/src/VersionCompatibility.test.ts new file mode 100644 index 0000000000..2e40bb7cf0 --- /dev/null +++ b/mcp/packages/plugin/src/VersionCompatibility.test.ts @@ -0,0 +1,28 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { extractMinorVersion, isCompatibleVersion } from "./VersionCompatibility.ts"; + +test("extracts major.minor from release and pre-release versions", () => { + assert.equal(extractMinorVersion("2.18.1"), "2.18"); + assert.equal(extractMinorVersion("2.17.0"), "2.17"); + assert.equal(extractMinorVersion("2.15.0-rc.1.116"), "2.15"); + assert.equal(extractMinorVersion("2.18.1-dev.140"), "2.18"); + assert.equal(extractMinorVersion("2.18"), "2.18"); +}); + +test("returns non-matching versions unchanged", () => { + assert.equal(extractMinorVersion("<2.15"), "<2.15"); + assert.equal(extractMinorVersion("develop"), "develop"); +}); + +test("treats patch releases of the same minor version as compatible", () => { + assert.equal(isCompatibleVersion(extractMinorVersion("2.17.2"), extractMinorVersion("2.17.0")), true); +}); + +test("reports a different minor version as incompatible", () => { + assert.equal(isCompatibleVersion(extractMinorVersion("2.18.1"), extractMinorVersion("2.17.0")), false); +}); + +test("treats local development builds of Penpot as compatible", () => { + assert.equal(isCompatibleVersion(extractMinorVersion("0.0.0"), extractMinorVersion("2.17.0")), true); +}); diff --git a/mcp/packages/plugin/src/VersionCompatibility.ts b/mcp/packages/plugin/src/VersionCompatibility.ts new file mode 100644 index 0000000000..f529491ea3 --- /dev/null +++ b/mcp/packages/plugin/src/VersionCompatibility.ts @@ -0,0 +1,27 @@ +/** + * Extracts the major.minor prefix from a version string. + * + * Patch releases of Penpot do not change the plugin API the MCP server is + * written against, and the MCP package version is only set per minor + * release, so the patch component is not part of the compatibility check. + * + * @param version - a version string starting with major.minor (e.g. "2.18.1") + * @returns the major.minor prefix, or the original string if it does not match + */ +export function extractMinorVersion(version: string): string { + const match = version.match(/^(\d+\.\d+)(?:\.|$)/); + return match ? match[1] : version; +} + +/** + * Returns whether an MCP build is intended for the running Penpot version. + * + * Local development builds of Penpot report "0.0.0" and are always treated + * as compatible. + * + * @param penpotVersion - the running Penpot version, as major.minor prefix + * @param mcpVersion - the version the MCP build targets, as major.minor prefix + */ +export function isCompatibleVersion(penpotVersion: string, mcpVersion: string): boolean { + return penpotVersion === "0.0" || penpotVersion === mcpVersion; +} diff --git a/mcp/packages/plugin/src/plugin.ts b/mcp/packages/plugin/src/plugin.ts index 502116707f..60f09ccc03 100644 --- a/mcp/packages/plugin/src/plugin.ts +++ b/mcp/packages/plugin/src/plugin.ts @@ -1,6 +1,7 @@ import { ExecuteCodeTaskHandler } from "./task-handlers/ExecuteCodeTaskHandler"; import { Task, TaskHandler } from "./TaskHandler"; import { formatTaskError } from "./ErrorUtils"; +import { extractMinorVersion, isCompatibleVersion } from "./VersionCompatibility"; /** * indicates whether the plugin is running in an environment with the Penpot-integrated remote MCP server @@ -8,17 +9,6 @@ import { formatTaskError } from "./ErrorUtils"; */ const isIntegratedRemoteMcp = !!mcp; -/** - * Extracts the major.minor.patch prefix from a version string. - * - * @param version - a version string starting with major.minor.patch - * @returns the major.minor.patch prefix, or the original string if it does not match - */ -function extractVersionPrefix(version: string): string { - const match = version.match(/^(\d+\.\d+\.\d+)/); - return match ? match[1] : version; -} - mcp?.setMcpStatus("connecting"); /** @@ -42,11 +32,10 @@ penpot.ui.onMessage