
Generate JSON Schemas and LLM function-calling specs from code — including third‑party classes — via compile‑time annotation processing, runtime reflection or serialization, with polymorphism, annotation reuse, and JSON Schema DSL.
Generate JSON schemas and LLM function calling schemas from Java and Kotlin code — including classes you don't own.
Full documentation lives at kpavlov.github.io/kt-schema. This README is the short tour; each topic below links to its guide.
@Schema/@Description referenceGeneration Modes:
SerializersModule
LLM Integration:
additionalProperties: false, null unions for optional valuesFlexible Annotation Support:
@Description, @LLMDescription, @JsonPropertyDescription, @P, and moreComprehensive Type Support:
SerializersModule) with automatic oneOf
generation and discriminator field["string", "null"])$ref/$defs deduplication: named types appear once in $defs and are referenced everywhere via $ref
kotlin.Any: maps to the empty schema {} (accepts any JSON value)Developer Experience:
[!TIP] Need to build JSON Schemas manually? The kt-schema-json module provides type-safe Kotlin models and DSL compliant with JSON Schema Draft 2020-12, with support for polymorphism, discriminators, and type-safe enums. See the JSON Schema DSL guide.
Table of contents:
This library solves three key challenges:
@Description-like annotations from other frameworks| 🔧 KSP Processor | ☕ Java APT | 📦 Serialization-based | 🔍 Runtime Reflection | |
|---|---|---|---|---|
| Platforms | JVM + Multiplatform | JVM only | JVM + Multiplatform | JVM only |
| When generated | Compile-time | Compile-time | Runtime | Runtime |
| Requires annotation processor | Yes (KSP) | Yes (APT) | No | No |
Class must be @Serializable |
No | No | Yes | No |
Annotate class with @Schema |
Required | Not required¹ | Not required | Not required |
| KDoc extracted to description | ✅ | ❌ | ❌ | ❌ |
| Extract default values | Partial² | Partial² | ❌ | ✅ |
| Third-party classes | ❌ | ❌ | ✅ (only @Serializable) |
✅ any JVM class |
¹ with the rootPackage option — see Java Annotation Processor Guide.
² Jackson's @JsonEnumDefaultValue becomes the enum's default. Kotlin/Java default-value expressions can't be
evaluated at compile time, so no property default is emitted — see
Multi-Framework Annotation Support.
Pick KSP when you own the classes, want zero runtime overhead, and target Multiplatform or need KDoc in your schema.
Pick Java APT when your code is plain Java (no Kotlin) and you want zero runtime overhead. Supports Java records, classes, interfaces and enums.
Pick Serialization-based when your classes are already @Serializable and you need
Multiplatform support without a build-time processor.
Pick Reflection when you need JVM-only runtime generation for third-party classes,
or
need to extract data class default values. Works equally well for @Serializable classes on JVM.
Refer to the example projects here.
/**
* A postal address for deliveries and billing.
*/
@Schema
data class Address(
@Description("Street address, including house number") @Size(max = 120) val street: String,
@Description("City or town name") @Size(min = 1, max = 80) val city: String,
@Description("Postal or ZIP code") @Pattern(regexp = "[0-9]{5}") val zipCode: String,
@Description("Two-letter ISO country code; defaults to US") @Pattern(regexp = "[A-Z]{2}") val country: String = "US",
)strict config){
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "com.example.Address",
"description": "A postal address for deliveries and billing.",
"type": "object",
"properties": {
"street": {
"type": "string",
"description": "Street address, including house number",
"maxLength": 120
},
"city": {
"type": "string",
"description": "City or town name",
"minLength": 1,
"maxLength": 80
},
"zipCode": {
"type": "string",
"description": "Postal or ZIP code",
"pattern": "[0-9]{5}"
},
"country": {
"type": "string",
"description": "Two-letter ISO country code; defaults to US",
"pattern": "[A-Z]{2}"
}
},
"additionalProperties": false,
"required": [
"street",
"city",
"zipCode",
"country"
]
}Note: KDoc comments on classes can also be used as descriptions. Jakarta Bean Validation annotations such as
@Sizeand@PatternbecomemaxLength,minLengthandpattern— see Validation Constraints.
Add the Google KSP plugin and the processor dependency:
// Multiplatform (KMP)
plugins {
kotlin("multiplatform")
id("com.google.devtools.ksp") version "<ksp-version>"
}
dependencies {
add("kspCommonMainMetadata", "me.kpavlov.kt.schema:kt-schema-ksp:<version>")
implementation("me.kpavlov.kt.schema:kt-schema-annotations:<version>")
}
kotlin {
sourceSets.commonMain.kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}For JVM-only projects and full configuration options, see the KSP Configuration Guide.
The me.kpavlov.kt.schema.config processor option selects the schema flavour (strict, lenient, openapi or your
own JsonSchemaConfig) for both KSP and the Java annotation processor. See
Configuration.
For JVM-only scenarios with classes you don't own or can't annotate, ReflectionClassJsonSchemaGenerator builds the
schema from Kotlin reflection at runtime. Add me.kpavlov.kt.schema:kt-schema-generator-json:<version> and call
generateSchema(User::class). See the Runtime Reflection guide.
Schemas follow JSON Schema Draft 2020-12: $id, $defs/$ref deduplication, ["string", "null"] unions for
nullable types, oneOf with a discriminator for sealed hierarchies, and description from @Description or KDoc.
Worked examples for data classes, enums, nested objects, generics and sealed classes, plus the @Schema and
@Description reference, are in the Generated Schemas guide.
ReflectionFunctionCallingSchemaGenerator (runtime) and @Schema on functions (KSP, compile time) produce tool
definitions in the OpenAI Responses API shape, with strict: true, parameter descriptions and ["string", "null"]
unions for nullable parameters. The same parameters object serves as MCP's inputSchema and as Anthropic's
input_schema, except that Anthropic strict tool use rejects numeric and string-length constraints. See the
Function Calling guide.
kt-schema recognizes @Description, Koog's @LLMDescription, Jackson's @JsonPropertyDescription/
@JsonClassDescription, LangChain4j's @P, plus Jackson/kotlinx.serialization name overrides, ignore markers and
discriminators, so existing annotated classes need no changes. Detection is configurable through
kt-schema.properties. See Configuration.
For manual schema construction, the kt-schema-json module provides type-safe models and a DSL for JSON Schema Draft 2020-12. See the JSON Schema DSL guide and kt-schema-json/README.md.
For build instructions, development setup, and contribution guidelines, see CONTRIBUTING.md.
@Schema parameters can be mistaken for defaults)Tip: If you use @Description on primary constructor parameters, enable
-Xannotation-default-target=param-property in Kotlin compiler options so the description applies to the backing
property.
This project and the corresponding community are governed by the Contributor Covenant Code of Conduct. Please make sure you read and adhere to it.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
kt-schema is an independently maintained fork of kotlinx.schema. Konstantin Pavlov originally started the work while at JetBrains.
This project contains modified and unmodified portions of the original work.
Notable changes include renaming the Kotlin packages from kotlinx.schema to me.kpavlov.kt.schema, renaming Maven
coordinates from org.jetbrains.kotlinx to me.kpavlov, and further independent development.
See the LICENSE and NOTICE files for licensing and attribution information.
kt-schema is an independent project and is not affiliated with, endorsed by, or sponsored by JetBrains. "Kotlin" and "JetBrains" are trademarks of their respective owners.
Generate JSON schemas and LLM function calling schemas from Java and Kotlin code — including classes you don't own.
Full documentation lives at kpavlov.github.io/kt-schema. This README is the short tour; each topic below links to its guide.
@Schema/@Description referenceGeneration Modes:
SerializersModule
LLM Integration:
additionalProperties: false, null unions for optional valuesFlexible Annotation Support:
@Description, @LLMDescription, @JsonPropertyDescription, @P, and moreComprehensive Type Support:
SerializersModule) with automatic oneOf
generation and discriminator field["string", "null"])$ref/$defs deduplication: named types appear once in $defs and are referenced everywhere via $ref
kotlin.Any: maps to the empty schema {} (accepts any JSON value)Developer Experience:
[!TIP] Need to build JSON Schemas manually? The kt-schema-json module provides type-safe Kotlin models and DSL compliant with JSON Schema Draft 2020-12, with support for polymorphism, discriminators, and type-safe enums. See the JSON Schema DSL guide.
Table of contents:
This library solves three key challenges:
@Description-like annotations from other frameworks| 🔧 KSP Processor | ☕ Java APT | 📦 Serialization-based | 🔍 Runtime Reflection | |
|---|---|---|---|---|
| Platforms | JVM + Multiplatform | JVM only | JVM + Multiplatform | JVM only |
| When generated | Compile-time | Compile-time | Runtime | Runtime |
| Requires annotation processor | Yes (KSP) | Yes (APT) | No | No |
Class must be @Serializable |
No | No | Yes | No |
Annotate class with @Schema |
Required | Not required¹ | Not required | Not required |
| KDoc extracted to description | ✅ | ❌ | ❌ | ❌ |
| Extract default values | Partial² | Partial² | ❌ | ✅ |
| Third-party classes | ❌ | ❌ | ✅ (only @Serializable) |
✅ any JVM class |
¹ with the rootPackage option — see Java Annotation Processor Guide.
² Jackson's @JsonEnumDefaultValue becomes the enum's default. Kotlin/Java default-value expressions can't be
evaluated at compile time, so no property default is emitted — see
Multi-Framework Annotation Support.
Pick KSP when you own the classes, want zero runtime overhead, and target Multiplatform or need KDoc in your schema.
Pick Java APT when your code is plain Java (no Kotlin) and you want zero runtime overhead. Supports Java records, classes, interfaces and enums.
Pick Serialization-based when your classes are already @Serializable and you need
Multiplatform support without a build-time processor.
Pick Reflection when you need JVM-only runtime generation for third-party classes,
or
need to extract data class default values. Works equally well for @Serializable classes on JVM.
Refer to the example projects here.
/**
* A postal address for deliveries and billing.
*/
@Schema
data class Address(
@Description("Street address, including house number") @Size(max = 120) val street: String,
@Description("City or town name") @Size(min = 1, max = 80) val city: String,
@Description("Postal or ZIP code") @Pattern(regexp = "[0-9]{5}") val zipCode: String,
@Description("Two-letter ISO country code; defaults to US") @Pattern(regexp = "[A-Z]{2}") val country: String = "US",
)strict config){
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "com.example.Address",
"description": "A postal address for deliveries and billing.",
"type": "object",
"properties": {
"street": {
"type": "string",
"description": "Street address, including house number",
"maxLength": 120
},
"city": {
"type": "string",
"description": "City or town name",
"minLength": 1,
"maxLength": 80
},
"zipCode": {
"type": "string",
"description": "Postal or ZIP code",
"pattern": "[0-9]{5}"
},
"country": {
"type": "string",
"description": "Two-letter ISO country code; defaults to US",
"pattern": "[A-Z]{2}"
}
},
"additionalProperties": false,
"required": [
"street",
"city",
"zipCode",
"country"
]
}Note: KDoc comments on classes can also be used as descriptions. Jakarta Bean Validation annotations such as
@Sizeand@PatternbecomemaxLength,minLengthandpattern— see Validation Constraints.
Add the Google KSP plugin and the processor dependency:
// Multiplatform (KMP)
plugins {
kotlin("multiplatform")
id("com.google.devtools.ksp") version "<ksp-version>"
}
dependencies {
add("kspCommonMainMetadata", "me.kpavlov.kt.schema:kt-schema-ksp:<version>")
implementation("me.kpavlov.kt.schema:kt-schema-annotations:<version>")
}
kotlin {
sourceSets.commonMain.kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}For JVM-only projects and full configuration options, see the KSP Configuration Guide.
The me.kpavlov.kt.schema.config processor option selects the schema flavour (strict, lenient, openapi or your
own JsonSchemaConfig) for both KSP and the Java annotation processor. See
Configuration.
For JVM-only scenarios with classes you don't own or can't annotate, ReflectionClassJsonSchemaGenerator builds the
schema from Kotlin reflection at runtime. Add me.kpavlov.kt.schema:kt-schema-generator-json:<version> and call
generateSchema(User::class). See the Runtime Reflection guide.
Schemas follow JSON Schema Draft 2020-12: $id, $defs/$ref deduplication, ["string", "null"] unions for
nullable types, oneOf with a discriminator for sealed hierarchies, and description from @Description or KDoc.
Worked examples for data classes, enums, nested objects, generics and sealed classes, plus the @Schema and
@Description reference, are in the Generated Schemas guide.
ReflectionFunctionCallingSchemaGenerator (runtime) and @Schema on functions (KSP, compile time) produce tool
definitions in the OpenAI Responses API shape, with strict: true, parameter descriptions and ["string", "null"]
unions for nullable parameters. The same parameters object serves as MCP's inputSchema and as Anthropic's
input_schema, except that Anthropic strict tool use rejects numeric and string-length constraints. See the
Function Calling guide.
kt-schema recognizes @Description, Koog's @LLMDescription, Jackson's @JsonPropertyDescription/
@JsonClassDescription, LangChain4j's @P, plus Jackson/kotlinx.serialization name overrides, ignore markers and
discriminators, so existing annotated classes need no changes. Detection is configurable through
kt-schema.properties. See Configuration.
For manual schema construction, the kt-schema-json module provides type-safe models and a DSL for JSON Schema Draft 2020-12. See the JSON Schema DSL guide and kt-schema-json/README.md.
For build instructions, development setup, and contribution guidelines, see CONTRIBUTING.md.
@Schema parameters can be mistaken for defaults)Tip: If you use @Description on primary constructor parameters, enable
-Xannotation-default-target=param-property in Kotlin compiler options so the description applies to the backing
property.
This project and the corresponding community are governed by the Contributor Covenant Code of Conduct. Please make sure you read and adhere to it.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
kt-schema is an independently maintained fork of kotlinx.schema. Konstantin Pavlov originally started the work while at JetBrains.
This project contains modified and unmodified portions of the original work.
Notable changes include renaming the Kotlin packages from kotlinx.schema to me.kpavlov.kt.schema, renaming Maven
coordinates from org.jetbrains.kotlinx to me.kpavlov, and further independent development.
See the LICENSE and NOTICE files for licensing and attribution information.
kt-schema is an independent project and is not affiliated with, endorsed by, or sponsored by JetBrains. "Kotlin" and "JetBrains" are trademarks of their respective owners.