{"record":{"id":"abf14b29bf02e79a","repo":"affaan-m/ECC","slug":"mcp-config-must-include-an-mcpservers-object","errorCode":null,"errorMessage":"MCP config must include an mcpServers object","messagePattern":"MCP config must include an mcpServers object","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/mcp-config.js","lineNumber":19,"sourceCode":"'use strict';\n\nfunction parseDisabledMcpServers(value) {\n  return [...new Set(\n    String(value || '')\n      .split(',')\n      .map((entry) => entry.trim())\n      .filter(Boolean)\n  )];\n}\n\nfunction filterMcpConfig(config, disabledServerNames = []) {\n  if (!config || typeof config !== 'object' || Array.isArray(config)) {\n    throw new Error('MCP config must be a JSON object');\n  }\n\n  const servers = config.mcpServers;\n  if (!servers || typeof servers !== 'object' || Array.isArray(servers)) {\n    throw new Error('MCP config must include an mcpServers object');\n  }\n\n  const disabled = new Set(parseDisabledMcpServers(disabledServerNames));\n  if (disabled.size === 0) {\n    return {\n      config: {\n        ...config,\n        mcpServers: { ...servers },\n      },\n      removed: [],\n    };\n  }\n\n  const nextServers = {};\n  const removed = [];\n\n  for (const [name, serverConfig] of Object.entries(servers)) {\n    if (disabled.has(name)) {","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/mcp-config.js#L1-L37","documentation":"filterMcpConfig expects the config object to contain an mcpServers key holding a plain object of named server entries. A config without mcpServers, or where mcpServers is an array/null/primitive, has nothing to filter and is treated as malformed.","triggerScenarios":"Passing a config object lacking mcpServers entirely, or where config.mcpServers is an array, null, or a string (e.g. a config file with the servers under a different key).","commonSituations":"Hand-edited mcp-servers.json where the mcpServers block was renamed or deleted; merging configs that flattened mcpServers; older config formats using a different schema key.","solutions":["Add an mcpServers object to the config, even if empty: { \"mcpServers\": {} }.","Rename the top-level key to mcpServers to match the expected schema.","Verify the config file matches the current schema version of the tool.","Ensure the value passed is the whole config object, not just a nested section."],"exampleFix":"// before\n{ \"servers\": { \"context7\": {} } }\n// after\n{ \"mcpServers\": { \"context7\": {} } }","handlingStrategy":"validation","validationCode":"if (!config || typeof config !== 'object' || Array.isArray(config) ||\n    !config.mcpServers || typeof config.mcpServers !== 'object' || Array.isArray(config.mcpServers)) {\n  throw new Error('Config must contain a plain mcpServers object')\n}","typeGuard":"function hasMcpServers(cfg) {\n  return cfg !== null && typeof cfg === 'object' && !Array.isArray(cfg) &&\n    cfg.mcpServers !== null && typeof cfg.mcpServers === 'object' && !Array.isArray(cfg.mcpServers)\n}","tryCatchPattern":"try {\n  const filtered = filterMcpConfig(config, disabled)\n} catch (err) {\n  if (err.message.includes('mcpServers object')) {\n    console.error('Add an \"mcpServers\": { ... } section to the config')\n  } else throw err\n}","preventionTips":["Keep the mcpServers key at the top level of every MCP config file.","Don't rename or nest mcpServers when merging configs.","Validate config JSON against the schema after hand edits.","Commit a known-good example config for reference."],"tags":["mcp","validation","config","schema"],"backgroundTag":"missing-required-config-field","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}