agentsclimarketplace

Kora grpc server

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

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-server

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 server handlers in Kora using GrpcServerModule, @Component handlers that extend the generated *GrpcImplBase, io.grpc ServerInterceptor beans, grpcServer HOCON/YAML config (port, maxMessageSize, telemetry), the com.google.protobuf Gradle plugin, and gRPC Server Reflection. Use when serving gRPC RPCs (unary/server/client/bidirectional streaming) from a Kora service, mapping protobuf messages to a service layer, returning io.grpc.Status errors, or enabling reflection for grpcurl. Triggers on grpc-server, GrpcServerModule, *GrpcImplBase, StreamObserver, ServerInterceptor, reflectionEnabled.

SKILL.md

11.3 KB, as published. Nobody here has run it

Kora gRPC Server

Serve gRPC RPCs from a Kora application. The .proto contract is the source of truth: the protobuf Gradle plugin generates message classes and a *Grpc.*ImplBase base type, and you implement a Kora @Component that extends that base. GrpcServerModule discovers every @Component handler and ServerInterceptor and starts the server — no reflection-based wiring, everything is resolved through the compile-time graph.

When to use vs NOT

Use this skill when:

  • implementing gRPC handlers that extend the generated *GrpcImplBase,
  • wiring GrpcServerModule into a @KoraApp,
  • configuring grpcServer (port, message size, telemetry, keepalive, reflection),
  • adding io.grpc.ServerInterceptor beans for auth/logging/metrics,
  • enabling gRPC Server Reflection for grpcurl.

Do NOT use this skill for:

  • consuming gRPC services (declarative stubs) — that is the kora-grpc-client skill,
  • HTTP/JSON endpoints — use kora-http-server.

Quick Start

1. Dependencies (BOM pins all ru.tinkoff.kora:* versions)

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

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

    // Optional: gRPC Server Reflection (grpcurl / Postman gRPC)
    implementation "io.grpc:grpc-services:1.74.0"
}

Kotlin: replace the processor with ksp "ru.tinkoff.kora:symbol-processors". Never put a version on a ru.tinkoff.kora:* artifact — the BOM controls them.

2. Protobuf Gradle plugin

plugins {
    id "com.google.protobuf" version "0.10.0"
}

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"
    }
}

3. .proto contract (src/main/proto/user_service.proto)

syntax = "proto3";

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

import "google/protobuf/timestamp.proto";

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

message CreateUserRequest { string name = 1; string email = 2; }
message GetUserRequest { string user_id = 1; }
message UserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
  google.protobuf.Timestamp created_at = 4;
}

4. Application module

@KoraApp
public interface Application extends
        HoconConfigModule,
        LogbackModule,
        GrpcServerModule {

    static void main(String[] args) {
        KoraApplication.run(ApplicationGraph::graph);
    }
}

5. Handler — @Component extending the generated *ImplBase

import io.grpc.Status;
import io.grpc.stub.StreamObserver;
import ru.tinkoff.kora.common.Component;

@Component
public final class UserServiceGrpcHandler extends UserServiceGrpc.UserServiceImplBase {

    private final UserService userService;

