Files
cc-switch/src/utils/errorUtils.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

109 lines
3.5 KiB
TypeScript

/**
* 从各种错误对象中提取错误信息
* @param error 错误对象
* @returns 提取的错误信息字符串
*/
export const extractErrorMessage = (error: unknown): string => {
if (!error) return "";
if (typeof error === "string") {
return error;
}
if (error instanceof Error && error.message.trim()) {
return error.message;
}
if (typeof error === "object") {
const errObject = error as Record<string, unknown>;
const candidate = errObject.message ?? errObject.error ?? errObject.detail;
if (typeof candidate === "string" && candidate.trim()) {
return candidate;
}
const payload = errObject.payload;
if (typeof payload === "string" && payload.trim()) {
return payload;
}
if (payload && typeof payload === "object") {
const payloadObj = payload as Record<string, unknown>;
const payloadCandidate =
payloadObj.message ?? payloadObj.error ?? payloadObj.detail;
if (typeof payloadCandidate === "string" && payloadCandidate.trim()) {
return payloadCandidate;
}
}
}
return "";
};
/**
* 将已知的 MCP 相关后端错误(通常为中文硬编码)映射为 i18n 文案
* 采用包含式匹配,尽量稳健地覆盖不同上下文的相似消息。
* 若无法识别,返回空字符串以便调用方回退到原始 detail 或默认 i18n。
*/
export const translateMcpBackendError = (
message: string,
t: (key: string, opts?: any) => string,
): string => {
if (!message) return "";
const msg = String(message).trim();
// 基础字段与结构校验相关
if (msg.includes("MCP 服务器 ID 不能为空")) {
return t("mcp.error.idRequired");
}
if (
msg.includes("MCP 服务器定义必须为 JSON 对象") ||
msg.includes("MCP 服务器条目必须为 JSON 对象") ||
msg.includes("MCP 服务器条目缺少 server 字段") ||
msg.includes("MCP 服务器 server 字段必须为 JSON 对象") ||
msg.includes("MCP 服务器连接定义必须为 JSON 对象") ||
msg.includes("MCP 服务器 '" /* 不是对象 */) ||
msg.includes("不是对象") ||
msg.includes("服务器配置必须是对象") ||
msg.includes("MCP 服务器 name 必须为字符串") ||
msg.includes("MCP 服务器 description 必须为字符串") ||
msg.includes("MCP 服务器 homepage 必须为字符串") ||
msg.includes("MCP 服务器 docs 必须为字符串") ||
msg.includes("MCP 服务器 tags 必须为字符串数组") ||
msg.includes("MCP 服务器 enabled 必须为布尔值")
) {
return t("mcp.error.jsonInvalid");
}
if (msg.includes("MCP 服务器 type 必须是")) {
return t("mcp.error.jsonInvalid");
}
// 必填字段
if (
msg.includes("stdio 类型的 MCP 服务器缺少 command 字段") ||
msg.includes("必须包含 command 字段")
) {
return t("mcp.error.commandRequired");
}
if (
msg.includes("http 类型的 MCP 服务器缺少 url 字段") ||
msg.includes("sse 类型的 MCP 服务器缺少 url 字段") ||
msg.includes("必须包含 url 字段") ||
msg === "URL 不能为空"
) {
return t("mcp.wizard.urlRequired");
}
// 文件解析/序列化
if (
msg.includes("解析 ~/.claude.json 失败") ||
msg.includes("解析 config.toml 失败") ||
msg.includes("无法识别的 TOML 格式") ||
msg.includes("TOML 内容不能为空")
) {
return t("mcp.error.tomlInvalid");
}
if (msg.includes("序列化 config.toml 失败")) {
return t("mcp.error.tomlInvalid");
}
return "";
};