🐛 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>
This commit is contained in:
Robert 2026-11-03 17:08:54 +02:00 committed by GitHub
parent 610813281b
commit 2d649bd53c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 63 additions and 18 deletions

View File

@ -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-<version>.tgz` for publishing)

View File

@ -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);
});

View File

@ -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;
}

View File

@ -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<string | { id: string; type?: string; status?: string; task:
integratedRemoteMcp: isIntegratedRemoteMcp,
});
// Check Penpot version compatibility
const penpotVersionPrefix = penpot.version ? extractVersionPrefix(penpot.version) : "<2.15"; // pre-2.15 versions don't have version info
const mcpVersionPrefix = extractVersionPrefix(PENPOT_MCP_VERSION);
const penpotVersionPrefix = penpot.version ? extractMinorVersion(penpot.version) : "<2.15"; // pre-2.15 versions don't have version info
const mcpVersionPrefix = extractMinorVersion(PENPOT_MCP_VERSION);
console.log(`Penpot version: ${penpotVersionPrefix}, MCP version: ${mcpVersionPrefix}`);
const isLocalPenpotVersion = penpotVersionPrefix == "0.0.0";
if (penpotVersionPrefix !== mcpVersionPrefix && !isLocalPenpotVersion) {
if (!isCompatibleVersion(penpotVersionPrefix, mcpVersionPrefix)) {
penpot.ui.sendMessage({
type: "version-mismatch",
mcpVersion: mcpVersionPrefix,