agentsclimarketplace

Kora openapi generator client

Skill kora-projects/kora-skills/plugins/kora-v1/skills/kora-openapi-generator-client

Generates declarative Kora HTTP clients from an OpenAPI 3.x contract using the org.openapi.generator Gradle plugin with generatorName "kora". Produces typed *Api interfaces whose methods return sealed *ApiResponses wrappers, plus model records. Use when scaffolding a Gradle GenerateTask for an OpenAPI client, choosing a client mode (java-client, java-async-client, java-reactive-client, kotlin-client, kotlin-suspend-client), wiring the generated *Api into a @Component, setting clientConfigPrefix, attaching @InterceptWith auth interceptors (ApiKeyHttpClientInterceptor, BasicAuthHttpClientInterceptor, BearerAuthHttpClientInterceptor) or generator securityConfigPrefix/primaryAuth, or testing the client with @KoraAppTest and MockServer.From its SKILL.md

Install
npx -y skills add kora-projects/kora-skills --skill kora-openapi-generator-client

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.6 KB, ~2.7k tokens by cl100k_base, as published. Nobody here has run it

Kora OpenAPI Generator — HTTP Client

Generate a typed, declarative Kora HTTP client from an OpenAPI 3.x contract. The org.openapi.generator Gradle plugin with generatorName = "kora" emits a @HttpClient-backed *Api interface plus model records at build time. Inject the *Api into a @Component and call its methods.

This skill covers clients only. For OpenAPI server handlers (*-server modes, delegates, HttpServerPrincipalExtractor), use the kora-openapi-generator-server skill.

Quick Start

1. Plugin and buildscript dependency

buildscript {
    dependencies {
        classpath "ru.tinkoff.kora:openapi-generator:$koraVersion"
    }
}

plugins {
    id "java"
    id "application"
    id "org.openapi.generator" version "7.14.0" // pin exactly; other versions are not guaranteed compatible
}

2. Runtime modules

A generated client needs an HTTP client transport plus the JSON module. Pick one transport:

dependencies {
    koraBom platform("ru.tinkoff.kora:kora-parent:1.2.17")
    annotationProcessor "ru.tinkoff.kora:annotation-processors"   // MANDATORY — nothing generates without it

    implementation "ru.tinkoff.kora:http-client-jdk"   // JdkHttpClientModule (or http-client-ok / http-client-async)
    implementation "ru.tinkoff.kora:json-module"
    implementation "ru.tinkoff.kora:config-hocon"
    implementation "ru.tinkoff.kora:logging-logback"
}

@KoraApp plugs the transport module in:

@KoraApp
public interface Application extends
        HoconConfigModule,
        LogbackModule,
        JsonModule,
        JdkHttpClientModule {   // ru.tinkoff.kora.http.client.jdk.JdkHttpClientModule

    static void main(String[] args) {
        KoraApplication.run(ApplicationGraph::graph); // ru.tinkoff.kora.application.graph.KoraApplication
    }
}

3. Generation task (one per spec, unique outputDir)

import org.openapitools.generator.gradle.plugin.tasks.GenerateTask

def openApiGenerateHttpClient = tasks.register("openApiGenerateHttpClient", GenerateTask) {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/pet.yaml"
    outputDir = "$buildDir/generated/pet-client"     // unique per task — required for incremental builds
    def corePackage = "com.example.pet"
    apiPackage = "${corePackage}.api"
    modelPackage = "${corePackage}.model"
    invokerPackage = "${corePackage}.invoker"
    configOptions = [
        mode              : "java-client",
        clientConfigPrefix: "httpClient.pet",        // config root; the Api name is appended (httpClient.pet.PetApi)
    ]
}
sourceSets.main { java.srcDirs += openApiGenerateHttpClient.get().outputDir }
compileJava.dependsOn openApiGenerateHttpClient

4. Inject and call the generated *Api

Generated methods return a sealed *ApiResponses wrapper, not the bare model. Pattern match the response.

@Component
public final class PetService {

    private final PetApi petApi;

    public PetService(PetApi petApi) {
        this.petApi = petApi;
    }

    public Pet getPet(long id) {
        var response = petApi.getPetById(id);
        if (response instanceof PetApiResponses.GetPetByIdApiResponse.GetPetById200ApiResponse ok) {
            return ok.content();
        }
        throw new IllegalStateException("Unexpected response: " + response);
    }
}

5. Configure the client (HOCON)

httpClient.pet.PetApi {
  url = ${PET_API_URL}
  requestTimeout = 10s
  telemetry.logging.enabled = true
}

What's in this skill

FilePurpose
references/openapi-codegen-reference.mdFull configOptions, modes, interceptors, tags, normalizer, discriminators
references/authorization-reference.mdClient-side auth: interceptors and generator securityConfigPrefix/primaryAuth
assets/build.gradle.client.templateReady-to-edit client build.gradle
assets/Application.client.java.template / .kt@KoraApp module wiring
assets/PetService.client.java.template / .kt*Api injection + response pattern matching
assets/openapi-spec.yaml.templateExample OpenAPI 3.x spec (includes a discriminator)
scripts/validate_openapi.pyPre-generation spec linter

Generation modes

ModeDescriptionMethod shape
java-clientSynchronous (recommended)T method(...)
java-async-clientCompletionStageCompletionStage<T> method(...)
java-reactive-clientProject Reactor — add io.projectreactor:reactor-core yourselfMono<T> / Flux<T>
kotlin-clientKotlin synchronousfun method(...): T
kotlin-suspend-clientKotlin coroutinessuspend fun method(...): T

