{"record":{"id":"032f73c524a30d82","repo":"coleam00/Archon","slug":"mcp-config-field-mcpservers-must-be-a-json-objec","errorCode":null,"errorMessage":"MCP config field \"mcpServers\" must be a JSON object: ${mcpPath}","messagePattern":"MCP config field \"mcpServers\" must be a JSON object: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/providers/src/mcp/config.ts","lineNumber":118,"sourceCode":"\nfunction normalizeMcpConfig(\n  parsed: Record<string, unknown>,\n  mcpPath: string\n): Record<string, unknown> {\n  const keys = Object.keys(parsed);\n  if (!keys.includes('mcpServers')) {\n    return parsed;\n  }\n\n  if (keys.length > 1) {\n    throw new Error(\n      `MCP config cannot mix top-level \"mcpServers\" with other keys: ${mcpPath}. Use either a direct server map or { \"mcpServers\": { ... } }.`\n    );\n  }\n\n  const servers = parsed.mcpServers;\n  if (typeof servers !== 'object' || servers === null || Array.isArray(servers)) {\n    throw new Error(`MCP config field \"mcpServers\" must be a JSON object: ${mcpPath}`);\n  }\n\n  return servers as Record<string, unknown>;\n}\n\n/**\n * Load MCP server config from a JSON file and expand environment variables.\n */\nexport async function loadMcpConfig(\n  mcpPath: string,\n  cwd: string,\n  envSource: EnvSource = process.env\n): Promise<LoadedMcpConfig> {\n  const fullPath = isAbsolute(mcpPath) ? mcpPath : resolve(cwd, mcpPath);\n\n  let raw: string;\n  try {\n    raw = await readFile(fullPath, 'utf-8');","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/providers/src/mcp/config.ts#L100-L136","documentation":"When a config uses the {\"mcpServers\": ...} wrapper form, the value of mcpServers must be a JSON object mapping server names to server objects. A null, array, or primitive value means there are no valid server definitions, so normalizeMcpConfig throws including the config path.","triggerScenarios":"loadMcpConfig on {\"mcpServers\": null}, {\"mcpServers\": []}, or {\"mcpServers\": \"...\"} (alone as the only key).","commonSituations":"A template left mcpServers empty/null; a script serialized an empty list instead of an object; hand-deleting all servers left the wrong JSON shape; YAML empty value became null.","solutions":["Replace null/array with an object: {\"mcpServers\": {}} for zero servers.","Add at least one server definition under mcpServers.","If the file was meant to disable MCP entirely, remove the mcpServers key and point the caller at a different config."],"exampleFix":"// before\n{\"mcpServers\": null}\n// after\n{\"mcpServers\": {\"fs\": {\"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]}}}","handlingStrategy":"validation","validationCode":"if ('mcpServers' in cfg) {\n  const s = cfg.mcpServers;\n  if (typeof s !== 'object' || s === null || Array.isArray(s)) {\n    throw new Error('mcpServers must be an object');\n  }\n}","typeGuard":"const isServerMap = (v: unknown): v is Record<string, Record<string, unknown>> =>\n  typeof v === 'object' && v !== null && !Array.isArray(v) &&\n  Object.values(v).every((x) => typeof x === 'object' && x !== null && !Array.isArray(x));","tryCatchPattern":null,"preventionTips":["Use {} for an empty server set, never null or [].","Generate configs from templates that emit object literals, not nullable values.","Validate with a schema: mcpServers?: { [name: string]: ServerConfig }."],"tags":["config","validation","mcp","json"],"backgroundTag":"mcp-config-schema-invalid","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}