mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 08:44:41 +08:00
bfc27349b3
Add comprehensive support for SSE transport type to MCP server configuration,
enabling real-time streaming connections alongside existing stdio and http types.
Backend Changes:
- Add SSE type validation in mcp.rs validate_server_spec()
- Extend Codex TOML import/export to handle SSE servers
- Update claude_mcp.rs legacy API for backward compatibility
- Unify http/sse handling in json_server_to_toml_table()
Frontend Changes:
- Extend McpServerSpec type definition to include "sse"
- Add SSE radio button to configuration wizard UI
- Update wizard form logic to handle SSE url and headers
- Add SSE validation in McpFormModal submission
Validation & Error Handling:
- Add SSE support in useMcpValidation hook (TOML/JSON)
- Extend tomlUtils normalizeServerConfig for SSE parsing
- Update Zod schemas (common.ts, mcp.ts) with SSE enum
- Add SSE error message mapping in errorUtils
Internationalization:
- Add "typeSse" translations (zh: "sse", en: "sse")
Tests:
- Add SSE validation test cases in useMcpValidation.test.tsx
SSE Configuration Format:
{
"type": "sse",
"url": "https://api.example.com/sse",
"headers": { "Authorization": "Bearer token" }
}
99 lines
2.6 KiB
TypeScript
99 lines
2.6 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/sse 的必填字段(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.type === "sse") &&
|
||
!server.url?.trim()
|
||
) {
|
||
ctx.addIssue({
|
||
code: z.ZodIssueCode.custom,
|
||
message: `${server.type} 类型需填写 url`,
|
||
});
|
||
}
|
||
} catch (e: any) {
|
||
ctx.addIssue({
|
||
code: z.ZodIssueCode.custom,
|
||
message: e?.message || "TOML 解析失败",
|
||
});
|
||
}
|
||
});
|