siyuan-note/siyuan · error
property : x-mcp-header can only be applied to primitive…
Error message
property %q: x-mcp-header can only be applied to primitive types (integer, string, boolean), got %q
What it means
The x-mcp-header keyword may only be attached to properties whose declared schema type is string, integer, or boolean, because HTTP header values are flat primitives. Validation rejects it on other types (object, array, number, etc.) and reports the actual type in the error.
Solutions
- Declare the property with type "string", "integer", or "boolean" when using x-mcp-header
- Move x-mcp-header to a sibling primitive property instead of a nested object/array
- Remove the annotation if the value must be structured
Example fix
// before
{"type":"object","properties":{...},"x-mcp-header":"X-Data"}
// after
{"type":"string","x-mcp-header":"X-Data"} Defensive patterns
Strategy: validation
Validate before calling
function checkHeaderTypes(schema) {
for (const [name, prop] of Object.entries(schema.properties || {})) {
if ('x-mcp-header' in prop && !['string','integer','boolean'].includes(prop.type)) {
throw new Error(`property ${name}: x-mcp-header requires primitive type, got ${prop.type}`);
}
}
} Type guard
const headerEligible = (p) => ['string','integer','boolean'].includes(p.type);
Try / catch
try { registerTool(schema) } catch (e) { if (String(e).includes('primitive types')) moveHeaderToPrimitiveProperty(schema); else throw e; } Prevention
- Only annotate scalar properties with x-mcp-header
- Always declare an explicit type on properties using x-mcp-header
- Keep structured data in body properties, not headers
When it happens
Trigger: A tool schema property with type "object", "array", "number", or missing type carries an x-mcp-header annotation and the schema is validated during MCP tool registration.
Common situations: Copying the annotation to a complex property, expecting nested object fields to map to headers, or omitting the type keyword so the type check fails.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- property : duplicate x-mcp-header value
- property : x-mcp-header must be a non-empty string
- property : x-mcp-header value is not a valid HTTP field name
- invalid input schema
- invalid output schema
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/98a11e94e82acc10.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/mcp/tools/validation.go:244
seen := map[string]bool{}
var walk func(map[string]any, string) error
walk = func(props map[string]any, prefix string) error {
for propertyName, rawProperty := range props {
property, ok := rawProperty.(map[string]any)
if !ok {
continue
}
path := propertyName
if prefix != "" {
path = prefix + "." + propertyName
}
if rawHeader, exists := property["x-mcp-header"]; exists {
header, ok := rawHeader.(string)
if !ok || header == "" {
return fmt.Errorf(`property %q: x-mcp-header must be a non-empty string`, path)
}
propertyType, _ := property["type"].(string)
if propertyType != "string" && propertyType != "integer" && propertyType != "boolean" {
return fmt.Errorf(
`property %q: x-mcp-header can only be applied to primitive types (integer, string, boolean), got %q`,
path, propertyType)
}
if !validHTTPFieldName(header) {
return fmt.Errorf(`property %q: x-mcp-header value %q is not a valid HTTP field name`, path, header)
}
normalized := strings.ToLower(header)
if seen[normalized] {
return fmt.Errorf(`property %q: duplicate x-mcp-header value %q`, path, header)
}
seen[normalized] = true
}
nested, _ := property["properties"].(map[string]any)
if err := walk(nested, path); err != nil {
return err
}
}View on GitHub (pinned to 9f775e8a12)