Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/cli/ui/Wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ function McpArgsStep({
}) {
const [value, setValue] = useState("");
const [pendingCreate, setPendingCreate] = useState<string | null>(null);
const summary = mcpArgsSummaryFor(entry);
const note = mcpArgsNoteFor(entry);

useInput((input, key) => {
if (!pendingCreate) return;
Expand Down Expand Up @@ -597,10 +599,10 @@ function McpArgsStep({
return (
<StepFrame title={t("wizard.mcpArgsTitle", { name: entry.name })} step={2} total={3}>
<Box flexDirection="column">
<Text>{entry.summary}</Text>
{entry.note ? (
<Text>{summary}</Text>
{note ? (
<Box marginTop={1}>
<Text dimColor>{entry.note}</Text>
<Text dimColor>{note}</Text>
</Box>
) : null}
<Box marginTop={1}>
Expand Down Expand Up @@ -726,6 +728,16 @@ function mcpItems(): SelectItem<string>[] {
});
}

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";
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/EN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ export interface TranslationSchema {
mcpArgsRequiredParam: string;
mcpArgsEmpty: string;
mcpArgsNotADir: string;
mcpArgsFilesystemSummary: string;
mcpArgsFilesystemNote: string;
themeTitle: string;
themeSubtitle: string;
themeSampleHeading: string;
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ export const zhCN: TranslationSchema = {
mcpArgsRequiredParam: "必填参数:",
mcpArgsEmpty: "{name} 需要一个值 — 不能为空。",
mcpArgsNotADir: "{path} 存在但不是目录。",
mcpArgsFilesystemSummary: "在沙盒目录内读写和搜索文件",
mcpArgsFilesystemNote: "该目录是严格沙盒,服务器会拒绝访问目录外的内容",
reviewTitle: "确认保存",
reviewLabelApiKey: "API key",
reviewLabelLanguage: "语言",
Expand Down
28 changes: 27 additions & 1 deletion tests/wizard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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: "<dir>",
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 });
Expand Down