coleam00/Archon · error
MCP config ${serverName}.headers must be a JSON object of st
Error message
MCP config ${serverName}.headers must be a JSON object of string values (got ${describeJsonType(server.headers)}) What it means
If a server entry defines headers (used by HTTP/SSE MCP transports), it must be a JSON object with string values because each value goes through $VAR interpolation. Arrays, null, primitives, or nested non-string values are rejected with the server name in the message.
Source
Thrown at packages/providers/src/mcp/config.ts:85
if (typeof server.env !== 'object' || server.env === null || Array.isArray(server.env)) {
throw new Error(
`MCP config ${serverName}.env must be a JSON object of string values (got ${describeJsonType(server.env)})`
);
}
server.env = expandEnvVarsInRecord(
server.env as Record<string, unknown>,
missingVars,
envSource,
`${serverName}.env`
);
}
if (server.headers !== undefined) {
if (
typeof server.headers !== 'object' ||
server.headers === null ||
Array.isArray(server.headers)
) {
throw new Error(
`MCP config ${serverName}.headers must be a JSON object of string values (got ${describeJsonType(server.headers)})`
);
}
server.headers = expandEnvVarsInRecord(
server.headers as Record<string, unknown>,
missingVars,
envSource,
`${serverName}.headers`
);
}
result[serverName] = server;
}
return { expanded: result, missingVars };
}
function normalizeMcpConfig(
parsed: Record<string, unknown>,
mcpPath: stringView on GitHub (pinned to 0773b97458)
Solutions
- Use an object of strings: {"headers": {"Authorization": "Bearer ${TOKEN}"}}.
- Remove the headers key if the transport does not need it.
- Stringify numeric header values ("5" not 5).
Example fix
// before
{"remote": {"url": "https://mcp.example.com", "headers": ["Authorization: Bearer ${TOKEN}"]}}
// after
{"remote": {"url": "https://mcp.example.com", "headers": {"Authorization": "Bearer ${TOKEN}"}}} Defensive patterns
Strategy: validation
Validate before calling
const s = cfg[serverName];
if ('headers' in s && (typeof s.headers !== 'object' || s.headers === null || Array.isArray(s.headers))) {
throw new Error(`${serverName}.headers must be an object of strings`);
} Type guard
const isHeaderMap = (v: unknown): v is Record<string, string> => typeof v === 'object' && v !== null && !Array.isArray(v) && Object.values(v).every((x) => typeof x === 'string');
Prevention
- Write headers as {"Name": "value"} pairs, not curl-style lists.
- Keep header values as strings even for counts and versions.
- Generate headers from a typed source so stringification happens once, at the boundary.
When it happens
Trigger: loadMcpConfig where a remote server has "headers": ["Authorization: Bearer x"], "headers": null, or "headers": {"X-Count": 5}.
Common situations: Copying curl -H style header lists into JSON; generating headers programmatically with numeric values; a template engine emitting null for unset header blocks.
Related errors
- MCP config ${fieldPath}.${key} must be a string (got ${descr
- MCP server "${serverName}" must be a JSON object (got ${desc
- MCP config ${serverName}.env must be a JSON object of string
- MCP config cannot mix top-level "mcpServers" with other keys
- MCP config field "mcpServers" must be a JSON object: ${mcpPa
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/75662f87fabdd5da.
Report an issue: GitHub.