{"record":{"id":"ae6b52a0e15a1a55","repo":"farion1231/cc-switch","slug":"stdio-mcp-command","errorCode":null,"errorMessage":"stdio 类型的 MCP 服务器必须包含 command 字段","messagePattern":"stdio 类型的 MCP 服务器必须包含 command 字段","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/utils/tomlUtils.ts","lineNumber":113,"sourceCode":"};\n\n/**\n * 规范化服务器配置对象为 McpServer 格式\n * 保留所有字段（包括扩展字段如 timeout_ms）\n */\nfunction normalizeServerConfig(config: any): McpServerSpec {\n  if (!config || typeof config !== \"object\") {\n    throw new Error(\"服务器配置必须是对象\");\n  }\n\n  const type = (config.type as string) || \"stdio\";\n\n  // 已知字段列表（用于后续排除）\n  const knownFields = new Set<string>();\n\n  if (type === \"stdio\") {\n    if (!config.command || typeof config.command !== \"string\") {\n      throw new Error(\"stdio 类型的 MCP 服务器必须包含 command 字段\");\n    }\n\n    const server: McpServerSpec = {\n      type: \"stdio\",\n      command: config.command,\n    };\n    knownFields.add(\"type\");\n    knownFields.add(\"command\");\n\n    // 可选字段\n    if (config.args && Array.isArray(config.args)) {\n      server.args = config.args.map((arg: any) => String(arg));\n      knownFields.add(\"args\");\n    }\n    if (config.env && typeof config.env === \"object\") {\n      const env: Record<string, string> = {};\n      for (const [k, v] of Object.entries(config.env)) {\n        env[k] = String(v);","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/farion1231/cc-switch/blob/a2e22f330273a5b6ffa87cb8b82b624601bac562/src/utils/tomlUtils.ts#L95-L131","documentation":"A server entry with no type defaults to 'stdio', and stdio servers must carry a string command ('stdio MCP server must include command field'). The classic trigger is a remote server config pasted without type = \"http\"/\"sse\": its url makes the direct-config branch fire, the type defaults to stdio, and command is absent.","triggerScenarios":"Entry with url but no type line; entry with only args/env fields; command set to a non-string TOML value (e.g. a bare number).","commonSituations":"Copying a remote server snippet that omits the type line; accidentally deleting or renaming the command key.","solutions":["For remote servers add type = \"http\" (or \"sse\") so the url branch is taken","For local servers add a string command, e.g. command = \"npx\"","Ensure command is a quoted string, not a bare TOML value"],"exampleFix":"# before (throws: defaults to stdio, no command)\nurl = \"https://mcp.example.com/sse\"\n\n# after\ntype = \"sse\"\nurl = \"https://mcp.example.com/sse\"","handlingStrategy":"validation","validationCode":"function inferServerType(entry: Record<string, unknown>): \"stdio\" | \"http\" | \"sse\" | null {\n  const type = typeof entry.type === \"string\" ? entry.type : \"stdio\";\n  if (type === \"stdio\") {\n    return typeof entry.command === \"string\" && entry.command ? \"stdio\" : null;\n  }\n  if (type === \"http\" || type === \"sse\") {\n    return typeof entry.url === \"string\" && entry.url ? type : null;\n  }\n  return null;\n}\n\nconst type = inferServerType(entry);\nif (!type) {\n  setImportError(\"Remote servers need type + url; local servers need command\");\n}","typeGuard":"function isStdioEntry(e: Record<string, unknown>): e is { type: \"stdio\"; command: string } {\n  const type = typeof e.type === \"string\" ? e.type : \"stdio\";\n  return type === \"stdio\" && typeof e.command === \"string\" && e.command.length > 0;\n}","tryCatchPattern":"try {\n  const server = tomlToMcpServer(tomlText);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"必须包含 command 字段\")) {\n    setImportError(\"Local stdio servers need a command; remote servers need type = \\\"http\\\"/\\\"sse\\\" plus url\");\n  } else {\n    throw e;\n  }\n}","preventionTips":["Always set type explicitly when the server is remote","Remember type defaults to stdio when omitted","Quote command values as strings"],"tags":["toml","mcp","stdio","validation"],"backgroundTag":null,"analyzedSha":"a2e22f330273a5b6ffa87cb8b82b624601bac562","analyzedAt":"2026-08-16T03:46:07.889Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}