[FEATURE]: JSON Schema as Source of Truth for API Contracts #4194

Open
opened 2026-02-16 17:42:59 -05:00 by yindo · 1 comment
Owner

Originally created by @TonyMarkham on GitHub (Jan 4, 2026).

Originally assigned to: @thdxr on GitHub.

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Proposal: JSON Schema as Source of Truth for API Contracts

Summary

Move API schema definitions from inline TypeScript/Zod code to formal JSON Schema files. Generate TypeScript types and Zod validators from these schemas. This improves API discoverability, enables client SDK generation, and provides versioning for API contracts.

Problem Statement

Currently, OpenCode's API contracts are defined inline within TypeScript source code using Zod:

// packages/opencode/src/provider/provider.ts
export const Info = z.object({
  id: z.string(),
  name: z.string(),
  source: z.enum(["env", "config", "custom", "api"]),
  // ... scattered across 500+ lines
})

This creates several issues:

  1. No single source of truth - Schema definitions are scattered across multiple files
  2. Hard to discover - External clients must read TypeScript source to understand the API
  3. No versioning - Breaking changes have no semantic versioning
  4. No client generation - Each client (desktop, SDK, integration) reverse-engineers the API
  5. Poor documentation - Schema docs are inline comments, not structured
  6. Difficult to validate - No way to validate JSON payloads against schemas at build time

Real-World Impact

I'm building a Tauri desktop client for OpenCode and had to:

  • Read through 1000+ lines of TypeScript to understand Provider.Info schema
  • Document commit hashes and line numbers as "source of truth" references
  • Manually translate Zod schemas to Protocol Buffers for type safety
  • Risk breaking changes with every OpenCode update

This shouldn't be necessary for a modern API.

Proposed Solution

1. Create schema/ Directory with JSON Schema Definitions

opencode/
├── schema/
│   ├── provider.schema.json       # Source of truth
│   ├── model.schema.json
│   ├── session.schema.json
│   ├── message.schema.json
│   ├── tool.schema.json
│   ├── agent.schema.json
│   └── auth.schema.json

2. Example: Provider Schema

schema/provider.schema.json:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://opencode.ai/schemas/v1/provider.json",
  "title": "Provider",
  "description": "AI model provider configuration and metadata",
  "type": "object",
  "required": ["id", "name", "source", "models"],
  "properties": {
    "id": {
      "type": "string",
      "description": "Unique provider identifier",
      "examples": ["anthropic", "openai", "google"]
    },
    "name": {
      "type": "string",
      "description": "Human-readable provider name",
      "examples": ["Anthropic", "OpenAI"]
    },
    "source": {
      "type": "string",
      "enum": ["env", "config", "custom", "api"],
      "description": "How the provider was configured"
    },
    "env": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Environment variables used for authentication",
      "examples": [["ANTHROPIC_API_KEY"]]
    },
    "options": {
      "$ref": "#/definitions/ProviderOptions"
    },
    "models": {
      "type": "object",
      "additionalProperties": {
        "$ref": "./model.schema.json"
      }
    }
  },
  "definitions": {
    "ProviderOptions": {
      "type": "object",
      "properties": {
        "baseURL": { 
          "type": "string", 
          "format": "uri",
          "description": "API endpoint override"
        },
        "apiKey": { 
          "type": "string",
          "description": "API authentication key"
        },
        "headers": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "Custom HTTP headers"
        },
        "timeout": { 
          "type": "integer", 
          "minimum": 0,
          "description": "Request timeout in milliseconds"
        }
      }
    }
  }
}

3. Generate TypeScript Types and Zod Validators

Build process:

