mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-06-16 13:34:04 +08:00
Fixed an issue where custom/extension fields (e.g., timeout_ms, retry_count) were silently dropped when editing Codex MCP server configurations in TOML format. Root cause: The TOML parser functions only extracted known fields (type, command, args, env, cwd, url, headers), discarding any additional fields during normalization. Changes: - mcpServerToToml: Now uses spread operator to copy all fields before stringification - normalizeServerConfig: Added logic to preserve unknown fields after processing known ones - Both stdio and http server types now retain custom configuration fields This fix enables forward compatibility with future MCP protocol extensions and allows users to add custom configurations without code changes.
96 lines
2.5 KiB
TypeScript
96 lines
2.5 KiB
TypeScript
import { z } from "zod";
|
||
import { validateToml, tomlToMcpServer } from "@/utils/tomlUtils";
|
||
|
||
/**
|
||
* 解析 JSON 语法错误,返回更友好的位置信息。
|
||
*/
|
||
function parseJsonError(error: unknown): string {
|
||
if (!(error instanceof SyntaxError)) {
|
||
return "JSON 格式错误";
|
||
}
|
||
|
||
const message = error.message || "JSON 解析失败";
|
||
|
||
// Chrome/V8: "Unexpected token ... in JSON at position 123"
|
||
const positionMatch = message.match(/at position (\d+)/i);
|
||
if (positionMatch) {
|
||
const position = parseInt(positionMatch[1], 10);
|
||
return `JSON 格式错误(位置:${position})`;
|
||
}
|
||
|
||
// Firefox: "JSON.parse: unexpected character at line 1 column 23"
|
||
const lineColumnMatch = message.match(/line (\d+) column (\d+)/i);
|
||
if (lineColumnMatch) {
|
||
const line = lineColumnMatch[1];
|
||
const column = lineColumnMatch[2];
|
||
return `JSON 格式错误:第 ${line} 行,第 ${column} 列`;
|
||
}
|
||
|
||
return `JSON 格式错误:${message}`;
|
||
}
|
||
|
||
/**
|
||
* 通用的 JSON 配置文本校验:
|
||
* - 非空
|
||
* - 可解析且为对象(非数组)
|
||
*/
|
||
export const jsonConfigSchema = z
|
||
.string()
|
||
.min(1, "配置不能为空")
|
||
.superRefine((value, ctx) => {
|
||
try {
|
||
const obj = JSON.parse(value);
|
||
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
||
ctx.addIssue({
|
||
code: z.ZodIssueCode.custom,
|
||
message: "需为单个对象配置",
|
||
});
|
||
}
|
||
} catch (e) {
|
||
ctx.addIssue({
|
||
code: z.ZodIssueCode.custom,
|
||
message: parseJsonError(e),
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 通用的 TOML 配置文本校验:
|
||
* - 允许为空(由上层业务决定是否必填)
|
||
* - 语法与结构有效
|
||
* - 针对 stdio/http 的必填字段(command/url)进行提示
|
||
*/
|
||
export const tomlConfigSchema = z.string().superRefine((value, ctx) => {
|
||
const err = validateToml(value);
|
||
if (err) {
|
||
ctx.addIssue({
|
||
code: z.ZodIssueCode.custom,
|
||
message: `TOML 无效:${err}`,
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (!value.trim()) return;
|
||
|
||
try {
|
||
const server = tomlToMcpServer(value);
|
||
if (server.type === "stdio" && !server.command?.trim()) {
|
||
ctx.addIssue({
|
||
code: z.ZodIssueCode.custom,
|
||
message: "stdio 类型需填写 command",
|
||
});
|
||
}
|
||
if (server.type === "http" && !server.url?.trim()) {
|
||
ctx.addIssue({
|
||
code: z.ZodIssueCode.custom,
|
||
message: "http 类型需填写 url",
|
||
});
|
||
}
|
||
} catch (e: any) {
|
||
ctx.addIssue({
|
||
code: z.ZodIssueCode.custom,
|
||
message: e?.message || "TOML 解析失败",
|
||
});
|
||
}
|
||
});
|