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: string

View on GitHub (pinned to 0773b97458)

Solutions

  1. Use an object of strings: {"headers": {"Authorization": "Bearer ${TOKEN}"}}.
  2. Remove the headers key if the transport does not need it.
  3. 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

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


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/75662f87fabdd5da. Report an issue: GitHub.