diff --git a/src/cli/ui/Wizard.tsx b/src/cli/ui/Wizard.tsx index 9706f8b..5d6e301 100644 --- a/src/cli/ui/Wizard.tsx +++ b/src/cli/ui/Wizard.tsx @@ -549,6 +549,8 @@ function McpArgsStep({ }) { const [value, setValue] = useState(""); const [pendingCreate, setPendingCreate] = useState(null); + const summary = mcpArgsSummaryFor(entry); + const note = mcpArgsNoteFor(entry); useInput((input, key) => { if (!pendingCreate) return; @@ -597,10 +599,10 @@ function McpArgsStep({ return ( - {entry.summary} - {entry.note ? ( + {summary} + {note ? ( - {entry.note} + {note} ) : null} @@ -726,6 +728,16 @@ function mcpItems(): SelectItem[] { }); } +export function mcpArgsSummaryFor(entry: CatalogEntry): string { + if (entry.name === "filesystem") return t("wizard.mcpArgsFilesystemSummary"); + return entry.summary; +} + +export function mcpArgsNoteFor(entry: CatalogEntry): string | undefined { + if (entry.name === "filesystem") return t("wizard.mcpArgsFilesystemNote"); + return entry.note; +} + function placeholderFor(entry: CatalogEntry): string { if (entry.name === "filesystem") return "e.g. /tmp/carboncode-sandbox"; if (entry.name === "sqlite") return "e.g. ./notes.sqlite"; diff --git a/src/i18n/EN.ts b/src/i18n/EN.ts index 0ac9539..208c095 100644 --- a/src/i18n/EN.ts +++ b/src/i18n/EN.ts @@ -464,6 +464,8 @@ export const EN: TranslationSchema = { mcpArgsRequiredParam: "Required parameter: ", mcpArgsEmpty: "{name} needs a value — got an empty string.", mcpArgsNotADir: "{path} exists but is not a directory.", + mcpArgsFilesystemSummary: "read/write/search files inside a sandboxed directory", + mcpArgsFilesystemNote: "the directory is a hard sandbox — the server refuses access outside it", reviewTitle: "Ready to save", reviewLabelApiKey: "API key", reviewLabelLanguage: "Language", diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 8282c0a..dd21386 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -301,6 +301,8 @@ export interface TranslationSchema { mcpArgsRequiredParam: string; mcpArgsEmpty: string; mcpArgsNotADir: string; + mcpArgsFilesystemSummary: string; + mcpArgsFilesystemNote: string; themeTitle: string; themeSubtitle: string; themeSampleHeading: string; diff --git a/src/i18n/zh-CN.ts b/src/i18n/zh-CN.ts index 7afc2e3..2c27a24 100644 --- a/src/i18n/zh-CN.ts +++ b/src/i18n/zh-CN.ts @@ -450,6 +450,8 @@ export const zhCN: TranslationSchema = { mcpArgsRequiredParam: "必填参数:", mcpArgsEmpty: "{name} 需要一个值 — 不能为空。", mcpArgsNotADir: "{path} 存在但不是目录。", + mcpArgsFilesystemSummary: "在沙盒目录内读写和搜索文件", + mcpArgsFilesystemNote: "该目录是严格沙盒,服务器会拒绝访问目录外的内容", reviewTitle: "确认保存", reviewLabelApiKey: "API key", reviewLabelLanguage: "语言", diff --git a/tests/wizard.test.tsx b/tests/wizard.test.tsx index 552e2b4..6783c17 100644 --- a/tests/wizard.test.tsx +++ b/tests/wizard.test.tsx @@ -3,7 +3,13 @@ import { render } from "ink-testing-library"; import React from "react"; import { afterEach, describe, expect, it } from "vitest"; -import { Wizard, buildSpec, validateDeepSeekApiKey } from "../src/cli/ui/Wizard.js"; +import { + Wizard, + buildSpec, + mcpArgsNoteFor, + mcpArgsSummaryFor, + validateDeepSeekApiKey, +} from "../src/cli/ui/Wizard.js"; import { setLanguageRuntime } from "../src/i18n/index.js"; import { parseMcpSpec } from "../src/mcp/spec.js"; @@ -66,6 +72,26 @@ describe("Wizard — first-launch language picker", () => { }); }); +describe("Wizard — localized MCP argument copy", () => { + afterEach(() => { + setLanguageRuntime("EN"); + }); + + it("localizes the filesystem argument step copy in zh-CN", () => { + setLanguageRuntime("zh-CN"); + const entry = { + name: "filesystem", + summary: "read/write/search files inside a sandboxed directory", + package: "@modelcontextprotocol/server-filesystem", + userArgs: "", + note: "the directory is a hard sandbox — the server refuses access outside it", + }; + + expect(mcpArgsSummaryFor(entry)).toBe("在沙盒目录内读写和搜索文件"); + expect(mcpArgsNoteFor(entry)).toBe("该目录是严格沙盒,服务器会拒绝访问目录外的内容"); + }); +}); + describe("Wizard API-key validation", () => { it("accepts a key when DeepSeek auth check succeeds", async () => { const fetcher = async () => new Response(JSON.stringify({ data: [] }), { status: 200 });