agentsclimarketplace

Kora grpc client

Skill kora-projects/kora-skills/plugins/kora-v1/skills/kora-grpc-client

Agent Skills for Kora Framework — compile-time DI for Java/Kotlin backend development.

Install
npx -y skills add kora-projects/kora-skills --skill kora-grpc-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.

What its author says it does

Copied from the file, not written here

Builds gRPC clients in Kora via GrpcClientModule, the protobuf Gradle plugin, and generated stubs injected directly into components. Covers grpcClient.<ServiceName> HOCON/YAML config, plaintext vs TLS through the URL scheme, custom ClientInterceptor scoped with @Tag(ServiceGrpc.class) for metadata auth and logging, and unary plus server/client/bidirectional streaming with blocking and async stubs. Use when wiring an outbound gRPC call, injecting a *BlockingStub / *FutureStub / *Stub, adding authorization headers via gRPC metadata, configuring keepAliveTime/timeout/loadBalancingPolicy, or debugging UNAVAILABLE/UNAUTHENTICATED and missing-stub graph errors.

SKILL.md

9.6 KB, as published. Nobody here has run it

Kora gRPC Client

Generate gRPC client stubs from a .proto contract and inject them as components through GrpcClientModule. Kora wires the configured channel and the generated stubs into the application graph; your code just builds protobuf requests and calls stub methods.

