{"record":{"id":"10930edd64c2ec23","repo":"coleam00/Archon","slug":"mcp-config-must-be-a-json-object-record-string-s","errorCode":null,"errorMessage":"MCP config must be a JSON object (Record<string, ServerConfig>): ${mcpPath}","messagePattern":"MCP config must be a JSON object \\(Record<string, ServerConfig>\\): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/providers/src/mcp/config.ts","lineNumber":154,"sourceCode":"    raw = await readFile(fullPath, 'utf-8');\n  } catch (err) {\n    const e = err as NodeJS.ErrnoException;\n    if (e.code === 'ENOENT') {\n      throw new Error(`MCP config file not found: ${mcpPath} (resolved to ${fullPath})`);\n    }\n    throw new Error(`Failed to read MCP config file: ${mcpPath} - ${e.message}`);\n  }\n\n  let parsed: Record<string, unknown>;\n  try {\n    parsed = JSON.parse(raw) as Record<string, unknown>;\n  } catch (parseErr) {\n    const detail = (parseErr as SyntaxError).message;\n    throw new Error(`MCP config file is not valid JSON: ${mcpPath} - ${detail}`);\n  }\n\n  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n    throw new Error(`MCP config must be a JSON object (Record<string, ServerConfig>): ${mcpPath}`);\n  }\n\n  const normalized = normalizeMcpConfig(parsed, mcpPath);\n  const { expanded, missingVars } = expandEnvVars(normalized, envSource);\n  const serverNames = Object.keys(expanded);\n  return { servers: expanded, serverNames, missingVars };\n}\n","sourceCodeStart":136,"sourceCodeEnd":162,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/providers/src/mcp/config.ts#L136-L162","documentation":"The top-level JSON document must be an object whose keys are server names. JSON files whose root is an array, a string, a number, or null have no server map to iterate, so loadMcpConfig throws with the config path and the expected Record<string, ServerConfig> shape.","triggerScenarios":"loadMcpConfig on files like [\"server1\", \"server2\"], \"npx ...\", or null (JSON 'null' literal), all of which parse fine but are not objects.","commonSituations":"A list of server names exported by a script instead of a map; an empty file containing just 'null'; pasting a CLI command into the config; a generator serializing an array of server descriptors.","solutions":["Wrap the content in an object keyed by server name, or use {\"mcpServers\": {...}}.","Convert an array of server descriptors into a map (key by the server's name field).","For zero servers, write {} rather than null or []."],"exampleFix":"// before\n[{\"name\": \"fs\", \"command\": \"npx\"}]\n// after\n{\"fs\": {\"command\": \"npx\"}}","handlingStrategy":"type-guard","validationCode":"const parsed: unknown = JSON.parse(readFileSync(mcpPath, 'utf-8'));\nif (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n  throw new Error(`${mcpPath} must be a JSON object keyed by server name`);\n}","typeGuard":"const isJsonObject = (v: unknown): v is Record<string, unknown> =>\n  typeof v === 'object' && v !== null && !Array.isArray(v);","tryCatchPattern":null,"preventionTips":["Root the config in an object: {} for empty, keyed servers otherwise.","Fix generators/scripts that serialize arrays or null at the top level.","Type the parsed value as unknown and narrow before passing it on."],"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"}