siyuan-note/siyuan · error
property %q: x-mcp-header can only be applied to primitive t
Error message
property %q: x-mcp-header can only be applied to primitive types (integer, string, boolean), got %q
What it means
Thrown by validateParamHeaderAnnotations when a property carries x-mcp-header but its declared type is not one of the allowed primitive types ("string", "integer", "boolean"). Header binding only makes sense for scalar values that can be serialized into a single header line; objects and arrays are rejected.
Source
Thrown at kernel/mcp/tools/validation.go:245
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
}
}
return nilView on GitHub (pinned to 251596fc0d)
Solutions
- Change the property type to string, integer, or boolean if header binding is intended.
- If the property must remain an object/array, remove the x-mcp-header annotation and bind it to the request body instead.
- If you need multiple header values, model them as separate primitive properties each with their own x-mcp-header.
Example fix
// before
{"tags": {"type": "array", "items": {"type": "string"}, "x-mcp-header": "X-Tags"}}
// after: bind each tag to its own header
{"tag1": {"type": "string", "x-mcp-header": "X-Tag-1"}} Defensive patterns
Strategy: validation
Validate before calling
if _, hasHeader := prop["x-mcp-header"]; hasHeader {
t, _ := prop["type"].(string)
if t != "string" && t != "integer" && t != "boolean" {
return fmt.Errorf("x-mcp-header on non-primitive type %q", t)
}
} Type guard
func headerTypeAllowed(prop map[string]any) bool {
if _, ok := prop["x-mcp-header"]; !ok { return true }
t, _ := prop["type"].(string)
return t == "string" || t == "integer" || t == "boolean"
} Prevention
- Only attach x-mcp-header to scalar properties.
- For multi-value headers, model distinct primitive properties per header.
- Review reused schema fragments after changing a property's type.
When it happens
Trigger: Registering a tool whose input schema has a property such as {"type": "array", "items": {...}, "x-mcp-header": "X-Tags"} or {"type": "object", "x-mcp-header": "X-Meta"}.
Common situations: A developer adds x-mcp-header to a property intended to carry structured data; a schema is reused and the header annotation is left on a now-array-typed property; missing type defaults to "" which also fails this check.
Related errors
- property %q: x-mcp-header must be a non-empty string
- property %q: x-mcp-header value %q is not a valid HTTP field
- property %q: duplicate x-mcp-header value %q
- schema exceeds %d bytes
- root type must be "object"
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/98a11e94e82acc10.
Report an issue: GitHub.