# Generate TypeScript interfaces from JSON Schema
json-schema-to-typescript schema/*.json --output src/generated/types/

# Generate Zod validators from JSON Schema  
json-schema-to-zod schema/*.json --output src/generated/validators/

Auto-generated types (src/generated/types/provider.ts):

export interface Provider {
  id: string;
  name: string;
  source: "env" | "config" | "custom" | "api";
  env?: string[];
  options?: ProviderOptions;
  models: Record<string, Model>;
}

export interface ProviderOptions {
  baseURL?: string;
  apiKey?: string;
  headers?: Record<string, string>;
  timeout?: number;
}

Auto-generated validators (src/generated/validators/provider.ts):

import { z } from "zod";

export const ProviderOptionsSchema = z.object({
  baseURL: z.string().url().optional(),
  apiKey: z.string().optional(),
  headers: z.record(z.string()).optional(),
  timeout: z.number().int().min(0).optional(),
});

export const ProviderSchema = z.object({
  id: z.string(),
  name: z.string(),
  source: z.enum(["env", "config", "custom", "api"]),
  env: z.array(z.string()).optional(),
  options: ProviderOptionsSchema.optional(),
  models: z.record(ModelSchema),
});

4. Update Source Code to Use Generated Types

Before:

// packages/opencode/src/provider/provider.ts
export const Info = z.object({
  id: z.string(),
  name: z.string(),
  // ... 50 lines of schema definition
})
export type Info = z.infer<typeof Info>

After:

// packages/opencode/src/provider/provider.ts
import type { Provider } from "../generated/types/provider"
import { ProviderSchema } from "../generated/validators/provider"

// Business logic uses generated types
export async function list(): Promise<Record<string, Provider>> {
  // ... implementation
}

// API validation uses generated validators
export function validate(data: unknown): Provider {
  return ProviderSchema.parse(data)
}

Zero runtime behavior changes. Pure refactor.

Benefits

For OpenCode Maintainers

  1. Single source of truth - All schemas in one place (schema/ directory)
  2. Better documentation - Structured, versionable schema docs
  3. Automatic validation - Catch schema bugs at build time
  4. VSCode integration - Auto-complete and validation in JSON Schema files
  5. No runtime changes - Generated code works exactly like hand-written code
  6. Industry standard - JSON Schema is used by OpenAPI, VSCode, npm, etc.

For External Developers

  1. API discovery - Read schemas without diving into TypeScript source
  2. Type-safe clients - Generate SDKs in any language (TypeScript, Python, Rust, C#)
  3. Version tracking - Know when breaking changes occur
  4. Better docs - Auto-generate API documentation from schemas
  5. Validation tools - Validate payloads against published schemas

For the Ecosystem

  1. Desktop clients - Can generate type-safe gRPC/REST clients
  2. SDK packages - Auto-generate @opencode/sdk-python, etc.
  3. IDE extensions - Better autocomplete and validation
  4. Testing tools - Schema-based fuzz testing and validation
  5. Documentation sites - Auto-generated API reference docs

Implementation Plan

Phase 1: Proof of Concept (Week 1)

  • Create schema/provider.schema.json
  • Set up build tooling (json-schema-to-typescript, json-schema-to-zod)
  • Generate types and validators
  • Update packages/opencode/src/provider/provider.ts to use generated code
  • Verify all existing tests pass (zero behavior changes)
  • Create PR with PoC

Phase 2: Core Schemas (Weeks 2-4)

Migrate one schema per PR:

  • model.schema.json - Model metadata, capabilities, options
  • session.schema.json - Session/conversation management
  • message.schema.json - User/assistant messages
  • tool.schema.json - Tool execution state
  • agent.schema.json - Agent definitions
  • auth.schema.json - Authentication

Phase 3: Tooling & Versioning (Week 5)

  • Add semantic versioning to schemas (v1.0.0, v1.1.0)
  • Set up schema validation in CI (catch breaking changes)
  • Document schema contribution guidelines
  • Add npm run schema:validate command

Phase 4: Documentation & SDKs (Week 6+)

  • Generate OpenAPI spec from JSON Schemas
  • Auto-generate API documentation site
  • Publish @opencode/schemas package to npm
  • Create SDK generation guide for external developers

Migration Strategy

Gradual, Non-Breaking

  1. Add schemas alongside existing code - No immediate changes required
  2. Generate code during build - Transparent to contributors
  3. Migrate one domain at a time - Provider → Model → Session → etc.
  4. Keep Zod validators working - Auto-generated, zero behavior changes
  5. Tests prove equivalence - All existing tests must pass

Backward Compatibility

  • Existing code continues to work during migration
  • Generated Zod schemas are functionally identical to hand-written ones
  • No API changes, no runtime changes
  • Contributors can still modify schemas (just edit JSON instead of TypeScript)

Tooling

Required Dependencies (dev only)

{
  "devDependencies": {
    "json-schema-to-typescript": "^13.1.0",
    "json-schema-to-zod": "^2.0.0",
    "ajv": "^8.12.0",
    "ajv-cli": "^5.0.0"
  }
}

Build Scripts

{
  "scripts": {
    "schema:types": "json-schema-to-typescript schema/*.json -o src/generated/types/",
    "schema:zod": "json-schema-to-zod schema/*.json -o src/generated/validators/",
    "schema:validate": "ajv validate -s schema/**/*.json",
    "schema:docs": "generate-schema-doc schema/ -o docs/api/",
    "prebuild": "npm run schema:types && npm run schema:zod"
  }
}

