Skip to content

Implement custom server schema validation (MCP Spec 4.1.4 compliance)#1312

Merged
lpcox merged 2 commits intomainfrom
claude/implement-custom-server-schema-validation
Feb 23, 2026
Merged

Implement custom server schema validation (MCP Spec 4.1.4 compliance)#1312
lpcox merged 2 commits intomainfrom
claude/implement-custom-server-schema-validation

Conversation

@Claude
Copy link
Contributor

@Claude Claude AI commented Feb 23, 2026

The gateway was acknowledging custom schema URLs but not fetching or validating against them, violating MCP Gateway Specification Section 4.1.4 MUST requirement: "If registered with an HTTPS URL, the gateway MUST fetch and apply the corresponding JSON Schema for validation."

Changes

  • Schema validation implementation (internal/config/validation.go)

    • Added validateAgainstCustomSchema() that fetches custom schemas via HTTPS, compiles them as JSON Schema Draft 7, and validates server configurations
    • Reuses existing fetchAndFixSchema() helper for schema retrieval with Draft 7 compatibility fixes
  • Custom fields preservation (internal/config/config_stdin.go)

    • Added AdditionalProperties field to StdinServerConfig to capture fields not in standard schema
    • Implemented custom UnmarshalJSON() to preserve custom schema-defined fields during JSON unmarshaling
    • Validation merges struct fields with additional properties before schema validation
  • Test coverage (internal/config/custom_types_test.go)

    • Added invalid_custom_config test case verifying validation fails when required custom fields are missing
    • Fixed test IDs to match specification (T-CFG-009→T-CFG-010, T-CFG-010→T-CFG-011, etc.)

Example

A custom server type with required fields now properly validates:

{
  "customSchemas": {
    "safeinputs": "https://example.com/safeinputs-schema.json"
  },
  "mcpServers": {
    "my-server": {
      "type": "safeinputs",
      "requiredField": "value",
      "container": "ghcr.io/example/safeinputs:latest"
    }
  }
}

If requiredField is missing and the schema requires it, validation now fails with a clear error message pointing to the schema URL.

Compliance Status

