From 9517f2ac9426f8058808f7df690acb23ad24d8f1 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 7 Oct 2026 15:20:32 +0200 Subject: [PATCH] :recycle: Type the MCP plugin with the repo plugin types Link the MCP plugin to plugins/libs/plugin-types instead of the published 1.5.0 package, which lacks PenpotMgmt. The repo types declare the penpotMgmt global, so the inline import in index.d.ts goes away and the plugin checks against the API it runs with. Type Context.management as PenpotMgmt without on/off, so new management methods need no extra listing. Mark the background plugin mock in load-plugin.spec.ts as NonNullable: createPlugin can return undefined, and the build type-check failed on it. --- mcp/packages/plugin/package.json | 2 +- mcp/packages/plugin/src/index.d.ts | 2 -- mcp/packages/plugin/src/plugin.ts | 3 ++- mcp/packages/server/data/api_types.yml | 23 ++++--------------- mcp/pnpm-lock.yaml | 9 ++------ plugins/libs/plugin-types/index.d.ts | 14 ++++------- .../src/lib/load-plugin.spec.ts | 2 +- 7 files changed, 16 insertions(+), 39 deletions(-) diff --git a/mcp/packages/plugin/package.json b/mcp/packages/plugin/package.json index 1a44059515..d4a3723755 100644 --- a/mcp/packages/plugin/package.json +++ b/mcp/packages/plugin/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@penpot/plugin-styles": "1.5.0", - "@penpot/plugin-types": "1.5.0" + "@penpot/plugin-types": "link:../../../plugins/libs/plugin-types" }, "devDependencies": { "cross-env": "^10.1.0", diff --git a/mcp/packages/plugin/src/index.d.ts b/mcp/packages/plugin/src/index.d.ts index b53afdef68..1fa11567bb 100644 --- a/mcp/packages/plugin/src/index.d.ts +++ b/mcp/packages/plugin/src/index.d.ts @@ -8,8 +8,6 @@ interface McpOptions { declare global { const mcp: undefined | McpOptions; - /** management API; only exposed to plugins running with global scope */ - const penpotMgmt: undefined | null | import("../../../../plugins/libs/plugin-types").PenpotMgmt; } export {}; diff --git a/mcp/packages/plugin/src/plugin.ts b/mcp/packages/plugin/src/plugin.ts index b3e8c691bc..025354df74 100644 --- a/mcp/packages/plugin/src/plugin.ts +++ b/mcp/packages/plugin/src/plugin.ts @@ -2,6 +2,7 @@ import { ExecuteCodeTaskHandler } from "./task-handlers/ExecuteCodeTaskHandler"; import { Task, TaskHandler } from "./TaskHandler"; import { formatTaskError } from "./ErrorUtils"; import type { PluginConnectionInit } from "../../common/src"; +import type { WorkspaceContext } from "@penpot/plugin-types"; /** * indicates whether the plugin is running in an environment with the Penpot-integrated remote MCP server @@ -102,7 +103,7 @@ penpot.ui.onMessage< * Provides the workspace metadata of this tab. * Without the management API, the metadata is derived from the open file. */ -function currentWorkspace(): NonNullable["workspace"] { +function currentWorkspace(): WorkspaceContext { if (management) { return management.workspace; } diff --git a/mcp/packages/server/data/api_types.yml b/mcp/packages/server/data/api_types.yml index 1fbccd549f..7fc050cec7 100644 --- a/mcp/packages/server/data/api_types.yml +++ b/mcp/packages/server/data/api_types.yml @@ -5302,15 +5302,7 @@ Context: ``` interface Context { - management?: Pick< - PenpotMgmt, - "workspace" - | "openFile" - | "listProjects" - | "listFiles" - | "createProject" - | "createFile", - >; + management?: Omit; version: string; root: Shape | null; currentFile: File | null; @@ -5384,16 +5376,11 @@ Context: Properties: management: |- ``` - readonly management?: Pick< - PenpotMgmt, - "workspace" - | "openFile" - | "listProjects" - | "listFiles" - | "createProject" - | "createFile", - > + readonly management?: Omit ``` + + Backs `penpotMgmt`. Its events go through the plugin listener registry, + so `on` and `off` are not part of it. version: |- ``` readonly version: string diff --git a/mcp/pnpm-lock.yaml b/mcp/pnpm-lock.yaml index 1ebfccbe6d..854a012147 100644 --- a/mcp/pnpm-lock.yaml +++ b/mcp/pnpm-lock.yaml @@ -188,8 +188,8 @@ importers: specifier: 1.5.0 version: 1.5.0 '@penpot/plugin-types': - specifier: 1.5.0 - version: 1.5.0 + specifier: link:../../../plugins/libs/plugin-types + version: link:../../../plugins/libs/plugin-types devDependencies: cross-env: specifier: ^10.1.0 @@ -663,9 +663,6 @@ packages: '@penpot/plugin-styles@1.5.0': resolution: {integrity: sha512-rwXFBRPd0IZs3ens+eRT8oA5WqkQgF6xlW6fCfH7U1NHRjej9HH7vTGXmfjoIVml6Xx1sbyYHZVxkQC8Ub8+zA==} - '@penpot/plugin-types@1.5.0': - resolution: {integrity: sha512-hFk4Rx66rLaSUWoN6bhxXkDI2ymwKg9xokdslvZvXM5MSmEmfhsJKUMwtuzgYWljbbfaQhK+DjK8g8yVbbavtA==} - '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -1880,8 +1877,6 @@ snapshots: '@penpot/plugin-styles@1.5.0': {} - '@penpot/plugin-types@1.5.0': {} - '@pinojs/redact@0.4.0': {} '@rolldown/binding-android-arm-eabi@1.2.11': diff --git a/plugins/libs/plugin-types/index.d.ts b/plugins/libs/plugin-types/index.d.ts index f375f42cd3..031f7cc3ba 100644 --- a/plugins/libs/plugin-types/index.d.ts +++ b/plugins/libs/plugin-types/index.d.ts @@ -902,15 +902,11 @@ export interface PenpotMgmt { * Represents the context of Penpot, providing access to various Penpot functionalities and data. */ export interface Context { - readonly management?: Pick< - PenpotMgmt, - | 'workspace' - | 'openFile' - | 'listProjects' - | 'listFiles' - | 'createProject' - | 'createFile' - >; + /** + * Backs `penpotMgmt`. Its events go through the plugin listener registry, + * so `on` and `off` are not part of it. + */ + readonly management?: Omit; /** * Returns the current penpot version. */ diff --git a/plugins/libs/plugins-runtime/src/lib/load-plugin.spec.ts b/plugins/libs/plugins-runtime/src/lib/load-plugin.spec.ts index eeffb3d236..853c1d12c7 100644 --- a/plugins/libs/plugins-runtime/src/lib/load-plugin.spec.ts +++ b/plugins/libs/plugins-runtime/src/lib/load-plugin.spec.ts @@ -216,7 +216,7 @@ describe('plugin-loader', () => { pluginId: 'background-plugin', allowBackground: true, }, - } as unknown as Awaited>; + } as unknown as NonNullable>>; vi.mocked(createPlugin).mockResolvedValue(backgroundPluginApi); await loadPlugin(manifest);