😸 kt-schema

Architecture

Table of contents

kt-schema is a layered library that generates schemas—primarily JSON Schema—from Kotlin declarations and Java classes. It unifies compile-time analysis (KSP) and runtime inspection (reflection) by translating both into a shared Internal Representation (IR).

The architecture is modular by design, so you can plug in new schema formats and generation strategies while keeping behavior consistent across JVM, JS, Native, and Wasm.

Architecture goals:

Overview

The library implements the following pipeline:

graph LR
    subgraph Sources["📦 SOURCES"]
        Java["Java Classes<br/>Third-party libs"]
        Functions["Kotlin Functions"]
        Kotlin["Kotlin Classes<br/>@Schema annotated"]
        KSerializer["SerialDescriptor"]
    end

    subgraph Introspectors["🔍 Stage 1: INTROSPECTORS"]
        KSP["KspSchemaIntrospector<br/><i>compile-time</i>"]
        Serialization["SerializationClassSchemaIntrospector<br/><i>runtime</i>"]
        Apt["AptClassIntrospector<br/><i>compile-time</i>"]
        Reflect["ReflectionSchemaIntrospector<br/><i>runtime</i>"]
    end

    subgraph IR["🧬 INTERNAL REPRESENTATION"]
        TypeGraph["TypeGraph<br/>Unified intermediate format<br/>Properties • Types • Descriptions<br/>Nullable • Defaults"]
    end

    subgraph Transformers["⚙️ TRANSFORMERS"]
        JsonTransform["JsonSchemaTransformer<br/>Draft 2020-12"]
        FuncTransform["FunctionCallingTransformer<br/>(OpenAI, Anthropic and MCP)"]
    end

    subgraph Output["📄 SCHEMA OUTPUTS"]
        KtClass["Generated .kt<br/>MyClass::class.jsonSchema<br/>MyClass::class.jsonSchemaString"]
        KtFunc["Generated .kt<br/>myMethodJsonSchema()<br/>myMethodJsonSchemaString()"]
        JsonSchema["JsonSchema<br/>(@Serializable)"]
        JsonObj["JsonObject<br/>kotlinx.serialization"]
        JsonStr["JSON String<br/>Serialized schema"]
        AptResource["Generated .json resource<br/>META-INF/kt-schema/schemas/..."]
        FuncSchema["FunctionCallingSchema<br/>(@Serializable)"]
    end

    Kotlin --> KSP
    KSerializer --> Serialization
    Java --> Reflect
    Java --> Apt
    Functions --> KSP
    Functions --> Reflect

    KSP --> TypeGraph
    Apt --> TypeGraph
    Reflect --> TypeGraph
    Serialization --> TypeGraph

    TypeGraph --> JsonTransform
    TypeGraph --> FuncTransform

    JsonTransform --> JsonSchema
    FuncTransform --> FuncSchema
    JsonSchema --> JsonObj
    JsonSchema --> AptResource
    FuncSchema --> KtFunc
    JsonObj --> JsonStr
    JsonObj --> KtClass

    classDef sourceStyle fill:#e3f2fd,stroke:#1565c0,stroke-width:3px,color:#000
    classDef introspectorStyle fill:#fff3e0,stroke:#ef6c00,stroke-width:3px,color:#000
    classDef irStyle fill:#fce4ec,stroke:#c2185b,stroke-width:4px,color:#000
    classDef transformStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:3px,color:#000
    classDef outputStyle fill:#e8f5e9,stroke:#2e7d32,stroke-width:3px,color:#000

    class Kotlin,Java,JavaApt,Functions,KSerializer sourceStyle
    class KSP,Apt,Reflect,Serialization introspectorStyle
    class TypeGraph irStyle
    class JsonTransform,FuncTransform transformStyle
    class KtClass,KtFunc,JsonSchema,FuncSchema,JsonObj,JsonStr,AptResource outputStyle

The Transformation Story:

  1. Sources — Kotlin classes, Java classes/records, Kotlin functions, or SerialDescriptor serve as input
  2. Introspectors — Extract type information at compile-time (KSP, Java APT ) or runtime (Reflection, Serialization)
  3. TypeGraph — Unified internal representation containing all type metadata
  4. Transformers — Convert TypeGraph to JSON Schema or Function Calling format
  5. Outputs — Generated Kotlin code, a generated .json resource (Java APT), JsonSchema, FunctionCallingSchema, and then to JsonObject, or JSON strings

Module Dependencies

graph LR
    subgraph lib["kt-schema"]
        kxsAnnotations["kt-schema-annotations"]
        kxsGenCore["kt-schema-generator-core"]
        kxsGenJson["kt-schema-generator-json"]
        kxsJsn["kt-schema-json"]
        kxsKsp["kt-schema-ksp"]
        kxsApt["kt-schema-apt"]
    end

    subgraph userCode["User's Application Code"]
        userModels["User Domain Models"]
        userModelsExt["User Models Extensions"]
    end

    kxsGenJson -->|uses| kxsGenCore
    kxsGenJson -->|uses| kxsJsn
    kxsGenCore -->|knows| kxsAnnotations
    kxsKsp -->|uses| kxsGenJson
    kxsApt -->|uses| kxsGenJson

    userModelsExt -->|uses| userModels
    userModels -->|uses| kxsAnnotations
    kxsKsp -->|generates| userModelsExt
    kxsApt -->|generates schema resource for| userModels

Top-level modules you might interact with:

Workflow

sequenceDiagram
    actor C as Client
    participant S as SchemaGeneratorService
    participant G as SchemaGenerator
    participant I as SchemaIntrospector
    participant T as TypeGraphTransformer
    note over T: has Config
    C ->> S: getGenerator(T::class, R::class)
    S -->> G: find
    activate G
    S -->> C: SchemaGenerator
    C ->> G: generate(T) : R?
    G ->> I: introspect(T)
    I -->> G: TypeGraph
    G ->> T: transform(TypeGraph, rootName)
    T -->> G: schema (R)
    G -->> C: schema (R)
    deactivate G
  1. Client (KSP Processor or Java class) calls SchemaGeneratorService to lookup SchemaGenerator by target type T and expected schema class. SchemaGeneratorService returns SchemaGenerator, if any.
  2. Client (KSP Processor or Java class) calls SchemaGenerator to generate a Schema string representation, and, optionally, object a Schema string representation.
  3. SchemaGenerator invokes SchemaIntrospector to convert an object into TypeGraph
  4. TypeGraphTransformer converts a TypeGraph to a target representation (e.g., JSON Schema) with respect to respecting Config object and returns it to SchemaGenerator