{"record":{"id":"13d1857bbcc240ef","repo":"can1357/oh-my-pi","slug":"invalid-server-config-errors-join","errorCode":null,"errorMessage":"Invalid server config: ${errors.join(\"; \")}","messagePattern":"Invalid server config: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/mcp/config-writer.ts","lineNumber":120,"sourceCode":"}\n\n/**\n * Add an MCP server to a config file.\n * Validates the config before writing.\n *\n * @throws Error if server name already exists or validation fails\n */\nexport async function addMCPServer(filePath: string, name: string, config: MCPServerConfig): Promise<void> {\n\t// Validate server name\n\tconst nameError = validateServerName(name);\n\tif (nameError) {\n\t\tthrow new Error(nameError);\n\t}\n\n\t// Validate the config\n\tconst errors = validateServerConfig(name, config);\n\tif (errors.length > 0) {\n\t\tthrow new Error(`Invalid server config: ${errors.join(\"; \")}`);\n\t}\n\n\t// Serialize the read-modify-write under a per-file lock so a concurrent\n\t// mutation cannot overwrite this one (lost update). The lock also guards\n\t// against cross-process writers sharing the same config file.\n\tawait withConfigLock(filePath, async () => {\n\t\tconst existing = await readMCPConfigFile(filePath);\n\n\t\t// Check for duplicate name\n\t\tif (existing.mcpServers?.[name]) {\n\t\t\tthrow new Error(`Server \"${name}\" already exists in ${filePath}`);\n\t\t}\n\n\t\tconst updated: MCPConfigFile = {\n\t\t\t...existing,\n\t\t\tmcpServers: {\n\t\t\t\t...existing.mcpServers,\n\t\t\t\t[name]: config,","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/mcp/config-writer.ts#L102-L138","documentation":"addMCPServer() validates the server name and config shape before writing to the MCP config JSON file. This error is thrown when validateServerConfig() returns one or more problems: the config mixes \"command\" and \"url\", a stdio server lacks \"command\", an http/sse server lacks \"url\", or an unknown \"type\" was given. All problems are joined with \"; \" in the message so one throw reports everything wrong.","triggerScenarios":"Calling addMCPServer(filePath, name, config) (directly or via handleAddCommand or the MCP wizard's #handleWizardComplete) with: config containing both command and url; a stdio config with no command; type \"http\" or \"sse\" with no url; or type set to an unrecognized value.","commonSituations":"Copying a config entry from a different MCP client and leaving stale fields (e.g. adding a url while keeping command); hand-editing JSON and dropping the command field; misspelling type (e.g. \"streamable\" instead of \"http\"); a wizard/UI form submitting an empty command.","solutions":["Read the joined messages in the error; each names the server and the exact missing/conflicting field.","For stdio servers keep \"command\" (plus optional args/env) and delete any \"url\" field.","For http/sse servers set \"url\" and delete \"command\"/\"args\".","Set \"type\" to one of \"stdio\" (default), \"http\", or \"sse\".","Pre-validate with validateServerConfig(name, config) before calling addMCPServer in scripts/UI flows."],"exampleFix":"// before\nawait addMCPServer(cfgPath, \"docs\", { type: \"stdio\", command: \"npx\", url: \"https://mcp.example.com\" } as any);\n// after\nawait addMCPServer(cfgPath, \"docs\", { type: \"http\", url: \"https://mcp.example.com\" });","handlingStrategy":"validation","validationCode":"import { validateServerConfig } from \"@oh-my-pi/pi-coding-agent/mcp/config\";\nconst errors = validateServerConfig(name, config);\nif (errors.length > 0) throw new Error(errors.join(\"; \"));","typeGuard":"function isStdioConfig(c: { type?: string; command?: string; url?: string }): boolean {\n  const t = c.type ?? \"stdio\";\n  return t === \"stdio\" ? !!c.command && !c.url : false;\n}","tryCatchPattern":"try {\n  await addMCPServer(cfgPath, name, config);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"Invalid server config:\")) {\n    showValidationHint(e.message);\n    return;\n  }\n  throw e;\n}","preventionTips":["Always run validateServerConfig() before any write API call.","Keep transport fields disjoint: command OR url, never both.","Omit \"type\" for stdio instead of inventing new type strings.","Build configs through a typed schema/form rather than hand-written JSON."],"tags":["mcp","config","validation"],"backgroundTag":"mcp-server-config-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}