Auto-runs before every build. No manual intervention needed.

Examples of This Pattern in the Wild

Projects successfully using JSON Schema as source of truth:

  • VSCode - Extension API schemas
  • npm - package.json schema
  • GitHub Actions - Workflow schema
  • Docker Compose - docker-compose.yml schema
  • Kubernetes - CRD schemas
  • OpenAPI/Swagger - Built on JSON Schema

This is an industry-standard approach, not experimental.

FAQ

Q: Won't this make contributions harder?

A: No. Contributors edit JSON instead of TypeScript. Both are human-readable. JSON Schema has better VSCode support (auto-complete, validation).

Q: What about runtime performance?

A: Zero impact. Generated Zod validators are identical to hand-written ones. No additional runtime overhead.

Q: Do we need to rewrite everything at once?

A: No. Migrate incrementally. Old code and new code coexist during transition.

Q: What if we need to change a schema?

A: Edit the JSON Schema file, run build, generated code updates automatically. Simpler than editing scattered TypeScript files.

Q: Why not just use TypeScript types?

A: TypeScript types don't exist at runtime. We need runtime validation (Zod). Also, TypeScript types aren't consumable by other languages (Python, Rust, C# clients).

Q: Why not Protocol Buffers?

A: Protobuf has a steep learning curve and "not JavaScript-native" stigma. JSON Schema is widely understood, used by OpenAPI, and has mature JS tooling.

Alternatives Considered

Alternative 1: Keep Current Approach

Pros:

  • No migration effort
  • Works today

Cons:

  • External clients struggle
  • No API versioning
  • Poor discoverability
  • Doesn't scale to ecosystem

Alternative 2: Protocol Buffers

Pros:

  • Strong typing
  • Efficient serialization
  • Multi-language support

Cons:

  • Steep learning curve
  • Not JavaScript-native
  • Resistance from JS community
  • Adds build complexity (protoc)

Alternative 3: OpenAPI Only

Pros:

  • Good for HTTP APIs
  • Auto-generates docs

Cons:

  • Doesn't cover internal models
  • Still need runtime validators
  • Harder to version individual schemas

Decision: JSON Schema is the best balance of power, simplicity, and JavaScript-native tooling.

Call to Action

I'm volunteering to do the implementation work:

  1. Create PoC PR - Provider schema migration (1-2 days)
  2. Gather feedback - Address concerns, iterate on approach
  3. Incremental migration - One schema per PR (1-2 weeks total)
  4. Documentation - Contribution guide, schema versioning docs

I believe this will make OpenCode's API more accessible and enable a healthier ecosystem of clients, SDKs, and integrations.

Related Work

I've already done significant schema reverse-engineering for a Tauri desktop client:

  • Documented all schemas with commit hashes and line numbers
  • Created Protocol Buffer definitions for type safety
  • Identified discrepancies and undocumented fields

This proposal builds on that work and makes it available to the entire community.


Would the maintainers be open to this approach? I'm happy to answer questions and create a PoC PR if there's interest.

Originally created by @TonyMarkham on GitHub (Jan 4, 2026). Originally assigned to: @thdxr on GitHub. ### Feature hasn't been suggested before. - [x] I have verified this feature I'm about to request hasn't been suggested before. ### Describe the enhancement you want to request # Proposal: JSON Schema as Source of Truth for API Contracts ## Summary Move API schema definitions from inline TypeScript/Zod code to formal JSON Schema files. Generate TypeScript types and Zod validators from these schemas. This improves API discoverability, enables client SDK generation, and provides versioning for API contracts. ## Problem Statement Currently, OpenCode's API contracts are defined inline within TypeScript source code using Zod: ```typescript // packages/opencode/src/provider/provider.ts export const Info = z.object({ id: z.string(), name: z.string(), source: z.enum(["env", "config", "custom", "api"]), // ... scattered across 500+ lines }) ``` This creates several issues: 1. **No single source of truth** - Schema definitions are scattered across multiple files 2. **Hard to discover** - External clients must read TypeScript source to understand the API 3. **No versioning** - Breaking changes have no semantic versioning 4. **No client generation** - Each client (desktop, SDK, integration) reverse-engineers the API 5. **Poor documentation** - Schema docs are inline comments, not structured 6. **Difficult to validate** - No way to validate JSON payloads against schemas at build time ### Real-World Impact I'm building a Tauri desktop client for OpenCode and had to: - Read through 1000+ lines of TypeScript to understand `Provider.Info` schema - Document commit hashes and line numbers as "source of truth" references - Manually translate Zod schemas to Protocol Buffers for type safety - Risk breaking changes with every OpenCode update This shouldn't be necessary for a modern API. ## Proposed Solution ### 1. Create `schema/` Directory with JSON Schema Definitions ``` opencode/ ├── schema/ │ ├── provider.schema.json # Source of truth │ ├── model.schema.json │ ├── session.schema.json │ ├── message.schema.json │ ├── tool.schema.json │ ├── agent.schema.json │ └── auth.schema.json ``` ### 2. Example: Provider Schema **schema/provider.schema.json:** ```json { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://opencode.ai/schemas/v1/provider.json", "title": "Provider", "description": "AI model provider configuration and metadata", "type": "object", "required": ["id", "name", "source", "models"], "properties": { "id": { "type": "string", "description": "Unique provider identifier", "examples": ["anthropic", "openai", "google"] }, "name": { "type": "string", "description": "Human-readable provider name", "examples": ["Anthropic", "OpenAI"] }, "source": { "type": "string", "enum": ["env", "config", "custom", "api"], "description": "How the provider was configured" }, "env": { "type": "array", "items": { "type": "string" }, "description": "Environment variables used for authentication", "examples": [["ANTHROPIC_API_KEY"]] }, "options": { "$ref": "#/definitions/ProviderOptions" }, "models": { "type": "object", "additionalProperties": { "$ref": "./model.schema.json" } } }, "definitions": { "ProviderOptions": { "type": "object", "properties": { "baseURL": { "type": "string", "format": "uri", "description": "API endpoint override" }, "apiKey": { "type": "string", "description": "API authentication key" }, "headers": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Custom HTTP headers" }, "timeout": { "type": "integer", "minimum": 0, "description": "Request timeout in milliseconds" } } } } } ``` ### 3. Generate TypeScript Types and Zod Validators **Build process:** ```bash # Generate TypeScript interfaces from JSON Schema json-schema-to-typescript schema/*.json --output src/generated/types/ # Generate Zod validators from JSON Schema json-schema-to-zod schema/*.json --output src/generated/validators/ ``` **Auto-generated types (src/generated/types/provider.ts):** ```typescript export interface Provider { id: string; name: string; source: "env" | "config" | "custom" | "api"; env?: string[]; options?: ProviderOptions; models: Record<string, Model>; } export interface ProviderOptions { baseURL?: string; apiKey?: string; headers?: Record<string, string>; timeout?: number; } ``` **Auto-generated validators (src/generated/validators/provider.ts):** ```typescript import { z } from "zod"; export const ProviderOptionsSchema = z.object({ baseURL: z.string().url().optional(), apiKey: z.string().optional(), headers: z.record(z.string()).optional(), timeout: z.number().int().min(0).optional(), }); export const ProviderSchema = z.object({ id: z.string(), name: z.string(), source: z.enum(["env", "config", "custom", "api"]), env: z.array(z.string()).optional(), options: ProviderOptionsSchema.optional(), models: z.record(ModelSchema), }); ``` ### 4. Update Source Code to Use Generated Types **Before:** ```typescript // packages/opencode/src/provider/provider.ts export const Info = z.object({ id: z.string(), name: z.string(), // ... 50 lines of schema definition }) export type Info = z.infer<typeof Info> ``` **After:** ```typescript // packages/opencode/src/provider/provider.ts import type { Provider } from "../generated/types/provider" import { ProviderSchema } from "../generated/validators/provider" // Business logic uses generated types export async function list(): Promise<Record<string, Provider>> { // ... implementation } // API validation uses generated validators export function validate(data: unknown): Provider { return ProviderSchema.parse(data) } ``` **Zero runtime behavior changes. Pure refactor.** ## Benefits ### For OpenCode Maintainers 1. **Single source of truth** - All schemas in one place (`schema/` directory) 2. **Better documentation** - Structured, versionable schema docs 3. **Automatic validation** - Catch schema bugs at build time 4. **VSCode integration** - Auto-complete and validation in JSON Schema files 5. **No runtime changes** - Generated code works exactly like hand-written code 6. **Industry standard** - JSON Schema is used by OpenAPI, VSCode, npm, etc. ### For External Developers 1. **API discovery** - Read schemas without diving into TypeScript source 2. **Type-safe clients** - Generate SDKs in any language (TypeScript, Python, Rust, C#) 3. **Version tracking** - Know when breaking changes occur 4. **Better docs** - Auto-generate API documentation from schemas 5. **Validation tools** - Validate payloads against published schemas ### For the Ecosystem 1. **Desktop clients** - Can generate type-safe gRPC/REST clients 2. **SDK packages** - Auto-generate `@opencode/sdk-python`, etc. 3. **IDE extensions** - Better autocomplete and validation 4. **Testing tools** - Schema-based fuzz testing and validation 5. **Documentation sites** - Auto-generated API reference docs ## Implementation Plan ### Phase 1: Proof of Concept (Week 1) - [ ] Create `schema/provider.schema.json` - [ ] Set up build tooling (`json-schema-to-typescript`, `json-schema-to-zod`) - [ ] Generate types and validators - [ ] Update `packages/opencode/src/provider/provider.ts` to use generated code - [ ] Verify all existing tests pass (zero behavior changes) - [ ] Create PR with PoC ### Phase 2: Core Schemas (Weeks 2-4) Migrate one schema per PR: - [ ] `model.schema.json` - Model metadata, capabilities, options - [ ] `session.schema.json` - Session/conversation management - [ ] `message.schema.json` - User/assistant messages - [ ] `tool.schema.json` - Tool execution state - [ ] `agent.schema.json` - Agent definitions - [ ] `auth.schema.json` - Authentication ### Phase 3: Tooling & Versioning (Week 5) - [ ] Add semantic versioning to schemas (`v1.0.0`, `v1.1.0`) - [ ] Set up schema validation in CI (catch breaking changes) - [ ] Document schema contribution guidelines - [ ] Add `npm run schema:validate` command ### Phase 4: Documentation & SDKs (Week 6+) - [ ] Generate OpenAPI spec from JSON Schemas - [ ] Auto-generate API documentation site - [ ] Publish `@opencode/schemas` package to npm - [ ] Create SDK generation guide for external developers ## Migration Strategy ### Gradual, Non-Breaking 1. **Add schemas alongside existing code** - No immediate changes required 2. **Generate code during build** - Transparent to contributors 3. **Migrate one domain at a time** - Provider → Model → Session → etc. 4. **Keep Zod validators working** - Auto-generated, zero behavior changes 5. **Tests prove equivalence** - All existing tests must pass ### Backward Compatibility - Existing code continues to work during migration - Generated Zod schemas are functionally identical to hand-written ones - No API changes, no runtime changes - Contributors can still modify schemas (just edit JSON instead of TypeScript) ## Tooling ### Required Dependencies (dev only) ```json { "devDependencies": { "json-schema-to-typescript": "^13.1.0", "json-schema-to-zod": "^2.0.0", "ajv": "^8.12.0", "ajv-cli": "^5.0.0" } } ``` ### Build Scripts ```json { "scripts": { "schema:types": "json-schema-to-typescript schema/*.json -o src/generated/types/", "schema:zod": "json-schema-to-zod schema/*.json -o src/generated/validators/", "schema:validate": "ajv validate -s schema/**/*.json", "schema:docs": "generate-schema-doc schema/ -o docs/api/", "prebuild": "npm run schema:types && npm run schema:zod" } } ``` Auto-runs before every build. No manual intervention needed. ## Examples of This Pattern in the Wild Projects successfully using JSON Schema as source of truth: - **VSCode** - Extension API schemas - **npm** - `package.json` schema - **GitHub Actions** - Workflow schema - **Docker Compose** - `docker-compose.yml` schema - **Kubernetes** - CRD schemas - **OpenAPI/Swagger** - Built on JSON Schema This is an industry-standard approach, not experimental. ## FAQ ### Q: Won't this make contributions harder? **A:** No. Contributors edit JSON instead of TypeScript. Both are human-readable. JSON Schema has better VSCode support (auto-complete, validation). ### Q: What about runtime performance? **A:** Zero impact. Generated Zod validators are identical to hand-written ones. No additional runtime overhead. ### Q: Do we need to rewrite everything at once? **A:** No. Migrate incrementally. Old code and new code coexist during transition. ### Q: What if we need to change a schema? **A:** Edit the JSON Schema file, run build, generated code updates automatically. Simpler than editing scattered TypeScript files. ### Q: Why not just use TypeScript types? **A:** TypeScript types don't exist at runtime. We need runtime validation (Zod). Also, TypeScript types aren't consumable by other languages (Python, Rust, C# clients). ### Q: Why not Protocol Buffers? **A:** Protobuf has a steep learning curve and "not JavaScript-native" stigma. JSON Schema is widely understood, used by OpenAPI, and has mature JS tooling. ## Alternatives Considered ### Alternative 1: Keep Current Approach **Pros:** - No migration effort - Works today **Cons:** - External clients struggle - No API versioning - Poor discoverability - Doesn't scale to ecosystem ### Alternative 2: Protocol Buffers **Pros:** - Strong typing - Efficient serialization - Multi-language support **Cons:** - Steep learning curve - Not JavaScript-native - Resistance from JS community - Adds build complexity (protoc) ### Alternative 3: OpenAPI Only **Pros:** - Good for HTTP APIs - Auto-generates docs **Cons:** - Doesn't cover internal models - Still need runtime validators - Harder to version individual schemas **Decision:** JSON Schema is the best balance of power, simplicity, and JavaScript-native tooling. ## Call to Action I'm volunteering to do the implementation work: 1. **Create PoC PR** - Provider schema migration (1-2 days) 2. **Gather feedback** - Address concerns, iterate on approach 3. **Incremental migration** - One schema per PR (1-2 weeks total) 4. **Documentation** - Contribution guide, schema versioning docs **I believe this will make OpenCode's API more accessible and enable a healthier ecosystem of clients, SDKs, and integrations.** ## Related Work I've already done significant schema reverse-engineering for a Tauri desktop client: - Documented all schemas with commit hashes and line numbers - Created Protocol Buffer definitions for type safety - Identified discrepancies and undocumented fields This proposal builds on that work and makes it available to the entire community. --- **Would the maintainers be open to this approach?** I'm happy to answer questions and create a PoC PR if there's interest.
yindo added the discussiondocs labels 2026-02-16 17:42:59 -05:00
Author
Owner

@TonyMarkham commented on GitHub (Jan 4, 2026):

Proof of Concept Complete - All 8 Domains Migrated

I've implemented the JSON Schema migration as a working proof of concept on my fork:

Branch: TonyMarkham/opencode@feature/json-schema-poc

What Was Implemented

All 8 domains now have JSON Schema definitions:

Domain Schemas Key Types
Model 8 files ModelInfo, ModelCost, ModelCapabilities, ModelLimits, etc.
Provider 6 files ProviderInfo, ProviderOptions, ProviderSource, etc.
Auth 4 files Auth, ApiAuth, OAuth, WellKnownAuth
Session 10 files SessionInfo, SessionTime, SessionSummary, FileDiff, etc.
Message 20 files Message, UserMessage, AssistantMessage, all Part types, Error types
Tool 9 files ToolState, ToolPart, PermissionRequest, PermissionReply, etc.
Agent 2 files AgentInfo, AgentModel
Event 13 files Event, GlobalEvent, SessionStatus, all event types

Total: 72+ JSON Schema files

Implementation Details

  1. Custom code generation tooling at script/generate-from-schemas.ts

    • Validates all schemas with AJV
    • Generates Zod validators + TypeScript types
    • Handles cross-file references correctly
    • Integrated with existing bun run generate workflow
  2. Refactored TypeScript source to use generated validators

    • provider.ts - Provider types
    • message-v2.ts - Message, Part, and Error types
    • permission/next.ts - Permission types
    • agent/agent.ts - Agent types
    • session/status.ts - Session status types
    • server/server.ts - Event schema for OpenAPI
  3. Comprehensive documentation in schema/README.md

    • Schema editing guidelines
    • Code generation workflow
    • External client examples (Rust, Python, C#)

Results

  • All 87 schemas validate successfully
  • All 544 tests pass
  • TypeScript compilation passes
  • Build succeeds (all 11 platform targets)
  • Runtime behavior unchanged

Key Commits

  • c7f799847 - Add JSON Schema validation and code generation tooling
  • f5327072e - Add JSON Schema for tool and permission types
  • a14f9d216 - Add JSON Schema for message and error types
  • 146a9b8ab - Add JSON Schema for agent types
  • 808820dad - Add JSON Schema for event types

Technical Notes

  • Custom generator was necessary - json-schema-to-zod CLI generates z.any() for cross-file $ref
  • Generated files are gitignored - Build artifacts, regenerated on bun run generate:schemas
  • Zod 4.x compatible - Uses z.record(z.string(), valueType) syntax
  • Behavioral equivalence preserved - z.union()z.discriminatedUnion() where schema has discriminator

External Client Benefits

With JSON Schema as the source of truth, external clients can now generate types automatically:

# Rust
typify schema/modelInfo.schema.json

# Python  
datamodel-codegen --input schema/ --output opencode_types/

# C#
NJsonSchema.CodeGeneration schema/modelInfo.schema.json

Next Steps (if maintainers are interested)

  1. Review the PoC branch
  2. Consider merging to bring JSON Schema to mainline
  3. CI integration for schema validation

Happy to create a PR or answer questions.

@TonyMarkham commented on GitHub (Jan 4, 2026): ## Proof of Concept Complete - All 8 Domains Migrated I've implemented the JSON Schema migration as a working proof of concept on my fork: **Branch:** [TonyMarkham/opencode@feature/json-schema-poc](https://github.com/TonyMarkham/opencode/tree/feature/json-schema-poc) ### What Was Implemented **All 8 domains now have JSON Schema definitions:** | Domain | Schemas | Key Types | |--------|---------|-----------| | Model | 8 files | `ModelInfo`, `ModelCost`, `ModelCapabilities`, `ModelLimits`, etc. | | Provider | 6 files | `ProviderInfo`, `ProviderOptions`, `ProviderSource`, etc. | | Auth | 4 files | `Auth`, `ApiAuth`, `OAuth`, `WellKnownAuth` | | Session | 10 files | `SessionInfo`, `SessionTime`, `SessionSummary`, `FileDiff`, etc. | | Message | 20 files | `Message`, `UserMessage`, `AssistantMessage`, all Part types, Error types | | Tool | 9 files | `ToolState`, `ToolPart`, `PermissionRequest`, `PermissionReply`, etc. | | Agent | 2 files | `AgentInfo`, `AgentModel` | | Event | 13 files | `Event`, `GlobalEvent`, `SessionStatus`, all event types | **Total: 72+ JSON Schema files** ### Implementation Details 1. **Custom code generation tooling** at `script/generate-from-schemas.ts` - Validates all schemas with AJV - Generates Zod validators + TypeScript types - Handles cross-file references correctly - Integrated with existing `bun run generate` workflow 2. **Refactored TypeScript source** to use generated validators - `provider.ts` - Provider types - `message-v2.ts` - Message, Part, and Error types - `permission/next.ts` - Permission types - `agent/agent.ts` - Agent types - `session/status.ts` - Session status types - `server/server.ts` - Event schema for OpenAPI 3. **Comprehensive documentation** in `schema/README.md` - Schema editing guidelines - Code generation workflow - External client examples (Rust, Python, C#) ### Results - ✅ All 87 schemas validate successfully - ✅ All 544 tests pass - ✅ TypeScript compilation passes - ✅ Build succeeds (all 11 platform targets) - ✅ Runtime behavior unchanged ### Key Commits - [`c7f799847`](https://github.com/TonyMarkham/opencode/commit/c7f799847) - Add JSON Schema validation and code generation tooling - [`f5327072e`](https://github.com/TonyMarkham/opencode/commit/f5327072e) - Add JSON Schema for tool and permission types - [`a14f9d216`](https://github.com/TonyMarkham/opencode/commit/a14f9d216) - Add JSON Schema for message and error types - [`146a9b8ab`](https://github.com/TonyMarkham/opencode/commit/146a9b8ab) - Add JSON Schema for agent types - [`808820dad`](https://github.com/TonyMarkham/opencode/commit/808820dad) - Add JSON Schema for event types ### Technical Notes - **Custom generator was necessary** - `json-schema-to-zod` CLI generates `z.any()` for cross-file `$ref` - **Generated files are gitignored** - Build artifacts, regenerated on `bun run generate:schemas` - **Zod 4.x compatible** - Uses `z.record(z.string(), valueType)` syntax - **Behavioral equivalence preserved** - `z.union()` → `z.discriminatedUnion()` where schema has discriminator ### External Client Benefits With JSON Schema as the source of truth, external clients can now generate types automatically: ```bash # Rust typify schema/modelInfo.schema.json # Python datamodel-codegen --input schema/ --output opencode_types/ # C# NJsonSchema.CodeGeneration schema/modelInfo.schema.json ``` ### Next Steps (if maintainers are interested) 1. Review the PoC branch 2. Consider merging to bring JSON Schema to mainline 3. CI integration for schema validation Happy to create a PR or answer questions.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#4194