siyuan-note/siyuan · error
property %q: duplicate x-mcp-header value %q
Error message
property %q: duplicate x-mcp-header value %q
What it means
Thrown by validateParamHeaderAnnotations when two (or more) properties declare the same x-mcp-header value, compared case-insensitively (the value is lowercased before the duplicate check). HTTP header names are case-insensitive, so two properties binding to the same header would conflict and the request behavior would be ambiguous.
Source
Thrown at kernel/mcp/tools/validation.go:254
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 nil
}
return walk(properties, "")
}
func validHTTPFieldName(name string) bool {
if name == "" {
return false
}
for _, character := range name {View on GitHub (pinned to 251596fc0d)
Solutions
- Give each header-bound property a distinct header name.
- Normalize all x-mcp-header values to a single casing convention (e.g. Title-Case-With-Hyphens) to make duplicates obvious.
- If two properties legitimately feed the same header, combine them into one property and format the value in the handler.
Example fix
// before
{"a": {"type": "string", "x-mcp-header": "X-Trace-Id"},
"b": {"type": "string", "x-mcp-header": "x-trace-id"}}
// after
{"a": {"type": "string", "x-mcp-header": "X-Trace-Id"},
"b": {"type": "string", "x-mcp-header": "X-Span-Id"}} Defensive patterns
Strategy: validation
Validate before calling
seen := map[string]bool{}
for _, prop := range props {
if h, ok := prop["x-mcp-header"].(string); ok && h != "" {
key := strings.ToLower(h)
if seen[key] { return fmt.Errorf("duplicate header %q", h) }
seen[key] = true
}
} Prevention
- Adopt a single casing convention for header names to surface duplicates.
- Give each header-bound property a distinct, purpose-specific header name.
- Run a schema linter that flags duplicate (case-insensitive) headers.
When it happens
Trigger: Registering a tool where, e.g., property "userId" has x-mcp-header "User-Id" and property "userID" has x-mcp-header "user-id"; or the same header name reused across nested object properties (the walker recurses into nested properties).
Common situations: Copy-pasting a property and forgetting to change the header; mixing case conventions ("X-Trace-Id" vs "x-trace-id"); nested objects reusing a parent's header name.
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
- schema exceeds %d bytes
- root type must be "object"
- property %q: x-mcp-header can only be applied to primitive t
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/11f9f935d2643f41.
Report an issue: GitHub.