From 419ef8a58f5fee8ad632db83c6683898bc64c64f Mon Sep 17 00:00:00 2001 From: Star-Star66 <289872070+Star-Star66@users.noreply.github.com> Date: Sat, 13 Jun 2026 14:06:10 +0800 Subject: [PATCH] Fix MCP hot-reload feedback --- dashboard/src/i18n/en.ts | 5 +++-- dashboard/src/i18n/zh-CN.ts | 5 +++-- dashboard/src/lib/mcp-spec.ts | 8 ++++++++ dashboard/src/panels/mcp.ts | 23 ++++++++++++++++++----- tests/dashboard-mcp-spec.test.ts | 22 +++++++++++++++++++++- 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/dashboard/src/i18n/en.ts b/dashboard/src/i18n/en.ts index 7fb9894..1d74629 100644 --- a/dashboard/src/i18n/en.ts +++ b/dashboard/src/i18n/en.ts @@ -443,7 +443,8 @@ export const en = { specPlaceholder: "spec — e.g. fs=npx -y @modelcontextprotocol/...", saved: "saved", savedRestart: "saved — restart carboncode code to bridge this server", - removed: "removed — restart to drop the live bridge", + removed: "removed + disconnected", + removedRestart: "removed — restart to drop the live bridge", removeConfirm: "Remove MCP spec from config?\n\n{spec}", noServers: "No MCP servers in this session.", tools: "tools", @@ -495,7 +496,7 @@ export const en = { marketplaceEnvHint: "Set these in your shell before next `carboncode code` so the bridged server can authenticate.", marketplaceRestartHint: - "Spec written to ~/.carboncode/config.json. Restart `carboncode code` to bridge the server (live hot-reload is on the roadmap).", + "Saved in ~/.carboncode/config.json but not bridged in this session. Restart `carboncode code` if live reload is unavailable or failed.", }, memory: { loading: "loading memory…", diff --git a/dashboard/src/i18n/zh-CN.ts b/dashboard/src/i18n/zh-CN.ts index 1ffe49e..2ef2cd0 100644 --- a/dashboard/src/i18n/zh-CN.ts +++ b/dashboard/src/i18n/zh-CN.ts @@ -418,7 +418,8 @@ export const zhCN = { specPlaceholder: "规格 — 例如 fs=npx -y @modelcontextprotocol/...", saved: "已保存", savedRestart: "已保存 — 重启 carboncode code 以桥接此服务器", - removed: "已移除 — 重启以断开实时桥接", + removed: "已移除并断开桥接", + removedRestart: "已移除 — 重启以断开实时桥接", removeConfirm: "从配置中移除 MCP 规格?\n\n{spec}", noServers: "此会话中无 MCP 服务器。", tools: "个工具", @@ -470,7 +471,7 @@ export const zhCN = { marketplaceEnvHint: "下次启动 `carboncode code` 之前在 shell 里设好,桥接的服务器才能正常鉴权。", marketplaceRestartHint: - "已写入 ~/.carboncode/config.json。重启 `carboncode code` 后服务器才会真正桥接(热重载在路线图上)。", + "已写入 ~/.carboncode/config.json,但当前会话尚未桥接。若实时重载不可用或失败,请重启 `carboncode code`。", }, memory: { loading: "加载记忆…", diff --git a/dashboard/src/lib/mcp-spec.ts b/dashboard/src/lib/mcp-spec.ts index 4f2c0ac..87daa74 100644 --- a/dashboard/src/lib/mcp-spec.ts +++ b/dashboard/src/lib/mcp-spec.ts @@ -44,3 +44,11 @@ export function mcpSpecCommand(spec: unknown): string { const eq = text.indexOf("="); return eq > 0 ? text.slice(eq + 1) : text; } + +export function mcpMutationNeedsRestart(result: { requiresRestart?: boolean }): boolean { + return result.requiresRestart === true; +} + +export function shouldShowMcpRestartHint(installed: boolean, bridged: boolean): boolean { + return installed && !bridged; +} diff --git a/dashboard/src/panels/mcp.ts b/dashboard/src/panels/mcp.ts index 61187f5..73f38cd 100644 --- a/dashboard/src/panels/mcp.ts +++ b/dashboard/src/panels/mcp.ts @@ -4,9 +4,11 @@ import { api } from "../lib/api.js"; import { fmtNum } from "../lib/format.js"; import { html } from "../lib/html.js"; import { - normalizeMcpSpec, + mcpMutationNeedsRestart, mcpSpecCommand as specCommand, mcpSpecLabel as specLabel, + normalizeMcpSpec, + shouldShowMcpRestartHint, } from "../lib/mcp-spec.js"; interface McpServer { @@ -216,7 +218,7 @@ export function McpPanel() { method: "POST", body: { spec: newSpec.trim() }, }); - setInfo(r.requiresRestart ? t("mcp.savedRestart") : t("mcp.saved")); + setInfo(mcpMutationNeedsRestart(r) ? t("mcp.savedRestart") : t("mcp.saved")); setTimeout(() => setInfo(null), 4000); setNewSpec(""); await load(); @@ -232,8 +234,13 @@ export function McpPanel() { if (!confirm(t("mcp.removeConfirm", { spec }))) return; setBusy(true); try { - await api("/mcp/specs", { method: "DELETE", body: { spec } }); - setInfo(t("mcp.removed")); + const r = await api<{ requiresRestart?: boolean }>("/mcp/specs", { + method: "DELETE", + body: { spec }, + }); + setInfo( + mcpMutationNeedsRestart(r) ? t("mcp.removedRestart") : t("mcp.removed"), + ); setTimeout(() => setInfo(null), 4000); await load(); } catch (err) { @@ -396,6 +403,10 @@ export function McpPanel() { const spec = specForEntry(openRegistry); return spec && (specs ?? []).includes(spec) ? spec : null; })(), + installedBridged: (() => { + const spec = specForEntry(openRegistry); + return !!spec && data.servers.some((server) => server.spec === spec); + })(), onInstall: () => installFromRegistry(openRegistry), onUninstall: (spec: string) => removeSpec(spec), onClose: () => setOpenRegistry(null), @@ -619,6 +630,7 @@ interface RegistryDetailArgs { entry: RegistryEntryDto; busy: boolean; installedSpec: string | null; + installedBridged: boolean; onInstall: () => void; onUninstall: (spec: string) => void; onClose: () => void; @@ -628,6 +640,7 @@ function renderRegistryDetail({ entry, busy, installedSpec, + installedBridged, onInstall, onUninstall, onClose, @@ -713,7 +726,7 @@ function renderRegistryDetail({ } ${ - installed + shouldShowMcpRestartHint(installed, installedBridged) ? html`