Install
$ agentstack add skill-kora-projects-kora-skills-kora-grpc-client ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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
GrpcClientModuleand injecting a generated*BlockingStub/*FutureStub/*Stub, - configuring a client under
grpcClient..*, - adding a
ClientInterceptorfor metadata headers, auth, or logging, - making unary or streaming calls.
Key facts (do not get these wrong)
- Stubs are injected directly by type — no
@Tagon the constructor parameter. Kora produces one stub per generated*Grpcclass. InjectingUserServiceGrpc.UserServiceBlockingStubis enough. @Tag(ServiceGrpc.class)belongs on aClientInterceptor, 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 anyannotation.processor.*package. - Plaintext vs TLS is chosen by the URL scheme (
http://= plaintext,https:///grpc://per transport). There is nousePlaintextconfig key. - The mandatory annotation processor must be present: Java
annotationProcessor "ru.tinkoff.kora:annotation-processors", Kotlinksp "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
| Stub | Use for | |------|---------| | *BlockingStub | Unary calls and server-streaming reads (returns Iterator) | | *FutureStub | Unary calls returning ListenableFuture | | *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](references/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 AUTHORIZATION =
Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
private final AuthConfig authConfig;
public AuthInterceptor(AuthConfig authConfig) {
this.authConfig = authConfig;
}
@Override
public ClientCall interceptCall(
MethodDescriptor method, CallOptions callOptions, Channel next) {
return new ForwardingClientCall.SimpleForwardingClientCall<>(next.newCall(method, callOptions)) {
@Override
public void start(Listener 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](references/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](references/grpc-client-streaming-reference.md).
Configuration
grpcClient..* 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](references/grpc-client-config-reference.md).
Common pitfalls
| Symptom | Fix | |---------|-----| | Required dependency not found: ...BlockingStub | Enable GrpcClientModule on @KoraApp; run ./gradlew classes so protobuf + processors run | | Stub class does not exist | Run protobuf generation and add the build/generated/source/proto/... source dirs to sourceSets | | Tried to add @Tag(...) on a stub parameter | Remove it — stubs inject by type; @Tag is only for interceptors | | UNAVAILABLE | Check grpcClient..url host/port and that the server is up | | UNAUTHENTICATED | Add an AuthInterceptor (above) putting credentials into Metadata | | Interceptor never runs | Ensure @Component + @Tag(ServiceGrpc.class) matches the generated class exactly | | Wanted plaintext but got TLS | Use an http:// URL; there is no usePlaintext key |
References & assets
| File | Purpose | |------|---------| | [references/grpc-client-stubs-reference.md](references/grpc-client-stubs-reference.md) | Stub types, direct injection, service wrappers | | [references/grpc-client-config-reference.md](references/grpc-client-config-reference.md) | Full config + protobuf plugin setup | | [references/grpc-client-interceptors-reference.md](references/grpc-client-interceptors-reference.md) | Interceptor patterns, metadata, GraphInterceptor | | [references/grpc-client-streaming-reference.md](references/grpc-client-streaming-reference.md) | Server/client/bidirectional streaming | | [assets/README.md](assets/README.md) | Template catalog and usage |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kora-projects
- Source: kora-projects/kora-skills
- License: Apache-2.0
- Homepage: http://kora-projects.github.io/kora-docs
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.