✅ T-CFG-010: Valid custom type with registered schema
✅ T-CFG-011: Reject unregistered custom type
✅ T-CFG-012: Validate against custom schema (newly implemented)
✅ T-CFG-013: Reject reserved type names
✅ T-CFG-014: Custom schema fetch and cache

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • example.com
    • Triggering command: /tmp/go-build3647879484/b275/launcher.test /tmp/go-build3647879484/b275/launcher.test -test.testlogfile=/tmp/go-build3647879484/b275/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true --glob !.git bin/git R) + 1) if (dirname --glob !.hg git chec�� claude/implement-custom-server-schema-validation 500 .12/x64/bin/git fetchAndFixSchembase64 /home/REDACTED/wor-d x_amd64/cgo git (dns block)
  • invalid-host-that-does-not-exist-12345.com
    • Triggering command: /tmp/go-build3647879484/b260/config.test /tmp/go-build3647879484/b260/config.test -test.testlogfile=/tmp/go-build3647879484/b260/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true -bool -buildtags ache/Python/3.12.12/x64/bin/git -errorsas -ifaceassert -nilfunc git rev-�� --abbrev-ref HEAD k/_temp/ghcca-node/node/bin/git go ternal/fips140/e-d 64/pkg/tool/linu-o git (dns block)
  • nonexistent.local
    • Triggering command: /tmp/go-build3647879484/b275/launcher.test /tmp/go-build3647879484/b275/launcher.test -test.testlogfile=/tmp/go-build3647879484/b275/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true --glob !.git bin/git R) + 1) if (dirname --glob !.hg git chec�� claude/implement-custom-server-schema-validation 500 .12/x64/bin/git fetchAndFixSchembase64 /home/REDACTED/wor-d x_amd64/cgo git (dns block)
  • slow.example.com
    • Triggering command: /tmp/go-build3647879484/b275/launcher.test /tmp/go-build3647879484/b275/launcher.test -test.testlogfile=/tmp/go-build3647879484/b275/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true --glob !.git bin/git R) + 1) if (dirname --glob !.hg git chec�� claude/implement-custom-server-schema-validation 500 .12/x64/bin/git fetchAndFixSchembase64 /home/REDACTED/wor-d x_amd64/cgo git (dns block)
  • this-host-does-not-exist-12345.com
    • Triggering command: /tmp/go-build3647879484/b284/mcp.test /tmp/go-build3647879484/b284/mcp.test -test.testlogfile=/tmp/go-build3647879484/b284/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true --abbrev-ref HEAD it go zyR10eQBb .12/x64/bin/as s#^v#node/v#; \#/home/REDACTED/.nvm/test rev-�� --abbrev-ref HEAD /home/REDACTED/.cargo/bin/git 64/src/runtime/cbash w52HAkEye ache/go/1.25.6/x--version ; \#system# !d; HEAD (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>[compliance] Compliance Gap: Custom Server Schema Validation Not Implemented (MUST Violation)</issue_title>
<issue_description>## MCP Gateway Compliance Review - 2026-02-22

Summary

Found 1 critical compliance issue during daily review of the current codebase (commit a6346a9). The custom server type schema validation is incomplete - the gateway acknowledges custom schema URLs but does not fetch or apply them for validation, violating a MUST requirement in the specification.

Recent Changes Reviewed

Critical Issues (MUST violations)

1. Custom Server Schema Validation Not Implemented

Specification Section: 4.1.4 Custom Server Types
Deep Link: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md#414-custom-server-types

Requirement (Spec v1.8.0, Section 4.1.4):

"If registered with an HTTPS URL, the gateway MUST fetch and apply the corresponding JSON Schema for validation"

This covers the compliance test T-CFG-012: "Validate custom configuration against registered schema."

Current State:

In internal/config/validation.go:194–200, the validateCustomServerConfig function explicitly skips schema validation with a TODO comment:

// Fetch and validate against custom schema
// For now, we just validate that the schema is fetchable
// Full JSON schema validation against custom schemas can be added in the future
logValidation.Printf("Custom schema validation passed: name=%s, type=%s", name, serverType)
return nil

The function returns nil (success) without fetching the schema URL or validating the server configuration against it.

Gap:

Any custom server configuration with any fields passes validation regardless of what its registered JSON Schema requires. For example, a custom type registered with a schema that requires a requiredField field will happily accept configs that omit that field entirely.

Severity: 🔴 Critical (MUST violation)

File References:

  • internal/config/validation.go:186–200validateCustomServerConfig function
  • internal/config/custom_types_test.go:105–183TestTCFG011_ValidateAgainstCustomSchema (test passes trivially because validation is never applied)

2. Test ID Numbering Misalignment for Custom Schema Tests

Specification Section: 10.1.1 Configuration Tests
Deep Link: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md#1011-configuration-tests

Current State:

The spec defines custom schema compliance tests starting at T-CFG-010, but the test file internal/config/custom_types_test.go labels them starting at T-CFG-009:

Spec Test ID Spec Description Code Test ID in File
T-CFG-010 Valid custom server type with registered schema T-CFG-009
T-CFG-011 Reject custom type without schema registration T-CFG-010
T-CFG-012 Validate custom configuration against registered schema T-CFG-011
T-CFG-013 Reject custom type conflicting with reserved types T-CFG-012
T-CFG-014 Custom schema URL fetch and cache T-CFG-013

In the spec, T-CFG-009 is "Port range validation" (a standard test), but the code reuses T-CFG-009 for the first custom schema test.

Severity: ⚠️ Minor (Test labeling/documentation inconsistency)

File References:

  • internal/config/custom_types_test.go:16,76,105,186,227 — Test ID comments

Compliance Status

Requirement Section Status
✅ Configuration Parsing (JSON stdin) 4.1 Compliant
✅ Variable Expression Resolution 4.2 Compliant
✅ Containerization Requirement 3.2.1 Compliant
✅ Volume Mount Validation 4.1.5 Compliant
✅ Payload Directory Path Validation 4.1.3.1 Compliant
✅ Unknown Field Rejection 4.3.1 Compliant
✅ Required Field Detection 4.3.2 Compliant
❌ Custom Server Schema Validation 4.1.4 Non-compliant (MUST violation)
✅ Protocol Translation (stdio/http) 5.2 Compliant
✅ Close Endpoint 5.1.3 Compliant
✅ Health Monitoring (specVersion, gatewayVersion) 8.1.1 Compliant
✅ Authentication 7 Compliant
✅ Error Handling 9 Compliant
✅ HTTP Connection Failure Handling 5.2.2 Compliant

Suggested Remediation Tasks

Task 1: Implement Custom Schema Validation

Description: Complete the validateCustomServerConfig function to actually fetch and validate server configurations against their registered custom schemas.

Files: internal/config/validation.go:186–200, internal/config/validation_schema.go

Specification Reference: https://github.com/github/gh-aw/blob/main/docs/src/conte...

- Add validateAgainstCustomSchema function that fetches and validates against custom schemas
- Add AdditionalProperties field to StdinServerConfig to preserve custom fields
- Implement UnmarshalJSON to capture additional properties for custom server types
- Add test case for invalid custom config (missing required fields)
- Fix test ID numbering: T-CFG-009→T-CFG-010, T-CFG-010→T-CFG-011, etc.
- All compliance tests passing (T-CFG-010 through T-CFG-014)

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
@Claude Claude AI changed the title [WIP] Add custom server schema validation implementation Implement custom server schema validation (MCP Spec 4.1.4 compliance) Feb 23, 2026
@lpcox lpcox marked this pull request as ready for review February 23, 2026 04:14
Copilot AI review requested due to automatic review settings February 23, 2026 04:14
@lpcox lpcox merged commit db4d659 into main Feb 23, 2026
12 checks passed
@lpcox lpcox deleted the claude/implement-custom-server-schema-validation branch February 23, 2026 04:14
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements custom server schema validation to achieve MCP Gateway Specification Section 4.1.4 compliance. Previously, the gateway acknowledged custom schema URLs but did not fetch or validate against them, violating a MUST requirement. The implementation adds schema fetching, compilation, and validation functionality, along with support for preserving custom fields during JSON unmarshaling.

Changes:

  • Implements validateAgainstCustomSchema() function that fetches custom JSON schemas via HTTPS, compiles them as JSON Schema Draft 7, and validates server configurations against them
  • Adds AdditionalProperties field and custom UnmarshalJSON() method to StdinServerConfig to preserve custom schema-defined fields during JSON unmarshaling
  • Adds negative test case for invalid custom configurations and renumbers test IDs to align with specification (T-CFG-009→T-CFG-010 through T-CFG-013→T-CFG-014)

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
internal/config/validation.go Implements custom schema fetching, compilation, and validation logic; replaces TODO with full implementation of validateAgainstCustomSchema()
internal/config/config_stdin.go Adds AdditionalProperties field with custom unmarshaling to capture non-standard fields for custom server type validation
internal/config/custom_types_test.go Adds test for invalid custom config validation and renumbers test IDs (T-CFG-010 through T-CFG-014) to match specification

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +202 to +267
func validateAgainstCustomSchema(name string, server *StdinServerConfig, schemaURL string, jsonPath string) error {
logValidation.Printf("Fetching custom schema for validation: name=%s, url=%s", name, schemaURL)

// Fetch the custom schema using the existing helper
schemaJSON, err := fetchAndFixSchema(schemaURL)
if err != nil {
logValidation.Printf("Failed to fetch custom schema: name=%s, url=%s, error=%v", name, schemaURL, err)
return &rules.ValidationError{
Field: "type",
Message: fmt.Sprintf("failed to fetch custom schema for server type '%s': %v", server.Type, err),
JSONPath: jsonPath,
Suggestion: fmt.Sprintf("Ensure the schema URL '%s' is accessible and returns a valid JSON Schema", schemaURL),
}
}

logValidation.Printf("Custom schema fetched successfully: name=%s, size=%d bytes", name, len(schemaJSON))

// Parse the schema to extract its $id
var schemaObj map[string]interface{}
if err := json.Unmarshal(schemaJSON, &schemaObj); err != nil {
return &rules.ValidationError{
Field: "type",
Message: fmt.Sprintf("failed to parse custom schema for server type '%s': %v", server.Type, err),
JSONPath: jsonPath,
Suggestion: fmt.Sprintf("The schema at '%s' must be valid JSON", schemaURL),
}
}

schemaID, ok := schemaObj["$id"].(string)
if !ok || schemaID == "" {
schemaID = schemaURL
}

// Compile the custom schema
compiler := jsonschema.NewCompiler()
compiler.Draft = jsonschema.Draft7

// Add the schema with both URLs (the fetch URL and the $id URL)
if err := compiler.AddResource(schemaURL, strings.NewReader(string(schemaJSON))); err != nil {
return &rules.ValidationError{
Field: "type",
Message: fmt.Sprintf("failed to compile custom schema for server type '%s': %v", server.Type, err),
JSONPath: jsonPath,
Suggestion: fmt.Sprintf("The schema at '%s' must be a valid JSON Schema Draft 7 document", schemaURL),
}
}
if schemaID != schemaURL {
if err := compiler.AddResource(schemaID, strings.NewReader(string(schemaJSON))); err != nil {
return &rules.ValidationError{
Field: "type",
Message: fmt.Sprintf("failed to compile custom schema with $id for server type '%s': %v", server.Type, err),
JSONPath: jsonPath,
Suggestion: fmt.Sprintf("Check the $id field in the schema at '%s'", schemaURL),
}
}
}

schema, err := compiler.Compile(schemaID)
if err != nil {
return &rules.ValidationError{
Field: "type",
Message: fmt.Sprintf("failed to compile custom schema for server type '%s': %v", server.Type, err),
JSONPath: jsonPath,
Suggestion: fmt.Sprintf("The schema at '%s' must be a valid JSON Schema Draft 7 document", schemaURL),
}
}
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom schema validation implementation fetches and compiles schemas on every validation call without caching. This differs from the main schema validation which uses sync.Once for efficient caching. If a configuration has multiple servers using the same custom type, each server will trigger a separate HTTP fetch and schema compilation, potentially impacting performance and causing unnecessary network traffic.

Consider implementing a caching mechanism similar to getOrCompileSchema in validation_schema.go using sync.Once or a map with sync.Mutex to cache compiled custom schemas by URL. This would be especially beneficial for configurations with multiple servers of the same custom type.

Copilot uses AI. Check for mistakes.
}))
defer mockSchemaServer.Close()

// Invalid configuration that MISSING requiredField
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor grammar issue in the comment. The word "MISSING" is unnecessarily capitalized and the phrasing could be improved. Consider: "Invalid configuration that is missing requiredField" or "Invalid configuration missing requiredField"

Suggested change
// Invalid configuration that MISSING requiredField
// Invalid configuration missing requiredField

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[compliance] Compliance Gap: Custom Server Schema Validation Not Implemented (MUST Violation)

3 participants