KSP Processor Configuration
Table of contents
Generate JSON schemas at compile time with zero runtime overhead using the kt-schema KSP processor.
Setup
Configure the KSP processor with the Google KSP Gradle plugin.
Google KSP gradle plugin
Add the Google KSP plugin (2.3.1 or newer) and processor dependency to your project.
Multiplatform projects
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")
}
// Ensure KSP runs before compilation
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().all {
if (name != "kspCommonMainKotlinMetadata") dependsOn("kspCommonMainKotlinMetadata")
}
ksp {
arg("me.kpavlov.kt.schema.rootPackage", "com.example")
}Check out an example project .
JVM-only projects
plugins {
kotlin("jvm")
id("com.google.devtools.ksp") version "<ksp-version>"
}
dependencies {
ksp("me.kpavlov.kt.schema:kt-schema-ksp:<version>")
implementation("me.kpavlov.kt.schema:kt-schema-annotations:<version>")
}
sourceSets.main.kotlin.srcDir("build/generated/ksp/main/kotlin")Configuration options
Options can be set globally in your build configuration or overridden per-class via @Schema.
Options reference
| Option | Type | Default | Description |
|---|---|---|---|
enabled | Boolean | true | Enable or disable schema generation. |
rootPackage | String | null | Limit processing to this package and its subpackages. Improves build performance. |
include | String | null | Comma- or semicolon-separated glob patterns. Only matching symbols are processed. |
exclude | String | null | Comma- or semicolon-separated glob patterns. Matching symbols are always skipped. |
withSchemaObject | Boolean | false | Generate jsonSchema: JsonObject property. Requires kotlinx-serialization-json. |
visibility | String | "" | Visibility modifier for generated extensions (public, internal, etc.). |
config | String | null | Schema flavour: strict, lenient, openapi or a JsonSchemaConfig class name. Unset: classes use strict, functions use the function-calling default. See Schema configuration
. |
Filtering by class/function name
Use me.kpavlov.kt.schema.include and me.kpavlov.kt.schema.exclude to control exactly which annotated classes
and functions get schemas generated โ without touching the annotations themselves.
Both options accept a comma- or semicolon-separated list of glob patterns matched against the fully qualified name.
Glob syntax:
| Pattern | Matches |
|---|---|
* | Any sequence of characters within a single package segment (no .) |
** | Any sequence of characters across package segments (including .) |
? | Any single character (no .) |
Rules:
- If
includeis set, only symbols matching at least one pattern are processed. Symbols without a qualified name (e.g. local or anonymous declarations) are excluded when any include pattern is present. excludeis applied afterincludeโ a symbol matching any exclude pattern is always skipped.- Both options apply to classes and functions, and work alongside
rootPackage; the root package filter runs first.
Google KSP plugin
ksp {
arg("me.kpavlov.kt.schema.include", "com.example.api.**, com.example.dto.**")
arg("me.kpavlov.kt.schema.exclude", "**.internal.**, **.*Internal")
}For large projects, combine rootPackage with include for maximum build performance:
rootPackage narrows the KSP symbol scan, then include filters the remaining candidates.
Option priority
- Annotation Parameter (highest) โ
@Schema(withSchemaObject = true) - KSP Argument โ Global processor options passed through the Gradle
ksp { arg(...) }block - Default Value (lowest)
Use an empty string visibility.set("") (default) for Multiplatform projects targeting Native
to avoid “redundant visibility modifier” warnings.
Generated Code
For each @Schema-annotated class, the processor generates extension properties:
@Schema(withSchemaObject = true)
data class User(val name: String)Access generated extensions
val jsonString: String = User::class.jsonSchemaString
val jsonObject: JsonObject = User::class.jsonSchemaFor each @Schema-annotated function, the processor generates additional top-level or extension function:
@Schema(withSchemaObject = true)
internal fun calculateArea(shape: Shape): Double = TODO("only signature matters")Access generated functions:
val functionCallSchemaString: String = calculateAreaJsonSchemaString() // <function name> + "jsonSchemaString()"
val functionCallSchema: JsonObject = calculateAreaJsonSchema() // <function name> + "jsonSchema()" A parameter with a Kotlin default value (e.g. unit: String = "celsius") is optional: with the lenient, openapi
or a custom non-strict config it is left out of required. The default strict config requires every parameter,
as OpenAI strict mode demands. KSP reports only that a default exists, not its value, so no default is emitted.
Jakarta Bean Validation annotations on properties and function parameters (@Min(1) quantity: Int) become JSON Schema
keywords. See the Validation Constraints guide
.
See Also
- Serialization-Based Generator
โ Runtime generation for any
@Serializableclass (no KSP required) - Annotation Reference
โ
@Schemaand@Descriptionusage - Runtime Schema Generation โ Alternative using Reflection (JVM only)
- Function Calling Schemas โ Generate LLM function schemas
- JSON Schema DSL โ Manual schema construction