Skip to content

refactor(schema): migrate from invopop/jsonschema to google/jsonschema-go#101

Draft
tak848 wants to merge 1 commit into
mainfrom
refactor/migrate-to-google-jsonschema-go
Draft

refactor(schema): migrate from invopop/jsonschema to google/jsonschema-go#101
tak848 wants to merge 1 commit into
mainfrom
refactor/migrate-to-google-jsonschema-go

Conversation

@tak848

@tak848 tak848 commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Why

We used invopop/jsonschema only to generate JSON Schema from Go types — the editor config schemas (schemas/*.json) and the LLM structured-output schema. Its Schema.Properties is an ordered-map whose backing package invopop swaps between releases (wk8/go-ordered-mappb33f/ordered-map in v0.14), which broke our build because config.go imported that package directly.

What

Migrate all four schema-generation sites to google/jsonschema-go, which is already in the dependency graph (via anthropic-sdk-gomodelcontextprotocol/go-sdk) and models property order with a plain []string field instead of an ordered-map dependency. Our code can no longer be broken by an ordered-map swap. invopop / pb33f stay in the build solely as anthropic-sdk-go transitive deps; we no longer import them directly.

  • config.go: build the provider.auth oneOf with google's Schema API, exported as AuthConfigSchema() (google has no custom-schema interface, so the generator wires it in explicitly).
  • genschema: reflect via jsonschema.For[config.Config]; collapse google's nullable spelling ("type": ["null", T], emitted for Go pointers/slices) back to the bare type so the editor schema stays strict; overwrite provider.auth to avoid the pointer-null widening making the oneOf unsatisfiable.
  • LLM clients: build the output schema via jsonschema.For[llm.Output]; output is object + additionalProperties:false + all-required, matching OpenAI strict mode and Anthropic tool schema. llm.Output struct tags: jsonschema_descriptionjsonschema.

Schema diff

Regenerated schemas/*.json are semantically identical (verified by deep key-sorted comparison) except provider.auth now carries the description the code always intended — invopop silently dropped it. Everything else is JSON key-order only.

Verification

  • go build ./..., go vet ./..., go test -race -cover ./... all pass.
  • No direct imports of invopop/jsonschema, pb33f/ordered-map, or wk8/go-ordered-map remain.
  • Deep-sorted schema comparison confirms no validation-behavior change.
  • LLM output schema is OpenAI strict-compatible (object, additionalProperties:false, all keys required, no $schema).

…a-go

We only used invopop/jsonschema to generate JSON Schema from Go types
(editor config schemas + the LLM structured-output schema). Its
Schema.Properties is an ordered-map whose backing package invopop swaps
between releases (wk8 -> pb33f in v0.14), which broke our build because
config.go imported that package directly.

google/jsonschema-go is already in the dependency graph (via
anthropic-sdk-go -> modelcontextprotocol/go-sdk) and models property
order with a plain []string field instead of an ordered-map dependency,
so our code can never again be broken by an ordered-map swap. invopop /
pb33f remain in the build only as anthropic-sdk-go transitive deps; we
no longer import them directly.

Changes:
- config.go: build the provider.auth oneOf with google's Schema API,
  exported as AuthConfigSchema (google has no custom-schema interface).
- genschema: reflect via jsonschema.For; collapse google's nullable
  spelling ("type": ["null", T]) for pointers/slices back to the bare
  type so the editor schema stays strict; overwrite provider.auth to
  avoid the pointer-null widening making the oneOf unsatisfiable.
- LLM clients: build the output schema via jsonschema.For[llm.Output];
  output is object + additionalProperties:false + all-required, matching
  OpenAI strict mode. llm.Output tags: jsonschema_description -> jsonschema.

Regenerated schemas are semantically identical except provider.auth now
carries the description the code always intended (invopop dropped it).
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.

1 participant