Files
CC-Switch/src/lib/schemas/common.ts
T
Jason bfc27349b3 feat(mcp): add SSE (Server-Sent Events) transport type support
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" }
}
2025-11-16 16:15:17 +08:00

99 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 解析失败",
});
}
});