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

  1. Declare the property with type "string", "integer", or "boolean" when using x-mcp-header
  2. Move x-mcp-header to a sibling primitive property instead of a nested object/array
  3. 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

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


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)