affaan-m/ECC · error
MCP config must include an mcpServers object
Error message
MCP config must include an mcpServers object
What it means
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.
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.
Example fix
// before
{ "servers": { "context7": {} } }
// after
{ "mcpServers": { "context7": {} } } Defensive patterns
Strategy: validation
Validate before calling
if (!config || typeof config !== 'object' || Array.isArray(config) ||
!config.mcpServers || typeof config.mcpServers !== 'object' || Array.isArray(config.mcpServers)) {
throw new Error('Config must contain a plain mcpServers object')
} Type guard
function hasMcpServers(cfg) {
return cfg !== null && typeof cfg === 'object' && !Array.isArray(cfg) &&
cfg.mcpServers !== null && typeof cfg.mcpServers === 'object' && !Array.isArray(cfg.mcpServers)
} Try / catch
try {
const filtered = filterMcpConfig(config, disabled)
} catch (err) {
if (err.message.includes('mcpServers object')) {
console.error('Add an "mcpServers": { ... } section to the config')
} else throw err
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- -32602
- Invalid install config
- workflow schema_version must be 1
- application config exceeds local size limit
- artifact path must be a non-empty relative path
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/abf14b29bf02e79a.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/mcp-config.js:19
'use strict';
function parseDisabledMcpServers(value) {
return [...new Set(
String(value || '')
.split(',')
.map((entry) => entry.trim())
.filter(Boolean)
)];
}
function filterMcpConfig(config, disabledServerNames = []) {
if (!config || typeof config !== 'object' || Array.isArray(config)) {
throw new Error('MCP config must be a JSON object');
}
const servers = config.mcpServers;
if (!servers || typeof servers !== 'object' || Array.isArray(servers)) {
throw new Error('MCP config must include an mcpServers object');
}
const disabled = new Set(parseDisabledMcpServers(disabledServerNames));
if (disabled.size === 0) {
return {
config: {
...config,
mcpServers: { ...servers },
},
removed: [],
};
}
const nextServers = {};
const removed = [];
for (const [name, serverConfig] of Object.entries(servers)) {
if (disabled.has(name)) {View on GitHub (pinned to 8321021c54)