    public UserServiceGrpcHandler(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void getUser(GetUserRequest request, StreamObserver<UserResponse> responseObserver) {
        var user = userService.getUser(request.getUserId())
            .orElseThrow(() -> Status.NOT_FOUND
                .withDescription("User not found: " + request.getUserId())
                .asRuntimeException());
        responseObserver.onNext(toGrpcUser(user));
        responseObserver.onCompleted();
    }
}

The handler is a plain Kora component: constructor injection, business logic delegated to UserService. The generated protobuf types are transport DTOs — keep domain logic out of the handler.

6. Configuration (application.conf)

grpcServer {
  port = ${GRPC_PORT}
  telemetry.logging.enabled = true
}

7. Run and probe

./gradlew clean classes   # generate proto + build the compile-time graph
./gradlew run
grpcurl -plaintext -d '{"user_id":"42"}' \
  localhost:8090 ru.tinkoff.kora.example.grpc.UserService/GetUser

The gRPC method's full name is <proto package>.<service>/<Method>.


References

FilePurpose
references/grpc-server-reference.mdModule, dependency, protobuf plugin, default interceptors, troubleshooting
references/grpc-service-reference.mdHandler patterns: unary + all three streaming kinds, message conversion
references/grpc-config-reference.mdFull grpcServer config, telemetry tags/attributes, Netty transport, metrics
references/grpc-interceptors-reference.mdServerInterceptor beans: auth, logging, metrics, exception mapping, ordering
references/grpc-error-handling-reference.mdio.grpc.Status codes, error metadata, streaming errors
references/grpc-reflection-reference.mdReflection setup and grpcurl/Postman usage

Assets (templates): see assets/README.md.


Core patterns

RPC method signatures

RPC typeHandler signature
Unaryvoid method(Req, StreamObserver<Resp>)
Server streamingvoid method(Req, StreamObserver<Resp>) — many onNext, one onCompleted
Client streamingStreamObserver<Req> method(StreamObserver<Resp>)
Bidirectional streamingStreamObserver<Req> method(StreamObserver<Resp>)

Server streaming: emit each item with onNext, then a single onCompleted.

@Override
public void getAllUsers(Empty request, StreamObserver<UserResponse> responseObserver) {
    for (var user : userService.getAllUsers()) {
        responseObserver.onNext(toGrpcUser(user));
    }
    responseObserver.onCompleted();
}

Client/bidirectional streaming: return a StreamObserver<Req> that accumulates onNext values and replies on onCompleted.

@Override
public StreamObserver<CreateUserRequest> createUsers(StreamObserver<CreateUsersResponse> responseObserver) {
    return new StreamObserver<>() {
        private final List<UserRequest> requests = new ArrayList<>();
        public void onNext(CreateUserRequest v) { requests.add(new UserRequest(v.getName(), v.getEmail())); }
        public void onError(Throwable t) { responseObserver.onError(t); }
        public void onCompleted() {
            var created = userService.createUsers(requests);
            responseObserver.onNext(CreateUsersResponse.newBuilder()
                .setCreatedCount(created.size()).build());
            responseObserver.onCompleted();
        }
    };
}

See references/grpc-service-reference.md for full examples including timestamp/ByteString conversion.

Errors via io.grpc.Status

Map domain failures to gRPC status codes. Common: NOT_FOUND, INVALID_ARGUMENT, ALREADY_EXISTS, PERMISSION_DENIED, UNAUTHENTICATED, INTERNAL, UNAVAILABLE.

throw Status.NOT_FOUND.withDescription("User not found: " + id).asRuntimeException();

Inside a handler use either throw ...asRuntimeException() (when the call propagates) or responseObserver.onError(...). Send exactly one terminal signal per call — never onNext after onError/onCompleted. Details: references/grpc-error-handling-reference.md.

Interceptors

Register a cross-cutting interceptor by implementing io.grpc.ServerInterceptor and annotating it @Component; GrpcServerModule adds it automatically.

import io.grpc.*;
import ru.tinkoff.kora.common.Component;

@Component
public final class MyServerInterceptor implements ServerInterceptor {
    private final Logger logger = LoggerFactory.getLogger(MyServerInterceptor.class);

    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
            ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
        logger.info("gRPC call: {}", call.getMethodDescriptor().getFullMethodName());
        return next.startCall(call, headers);
    }
}

Default interceptors registered by the module: ContextServerInterceptor, CoroutineContextInjectInterceptor, MetricCollectorServerInterceptor, LoggingServerInterceptor. To replace the default list, override the serverBuilder method from GrpcModule. Auth and exception-mapping examples: references/grpc-interceptors-reference.md.

Reflection

Add io.grpc:grpc-services and set reflectionEnabled = true (default is false) for grpcurl/Postman discovery.

grpcServer { reflectionEnabled = ${?GRPC_REFLECTION_ENABLED} }
grpcurl -plaintext localhost:8090 list

Keep it disabled in production unless the endpoint is internal-only. See references/grpc-reflection-reference.md.

Telemetry

The module emits metrics (rpc.server.duration, rpc.server.requests_per_rpc, rpc.server.responses_per_rpc), tracing, and logging — all toggled under grpcServer.telemetry. Metrics default on, tracing default on, logging default off. Add custom metric tags / trace attributes under telemetry.metrics.tags / telemetry.tracing.attributes (references/grpc-config-reference.md).


Common pitfalls

SymptomFix
Generated classes not foundRun ./gradlew clean classes; verify the com.google.protobuf plugin and the proto srcDirs source sets
Handler not registeredAnnotate it @Component and ensure it extends the generated *GrpcImplBase
Component import won't resolveImport ru.tinkoff.kora.common.Component (not any annotation.processor package)
Client hangs on a streamAlways finish with onCompleted() (or onError) after the onNext calls
INVALID_ARGUMENT returned for everythingUse specific Status codes instead of a blanket INTERNAL
UNIMPLEMENTED: unknown service via grpcurlAdd io.grpc:grpc-services and set reflectionEnabled = true
RPC returns UNIMPLEMENTED from a real clientGenerated service/method names must match the .proto used by the client
Build hangs after clean./gradlew --stop, then rebuild

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.