From 701443c3d71d09aeb7522f8ead21e5b4d2ad43f9 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 25 Feb 2026 12:09:57 +0100 Subject: [PATCH] :sparkles: Add disconnect to MCP plugin --- frontend/src/app/main/data/workspace/mcp.cljs | 35 ++++++++++++++++--- mcp/packages/plugin/src/index.d.ts | 1 + mcp/packages/plugin/src/main.ts | 3 ++ mcp/packages/plugin/src/plugin.ts | 15 ++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/data/workspace/mcp.cljs b/frontend/src/app/main/data/workspace/mcp.cljs index fcb8de06b9..fb86ab5c11 100644 --- a/frontend/src/app/main/data/workspace/mcp.cljs +++ b/frontend/src/app/main/data/workspace/mcp.cljs @@ -11,6 +11,7 @@ [app.config :as cf] [app.main.data.plugins :as dp] [app.main.repo :as rp] + [app.main.store :as st] [app.plugins.register :refer [mcp-plugin-id]] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -29,8 +30,12 @@ "comment:read" "comment:write" "content:write" "content:read"}}) +(defn finalize-workspace? + [event] + (= (ptk/type event) :app.main.data.workspace/finalize-workspace)) + (defn init-mcp! - [] + [stream] (->> (rp/cmd! :get-current-mcp-token) (rx/subs! (fn [{:keys [token]}] @@ -48,13 +53,35 @@ :setMcpStatus (fn [status] ;; TODO: Visual feedback - (log/info :hint "MCP STATUS" :status status))}})))))) + (log/info :hint "MCP STATUS" :status status)) + + :on + (fn [event cb] + (when-let [event + (case event + "disconnect" ::disconnect + "connect" ::connect + nil)] + + (let [stopper (rx/filter finalize-workspace? stream)] + (->> stream + (rx/filter (ptk/type? event)) + (rx/take-until stopper) + (rx/subs! #(cb))))))}})))))) + +(defn disconnect-mcp + [] + (st/emit! (ptk/data-event ::disconnect))) + +(defn connect-mcp + [] + (st/emit! (ptk/data-event ::connect))) (defn init-mcp-connexion [] (ptk/reify ::init-mcp-connexion ptk/EffectEvent - (effect [_ state _] + (effect [_ state stream] (when (and (contains? cf/flags :mcp) (-> state :profile :props :mcp-status)) - (init-mcp!))))) + (init-mcp! stream))))) diff --git a/mcp/packages/plugin/src/index.d.ts b/mcp/packages/plugin/src/index.d.ts index 0670844c50..a0eda651e1 100644 --- a/mcp/packages/plugin/src/index.d.ts +++ b/mcp/packages/plugin/src/index.d.ts @@ -2,6 +2,7 @@ interface McpOptions { getToken(): string; getServerUrl(): string; setMcpStatus(status: string); + on(eventType: "disconnect" | "connect", cb: () => void); } declare global { diff --git a/mcp/packages/plugin/src/main.ts b/mcp/packages/plugin/src/main.ts index 8ead32231c..17744bf5e0 100644 --- a/mcp/packages/plugin/src/main.ts +++ b/mcp/packages/plugin/src/main.ts @@ -118,6 +118,9 @@ document.querySelector("[data-handler='connect-mcp']")?.addEventListener("click" window.addEventListener("message", (event) => { if (event.data.type === "start-server") { connectToMcpServer(event.data.url, event.data.token); + } + if (event.data.type === "stop-server") { + ws?.close(); } else if (event.data.source === "penpot") { document.body.dataset.theme = event.data.theme; } else if (event.data.type === "task-response") { diff --git a/mcp/packages/plugin/src/plugin.ts b/mcp/packages/plugin/src/plugin.ts index 8b33772e3a..d579e4e86d 100644 --- a/mcp/packages/plugin/src/plugin.ts +++ b/mcp/packages/plugin/src/plugin.ts @@ -73,6 +73,21 @@ async function handlePluginTaskRequest(request: { id: string; task: string; para } } +if (mcp) { + mcp.on("disconnect", async () => { + penpot.ui.sendMessage({ + type: "stop-server", + }); + }); + mcp.on("connect", async () => { + penpot.ui.sendMessage({ + type: "start-server", + url: mcp?.getServerUrl(), + token: mcp?.getToken(), + }); + }); +} + // Handle theme change in the iframe penpot.on("themechange", (theme) => { penpot.ui.sendMessage({