vercel/ai · error · TypeError

Tool argument "${binding.path.join('.')}" does not match its

Error message

Tool argument "${binding.path.join('.')}" does not match its x-mcp-header type

What it means

createMCPToolHeaders maps tool arguments annotated with x-mcp-header into Mcp-Param-* HTTP headers. Each binding declares a valueType ('string' | 'boolean' | 'integer'); if the resolved argument value's runtime type does not match (non-string, non-boolean, or non-safe-integer), a TypeError is thrown rather than silently coercing the value into a header.

Source

Thrown at packages/mcp/src/tool/mcp-http-headers.ts:150

  args,
}: {
  bindings: MCPToolHeaderBinding[];
  args: Record<string, unknown>;
}): Record<string, string> {
  const headers: Record<string, string> = {};

  for (const binding of bindings) {
    const value = getValueAtPath(args, binding.path);
    if (value == null) {
      continue;
    }

    if (
      (binding.valueType === 'string' && typeof value !== 'string') ||
      (binding.valueType === 'boolean' && typeof value !== 'boolean') ||
      (binding.valueType === 'integer' && !Number.isSafeInteger(value))
    ) {
      throw new TypeError(
        `Tool argument "${binding.path.join('.')}" does not match its x-mcp-header type`,
      );
    }

    headers[`Mcp-Param-${binding.headerName}`] = encodeMCPHeaderValue(
      String(value),
    );
  }

  return headers;
}

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Pass the argument with the exact declared type: coerce strings to Number for integer headers (after validating Number.isSafeInteger), numbers to String for string headers, and real booleans (not "true"/"false" strings) for boolean headers
  2. Validate or constrain the tool input schema (e.g. zod) so arguments bound to headers are guaranteed to have the declared type before invocation
  3. Ensure the argument is always provided; missing values become undefined and fail the type check

Example fix

// before
await tool.execute({ requestId: 42, dryRun: "true" });
// after
await tool.execute({ requestId: "42", dryRun: true });
Defensive patterns

Strategy: validation

Validate before calling

function assertHeaderArg(name, value, valueType) {
  const ok = valueType === 'string' ? typeof value === 'string'
    : valueType === 'boolean' ? typeof value === 'boolean'
    : valueType === 'integer' ? typeof value === 'number' && Number.isSafeInteger(value)
    : false;
  if (!ok) throw new TypeError(`argument ${name} must be ${valueType} for its x-mcp-header`);
}
// call before tool execution for each header-bound argument

Type guard

function matchesHeaderType(value: unknown, valueType: 'string' | 'boolean' | 'integer'): value is string | boolean | number {
  return (valueType === 'string' && typeof value === 'string') ||
    (valueType === 'boolean' && typeof value === 'boolean') ||
    (valueType === 'integer' && typeof value === 'number' && Number.isSafeInteger(value));
}

Try / catch

try {
  await tool.execute(args);
} catch (error) {
  if (error instanceof TypeError && error.message.includes('x-mcp-header type')) {
    // coerce/validate the offending argument and retry
  } else throw error;
}

Prevention

When it happens

Trigger: Calling the tool with an argument bound to an x-mcp-header whose value is the wrong type: a number for a string header, a string "true" for a boolean header, a float/NaN/unsafe-integer for an integer header, or undefined/null when the argument is missing.

Common situations: Sending numeric IDs as numbers when the header binding declares string; LLM-produced tool arguments arriving as strings; values pulled through binding.path from a nested object being undefined because the caller omitted them.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/168b12fe20350cba. Report an issue: GitHub.