From ecf66ca1c582983addfe6605e059e2ac5d392553 Mon Sep 17 00:00:00 2001 From: shizhigu Date: Sun, 26 Apr 2026 12:41:02 -0500 Subject: [PATCH 1/4] feat: add MCP pause/resume controls --- .../mcp-enabled-pause-light.png | Bin 0 -> 80187 bytes .../mcp-paused-resume-dark.png | Bin 0 -> 85215 bytes apps/app/src/app/lib/openwork-server.ts | 11 + apps/app/src/app/mcp.test.ts | 147 +++++++++++ apps/app/src/app/mcp.ts | 111 +++++++- apps/app/src/i18n/locales/ca.ts | 15 +- apps/app/src/i18n/locales/en.ts | 15 +- apps/app/src/i18n/locales/es.ts | 15 +- apps/app/src/i18n/locales/fr.ts | 15 +- apps/app/src/i18n/locales/ja.ts | 15 +- apps/app/src/i18n/locales/pt-BR.ts | 15 +- apps/app/src/i18n/locales/th.ts | 15 +- apps/app/src/i18n/locales/vi.ts | 15 +- apps/app/src/i18n/locales/zh.ts | 15 +- .../domains/connections/mcp-view.tsx | 4 + .../react-app/domains/connections/store.ts | 117 +++++++-- .../settings/pages/extensions-view.tsx | 20 +- .../domains/settings/pages/mcp-view.tsx | 221 ++++++++++++---- .../shell/settings-route-permissions.ts | 10 + .../src/react-app/shell/settings-route.tsx | 21 +- .../connections-store-mcp-toggle.test.ts | 63 +++++ .../tests/settings-route-permissions.test.ts | 20 ++ apps/server/src/mcp-routes.e2e.test.ts | 242 ++++++++++++++++++ .../server/src/mcp.remote-connect.e2e.test.ts | 155 ++++++++++- apps/server/src/mcp.ts | 124 +++++++-- apps/server/src/server.ts | 45 ++++ 26 files changed, 1318 insertions(+), 128 deletions(-) create mode 100644 apps/app/pr/mcp-enable-disable/mcp-enabled-pause-light.png create mode 100644 apps/app/pr/mcp-enable-disable/mcp-paused-resume-dark.png create mode 100644 apps/app/src/app/mcp.test.ts create mode 100644 apps/app/src/react-app/shell/settings-route-permissions.ts create mode 100644 apps/app/tests/connections-store-mcp-toggle.test.ts create mode 100644 apps/app/tests/settings-route-permissions.test.ts create mode 100644 apps/server/src/mcp-routes.e2e.test.ts diff --git a/apps/app/pr/mcp-enable-disable/mcp-enabled-pause-light.png b/apps/app/pr/mcp-enable-disable/mcp-enabled-pause-light.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7d0e785be0ed630c497872c03e9e429f44dc1c GIT binary patch literal 80187 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%0DqFO$2jf5s$vSK41px)$3=$4UK}Di5@W0FqX$S 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 literal 0 HcmV?d00001 diff --git a/apps/app/pr/mcp-enable-disable/mcp-paused-resume-dark.png b/apps/app/pr/mcp-enable-disable/mcp-paused-resume-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb10b1ee6951a76e85bf5b22c16137980f8938c GIT binary patch literal 85215 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 literal 0 HcmV?d00001 diff --git a/apps/app/src/app/lib/openwork-server.ts b/apps/app/src/app/lib/openwork-server.ts index 7b7eb1963..620229dcd 100644 --- a/apps/app/src/app/lib/openwork-server.ts +++ b/apps/app/src/app/lib/openwork-server.ts @@ -1036,6 +1036,17 @@ export function createOpenworkServerClient(options: { baseUrl: string; token?: s method: "POST", body: payload, }), + setMcpEnabled: (workspaceId: string, name: string, enabled: boolean) => + requestJson<{ items: OpenworkMcpItem[]; enabled: boolean; changed: boolean }>( + baseUrl, + `/workspace/${workspaceId}/mcp/${encodeURIComponent(name)}`, + { + token, + hostToken, + method: "PATCH", + body: { enabled }, + }, + ), removeMcp: (workspaceId: string, name: string) => requestJson<{ items: OpenworkMcpItem[] }>(baseUrl, `/workspace/${workspaceId}/mcp/${encodeURIComponent(name)}`, { token, diff --git a/apps/app/src/app/mcp.test.ts b/apps/app/src/app/mcp.test.ts new file mode 100644 index 000000000..26bd15456 --- /dev/null +++ b/apps/app/src/app/mcp.test.ts @@ -0,0 +1,147 @@ +import { describe, expect, test } from "bun:test"; +import { parse } from "jsonc-parser"; + +import { parseMcpServersFromContent, updateMcpEnabledInConfigContent } from "./mcp"; + +describe("mcp config helpers", () => { + test("updates enabled without changing the MCP config shape", () => { + const updated = updateMcpEnabledInConfigContent( + `{ + // keep workspace comments + "mcp": { + "stripe": { + "type": "remote", + "url": "https://example.com/mcp", + "headers": { + "x-team": "payments" + } + } + } +} +`, + "stripe", + false, + ); + + expect(updated).toContain("// keep workspace comments"); + const parsed = parse(updated) as { + mcp: { stripe: { type: string; url: string; enabled: boolean; headers: Record } }; + }; + expect(parsed.mcp.stripe).toEqual({ + type: "remote", + url: "https://example.com/mcp", + headers: { "x-team": "payments" }, + enabled: false, + }); + }); + + test("parses paused MCP apps as configured entries", () => { + const entries = parseMcpServersFromContent( + JSON.stringify({ + mcp: { + stripe: { + type: "remote", + url: "https://example.com/mcp", + enabled: false, + }, + }, + }), + ); + + expect(entries).toEqual([ + { + name: "stripe", + config: { + type: "remote", + url: "https://example.com/mcp", + enabled: false, + }, + }, + ]); + }); + + test("rejects unknown MCP app names", () => { + expect(() => + updateMcpEnabledInConfigContent( + JSON.stringify({ + mcp: { + stripe: { type: "remote", url: "https://example.com/mcp" }, + }, + }), + "linear", + false, + ), + ).toThrow("MCP server not found"); + }); + + test("pauses an inherited global MCP with a minimal workspace override", () => { + const updated = updateMcpEnabledInConfigContent( + "{}\n", + "linear", + false, + { + inheritedMcpServers: [ + { + name: "linear", + config: { + type: "remote", + url: "https://example.com/linear", + headers: { Authorization: "Bearer secret" }, + }, + }, + ], + }, + ); + + const parsed = parse(updated) as { mcp: { linear: Record } }; + expect(parsed.mcp.linear).toEqual({ enabled: false }); + expect(updated).not.toContain("example.com/linear"); + expect(updated).not.toContain("secret"); + }); + + test("resumes an inherited global MCP by removing the workspace override", () => { + const updated = updateMcpEnabledInConfigContent( + JSON.stringify({ + mcp: { + linear: { enabled: false }, + stripe: { type: "remote", url: "https://example.com/stripe" }, + }, + }), + "linear", + true, + { + inheritedMcpServers: [ + { + name: "linear", + config: { + type: "remote", + url: "https://example.com/linear", + }, + }, + ], + }, + ); + + const parsed = parse(updated) as { mcp: Record }; + expect(parsed.mcp.linear).toBeUndefined(); + expect(parsed.mcp.stripe).toEqual({ type: "remote", url: "https://example.com/stripe" }); + }); + + test("does not write an override when inherited global MCP is already disabled", () => { + const source = "{}\n"; + const updated = updateMcpEnabledInConfigContent(source, "linear", false, { + inheritedMcpServers: [ + { + name: "linear", + config: { + type: "remote", + url: "https://example.com/linear", + enabled: false, + }, + }, + ], + }); + + expect(updated).toBe(source); + }); +}); diff --git a/apps/app/src/app/mcp.ts b/apps/app/src/app/mcp.ts index d740a6e32..86d693dbc 100644 --- a/apps/app/src/app/mcp.ts +++ b/apps/app/src/app/mcp.ts @@ -1,9 +1,10 @@ -import { parse } from "jsonc-parser"; +import { applyEdits, modify, parse, printParseErrorCode } from "jsonc-parser"; import type { McpServerConfig, McpServerEntry } from "./types"; import { readOpencodeConfig, writeOpencodeConfig } from "./lib/desktop"; import { CHROME_DEVTOOLS_MCP_COMMAND, CHROME_DEVTOOLS_MCP_ID } from "./constants"; type McpConfigValue = Record | null | undefined; +const jsoncFormattingOptions = { insertSpaces: true, tabSize: 2, eol: "\n" }; export const CHROME_DEVTOOLS_AUTO_CONNECT_ARG = "--autoConnect"; @@ -51,6 +52,22 @@ export function validateMcpServerName(name: string): string { return trimmed; } +function isConfiguredMcpEntry(entry: unknown): entry is Record { + if (!entry || typeof entry !== "object" || Array.isArray(entry)) return false; + const type = (entry as Record).type; + return type === "local" || type === "remote"; +} + +function isDisabledMcpOverride(entry: unknown): boolean { + return ( + Boolean(entry) && + typeof entry === "object" && + !Array.isArray(entry) && + (entry as Record).enabled === false && + !isConfiguredMcpEntry(entry) + ); +} + export async function removeMcpFromConfig( projectDir: string, name: string, @@ -79,6 +96,98 @@ export async function removeMcpFromConfig( } } +export function updateMcpEnabledInConfigContent( + content: string, + name: string, + enabled: boolean, + options: { inheritedMcpServers?: readonly McpServerEntry[] } = {}, +): string { + const source = content.trim() ? content : "{}\n"; + const errors: { error: number; offset: number; length: number }[] = []; + const parsed = parse(source, errors, { allowTrailingComma: true }) as Record | undefined; + if (errors.length > 0) { + const detail = errors.map((error) => printParseErrorCode(error.error)).join(", "); + throw new Error(`Failed to parse opencode config${detail ? `: ${detail}` : ""}`); + } + + const mcpSection = parsed?.mcp as Record | undefined; + const entry = mcpSection?.[name]; + const inheritedEntry = options.inheritedMcpServers?.find((item) => item.name === name); + const inheritedEnabled = inheritedEntry ? inheritedEntry.config.enabled !== false : false; + + if (isConfiguredMcpEntry(entry)) { + const updated = applyEdits( + source, + modify(source, ["mcp", name, "enabled"], enabled, { + formattingOptions: jsoncFormattingOptions, + }), + ); + return updated.endsWith("\n") ? updated : `${updated}\n`; + } + + if (isDisabledMcpOverride(entry)) { + if (!inheritedEntry) { + throw new Error("MCP server not found"); + } + if (!enabled) return source.endsWith("\n") ? source : `${source}\n`; + if (!inheritedEnabled) { + throw new Error("This MCP server is disabled in the global config."); + } + const updated = applyEdits( + source, + modify(source, ["mcp", name], undefined, { + formattingOptions: jsoncFormattingOptions, + }), + ); + return updated.endsWith("\n") ? updated : `${updated}\n`; + } + + if (entry != null) { + throw new Error("MCP config is not a configurable server"); + } + + if (inheritedEntry) { + if (enabled) { + if (!inheritedEnabled) { + throw new Error("This MCP server is disabled in the global config."); + } + return source.endsWith("\n") ? source : `${source}\n`; + } + if (!inheritedEnabled) return source.endsWith("\n") ? source : `${source}\n`; + const updated = applyEdits( + source, + modify(source, ["mcp", name], { enabled: false }, { + formattingOptions: jsoncFormattingOptions, + }), + ); + return updated.endsWith("\n") ? updated : `${updated}\n`; + } + + throw new Error(mcpSection ? "MCP server not found" : "Workspace config has no MCP section yet"); +} + +export async function setMcpEnabledInConfig( + projectDir: string, + name: string, + enabled: boolean, +): Promise<{ changed: boolean }> { + const safeName = validateMcpServerName(name); + const configFile = await readOpencodeConfig("project", projectDir); + const projectContent = configFile.exists && configFile.content ? configFile.content : "{}\n"; + const globalConfigFile = await readOpencodeConfig("global", projectDir); + const inheritedMcpServers = parseMcpServersFromContent(globalConfigFile.content ?? ""); + + const nextContent = updateMcpEnabledInConfigContent(projectContent, safeName, enabled, { + inheritedMcpServers, + }); + if (nextContent === projectContent) return { changed: false }; + const writeResult = await writeOpencodeConfig("project", projectDir, nextContent); + if (!writeResult.ok) { + throw new Error(writeResult.stderr || writeResult.stdout || "Failed to write opencode.json"); + } + return { changed: true }; +} + export function parseMcpServersFromContent(content: string): McpServerEntry[] { if (!content.trim()) return []; diff --git a/apps/app/src/i18n/locales/ca.ts b/apps/app/src/i18n/locales/ca.ts index 12b9d68e3..4ec4fcb86 100644 --- a/apps/app/src/i18n/locales/ca.ts +++ b/apps/app/src/i18n/locales/ca.ts @@ -476,8 +476,8 @@ export default { "den.worker_not_ready_title": "Aquest worker encara no està preparat per obrir-se.", "den.worker_provider_label": "{provider} worker", "den.worker_secondary_cloud": "Worker Cloud", - "extensions.app_count_one": "Aplicació {count} connectada", - "extensions.app_count_many": "Aplicacions {count} connectades", + "extensions.app_count_one": "Aplicació {count} configurada", + "extensions.app_count_many": "Aplicacions {count} configurades", "extensions.apps_mcp_header": "Aplicacions (MCP)", "extensions.filter_all": "Tots", "extensions.filter_apps": "Aplicacions", @@ -654,7 +654,9 @@ export default { "mcp.advanced_settings": "Configuració avançada", "mcp.advanced_settings_hint": "Edita els fitxers de configuració i gestiona les connexions manualment.", "mcp.app_connected": "aplicació connectada", + "mcp.app_configured": "app configured", "mcp.apps_connected": "aplicacions connectades", + "mcp.apps_configured": "apps configured", "mcp.apps_subtitle": "Connecta les teves eines preferides perquè OpenWork les pugui fer servir per tu.", "mcp.apps_title": "Aplicacions", "mcp.auth.already_connected": "Ja està connectat", @@ -780,6 +782,11 @@ export default { "mcp.opening_label": "S'està obrint...", "mcp.pick_workspace_error": "Tria primer una carpeta de workspace.", "mcp.pick_workspace_first": "Tria primer una carpeta de workspace.", + "mcp.pause_app": "Pause", + "mcp.pause_modal_message": "Pause {server}? OpenWork will keep the app in your config, but its tools will stop loading after OpenCode reloads.", + "mcp.pause_modal_title": "Pause app?", + "mcp.pause_success": "Paused {server}.", + "mcp.pause_working": "Pausing...", "mcp.quick_connect_chrome_desc": "Controla pestanyes de Chrome amb automatització del navegador.", "mcp.quick_connect_chrome_title": "Control Chrome", "mcp.quick_connect_context7_desc": "Cerca documentació del producte amb més context.", @@ -796,12 +803,15 @@ export default { "mcp.reload_banner_description": "Toca Activa per acabar de connectar l'aplicació.", "mcp.reload_banner_description_blocked": "S'està executant una tasca. Atura-la primer i després activa-la.", "mcp.remote_workspace_url_hint": "Els workers remots es connecten més ràpidament amb servidors MCP basats en URL.", + "mcp.read_only": "MCP config is read-only.", "mcp.remove_app": "Eliminar", "mcp.remove_failed": "No s'ha pogut eliminar l'aplicació.", "mcp.remove_modal_message": "Esteu segur que voleu eliminar {server}? Sempre el podeu tornar a afegir més tard.", "mcp.remove_modal_title": "Elimina l'aplicació", "mcp.reveal_config_failed": "No s'ha pogut obrir el fitxer de configuració", "mcp.reveal_in_finder": "Mostra a Finder", + "mcp.resume_app": "Resume", + "mcp.resume_success": "Resumed {server}.", "mcp.scope_global": "Tots els workspaces", "mcp.scope_project": "Aquest workspace", "mcp.server_command": "Command", @@ -812,6 +822,7 @@ export default { "mcp.server_type": "Tipus", "mcp.server_url": "URL del servidor", "mcp.server_url_placeholder": "https://api.githubcopilot.com/mcp/", + "mcp.toggle_failed": "Couldn't update the app.", "mcp.sign_in_section_label": "Inicia la sessió", "mcp.tap_to_connect": "Toca per connectar", "mcp.technical_details": "Detalls tècnics", diff --git a/apps/app/src/i18n/locales/en.ts b/apps/app/src/i18n/locales/en.ts index 166391e77..38f0da2bf 100644 --- a/apps/app/src/i18n/locales/en.ts +++ b/apps/app/src/i18n/locales/en.ts @@ -478,8 +478,8 @@ export default { "den.worker_not_ready_title": "This worker is not ready to open yet.", "den.worker_provider_label": "{provider} worker", "den.worker_secondary_cloud": "Cloud worker", - "extensions.app_count_one": "{count} app connected", - "extensions.app_count_many": "{count} apps connected", + "extensions.app_count_one": "{count} app configured", + "extensions.app_count_many": "{count} apps configured", "extensions.apps_mcp_header": "Apps (MCP)", "extensions.filter_all": "All", "extensions.filter_apps": "Apps", @@ -661,7 +661,9 @@ export default { "mcp.advanced_settings": "Advanced settings", "mcp.advanced_settings_hint": "Edit config files and manage connections manually.", "mcp.app_connected": "app connected", + "mcp.app_configured": "app configured", "mcp.apps_connected": "apps connected", + "mcp.apps_configured": "apps configured", "mcp.apps_subtitle": "Connect your favorite tools so OpenWork can use them on your behalf.", "mcp.apps_title": "Apps", "mcp.auth.already_connected": "Already Connected", @@ -787,6 +789,11 @@ export default { "mcp.opening_label": "Opening...", "mcp.pick_workspace_error": "Choose a workspace folder first.", "mcp.pick_workspace_first": "Choose a workspace folder first.", + "mcp.pause_app": "Pause", + "mcp.pause_modal_message": "Pause {server}? OpenWork will keep the app in your config, but its tools will stop loading after OpenCode reloads.", + "mcp.pause_modal_title": "Pause app?", + "mcp.pause_success": "Paused {server}.", + "mcp.pause_working": "Pausing...", "mcp.quick_connect_chrome_desc": "Drive Chrome tabs with browser automation.", "mcp.quick_connect_chrome_title": "Control Chrome", "mcp.quick_connect_context7_desc": "Search product docs with richer context.", @@ -803,12 +810,15 @@ export default { "mcp.reload_banner_description": "Tap Activate to finish connecting your app.", "mcp.reload_banner_description_blocked": "A task is running. Stop it first, then activate.", "mcp.remote_workspace_url_hint": "Remote workers connect fastest with URL-based MCP servers.", + "mcp.read_only": "MCP config is read-only.", "mcp.remove_app": "Remove", "mcp.remove_failed": "Couldn't remove the app.", "mcp.remove_modal_message": "Are you sure you want to remove {server}? You can always add it back later.", "mcp.remove_modal_title": "Remove app", "mcp.reveal_config_failed": "Couldn't open the config file", "mcp.reveal_in_finder": "Show in Finder", + "mcp.resume_app": "Resume", + "mcp.resume_success": "Resumed {server}.", "mcp.scope_global": "All workspaces", "mcp.scope_project": "This workspace", "mcp.server_command": "Command", @@ -822,6 +832,7 @@ export default { "mcp.sign_in_section_label": "Sign-in", "mcp.tap_to_connect": "Tap to connect", "mcp.technical_details": "Technical details", + "mcp.toggle_failed": "Couldn't update the app.", "mcp.type_cloud": "Cloud (sign in with your account)", "mcp.type_local": "Local (runs on this device)", "mcp.type_local_cmd": "Local (command)", diff --git a/apps/app/src/i18n/locales/es.ts b/apps/app/src/i18n/locales/es.ts index da84f9ec7..00fdfc6d5 100644 --- a/apps/app/src/i18n/locales/es.ts +++ b/apps/app/src/i18n/locales/es.ts @@ -476,8 +476,8 @@ export default { "den.worker_not_ready_title": "Este worker aún no está listo para abrir.", "den.worker_provider_label": "Worker de {provider}", "den.worker_secondary_cloud": "Worker de Cloud", - "extensions.app_count_one": "Aplicación {count} conectada", - "extensions.app_count_many": "Aplicaciones {count} conectadas", + "extensions.app_count_one": "Aplicación {count} configurada", + "extensions.app_count_many": "Aplicaciones {count} configuradas", "extensions.apps_mcp_header": "Aplicaciones (MCP)", "extensions.filter_all": "Todo", "extensions.filter_apps": "Aplicaciones", @@ -654,7 +654,9 @@ export default { "mcp.advanced_settings": "Ajustes avanzados", "mcp.advanced_settings_hint": "Edita archivos de configuración y gestiona conexiones manualmente.", "mcp.app_connected": "aplicación conectada", + "mcp.app_configured": "app configured", "mcp.apps_connected": "aplicaciones conectadas", + "mcp.apps_configured": "apps configured", "mcp.apps_subtitle": "Conecta tus herramientas favoritas para que OpenWork pueda usarlas por ti.", "mcp.apps_title": "Aplicaciones", "mcp.auth.already_connected": "Ya conectado", @@ -780,6 +782,11 @@ export default { "mcp.opening_label": "Abriendo...", "mcp.pick_workspace_error": "Elige primero una carpeta del espacio de trabajo.", "mcp.pick_workspace_first": "Elige primero una carpeta del espacio de trabajo.", + "mcp.pause_app": "Pause", + "mcp.pause_modal_message": "Pause {server}? OpenWork will keep the app in your config, but its tools will stop loading after OpenCode reloads.", + "mcp.pause_modal_title": "Pause app?", + "mcp.pause_success": "Paused {server}.", + "mcp.pause_working": "Pausing...", "mcp.quick_connect_chrome_desc": "Controla pestañas de Chrome con automatización del navegador.", "mcp.quick_connect_chrome_title": "Control Chrome", "mcp.quick_connect_context7_desc": "Busca documentación del producto con más contexto.", @@ -796,12 +803,15 @@ export default { "mcp.reload_banner_description": "Toca Activar para terminar de conectar tu app.", "mcp.reload_banner_description_blocked": "Hay una tarea en ejecución. Deténla primero y luego actívalo.", "mcp.remote_workspace_url_hint": "Los workers remotos se conectan más rápido con servidores MCP basados en URL.", + "mcp.read_only": "MCP config is read-only.", "mcp.remove_app": "Eliminar", "mcp.remove_failed": "No se pudo eliminar la aplicación.", "mcp.remove_modal_message": "¿Seguro que quieres eliminar {server}? Siempre podrás volver a añadirlo más tarde.", "mcp.remove_modal_title": "Quitar aplicación", "mcp.reveal_config_failed": "No se pudo abrir el archivo de configuración", "mcp.reveal_in_finder": "Mostrar en Finder", + "mcp.resume_app": "Resume", + "mcp.resume_success": "Resumed {server}.", "mcp.scope_global": "Todos los espacios de trabajo", "mcp.scope_project": "Este espacio de trabajo", "mcp.server_command": "Command", @@ -812,6 +822,7 @@ export default { "mcp.server_type": "Tipo", "mcp.server_url": "URL del servidor", "mcp.server_url_placeholder": "https://api.githubcopilot.com/mcp/", + "mcp.toggle_failed": "Couldn't update the app.", "mcp.sign_in_section_label": "Iniciar sesión", "mcp.tap_to_connect": "Toca para conectarte", "mcp.technical_details": "Detalles técnicos", diff --git a/apps/app/src/i18n/locales/fr.ts b/apps/app/src/i18n/locales/fr.ts index f79a39fc0..ddb8c3c01 100644 --- a/apps/app/src/i18n/locales/fr.ts +++ b/apps/app/src/i18n/locales/fr.ts @@ -476,8 +476,8 @@ export default { "den.worker_not_ready_title": "Ce worker n'est pas encore prêt à être ouvert.", "den.worker_provider_label": "worker {provider}", "den.worker_secondary_cloud": "Worker cloud", - "extensions.app_count_one": "{count} application connectée", - "extensions.app_count_many": "{count} applications connectées", + "extensions.app_count_one": "{count} application configurée", + "extensions.app_count_many": "{count} applications configurées", "extensions.apps_mcp_header": "Applications (MCP)", "extensions.filter_all": "Tout", "extensions.filter_apps": "Applications", @@ -654,7 +654,9 @@ export default { "mcp.advanced_settings": "Paramètres avancés", "mcp.advanced_settings_hint": "Modifiez les fichiers de configuration et gérez les connexions manuellement.", "mcp.app_connected": "application connectée", + "mcp.app_configured": "app configured", "mcp.apps_connected": "applications connectées", + "mcp.apps_configured": "apps configured", "mcp.apps_subtitle": "Connectez vos outils préférés pour qu'OpenWork puisse les utiliser en votre nom.", "mcp.apps_title": "Applications", "mcp.auth.already_connected": "Déjà connecté", @@ -780,6 +782,11 @@ export default { "mcp.opening_label": "Ouverture...", "mcp.pick_workspace_error": "Choisissez d'abord un dossier d'espace de travail.", "mcp.pick_workspace_first": "Choisissez d'abord un dossier d'espace de travail.", + "mcp.pause_app": "Pause", + "mcp.pause_modal_message": "Pause {server}? OpenWork will keep the app in your config, but its tools will stop loading after OpenCode reloads.", + "mcp.pause_modal_title": "Pause app?", + "mcp.pause_success": "Paused {server}.", + "mcp.pause_working": "Pausing...", "mcp.quick_connect_chrome_desc": "Pilotez les onglets Chrome avec l'automatisation du navigateur.", "mcp.quick_connect_chrome_title": "Control Chrome", "mcp.quick_connect_context7_desc": "Recherchez dans la documentation produit avec un contexte plus riche.", @@ -796,12 +803,15 @@ export default { "mcp.reload_banner_description": "Appuyez sur Activer pour terminer la connexion de votre application.", "mcp.reload_banner_description_blocked": "Une tâche est en cours. Arrêtez-la d'abord, puis activez.", "mcp.remote_workspace_url_hint": "Les workers distants se connectent plus rapidement avec des serveurs MCP basés sur une URL.", + "mcp.read_only": "MCP config is read-only.", "mcp.remove_app": "Supprimer", "mcp.remove_failed": "Impossible de supprimer l'application.", "mcp.remove_modal_message": "Êtes-vous sûr de vouloir supprimer {server} ? Vous pourrez toujours l'ajouter de nouveau plus tard.", "mcp.remove_modal_title": "Supprimer l'application", "mcp.reveal_config_failed": "Impossible d'ouvrir le fichier de configuration", "mcp.reveal_in_finder": "Afficher dans le Finder", + "mcp.resume_app": "Resume", + "mcp.resume_success": "Resumed {server}.", "mcp.scope_global": "Tous les espaces de travail", "mcp.scope_project": "Cet espace de travail", "mcp.server_command": "Commande", @@ -812,6 +822,7 @@ export default { "mcp.server_type": "Type", "mcp.server_url": "URL du serveur", "mcp.server_url_placeholder": "https://api.githubcopilot.com/mcp/", + "mcp.toggle_failed": "Couldn't update the app.", "mcp.sign_in_section_label": "Connexion", "mcp.tap_to_connect": "Appuyez pour connecter", "mcp.technical_details": "Détails techniques", diff --git a/apps/app/src/i18n/locales/ja.ts b/apps/app/src/i18n/locales/ja.ts index f22e9cdd7..e2d7ea499 100644 --- a/apps/app/src/i18n/locales/ja.ts +++ b/apps/app/src/i18n/locales/ja.ts @@ -458,8 +458,8 @@ export default { "den.worker_not_ready_title": "このワーカーはまだ開ける状態ではありません。", "den.worker_provider_label": "{provider}ワーカー", "den.worker_secondary_cloud": "クラウドワーカー", - "extensions.app_count_one": "{count}件のアプリ接続済み", - "extensions.app_count_many": "{count}件のアプリ接続済み", + "extensions.app_count_one": "{count}件のアプリ設定済み", + "extensions.app_count_many": "{count}件のアプリ設定済み", "extensions.apps_mcp_header": "アプリ(MCP)", "extensions.filter_all": "すべて", "extensions.filter_apps": "アプリ", @@ -636,7 +636,9 @@ export default { "mcp.advanced_settings": "詳細設定", "mcp.advanced_settings_hint": "設定ファイルを編集し、接続を手動で管理します。", "mcp.app_connected": "アプリ接続済み", + "mcp.app_configured": "アプリ設定済み", "mcp.apps_connected": "アプリ接続済み", + "mcp.apps_configured": "アプリ設定済み", "mcp.apps_subtitle": "ツールを接続して、OpenWorkが代わりに操作できるようにしましょう。", "mcp.apps_title": "アプリ", "mcp.auth.already_connected": "既に接続済み", @@ -762,6 +764,11 @@ export default { "mcp.opening_label": "開いています…", "mcp.pick_workspace_error": "最初にワークスペースフォルダを選択してください。", "mcp.pick_workspace_first": "最初にワークスペースフォルダを選択してください。", + "mcp.pause_app": "一時停止", + "mcp.pause_modal_message": "{server} を一時停止しますか?OpenWork はこのアプリ設定を残しますが、OpenCode の再読み込み後はツールを読み込みません。", + "mcp.pause_modal_title": "アプリを一時停止しますか?", + "mcp.pause_success": "{server} を一時停止しました。", + "mcp.pause_working": "一時停止中…", "mcp.quick_connect_chrome_desc": "ブラウザ自動化でChromeタブを操作。", "mcp.quick_connect_chrome_title": "Chromeを操作", "mcp.quick_connect_context7_desc": "より豊富なコンテキストで製品ドキュメントを検索。", @@ -778,12 +785,15 @@ export default { "mcp.reload_banner_description": "有効化をタップしてアプリの接続を完了してください。", "mcp.reload_banner_description_blocked": "タスクが実行中です。先に停止してから有効化してください。", "mcp.remote_workspace_url_hint": "リモートワーカーはURLベースのMCPサーバーとの接続が最も速いです。", + "mcp.read_only": "MCP設定は読み取り専用です。", "mcp.remove_app": "削除", "mcp.remove_failed": "アプリを削除できませんでした。", "mcp.remove_modal_message": "{server} を削除してもよろしいですか?後で再度追加できます。", "mcp.remove_modal_title": "アプリを削除", "mcp.reveal_config_failed": "設定ファイルを開けませんでした", "mcp.reveal_in_finder": "Finderで表示", + "mcp.resume_app": "再開", + "mcp.resume_success": "{server} を再開しました。", "mcp.scope_global": "すべてのワークスペース", "mcp.scope_project": "このワークスペース", "mcp.server_command": "コマンド", @@ -797,6 +807,7 @@ export default { "mcp.sign_in_section_label": "サインイン", "mcp.tap_to_connect": "タップして接続", "mcp.technical_details": "技術的な詳細", + "mcp.toggle_failed": "アプリを更新できませんでした。", "mcp.type_cloud": "クラウド(アカウントでサインイン)", "mcp.type_local": "ローカル(このデバイスで実行)", "mcp.type_local_cmd": "ローカル(コマンド)", diff --git a/apps/app/src/i18n/locales/pt-BR.ts b/apps/app/src/i18n/locales/pt-BR.ts index 96e94bddf..f841f7aa6 100644 --- a/apps/app/src/i18n/locales/pt-BR.ts +++ b/apps/app/src/i18n/locales/pt-BR.ts @@ -459,8 +459,8 @@ export default { "den.worker_not_ready_title": "Este worker ainda não está pronto para abrir.", "den.worker_provider_label": "Worker {provider}", "den.worker_secondary_cloud": "Worker na nuvem", - "extensions.app_count_one": "{count} app conectado", - "extensions.app_count_many": "{count} apps conectados", + "extensions.app_count_one": "{count} app configurado", + "extensions.app_count_many": "{count} apps configurados", "extensions.apps_mcp_header": "Apps (MCP)", "extensions.filter_all": "Todos", "extensions.filter_apps": "Apps", @@ -637,7 +637,9 @@ export default { "mcp.advanced_settings": "Configurações avançadas", "mcp.advanced_settings_hint": "Edite arquivos de configuração e gerencie conexões manualmente.", "mcp.app_connected": "app conectado", + "mcp.app_configured": "app configured", "mcp.apps_connected": "apps conectados", + "mcp.apps_configured": "apps configured", "mcp.apps_subtitle": "Conecte suas ferramentas favoritas para que o OpenWork as use em seu nome.", "mcp.apps_title": "Apps", "mcp.auth.already_connected": "Já Conectado", @@ -763,6 +765,11 @@ export default { "mcp.opening_label": "Abrindo...", "mcp.pick_workspace_error": "Escolha primeiro uma pasta de workspace.", "mcp.pick_workspace_first": "Escolha primeiro uma pasta de workspace.", + "mcp.pause_app": "Pause", + "mcp.pause_modal_message": "Pause {server}? OpenWork will keep the app in your config, but its tools will stop loading after OpenCode reloads.", + "mcp.pause_modal_title": "Pause app?", + "mcp.pause_success": "Paused {server}.", + "mcp.pause_working": "Pausing...", "mcp.quick_connect_chrome_desc": "Controle abas do Chrome com automação de navegador.", "mcp.quick_connect_chrome_title": "Controlar Chrome", "mcp.quick_connect_context7_desc": "Pesquise docs de produto com contexto mais rico.", @@ -779,12 +786,15 @@ export default { "mcp.reload_banner_description": "Toque em Ativar para terminar de conectar seu app.", "mcp.reload_banner_description_blocked": "Uma tarefa está em execução. Pare-a primeiro e então ative.", "mcp.remote_workspace_url_hint": "Workers remotos se conectam mais rápido com servidores MCP baseados em URL.", + "mcp.read_only": "MCP config is read-only.", "mcp.remove_app": "Remover", "mcp.remove_failed": "Não foi possível remover o app.", "mcp.remove_modal_message": "Tem certeza que deseja remover {server}? Você pode adicioná-lo de volta a qualquer momento.", "mcp.remove_modal_title": "Remover app", "mcp.reveal_config_failed": "Não foi possível abrir o arquivo de configuração", "mcp.reveal_in_finder": "Mostrar no Finder", + "mcp.resume_app": "Resume", + "mcp.resume_success": "Resumed {server}.", "mcp.scope_global": "Todos os workspaces", "mcp.scope_project": "Este workspace", "mcp.server_command": "Comando", @@ -795,6 +805,7 @@ export default { "mcp.server_type": "Tipo", "mcp.server_url": "URL do servidor", "mcp.server_url_placeholder": "https://api.githubcopilot.com/mcp/", + "mcp.toggle_failed": "Couldn't update the app.", "mcp.sign_in_section_label": "Login", "mcp.tap_to_connect": "Toque para conectar", "mcp.technical_details": "Detalhes técnicos", diff --git a/apps/app/src/i18n/locales/th.ts b/apps/app/src/i18n/locales/th.ts index 94ac119a1..623357abc 100644 --- a/apps/app/src/i18n/locales/th.ts +++ b/apps/app/src/i18n/locales/th.ts @@ -459,8 +459,8 @@ export default { "den.worker_not_ready_title": "Worker นี้ยังไม่พร้อมเปิด", "den.worker_provider_label": "{provider} worker", "den.worker_secondary_cloud": "Cloud worker", - "extensions.app_count_one": "{count} แอปเชื่อมต่อแล้ว", - "extensions.app_count_many": "{count} แอปเชื่อมต่อแล้ว", + "extensions.app_count_one": "{count} แอปตั้งค่าแล้ว", + "extensions.app_count_many": "{count} แอปตั้งค่าแล้ว", "extensions.apps_mcp_header": "แอป (MCP)", "extensions.filter_all": "ทั้งหมด", "extensions.filter_apps": "แอป", @@ -637,7 +637,9 @@ export default { "mcp.advanced_settings": "การตั้งค่าขั้นสูง", "mcp.advanced_settings_hint": "แก้ไขไฟล์ config และจัดการการเชื่อมต่อด้วยตนเอง", "mcp.app_connected": "แอปที่เชื่อมต่อ", + "mcp.app_configured": "app configured", "mcp.apps_connected": "แอปที่เชื่อมต่อ", + "mcp.apps_configured": "apps configured", "mcp.apps_subtitle": "เชื่อมต่อเครื่องมือที่คุณชื่นชอบเพื่อให้ OpenWork ใช้งานแทนคุณ", "mcp.apps_title": "แอป", "mcp.auth.already_connected": "เชื่อมต่อแล้ว", @@ -763,6 +765,11 @@ export default { "mcp.opening_label": "กำลังเปิด...", "mcp.pick_workspace_error": "เลือกโฟลเดอร์พื้นที่ทำงานก่อน", "mcp.pick_workspace_first": "เลือกโฟลเดอร์พื้นที่ทำงานก่อน", + "mcp.pause_app": "Pause", + "mcp.pause_modal_message": "Pause {server}? OpenWork will keep the app in your config, but its tools will stop loading after OpenCode reloads.", + "mcp.pause_modal_title": "Pause app?", + "mcp.pause_success": "Paused {server}.", + "mcp.pause_working": "Pausing...", "mcp.quick_connect_chrome_desc": "ควบคุมแท็บ Chrome ด้วย browser automation", "mcp.quick_connect_chrome_title": "ควบคุม Chrome", "mcp.quick_connect_context7_desc": "ค้นหาเอกสารผลิตภัณฑ์ด้วยบริบทที่สมบูรณ์ยิ่งขึ้น", @@ -779,12 +786,15 @@ export default { "mcp.reload_banner_description": "แตะ เปิดใช้งาน เพื่อเชื่อมต่อแอปให้เสร็จ", "mcp.reload_banner_description_blocked": "มีงานกำลังทำงานอยู่ หยุดก่อน แล้วเปิดใช้งาน", "mcp.remote_workspace_url_hint": "Remote workers เชื่อมต่อได้เร็วที่สุดกับ MCP server แบบ URL", + "mcp.read_only": "MCP config is read-only.", "mcp.remove_app": "ลบ", "mcp.remove_failed": "ลบแอปไม่สำเร็จ", "mcp.remove_modal_message": "คุณแน่ใจหรือไม่ว่าต้องการลบ {server}? คุณสามารถเพิ่มกลับได้ในภายหลัง", "mcp.remove_modal_title": "ลบแอป", "mcp.reveal_config_failed": "ไม่สามารถเปิดไฟล์ config", "mcp.reveal_in_finder": "เปิดในตัวจัดการไฟล์", + "mcp.resume_app": "Resume", + "mcp.resume_success": "Resumed {server}.", "mcp.scope_global": "ทุกพื้นที่ทำงาน", "mcp.scope_project": "พื้นที่ทำงานนี้", "mcp.server_command": "คำสั่ง", @@ -795,6 +805,7 @@ export default { "mcp.server_type": "ประเภท", "mcp.server_url": "URL ของเซิร์ฟเวอร์", "mcp.server_url_placeholder": "https://api.githubcopilot.com/mcp/", + "mcp.toggle_failed": "Couldn't update the app.", "mcp.sign_in_section_label": "การเข้าสู่ระบบ", "mcp.tap_to_connect": "แตะเพื่อเชื่อมต่อ", "mcp.technical_details": "รายละเอียดทางเทคนิค", diff --git a/apps/app/src/i18n/locales/vi.ts b/apps/app/src/i18n/locales/vi.ts index 1076b9ebc..55f167f0d 100644 --- a/apps/app/src/i18n/locales/vi.ts +++ b/apps/app/src/i18n/locales/vi.ts @@ -459,8 +459,8 @@ export default { "den.worker_not_ready_title": "Worker này chưa sẵn sàng.", "den.worker_provider_label": "Worker {provider}", "den.worker_secondary_cloud": "Worker Cloud", - "extensions.app_count_one": "{count} ứng dụng đã kết nối", - "extensions.app_count_many": "{count} ứng dụng đã kết nối", + "extensions.app_count_one": "{count} ứng dụng đã cấu hình", + "extensions.app_count_many": "{count} ứng dụng đã cấu hình", "extensions.apps_mcp_header": "Ứng dụng (MCP)", "extensions.filter_all": "Tất cả", "extensions.filter_apps": "Ứng dụng", @@ -637,7 +637,9 @@ export default { "mcp.advanced_settings": "Cài đặt nâng cao", "mcp.advanced_settings_hint": "Chỉnh sửa tệp cấu hình và quản lý kết nối thủ công.", "mcp.app_connected": "ứng dụng đã kết nối", + "mcp.app_configured": "app configured", "mcp.apps_connected": "ứng dụng đã kết nối", + "mcp.apps_configured": "apps configured", "mcp.apps_subtitle": "Kết nối các công cụ yêu thích để OpenWork có thể sử dụng thay bạn.", "mcp.apps_title": "Ứng dụng", "mcp.auth.already_connected": "Đã kết nối", @@ -763,6 +765,11 @@ export default { "mcp.opening_label": "Đang mở...", "mcp.pick_workspace_error": "Vui lòng chọn thư mục workspace trước.", "mcp.pick_workspace_first": "Vui lòng chọn thư mục workspace trước.", + "mcp.pause_app": "Pause", + "mcp.pause_modal_message": "Pause {server}? OpenWork will keep the app in your config, but its tools will stop loading after OpenCode reloads.", + "mcp.pause_modal_title": "Pause app?", + "mcp.pause_success": "Paused {server}.", + "mcp.pause_working": "Pausing...", "mcp.quick_connect_chrome_desc": "Điều khiển tab Chrome với browser automation.", "mcp.quick_connect_chrome_title": "Điều khiển Chrome", "mcp.quick_connect_context7_desc": "Tìm kiếm tài liệu sản phẩm với ngữ cảnh phong phú hơn.", @@ -779,12 +786,15 @@ export default { "mcp.reload_banner_description": "Nhấn Kích hoạt để hoàn tất kết nối ứng dụng.", "mcp.reload_banner_description_blocked": "Một task đang chạy. Dừng trước rồi kích hoạt.", "mcp.remote_workspace_url_hint": "Workspace từ xa kết nối nhanh nhất với MCP server dạng URL.", + "mcp.read_only": "MCP config is read-only.", "mcp.remove_app": "Xóa", "mcp.remove_failed": "Không thể xóa ứng dụng.", "mcp.remove_modal_message": "Bạn có chắc muốn xóa {server}? Bạn luôn có thể thêm lại sau.", "mcp.remove_modal_title": "Xóa ứng dụng", "mcp.reveal_config_failed": "Không thể mở tệp cấu hình", "mcp.reveal_in_finder": "Hiện trong Finder", + "mcp.resume_app": "Resume", + "mcp.resume_success": "Resumed {server}.", "mcp.scope_global": "Tất cả workspaces", "mcp.scope_project": "Workspace này", "mcp.server_command": "Lệnh", @@ -795,6 +805,7 @@ export default { "mcp.server_type": "Loại", "mcp.server_url": "URL máy chủ", "mcp.server_url_placeholder": "https://api.githubcopilot.com/mcp/", + "mcp.toggle_failed": "Couldn't update the app.", "mcp.sign_in_section_label": "Đăng nhập", "mcp.tap_to_connect": "Nhấn để kết nối", "mcp.technical_details": "Chi tiết kỹ thuật", diff --git a/apps/app/src/i18n/locales/zh.ts b/apps/app/src/i18n/locales/zh.ts index 3418fb564..4b48b72c7 100644 --- a/apps/app/src/i18n/locales/zh.ts +++ b/apps/app/src/i18n/locales/zh.ts @@ -462,8 +462,8 @@ export default { "den.worker_not_ready_title": "此工作区尚未准备就绪。", "den.worker_provider_label": "{provider}工作区", "den.worker_secondary_cloud": "云端工作区", - "extensions.app_count_one": "{count}个应用已连接", - "extensions.app_count_many": "{count}个应用已连接", + "extensions.app_count_one": "{count}个应用已配置", + "extensions.app_count_many": "{count}个应用已配置", "extensions.apps_mcp_header": "应用(MCP)", "extensions.filter_all": "全部", "extensions.filter_apps": "应用", @@ -640,7 +640,9 @@ export default { "mcp.advanced_settings": "高级设置", "mcp.advanced_settings_hint": "编辑配置文件和手动管理连接。", "mcp.app_connected": "个应用已连接", + "mcp.app_configured": "个应用已配置", "mcp.apps_connected": "个应用已连接", + "mcp.apps_configured": "个应用已配置", "mcp.apps_subtitle": "连接常用工具,让OpenWork代你使用。", "mcp.apps_title": "应用", "mcp.auth.already_connected": "已连接", @@ -766,6 +768,11 @@ export default { "mcp.opening_label": "正在打开", "mcp.pick_workspace_error": "请先选择工作区文件夹。", "mcp.pick_workspace_first": "请先选择工作区文件夹。", + "mcp.pause_app": "暂停", + "mcp.pause_modal_message": "暂停{server}?OpenWork会保留此应用配置,但OpenCode重新加载后不再加载它的工具。", + "mcp.pause_modal_title": "暂停应用?", + "mcp.pause_success": "已暂停{server}。", + "mcp.pause_working": "正在暂停...", "mcp.quick_connect_chrome_desc": "通过浏览器自动化操控Chrome标签页。", "mcp.quick_connect_chrome_title": "控制Chrome", "mcp.quick_connect_context7_desc": "以更丰富的上下文搜索产品文档。", @@ -782,12 +789,15 @@ export default { "mcp.reload_banner_description": "点击激活以完成应用连接。", "mcp.reload_banner_description_blocked": "任务正在运行。请先停止任务再激活。", "mcp.remote_workspace_url_hint": "远程工作区建议优先使用URL类型的MCP服务器。", + "mcp.read_only": "MCP配置为只读。", "mcp.remove_app": "移除", "mcp.remove_failed": "无法移除应用。", "mcp.remove_modal_message": "确定要移除{server}吗?你可以随时重新添加。", "mcp.remove_modal_title": "移除应用", "mcp.reveal_config_failed": "无法打开配置文件", "mcp.reveal_in_finder": "在文件管理器中显示", + "mcp.resume_app": "恢复", + "mcp.resume_success": "已恢复{server}。", "mcp.scope_global": "所有工作区", "mcp.scope_project": "此工作区", "mcp.server_command": "命令", @@ -801,6 +811,7 @@ export default { "mcp.sign_in_section_label": "登录", "mcp.tap_to_connect": "点击连接", "mcp.technical_details": "技术详情", + "mcp.toggle_failed": "无法更新应用。", "mcp.type_cloud": "Cloud(使用账户登录)", "mcp.type_local": "本地(在此设备运行)", "mcp.type_local_cmd": "本地(命令)", diff --git a/apps/app/src/react-app/domains/connections/mcp-view.tsx b/apps/app/src/react-app/domains/connections/mcp-view.tsx index 04888c973..17b78bdb3 100644 --- a/apps/app/src/react-app/domains/connections/mcp-view.tsx +++ b/apps/app/src/react-app/domains/connections/mcp-view.tsx @@ -19,6 +19,8 @@ export type ConnectionsMcpStore = { authorizeMcp: (entry: McpServerEntry) => void; logoutMcpAuth: (name: string) => Promise | void; removeMcp: (name: string) => void; + setMcpEnabled: (name: string, enabled: boolean) => Promise | void; + canManageMcp?: boolean; }; export type ConnectionsMcpViewProps = { @@ -51,6 +53,8 @@ export default function ConnectionsMcpView(props: ConnectionsMcpViewProps) { authorizeMcp={connections.authorizeMcp} logoutMcpAuth={connections.logoutMcpAuth} removeMcp={connections.removeMcp} + setMcpEnabled={connections.setMcpEnabled} + canManageMcp={connections.canManageMcp} /> ); } diff --git a/apps/app/src/react-app/domains/connections/store.ts b/apps/app/src/react-app/domains/connections/store.ts index 0629be39d..b9011b9e1 100644 --- a/apps/app/src/react-app/domains/connections/store.ts +++ b/apps/app/src/react-app/domains/connections/store.ts @@ -20,6 +20,7 @@ import { toSessionTransportDirectory } from "../../../app/lib/session-scope"; import { parseMcpServersFromContent, removeMcpFromConfig, + setMcpEnabledInConfig, usesChromeDevtoolsAutoConnect, validateMcpServerName, } from "../../../app/mcp"; @@ -137,6 +138,20 @@ export function createConnectionsStore(options: { ) as McpStatusMap; }; + const applyServerMcpItems = (items: Array<{ name: string; config: Record }>) => { + const next = items.map((entry) => ({ + name: entry.name, + config: entry.config as McpServerEntry["config"], + })); + mutateState((current) => ({ + ...current, + mcpServers: next, + mcpLastUpdatedAt: Date.now(), + mcpStatuses: filterConfiguredStatuses(current.mcpStatuses, next), + mcpStatus: next.length ? current.mcpStatus : "No MCP servers configured yet.", + })); + }; + const readMcpConfigFile = async (scope: "project" | "global"): Promise => { const projectDir = options.projectDir().trim(); const openworkSnapshot = getOpenworkSnapshot(); @@ -770,34 +785,102 @@ export function createConnectionsStore(options: { } } - // Server-only path. Local fallback would rewrite opencode.jsonc whole and - // clobber inline comments — settings-route.tsx already gates the prop so - // this never gets called when the server is unavailable. Reload UX comes - // from the existing reload-required popup; no extra banner here. async function setMcpEnabled(name: string, enabled: boolean) { + const safeName = validateMcpServerName(name); + const openworkSnapshot = getOpenworkSnapshot(); + const serverConnected = openworkSnapshot.openworkServerStatus === "connected"; + const serverReadOnly = + serverConnected && + openworkSnapshot.openworkServerCapabilities != null && + !openworkSnapshot.openworkServerCapabilities.mcp?.write; + const isRemoteWorkspace = + options.workspaceType() === "remote" || + (!isDesktopRuntime() && serverConnected); + try { - const openworkSnapshot = getOpenworkSnapshot(); - const openworkClient = openworkSnapshot.openworkServerClient; - const openworkWorkspaceId = options.runtimeWorkspaceId(); - const canUseOpenworkServer = - openworkSnapshot.openworkServerStatus === "connected" && - openworkClient && - openworkWorkspaceId && - openworkSnapshot.openworkServerCapabilities?.mcp?.write; + setStateField("mcpStatus", null); + setStateField("mcpConnectingName", safeName); + + const { openworkClient, openworkWorkspaceId, canUseOpenworkServer } = + await resolveWritableOpenworkTarget(); - if (!canUseOpenworkServer || !openworkClient || !openworkWorkspaceId) { - setStateField("mcpStatus", translate("mcp.toggle_requires_server")); + if (serverReadOnly) { + setStateField("mcpStatus", translate("mcp.read_only")); return; } - await openworkClient.setMcpEnabled(openworkWorkspaceId, name, enabled); - options.markReloadRequired?.("mcp", { type: "mcp", name, action: "updated" }); - await refreshMcpServers(); + if (isRemoteWorkspace && !canUseOpenworkServer) { + setStateField("mcpStatus", "OpenWork server unavailable. MCP config is read-only."); + return; + } + + let serverItems: Array<{ name: string; config: Record }> | null = null; + let changed = true; + + if (canUseOpenworkServer && openworkClient && openworkWorkspaceId) { + const response = await openworkClient.setMcpEnabled(openworkWorkspaceId, safeName, enabled); + serverItems = response.items; + changed = response.changed; + } else { + if (!isDesktopRuntime()) { + setStateField("mcpStatus", translate("mcp.desktop_required")); + return; + } + const projectDir = options.projectDir().trim(); + if (!projectDir) { + setStateField("mcpStatus", translate("mcp.pick_workspace_first")); + return; + } + const response = await setMcpEnabledInConfig(projectDir, safeName, enabled); + changed = response.changed; + } + + if (!changed) { + if (serverItems) { + applyServerMcpItems(serverItems); + } + setStateField( + "mcpStatus", + translate(enabled ? "mcp.resume_success" : "mcp.pause_success").replace("{server}", safeName), + ); + return; + } + + const activeClient = options.client(); + const resolvedProjectDir = activeClient + ? await resolveProjectDir(activeClient, options.projectDir().trim()) + : ""; + if (activeClient && resolvedProjectDir) { + try { + if (enabled) { + await activeClient.mcp.connect({ directory: resolvedProjectDir, name: safeName }); + } else { + await activeClient.mcp.disconnect({ directory: resolvedProjectDir, name: safeName }); + } + const status = unwrap(await activeClient.mcp.status({ directory: resolvedProjectDir })); + setStateField("mcpStatuses", status as McpStatusMap); + } catch { + // Persisted config is the source of truth; runtime changes are best-effort. + } + } + + options.markReloadRequired?.("mcp", { type: "mcp", name: safeName, action: "updated" }); + if (serverItems) { + applyServerMcpItems(serverItems); + } else { + await refreshMcpServers(); + } + setStateField( + "mcpStatus", + translate(enabled ? "mcp.resume_success" : "mcp.pause_success").replace("{server}", safeName), + ); } catch (error) { setStateField( "mcpStatus", error instanceof Error ? error.message : translate("mcp.toggle_failed"), ); + } finally { + setStateField("mcpConnectingName", null); } } diff --git a/apps/app/src/react-app/domains/settings/pages/extensions-view.tsx b/apps/app/src/react-app/domains/settings/pages/extensions-view.tsx index 51bc3d54c..f11619f01 100644 --- a/apps/app/src/react-app/domains/settings/pages/extensions-view.tsx +++ b/apps/app/src/react-app/domains/settings/pages/extensions-view.tsx @@ -26,10 +26,8 @@ type SuggestedPlugin = { }>; }; -// The Solid ExtensionsView pulled the MCP-connected count from -// useConnections(). In React we let the parent pass that plus an -// already-rendered MCP view so we can ship this page before the full -// connections provider is ported. +// The Solid ExtensionsView pulled the MCP app count from useConnections(). +// In React we let the parent pass that plus an already-rendered MCP view. export type ExtensionsViewProps = { busy: boolean; selectedWorkspaceRoot: string; @@ -39,7 +37,7 @@ export type ExtensionsViewProps = { accessHint?: string | null; suggestedPlugins: SuggestedPlugin[]; extensions: PluginsExtensionsStore; - mcpConnectedAppsCount: number; + mcpConfiguredAppsCount: number; mcpView: ReactNode; onRefresh: () => void; initialSection?: ExtensionsSection; @@ -95,16 +93,16 @@ export function ExtensionsView(props: ExtensionsViewProps) {
- {props.mcpConnectedAppsCount > 0 ? ( -
-
- + {props.mcpConfiguredAppsCount > 0 ? ( +
+
+ {t( - props.mcpConnectedAppsCount === 1 + props.mcpConfiguredAppsCount === 1 ? "extensions.app_count_one" : "extensions.app_count_many", undefined, - { count: props.mcpConnectedAppsCount }, + { count: props.mcpConfiguredAppsCount }, )}
diff --git a/apps/app/src/react-app/domains/settings/pages/mcp-view.tsx b/apps/app/src/react-app/domains/settings/pages/mcp-view.tsx index df357832d..06cf79400 100644 --- a/apps/app/src/react-app/domains/settings/pages/mcp-view.tsx +++ b/apps/app/src/react-app/domains/settings/pages/mcp-view.tsx @@ -12,9 +12,10 @@ import { Globe, Loader2, MonitorSmartphone, + Pause, Plug2, + Play, Plus, - Power, Settings, Settings2, Unplug, @@ -69,7 +70,8 @@ export type McpViewProps = { authorizeMcp: (entry: McpServerEntry) => void; logoutMcpAuth: (name: string) => Promise | void; removeMcp: (name: string) => void; - setMcpEnabled?: (name: string, enabled: boolean) => Promise | void; + setMcpEnabled: (name: string, enabled: boolean) => Promise | void; + canManageMcp?: boolean; }; const statusDot = (status: ReactMcpStatus) => { @@ -119,6 +121,36 @@ const statusBadgeStyle = (status: ReactMcpStatus) => { } }; +const statusPillStyle = (status: ReactMcpStatus) => { + switch (status) { + case "connected": + return "border-green-6 bg-green-3 text-green-11"; + case "needs_auth": + case "needs_client_registration": + return "border-amber-6 bg-amber-3 text-amber-11"; + case "disabled": + case "disconnected": + return "border-dls-border bg-dls-hover text-dls-secondary"; + default: + return "border-red-6 bg-red-3 text-red-11"; + } +}; + +const StatusPill = ({ + status, + locale, +}: { + status: ReactMcpStatus; + locale: Language; +}) => ( + + + {friendlyStatus(status, locale)} + +); + const serviceIcon = (name: string) => { const lower = name.toLowerCase(); if (lower.includes("notion")) return BookOpen; @@ -168,6 +200,9 @@ export function McpView(props: McpViewProps) { const [logoutBusy, setLogoutBusy] = useState(false); const [removeOpen, setRemoveOpen] = useState(false); const [removeTarget, setRemoveTarget] = useState(null); + const [pauseOpen, setPauseOpen] = useState(false); + const [pauseTarget, setPauseTarget] = useState(null); + const [pauseBusy, setPauseBusy] = useState(false); const [configScope, setConfigScope] = useState<"project" | "global">("project"); const [projectConfig, setProjectConfig] = useState(null); const [globalConfig, setGlobalConfig] = useState(null); @@ -175,13 +210,13 @@ export function McpView(props: McpViewProps) { const [revealBusy, setRevealBusy] = useState(false); const [showAdvanced, setShowAdvanced] = useState(false); const [addMcpModalOpen, setAddMcpModalOpen] = useState(false); - const [togglingMcp, setTogglingMcp] = useState(null); const [controlChromeModalOpen, setControlChromeModalOpen] = useState(false); const [controlChromeModalMode, setControlChromeModalMode] = useState<"connect" | "edit">("connect"); const [controlChromeExistingProfile, setControlChromeExistingProfile] = useState(false); const configRequestId = useRef(0); const quickConnectList = props.quickConnect; + const canManageMcp = props.canManageMcp !== false; useEffect(() => { const root = props.selectedWorkspaceRoot.trim(); @@ -248,8 +283,8 @@ export function McpView(props: McpViewProps) { const quickConnectStatus = (entry: McpDirectoryInfo) => props.mcpStatuses[getMcpIdentityKey(entry)]; - const isQuickConnectConfigured = (entry: McpDirectoryInfo) => - props.mcpServers.some((server) => server.name === getMcpIdentityKey(entry)); + const configuredMcpEntry = (entry: McpDirectoryInfo) => + props.mcpServers.find((server) => server.name === getMcpIdentityKey(entry)); const openControlChromeModal = ( mode: "connect" | "edit", @@ -286,9 +321,9 @@ export function McpView(props: McpViewProps) { return resolved?.status ?? "disconnected"; }; - const connectedCount = props.mcpServers.filter( - (entry) => resolveStatus(entry) === "connected", - ).length; + const configuredCount = props.mcpServers.length; + const connectedCount = props.mcpServers.filter((entry) => resolveStatus(entry) === "connected").length; + const pausedCount = props.mcpServers.filter((entry) => entry.config.enabled === false).length; const requestLogout = (name: string) => { if (!name.trim()) return; @@ -309,6 +344,19 @@ export function McpView(props: McpViewProps) { } }; + const confirmPause = async () => { + const name = pauseTarget; + if (!name || pauseBusy) return; + setPauseBusy(true); + try { + await props.setMcpEnabled(name, false); + } finally { + setPauseBusy(false); + setPauseOpen(false); + setPauseTarget(null); + } + }; + const revealConfig = async () => { if (!isDesktopRuntime() || revealBusy) return; const root = props.selectedWorkspaceRoot.trim(); @@ -347,12 +395,32 @@ export function McpView(props: McpViewProps) {

{tr("mcp.apps_title")}

{tr("mcp.apps_subtitle")}

- {connectedCount > 0 ? ( -
-
- - {connectedCount} {connectedCount === 1 ? tr("mcp.app_connected") : tr("mcp.apps_connected")} - + {configuredCount > 0 ? ( +
+ {connectedCount > 0 ? ( +
+
+ + {connectedCount} {connectedCount === 1 ? tr("mcp.app_connected") : tr("mcp.apps_connected")} + +
+ ) : null} + {pausedCount > 0 ? ( +
+
+ + {pausedCount} {tr("mcp.friendly_status_paused")} + +
+ ) : null} + {connectedCount === 0 && pausedCount === 0 ? ( +
+
+ + {configuredCount} {configuredCount === 1 ? tr("mcp.app_configured") : tr("mcp.apps_configured")} + +
+ ) : null}
) : null}
@@ -364,13 +432,18 @@ export function McpView(props: McpViewProps) {
) : null} -
+
-
-
{tr("mcp.add_modal_title")}
-
{tr("mcp.custom_app_cta_hint")}
+
+
+ +
+
+
{tr("mcp.add_modal_title")}
+
{tr("mcp.custom_app_cta_hint")}
+
- @@ -387,11 +460,19 @@ export function McpView(props: McpViewProps) {
{quickConnectList.map((entry) => { - const configured = isQuickConnectConfigured(entry); + const configuredEntry = configuredMcpEntry(entry); + const configured = Boolean(configuredEntry); + const configuredStatus = configuredEntry ? resolveStatus(configuredEntry) : undefined; const connecting = props.mcpConnectingName === entry.name; const Icon = serviceIcon(entry.name); const controlChrome = isChromeDevtoolsMcp(entry); const quickStatus = !configured ? quickConnectStatus(entry) : undefined; + const configuredCardClass = + configuredStatus === "connected" + ? "border-green-6 bg-green-2" + : configuredStatus === "disabled" + ? "border-gray-6 bg-gray-2" + : "border-dls-border bg-dls-surface"; return (
@@ -425,20 +506,26 @@ export function McpView(props: McpViewProps) { }} className={`group w-full rounded-xl border p-4 text-left transition-all ${ configured - ? "border-green-6 bg-green-2" - : "border-dls-border bg-dls-surface hover:bg-dls-hover hover:shadow-[0_4px_16px_rgba(17,24,39,0.06)]" + ? configuredCardClass + : "border-dls-border bg-dls-surface hover:border-gray-7 hover:bg-dls-hover" }`} >
{connecting ? ( - ) : configured ? ( + ) : configuredStatus === "connected" ? ( + ) : configuredStatus === "disabled" ? ( + ) : ( )} @@ -447,9 +534,9 @@ export function McpView(props: McpViewProps) {

{entry.name}

- {configured ? ( - - {tr("mcp.connected_badge")} + {configured && configuredStatus ? ( + + {friendlyStatus(configuredStatus, locale)} ) : null} {!configured && quickStatus ? ( @@ -498,6 +585,7 @@ export function McpView(props: McpViewProps) { const Icon = serviceIcon(entry.name); const isSelected = props.selectedMcp === entry.name; const resolvedStatus = props.mcpStatuses[entry.name]; + const toggleBusy = props.mcpConnectingName === entry.name; const errorInfo = resolvedStatus && resolvedStatus.status === "failed" ? "error" in resolvedStatus @@ -508,15 +596,18 @@ export function McpView(props: McpViewProps) { return (
+ {isSelected ? ( +
+ ) : null}
-
- - {friendlyStatus(status, locale)} - +
@@ -552,7 +640,7 @@ export function McpView(props: McpViewProps) { {isSelected ? ( -
+
{tr("mcp.connection_type")} @@ -590,7 +678,9 @@ export function McpView(props: McpViewProps) {
- {supportsOauth(entry) && status !== "connected" ? ( + {supportsOauth(entry) && + status !== "connected" && + status !== "disabled" ? ( <>
{tr("mcp.logout_label")}
@@ -637,30 +727,46 @@ export function McpView(props: McpViewProps) { {tr("mcp.control_chrome_edit")} ) : null} - {props.setMcpEnabled && entry.source !== "config.global" ? ( + {status === "disabled" ? ( + + ) : ( - ) : null} + )}
{errorInfo ? ( @@ -763,18 +769,20 @@ export function McpView(props: McpViewProps) { {tr("mcp.pause_app")} )} - + {!isInherited ? ( + + ) : null}
) : null} diff --git a/apps/app/tests/connections-store-mcp-toggle.test.ts b/apps/app/tests/connections-store-mcp-toggle.test.ts index 8582797f4..720702015 100644 --- a/apps/app/tests/connections-store-mcp-toggle.test.ts +++ b/apps/app/tests/connections-store-mcp-toggle.test.ts @@ -60,4 +60,82 @@ describe("connections MCP toggle flow", () => { }, ]); }); + + test("ignores a toggle result after switching workspaces", async () => { + const reloads: unknown[] = []; + let selectedWorkspaceId = "ws_1"; + let selectedWorkspaceRoot = "/tmp/ws_1"; + let runtimeWorkspaceId = "ws_1"; + let patchCalls = 0; + let resolvePatch: ((value: { + changed: boolean; + enabled: boolean; + items: Array<{ name: string; config: Record }>; + }) => void) | undefined; + let markPatchStarted: (() => void) | undefined; + const patchStarted = new Promise((resolve) => { + markPatchStarted = resolve; + }); + const patchResult = new Promise<{ + changed: boolean; + enabled: boolean; + items: Array<{ name: string; config: Record }>; + }>((resolve) => { + resolvePatch = resolve; + }); + const openworkClient = { + setMcpEnabled: async () => { + patchCalls += 1; + markPatchStarted?.(); + return patchResult; + }, + listMcp: async () => ({ items: [] }), + }; + + const store = createConnectionsStore({ + client: () => null, + setClient: () => {}, + projectDir: () => selectedWorkspaceRoot, + selectedWorkspaceId: () => selectedWorkspaceId, + selectedWorkspaceRoot: () => selectedWorkspaceRoot, + workspaceType: () => "remote", + runtimeWorkspaceId: () => runtimeWorkspaceId, + developerMode: () => false, + markReloadRequired: (...args) => { + reloads.push(args); + }, + openworkServer: { + getSnapshot: () => ({ + openworkServerClient: openworkClient, + openworkServerStatus: "connected", + openworkServerCapabilities: { mcp: { read: true, write: true } }, + }), + } as never, + }); + + const pending = store.setMcpEnabled("stripe", false); + await patchStarted; + + selectedWorkspaceId = "ws_2"; + selectedWorkspaceRoot = "/tmp/ws_2"; + runtimeWorkspaceId = "ws_2"; + store.syncFromOptions(); + + resolvePatch?.({ + changed: true, + enabled: false, + items: [ + { + name: "stripe", + config: { type: "remote", url: "https://example.com/mcp", enabled: false }, + }, + ], + }); + await pending; + + expect(patchCalls).toBe(1); + expect(reloads).toHaveLength(0); + expect(store.getSnapshot().mcpServers).toEqual([]); + expect(store.getSnapshot().mcpConnectingName).toBeNull(); + }); }); diff --git a/apps/server/src/mcp-routes.e2e.test.ts b/apps/server/src/mcp-routes.e2e.test.ts index 4c87a215c..8998a428f 100644 --- a/apps/server/src/mcp-routes.e2e.test.ts +++ b/apps/server/src/mcp-routes.e2e.test.ts @@ -15,6 +15,7 @@ type Served = { const stops: Array<() => void | Promise> = []; const roots: string[] = []; const priorDataDir = process.env.OPENWORK_DATA_DIR; +const priorHome = process.env.HOME; afterEach(async () => { while (stops.length) { @@ -28,6 +29,11 @@ afterEach(async () => { } else { process.env.OPENWORK_DATA_DIR = priorDataDir; } + if (priorHome === undefined) { + delete process.env.HOME; + } else { + process.env.HOME = priorHome; + } }); async function createWorkspaceRoot() { @@ -58,6 +64,31 @@ async function createWorkspaceRoot() { return root; } +async function createGlobalConfigRoot() { + const homeRoot = await mkdtemp(join(tmpdir(), "openwork-mcp-routes-home-")); + roots.push(homeRoot); + process.env.HOME = homeRoot; + await mkdir(join(homeRoot, ".config", "opencode"), { recursive: true }); + await writeFile( + join(homeRoot, ".config", "opencode", "opencode.jsonc"), + `${JSON.stringify( + { + mcp: { + inherited: { + type: "remote", + url: "https://example.com/global-mcp", + enabled: true, + }, + }, + }, + null, + 2, + )}\n`, + "utf8", + ); + return homeRoot; +} + function auth(token: string) { return { Authorization: `Bearer ${token}`, "content-type": "application/json" }; } @@ -260,6 +291,51 @@ describe("mcp routes", () => { expect(audits).toHaveLength(0); }); + test("DELETE rejects inherited global MCP apps without audit or reload", async () => { + await createGlobalConfigRoot(); + const workspaceRoot = await createWorkspaceRoot(); + const { base, token } = startOpenworkServer({ workspaceRoot }); + + const pause = await fetch(`${base}/workspace/ws_1/mcp/inherited`, { + method: "PATCH", + headers: auth(token), + body: JSON.stringify({ enabled: false }), + }); + expect(pause.status).toBe(200); + + const auditsBeforeDelete = await readAuditEntries(workspaceRoot, "ws_1"); + const eventsBeforeDelete = await fetch(`${base}/workspace/ws_1/events?since=0`, { headers: auth(token) }); + expect(eventsBeforeDelete.status).toBe(200); + await eventsBeforeDelete.json(); + + const response = await fetch(`${base}/workspace/ws_1/mcp/inherited`, { + method: "DELETE", + headers: auth(token), + }); + expect(response.status).toBe(409); + const body = (await response.json()) as { code: string }; + expect(body.code).toBe("inherited_mcp_not_removable"); + + const auditsAfterDelete = await readAuditEntries(workspaceRoot, "ws_1"); + expect(auditsAfterDelete).toHaveLength(auditsBeforeDelete.length); + + const eventsAfterDelete = await fetch(`${base}/workspace/ws_1/events?since=0`, { headers: auth(token) }); + expect(eventsAfterDelete.status).toBe(200); + const eventsAfterDeleteBody = (await eventsAfterDelete.json()) as { + items: Array<{ trigger?: { type?: string; name?: string; action?: string } }>; + }; + expect(eventsAfterDeleteBody.items).not.toContainEqual( + expect.objectContaining({ + trigger: { type: "mcp", name: "inherited", action: "removed" }, + }), + ); + + const configText = await readFile(join(workspaceRoot, "opencode.jsonc"), "utf8"); + expect(configText).toContain("\"inherited\""); + expect(configText).toContain("\"enabled\": false"); + expect(configText).not.toContain("global-mcp"); + }); + test("PATCH requires authenticated collaborator access", async () => { const workspaceRoot = await createWorkspaceRoot(); const { base, hostToken } = startOpenworkServer({ workspaceRoot }); diff --git a/apps/server/src/mcp.remote-connect.e2e.test.ts b/apps/server/src/mcp.remote-connect.e2e.test.ts index a1e5d668e..6bd01d60a 100644 --- a/apps/server/src/mcp.remote-connect.e2e.test.ts +++ b/apps/server/src/mcp.remote-connect.e2e.test.ts @@ -116,6 +116,7 @@ describe("mcp remote connect flow", () => { const listedBeforePause = await listMcp(workspaceRoot); expect(listedBeforePause.find((entry) => entry.name === "inherited")).toMatchObject({ source: "config.global", + inherited: true, config: { type: "remote", url: "https://example.com/global-mcp", @@ -123,6 +124,11 @@ describe("mcp remote connect flow", () => { }, }); + await expect(removeMcp(workspaceRoot, "inherited")).rejects.toMatchObject({ + status: 409, + code: "inherited_mcp_not_removable", + }); + const paused = await setMcpEnabled(workspaceRoot, "inherited", false); expect(paused).toEqual({ changed: true, enabled: false }); @@ -134,6 +140,7 @@ describe("mcp remote connect flow", () => { const listedAfterPause = await listMcp(workspaceRoot); expect(listedAfterPause.find((entry) => entry.name === "inherited")).toMatchObject({ source: "config.project", + inherited: true, config: { type: "remote", url: "https://example.com/global-mcp", @@ -141,12 +148,18 @@ describe("mcp remote connect flow", () => { }, }); + await expect(removeMcp(workspaceRoot, "inherited")).rejects.toMatchObject({ + status: 409, + code: "inherited_mcp_not_removable", + }); + const resumed = await setMcpEnabled(workspaceRoot, "inherited", true); expect(resumed).toEqual({ changed: true, enabled: true }); const listedAfterResume = await listMcp(workspaceRoot); expect(listedAfterResume.find((entry) => entry.name === "inherited")).toMatchObject({ source: "config.global", + inherited: true, config: { type: "remote", url: "https://example.com/global-mcp", diff --git a/apps/server/src/mcp.ts b/apps/server/src/mcp.ts index b787a8dcd..4a635c0c8 100644 --- a/apps/server/src/mcp.ts +++ b/apps/server/src/mcp.ts @@ -65,6 +65,7 @@ export async function listMcp(workspaceRoot: string): Promise { // Global MCPs first; project-level entries override global ones with the same name. for (const [name, entry] of Object.entries(globalMcpMap)) { + if (!isConfiguredMcp(entry)) continue; const projectEntry = projectMcpMap[name]; if (hasOwn(projectMcpMap, name)) { if (isDisabledOverride(projectEntry) && isConfiguredMcp(entry)) { @@ -72,6 +73,7 @@ export async function listMcp(workspaceRoot: string): Promise { name, config: { ...entry, enabled: false }, source: "config.project", + inherited: true, disabledByTools: (isMcpDisabledByTools(globalConfig, name) || isMcpDisabledByTools(config, name)) || undefined, }); @@ -82,6 +84,7 @@ export async function listMcp(workspaceRoot: string): Promise { name, config: entry, source: "config.global", + inherited: true, disabledByTools: (isMcpDisabledByTools(globalConfig, name) || isMcpDisabledByTools(config, name)) || undefined, }); @@ -116,12 +119,40 @@ export async function addMcp( return { action: existed ? "updated" : "added" }; } -export async function removeMcp(workspaceRoot: string, name: string): Promise { +export async function removeMcp( + workspaceRoot: string, + name: string, + options: { dryRun?: boolean } = {}, +): Promise { + validateMcpName(name); const { data } = await readJsoncFile(opencodeConfigPath(workspaceRoot), {} as Record); + const { data: globalConfig } = await readJsoncFile(globalOpenCodeConfigPath(), {} as Record); const mcpMap = getMcpConfig(data); - if (!hasOwn(mcpMap, name)) return false; - delete mcpMap[name]; - await updateJsoncTopLevel(opencodeConfigPath(workspaceRoot), { mcp: mcpMap }); + const globalMcpMap = getMcpConfig(globalConfig); + const projectEntry = mcpMap[name]; + const globalEntry = globalMcpMap[name]; + + if (isDisabledOverride(projectEntry) && isConfiguredMcp(globalEntry)) { + throw new ApiError( + 409, + "inherited_mcp_not_removable", + "This MCP app is inherited from the global config. Resume it here or remove it from the global config.", + ); + } + if (!hasOwn(mcpMap, name)) { + if (isConfiguredMcp(globalEntry)) { + throw new ApiError( + 409, + "inherited_mcp_not_removable", + "This MCP app is inherited from the global config. Remove it from the global config.", + ); + } + return false; + } + if (!options.dryRun) { + delete mcpMap[name]; + await updateJsoncTopLevel(opencodeConfigPath(workspaceRoot), { mcp: mcpMap }); + } return true; } diff --git a/apps/server/src/server.ts b/apps/server/src/server.ts index b9e06ed77..affa40611 100644 --- a/apps/server/src/server.ts +++ b/apps/server/src/server.ts @@ -2642,6 +2642,11 @@ function createRoutes( requireClientScope(ctx, "collaborator"); const workspace = await resolveWorkspace(config, ctx.params.id); const name = ctx.params.name ?? ""; + const plannedRemoved = await removeMcp(workspace.path, name, { dryRun: true }); + if (!plannedRemoved) { + const items = await listMcp(workspace.path); + return jsonResponse({ items, removed: false }); + } await requireApproval(ctx, { workspaceId: workspace.id, action: "mcp.remove", @@ -2649,16 +2654,16 @@ function createRoutes( paths: [opencodeConfigPath(workspace.path)], }); const removed = await removeMcp(workspace.path, name); - await recordAudit(workspace.path, { - id: shortId(), - workspaceId: workspace.id, - actor: ctx.actor ?? { type: "remote" }, - action: "mcp.remove", - target: "opencode.json", - summary: `Removed MCP ${name}`, - timestamp: Date.now(), - }); if (removed) { + await recordAudit(workspace.path, { + id: shortId(), + workspaceId: workspace.id, + actor: ctx.actor ?? { type: "remote" }, + action: "mcp.remove", + target: "opencode.json", + summary: `Removed MCP ${name}`, + timestamp: Date.now(), + }); emitReloadEvent(ctx.reloadEvents, workspace, "mcp", { type: "mcp", name, @@ -2666,7 +2671,7 @@ function createRoutes( }); } const items = await listMcp(workspace.path); - return jsonResponse({ items }); + return jsonResponse({ items, removed }); }); // Toggle `enabled` on a workspace MCP. Strict body validation — `Boolean(body.enabled)` diff --git a/apps/server/src/types.ts b/apps/server/src/types.ts index f737b30c8..e44505a04 100644 --- a/apps/server/src/types.ts +++ b/apps/server/src/types.ts @@ -167,6 +167,7 @@ export interface McpItem { name: string; config: Record; source: "config.project" | "config.global" | "config.remote"; + inherited?: boolean; disabledByTools?: boolean; } From 64b871feaa39de1a16ef123049864cc5340b18d9 Mon Sep 17 00:00:00 2001 From: shizhigu Date: Thu, 30 Apr 2026 00:00:38 -0500 Subject: [PATCH 4/4] fix legacy mcp toggle route semantics --- apps/server/src/mcp-routes.e2e.test.ts | 44 +++++++++++++++ apps/server/src/server.ts | 78 ++++++++++---------------- 2 files changed, 74 insertions(+), 48 deletions(-) diff --git a/apps/server/src/mcp-routes.e2e.test.ts b/apps/server/src/mcp-routes.e2e.test.ts index 8998a428f..204c1c9e4 100644 --- a/apps/server/src/mcp-routes.e2e.test.ts +++ b/apps/server/src/mcp-routes.e2e.test.ts @@ -213,6 +213,50 @@ describe("mcp routes", () => { expect(eventsBody.items.some((event) => event.reason === "mcp")).toBe(false); }); + test("legacy POST enable route shares PATCH toggle semantics", async () => { + const workspaceRoot = await createWorkspaceRoot(); + const { base, token } = startOpenworkServer({ workspaceRoot }); + + const pause = await fetch(`${base}/workspace/ws_1/mcp/stripe/enabled`, { + method: "POST", + headers: auth(token), + body: JSON.stringify({ enabled: false }), + }); + expect(pause.status).toBe(200); + const pauseBody = (await pause.json()) as { + changed: boolean; + enabled: boolean; + items: Array<{ name: string; config: Record }>; + }; + expect(pauseBody.changed).toBe(true); + expect(pauseBody.enabled).toBe(false); + expect(pauseBody.items.find((entry) => entry.name === "stripe")?.config.enabled).toBe(false); + + const audits = await readAuditEntries(workspaceRoot, "ws_1"); + expect(audits).toHaveLength(1); + expect(audits[0]).toMatchObject({ + action: "mcp.disable", + summary: "Disabled MCP stripe", + }); + + const noOp = await fetch(`${base}/workspace/ws_1/mcp/stripe/enabled`, { + method: "POST", + headers: auth(token), + body: JSON.stringify({ enabled: false }), + }); + expect(noOp.status).toBe(200); + const noOpBody = (await noOp.json()) as { changed: boolean; enabled: boolean }; + expect(noOpBody).toMatchObject({ changed: false, enabled: false }); + + const auditsAfterNoOp = await readAuditEntries(workspaceRoot, "ws_1"); + expect(auditsAfterNoOp).toHaveLength(1); + + const events = await fetch(`${base}/workspace/ws_1/events?since=0`, { headers: auth(token) }); + expect(events.status).toBe(200); + const eventsBody = (await events.json()) as { items: Array<{ reason: string }> }; + expect(eventsBody.items.filter((event) => event.reason === "mcp")).toHaveLength(1); + }); + test("PATCH returns current state if the MCP app is removed during approval", async () => { const workspaceRoot = await createWorkspaceRoot(); const { base, token, hostToken } = startOpenworkServer({ workspaceRoot, approvalMode: "manual" }); diff --git a/apps/server/src/server.ts b/apps/server/src/server.ts index affa40611..420bd0f57 100644 --- a/apps/server/src/server.ts +++ b/apps/server/src/server.ts @@ -2583,17 +2583,25 @@ function createRoutes( return jsonResponse({ items }); }); - addRoute(routes, "PATCH", "/workspace/:id/mcp/:name", "client", async (ctx) => { + const updateMcpEnabled = async ( + ctx: RequestContext, + labels: { + action: (enabled: boolean) => string; + approvalVerb: (enabled: boolean) => string; + auditVerb: (enabled: boolean) => string; + }, + ) => { ensureWritable(config); requireClientScope(ctx, "collaborator"); const workspace = await resolveWorkspace(config, ctx.params.id); const name = String(ctx.params.name ?? "").trim(); const body = await readJsonBody(ctx.request); - if (typeof body.enabled !== "boolean") { + if (!body || typeof body !== "object" || Array.isArray(body) || typeof body.enabled !== "boolean") { throw new ApiError(400, "invalid_payload", "enabled must be a boolean"); } + const enabled = body.enabled; - const planned = await setMcpEnabled(workspace.path, name, body.enabled, { dryRun: true }); + const planned = await setMcpEnabled(workspace.path, name, enabled, { dryRun: true }); if (!planned.changed) { const items = await listMcp(workspace.path); return jsonResponse({ items, enabled: planned.enabled, changed: false }); @@ -2601,18 +2609,18 @@ function createRoutes( await requireApproval(ctx, { workspaceId: workspace.id, - action: "mcp.update", - summary: `${body.enabled ? "Resume" : "Pause"} MCP ${name}`, + action: labels.action(enabled), + summary: `${labels.approvalVerb(enabled)} MCP ${name}`, paths: [opencodeConfigPath(workspace.path)], }); let result: Awaited>; try { - result = await setMcpEnabled(workspace.path, name, body.enabled); + result = await setMcpEnabled(workspace.path, name, enabled); } catch (error) { if (error instanceof ApiError && error.status === 404 && error.code === "mcp_not_found") { const items = await listMcp(workspace.path); - return jsonResponse({ items, enabled: body.enabled, changed: false }); + return jsonResponse({ items, enabled, changed: false }); } throw error; } @@ -2621,9 +2629,9 @@ function createRoutes( id: shortId(), workspaceId: workspace.id, actor: ctx.actor ?? { type: "remote" }, - action: "mcp.update", + action: labels.action(enabled), target: "opencode.json", - summary: `${body.enabled ? "Resumed" : "Paused"} MCP ${name}`, + summary: `${labels.auditVerb(enabled)} MCP ${name}`, timestamp: Date.now(), }); emitReloadEvent(ctx.reloadEvents, workspace, "mcp", { @@ -2635,6 +2643,14 @@ function createRoutes( const items = await listMcp(workspace.path); return jsonResponse({ items, enabled: result.enabled, changed: result.changed }); + }; + + addRoute(routes, "PATCH", "/workspace/:id/mcp/:name", "client", async (ctx) => { + return updateMcpEnabled(ctx, { + action: () => "mcp.update", + approvalVerb: (enabled) => (enabled ? "Resume" : "Pause"), + auditVerb: (enabled) => (enabled ? "Resumed" : "Paused"), + }); }); addRoute(routes, "DELETE", "/workspace/:id/mcp/:name", "client", async (ctx) => { @@ -2674,47 +2690,13 @@ function createRoutes( return jsonResponse({ items, removed }); }); - // Toggle `enabled` on a workspace MCP. Strict body validation — `Boolean(body.enabled)` - // would silently disable on `{}` or coerce `"false"` to true. + // Compatibility route kept for clients that adopted #1562 before PATCH existed. addRoute(routes, "POST", "/workspace/:id/mcp/:name/enabled", "client", async (ctx) => { - ensureWritable(config); - requireClientScope(ctx, "collaborator"); - const workspace = await resolveWorkspace(config, ctx.params.id); - const name = ctx.params.name ?? ""; - const body = await readJsonBody(ctx.request); - if (!body || typeof body !== "object" || Array.isArray(body) || typeof body.enabled !== "boolean") { - throw new ApiError(400, "invalid_payload", "enabled must be a boolean"); - } - const enabled = body.enabled; - const action = enabled ? "mcp.enable" : "mcp.disable"; - const summary = `${enabled ? "Enable" : "Disable"} MCP ${name}`; - await requireApproval(ctx, { - workspaceId: workspace.id, - action, - summary, - paths: [opencodeConfigPath(workspace.path)], + return updateMcpEnabled(ctx, { + action: (enabled) => (enabled ? "mcp.enable" : "mcp.disable"), + approvalVerb: (enabled) => (enabled ? "Enable" : "Disable"), + auditVerb: (enabled) => (enabled ? "Enabled" : "Disabled"), }); - const updated = await setMcpEnabled(workspace.path, name, enabled); - if (!updated) { - throw new ApiError(404, "mcp_not_found", `MCP ${name} not found in workspace config`); - } - await recordAudit(workspace.path, { - id: shortId(), - workspaceId: workspace.id, - actor: ctx.actor ?? { type: "remote" }, - action, - target: "opencode.json", - summary: `${enabled ? "Enabled" : "Disabled"} MCP ${name}`, - timestamp: Date.now(), - }); - // ReloadTrigger.action only allows added/removed/updated, so toggle => "updated". - emitReloadEvent(ctx.reloadEvents, workspace, "mcp", { - type: "mcp", - name, - action: "updated", - }); - const items = await listMcp(workspace.path); - return jsonResponse({ items }); }); addRoute(routes, "DELETE", "/workspace/:id/mcp/:name/auth", "client", async (ctx) => {