Read this first when:

  • enabling GrpcClientModule and injecting a generated *BlockingStub/*FutureStub/*Stub,
  • configuring a client under grpcClient.<ServiceName>.*,
  • adding a ClientInterceptor for metadata headers, auth, or logging,
  • making unary or streaming calls.

Key facts (do not get these wrong)

  • Stubs are injected directly by type — no @Tag on the constructor parameter. Kora produces one stub per generated *Grpc class. Injecting UserServiceGrpc.UserServiceBlockingStub is enough.
  • @Tag(ServiceGrpc.class) belongs on a ClientInterceptor, to scope that interceptor to one service's client. It is not used for stub injection.
  • Annotations come from ru.tinkoff.kora.common.* (@Component, @Tag, @KoraApp), not from any annotation.processor.* package.
  • Plaintext vs TLS is chosen by the URL scheme (http:// = plaintext, https:///grpc:// per transport). There is no usePlaintext config key.
  • The mandatory annotation processor must be present: Java annotationProcessor "ru.tinkoff.kora:annotation-processors", Kotlin ksp "ru.tinkoff.kora:symbol-processors".

Quick Start

1. Dependencies

Pin the BOM (kora-parent); never version individual ru.tinkoff.kora:* artifacts.

plugins {
    id "application"
    id "com.google.protobuf" version "0.9.4"
}

configurations {
    koraBom
    annotationProcessor.extendsFrom(koraBom)
    implementation.extendsFrom(koraBom)
}

dependencies {
    koraBom platform("ru.tinkoff.kora:kora-parent:1.2.17")
    annotationProcessor "ru.tinkoff.kora:annotation-processors"

    implementation "ru.tinkoff.kora:grpc-client"
    implementation "ru.tinkoff.kora:config-hocon"
    implementation "ru.tinkoff.kora:logging-logback"
    implementation "io.grpc:grpc-protobuf:1.74.0"
    compileOnly "javax.annotation:javax.annotation-api:1.3.2"
}

protobuf {
    protoc { artifact = "com.google.protobuf:protoc:3.25.3" }
    plugins {
        grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.74.0" }
    }
    generateProtoTasks {
        all()*.plugins { grpc {} }
    }
}

sourceSets {
    main {
        java {
            srcDirs "build/generated/source/proto/main/grpc"
            srcDirs "build/generated/source/proto/main/java"
        }
    }
}

2. Define the protobuf service

src/main/proto/user_service.proto:

syntax = "proto3";
package ru.tinkoff.kora.example.grpc;
option java_multiple_files = true;

service UserService {
  rpc GetUser (GetUserRequest) returns (UserResponse) {}
}

message GetUserRequest { string user_id = 1; }
message UserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}

3. Enable the module

import ru.tinkoff.grpc.client.GrpcClientModule;
import ru.tinkoff.kora.application.graph.KoraApplication;
import ru.tinkoff.kora.common.KoraApp;
import ru.tinkoff.kora.config.hocon.HoconConfigModule;
import ru.tinkoff.kora.logging.logback.LogbackModule;

@KoraApp
public interface Application extends HoconConfigModule, LogbackModule, GrpcClientModule {
    static void main(String[] args) {
        KoraApplication.run(ApplicationGraph::graph);
    }
}

4. Inject the stub and wrap it in a service

Inject the generated stub directly by type. Wrap it so protobuf builders and gRPC status handling stay at the transport boundary.

import ru.tinkoff.kora.common.Component;
import ru.tinkoff.kora.example.grpc.UserServiceGrpc;
import ru.tinkoff.kora.example.grpc.GetUserRequest;

@Component
public final class UserClientService {

    private final UserServiceGrpc.UserServiceBlockingStub userService;

    public UserClientService(UserServiceGrpc.UserServiceBlockingStub userService) {
        this.userService = userService;
    }

    public UserDto getUser(String userId) {
        var response = userService.getUser(GetUserRequest.newBuilder()
            .setUserId(userId)
            .build());
        return new UserDto(response.getId(), response.getName(), response.getEmail());
    }
}

5. Configure the client

A service named UserService is configured under grpcClient.UserService. Use the URL scheme to control plaintext vs TLS; externalize the URL with ${?VAR}.

grpcClient {
  UserService {
    url = "http://localhost:8090"   // http:// => plaintext for local
    url = ${?GRPC_SERVER_URL}
    timeout = "10s"
    telemetry.logging.enabled = true
  }
}

Stub types

StubUse for
*BlockingStubUnary calls and server-streaming reads (returns Iterator<Response>)
*FutureStubUnary calls returning ListenableFuture<Response>
*Stub (async)Client-streaming and bidirectional streaming via StreamObserver

A service may inject several stub types at once (e.g. a *BlockingStub for reads and a *Stub for client streaming). See grpc-client-stubs-reference.md.

Client interceptors

Register a ClientInterceptor as a @Component and scope it to one service with @Tag(ServiceGrpc.class). Kora applies it to that service's channel automatically; it does not change stub injection.

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import ru.tinkoff.kora.common.Component;
import ru.tinkoff.kora.common.Tag;
import ru.tinkoff.kora.example.grpc.UserServiceGrpc;

@Tag(UserServiceGrpc.class)
@Component
public final class AuthInterceptor implements ClientInterceptor {

    private static final Metadata.Key<String> AUTHORIZATION =
        Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);

    private final AuthConfig authConfig;

    public AuthInterceptor(AuthConfig authConfig) {
        this.authConfig = authConfig;
    }

    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
            MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
        return new ForwardingClientCall.SimpleForwardingClientCall<>(next.newCall(method, callOptions)) {
            @Override
            public void start(Listener<RespT> responseListener, Metadata headers) {
                headers.put(AUTHORIZATION, authConfig.value());
                super.start(responseListener, headers);
            }
        };
    }
}

The default GrpcClientConfigInterceptor (applies grpcClient.* config) is always present. See grpc-client-interceptors-reference.md for logging, metadata, and GraphInterceptor patterns.

Streaming

Server streaming can be consumed with a *BlockingStub (returns an Iterator) or asynchronously with a *Stub. Client and bidirectional streaming require the async *Stub and a StreamObserver. See grpc-client-streaming-reference.md.

Configuration

grpcClient.<ServiceName>.* keys: url (required), timeout, keepAliveTime, keepAliveTimeout, loadBalancingPolicy, maxInboundMessageSize, and telemetry.{logging,metrics,tracing}. Full reference (HOCON + YAML, defaults, env substitution): grpc-client-config-reference.md.

Common pitfalls

SymptomFix
Required dependency not found: ...BlockingStubEnable GrpcClientModule on @KoraApp; run ./gradlew classes so protobuf + processors run
Stub class does not existRun protobuf generation and add the build/generated/source/proto/... source dirs to sourceSets
Tried to add @Tag(...) on a stub parameterRemove it — stubs inject by type; @Tag is only for interceptors
UNAVAILABLECheck grpcClient.<ServiceName>.url host/port and that the server is up
UNAUTHENTICATEDAdd an AuthInterceptor (above) putting credentials into Metadata
Interceptor never runsEnsure @Component + @Tag(ServiceGrpc.class) matches the generated class exactly
Wanted plaintext but got TLSUse an http:// URL; there is no usePlaintext key

References & assets

FilePurpose
references/grpc-client-stubs-reference.mdStub types, direct injection, service wrappers
references/grpc-client-config-reference.mdFull config + protobuf plugin setup
references/grpc-client-interceptors-reference.mdInterceptor patterns, metadata, GraphInterceptor
references/grpc-client-streaming-reference.mdServer/client/bidirectional streaming
assets/README.mdTemplate catalog and usage
</invoke>

Keep looking

Skills are one crate of 328,083. 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.