The return type T is always a sealed *ApiResponses.*ApiResponse, wrapped in the async/reactive container for the corresponding mode.

Transport choice

A generated client depends on an HTTP client transport module — choose one and plug its module into @KoraApp:

ArtifactModulePackage
ru.tinkoff.kora:http-client-jdkJdkHttpClientModuleru.tinkoff.kora.http.client.jdk
ru.tinkoff.kora:http-client-okOkHttpClientModuleru.tinkoff.kora.http.client.ok
ru.tinkoff.kora:http-client-asyncAsyncHttpClientModuleru.tinkoff.kora.http.client.async

The generator is transport-agnostic; the same generated *Api works with any of them.


When to use vs NOT

Use this skill when:

  • you have an OpenAPI 3.x contract and want a typed outbound client,
  • you want request/response models and a @HttpClient-backed *Api generated at build time,
  • you are wiring clientConfigPrefix, interceptors, tags, or generator-driven auth.

Do NOT use this skill when:

  • you are generating server handlers/delegates → kora-openapi-generator-server,
  • you are hand-writing a @HttpClient interface without a spec → kora-http-client,
  • you only need client-side auth interceptors on a hand-written client → kora-http-client-auth.

Core patterns

Multiple specs in one module

Each spec gets its own GenerateTask with a unique outputDir and its own corePackage. Repeat the sourceSets/dependsOn lines per task. Sharing an outputDir breaks Gradle incremental builds and caching.

Response wrappers

For GET /pet/{id} returning 200 and 404, the generator emits a sealed interface PetApiResponses.GetPetByIdApiResponse with nested record variants GetPetById200ApiResponse (carrying .content()) and GetPetById404ApiResponse. Use instanceof pattern matching (Java) or is/when (Kotlin) to branch.

var response = petApi.getPetById(id);
return switch (response) {
    case PetApiResponses.GetPetByIdApiResponse.GetPetById200ApiResponse ok -> ok.content();
    case PetApiResponses.GetPetByIdApiResponse.GetPetById404ApiResponse nf -> throw new NotFoundException(id);
    default -> throw new IllegalStateException("Unexpected: " + response);
};

Per-client configuration

clientConfigPrefix is the config root; the generated *Api class name is appended. With clientConfigPrefix = "httpClient.pet" and a PetApi, the config section is httpClient.pet.PetApi. Per-operation overrides nest under <operationId>Config:

httpClient.pet.PetApi {
  url = ${PET_API_URL}
  requestTimeout = 10s
  getPetByIdConfig { requestTimeout = 20s }
  telemetry { logging.enabled = true, metrics.enabled = true }
}

Authorization (two routes)

  1. Generator-driven — when the spec declares securitySchemes, set primaryAuth and securityConfigPrefix in configOptions. Credentials come from config under <securityConfigPrefix>.<schemeName>.
  2. Manual interceptor — provide an auth interceptor (ApiKeyHttpClientInterceptor, BasicAuthHttpClientInterceptor, BearerAuthHttpClientInterceptor) as a @Module component and attach it with @InterceptWith.

Full detail in references/authorization-reference.md.

Testing the client

Stub the remote API with a MockServer container and inject the *Api via @TestComponent; point the client at the stub with KoraAppTestConfigModifier.

@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class PetApiTest implements KoraAppTestConfigModifier {

    @ConnectionMockServer
    private MockServerConnection mockserver;

    @TestComponent
    private PetApi petApi;

    @Override
    public KoraConfigModification config() {
        return KoraConfigModification.ofSystemProperty("PET_API_URL", mockserver.params().uri().toString());
    }

    @Test
    void getPetById() {
        mockserver.client()
            .when(request().withMethod("GET").withPath("/v2/pet/1"))
            .respond(response().withBody("{\"id\":1,\"name\":\"Rex\",\"status\":\"available\"}"));

        var response = petApi.getPetById(1L);
        assertTrue(response instanceof PetApiResponses.GetPetByIdApiResponse.GetPetById200ApiResponse);
    }
}

Test dependency: ru.tinkoff.kora:test-junit5 plus io.goodforgod:testcontainers-extensions-mockserver.


Common pitfalls

SymptomCause / fix
Generated method "returns the model" assumption fails to compileMethods return sealed *ApiResponses.*ApiResponse; pattern match and call .content()
"Required dependency PetApi not found"Missing transport module on @KoraApp (JdkHttpClientModule / OkHttpClientModule / AsyncHttpClientModule) or json-module absent
Config not picked upPrefix must include the Api class name: httpClient.pet.PetApi, not httpClient.pet
Stale or duplicated generated classesTwo GenerateTasks share an outputDir; give each a unique directory and clean build/generated/
Unexpected oneOf/anyOf outputPlugin ≥ 7.0.0 enables SIMPLIFY_ONEOF_ANYOF; set openapiNormalizer = [DISABLE_ALL: "true"]
Plugin task type unresolvedAdd import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
Reactive mode fails to compilejava-reactive-client needs io.projectreactor:reactor-core added manually

Source of truth

  • Doc: .kora-agent/kora-docs/mkdocs/docs/en/documentation/openapi-codegen.md
  • HTTP client doc: .kora-agent/kora-docs/mkdocs/docs/en/documentation/http-client.md
  • Guide: .kora-agent/kora-docs/mkdocs/docs/en/guides/openapi-http-client.md
  • Examples: .kora-agent/kora-examples/examples/java/kora-java-openapi-generator-http-client, .kora-agent/kora-examples/guides/java/kora-java-guide-openapi-http-client-app

What ships with it: 10 files

49.5 KB alongside SKILL.md, 1 of them executable

evals/

scripts/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.