{"record":{"id":"daf7f7a3ce4c59d7","repo":"coleam00/Archon","slug":"mcp-config-cannot-mix-top-level-mcpservers-with","errorCode":null,"errorMessage":"MCP config cannot mix top-level \"mcpServers\" with other keys: ${mcpPath}. Use either a direct server map or { \"mcpServers\": { ... } }.","messagePattern":"MCP config cannot mix top-level \"mcpServers\" with other keys: (.+?)\\. Use either a direct server map or (.+?) \\}\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/providers/src/mcp/config.ts","lineNumber":111,"sourceCode":"        `${serverName}.headers`\n      );\n    }\n    result[serverName] = server;\n  }\n  return { expanded: result, missingVars };\n}\n\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,","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/providers/src/mcp/config.ts#L93-L129","documentation":"normalizeMcpConfig accepts two shapes: a direct server map, or exactly {\"mcpServers\": {...}}. If the parsed file has a top-level mcpServers key plus any other keys, the intended shape is ambiguous, so it throws instead of guessing. This catches configs that mix the Claude Desktop wrapper format with stray top-level keys.","triggerScenarios":"loadMcpConfig on a file like {\"mcpServers\": {...}, \"notes\": \"...\"} or {\"mcpServers\": {...}, \"version\": 1}.","commonSituations":"Starting from a Claude Desktop config and adding custom metadata keys; merge tools combining two config files; adding a \"comment\" key at top level for documentation.","solutions":["Delete the extra top-level keys so only \"mcpServers\" remains.","Or drop the \"mcpServers\" wrapper and place servers directly at top level.","Move metadata into a sibling file or into per-server fields instead of top level."],"exampleFix":"// before\n{\"mcpServers\": {\"fs\": {\"command\": \"npx\"}}, \"version\": 1}\n// after\n{\"mcpServers\": {\"fs\": {\"command\": \"npx\"}}}","handlingStrategy":"validation","validationCode":"const keys = Object.keys(cfg);\nif (keys.includes('mcpServers') && keys.length > 1) {\n  throw new Error('config must be either a direct server map or only {mcpServers: {...}}');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pick one format (wrapper or direct map) per project and stick to it.","Keep comments/metadata out of machine-consumed JSON files.","Check any merge tool output for accidental top-level key mixing."],"tags":["config","validation","mcp","ambiguity"],"backgroundTag":"mcp-config-ambiguous-format","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}