{"record":{"id":"75662f87fabdd5da","repo":"coleam00/Archon","slug":"mcp-config-servername-headers-must-be-a-json-ob","errorCode":null,"errorMessage":"MCP config ${serverName}.headers must be a JSON object of string values (got ${describeJsonType(server.headers)})","messagePattern":"MCP config (.+?)\\.headers must be a JSON object of string values \\(got (.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/providers/src/mcp/config.ts","lineNumber":85,"sourceCode":"      if (typeof server.env !== 'object' || server.env === null || Array.isArray(server.env)) {\n        throw new Error(\n          `MCP config ${serverName}.env must be a JSON object of string values (got ${describeJsonType(server.env)})`\n        );\n      }\n      server.env = expandEnvVarsInRecord(\n        server.env as Record<string, unknown>,\n        missingVars,\n        envSource,\n        `${serverName}.env`\n      );\n    }\n    if (server.headers !== undefined) {\n      if (\n        typeof server.headers !== 'object' ||\n        server.headers === null ||\n        Array.isArray(server.headers)\n      ) {\n        throw new Error(\n          `MCP config ${serverName}.headers must be a JSON object of string values (got ${describeJsonType(server.headers)})`\n        );\n      }\n      server.headers = expandEnvVarsInRecord(\n        server.headers as Record<string, unknown>,\n        missingVars,\n        envSource,\n        `${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","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/providers/src/mcp/config.ts#L67-L103","documentation":"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.","triggerScenarios":"loadMcpConfig where a remote server has \"headers\": [\"Authorization: Bearer x\"], \"headers\": null, or \"headers\": {\"X-Count\": 5}.","commonSituations":"Copying curl -H style header lists into JSON; generating headers programmatically with numeric values; a template engine emitting null for unset header blocks.","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)."],"exampleFix":"// before\n{\"remote\": {\"url\": \"https://mcp.example.com\", \"headers\": [\"Authorization: Bearer ${TOKEN}\"]}}\n// after\n{\"remote\": {\"url\": \"https://mcp.example.com\", \"headers\": {\"Authorization\": \"Bearer ${TOKEN}\"}}}","handlingStrategy":"validation","validationCode":"const s = cfg[serverName];\nif ('headers' in s && (typeof s.headers !== 'object' || s.headers === null || Array.isArray(s.headers))) {\n  throw new Error(`${serverName}.headers must be an object of strings`);\n}","typeGuard":"const isHeaderMap = (v: unknown): v is Record<string, string> =>\n  typeof v === 'object' && v !== null && !Array.isArray(v) &&\n  Object.values(v).every((x) => typeof x === 'string');","tryCatchPattern":null,"preventionTips":["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."],"tags":["config","validation","mcp","http"],"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"}