Postpartum-genushyacinthus29/dotnet-skills/skills/dotnet-grpc/SKILL.md
dotnet-grpc
Build or review gRPC services and clients in .NET with correct contract-first design, streaming behavior, transport assumptions, and backend service integration.
- Source repository stars
- 9
- Declared platforms
- 0
- Static risk flags
- 1
- Last source update
- 2026-08-23
- Source checked
- 2026-08-25
Decision brief
What it does: where it fits
Build or review gRPC services and clients in . NET with correct contract-first design, streaming behavior, transport assumptions, and backend service integration.
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/Postpartum-genushyacinthus29/dotnet-skills --skill "skills/dotnet-grpc"Inspect the Agent Skill "dotnet-grpc" from https://github.com/Postpartum-genushyacinthus29/dotnet-skills/blob/f9c1a213bc25d95641adc3a59f8048cb5656741c/skills/dotnet-grpc/SKILL.md at commit f9c1a213bc25d95641adc3a59f8048cb5656741c. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
Workflow
1. Use gRPC where low-latency backend communication, strong contracts, or streaming are the real drivers. 2. Treat .proto files as source of truth and keep generated code ownership clear. 3. Choose unary, server streaming, client streaming, or bidirectional streaming based on th…
Use gRPC where low-latency backend communication, strong contracts, or streaming are the real drivers.Treat .proto files as source of truth and keep generated code ownership clear.Choose unary, server streaming, client streaming, or bidirectional streaming based on the interaction model, not by default. - 02
Trigger On
building backend-to-backend RPC services or clients
building backend-to-backend RPC services or clientsadding protobuf contracts, streaming calls, or interceptorsdeciding between gRPC, HTTP APIs, and SignalR - 03
Documentation
gRPC on .NET Overview
gRPC on .NET OverviewPerformance Best Practices with gRPCgRPC Client Factory - 04
Service Patterns
Review the “Service Patterns” section in the pinned source before continuing.
Review and apply the “Service Patterns” source section. - 05
Basic Unary Service
Review the “Basic Unary Service” section in the pinned source before continuing.
Review and apply the “Basic Unary Service” source section.
Permission review
Static risk signals and limitations
Network access
The documentation includes network, browsing, or remote request actions.
options.Address = new Uri("https://localhost:5001");Network access
The documentation includes network, browsing, or remote request actions.
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptionsEvidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 93/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 9 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- Postpartum-genushyacinthus29/dotnet-skills
- Skill path
- skills/dotnet-grpc/SKILL.md
- Commit
- f9c1a213bc25d95641adc3a59f8048cb5656741c
- License
- MIT
- Collected
- 2026-08-25
- Default branch
- main
View the original SKILL.md
gRPC for .NET
Trigger On
- building backend-to-backend RPC services or clients
- adding protobuf contracts, streaming calls, or interceptors
- deciding between gRPC, HTTP APIs, and SignalR
- optimizing gRPC performance and connection management
- implementing service-to-service communication in microservices
Documentation
- gRPC on .NET Overview
- Performance Best Practices with gRPC
- gRPC Client Factory
- gRPC Interceptors
- Call gRPC Services with .NET Client
References
- patterns.md - Detailed proto patterns, streaming implementations, interceptors, health checks, and load balancing
- anti-patterns.md - Common gRPC mistakes with explanations and corrections
Workflow
- Use gRPC where low-latency backend communication, strong contracts, or streaming are the real drivers.
- Treat
.protofiles as source of truth and keep generated code ownership clear. - Choose unary, server streaming, client streaming, or bidirectional streaming based on the interaction model, not by default.
- Do not use gRPC for broad browser-facing APIs unless the limitations and gRPC-Web tradeoffs are explicitly acceptable.
- Handle deadlines, cancellation, auth, and retry behavior explicitly on both server and client paths.
- Validate contract changes carefully because gRPC drift breaks callers fast.
Service Patterns
Basic Unary Service
// greeter.proto
syntax = "proto3";
option csharp_namespace = "GrpcService";
package greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
// GreeterService.cs
public class GreeterService : Greeter.GreeterBase
{
private readonly ILogger<GreeterService> _logger;
public GreeterService(ILogger<GreeterService> logger)
{
_logger = logger;
}
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
_logger.LogInformation("Greeting {Name}", request.Name);
return Task.FromResult(new HelloReply
{
Message = $"Hello {request.Name}"
});
}
}
Server Streaming
// In .proto file
service DataStream {
rpc StreamData (DataRequest) returns (stream DataChunk);
}
// Service implementation
public override async Task StreamData(
DataRequest request,
IServerStreamWriter<DataChunk> responseStream,
ServerCallContext context)
{
for (int i = 0; i < request.Count; i++)
{
// Check for cancellation to avoid wasted work
if (context.CancellationToken.IsCancellationRequested)
{
_logger.LogInformation("Stream cancelled by client");
break;
}
await responseStream.WriteAsync(new DataChunk
{
Index = i,
Data = await GetDataAsync(i)
});
// Respect backpressure
await Task.Delay(10, context.CancellationToken);
}
}
Bidirectional Streaming
// In .proto file
service Chat {
rpc ChatStream (stream ChatMessage) returns (stream ChatMessage);
}
// Service implementation
public override async Task ChatStream(
IAsyncStreamReader<ChatMessage> requestStream,
IServerStreamWriter<ChatMessage> responseStream,
ServerCallContext context)
{
await foreach (var message in requestStream.ReadAllAsync(context.CancellationToken))
{
_logger.LogInformation("Received: {Message}", message.Text);
// Echo back with transformation
await responseStream.WriteAsync(new ChatMessage
{
Text = $"Echo: {message.Text}",
Timestamp = Timestamp.FromDateTime(DateTime.UtcNow)
});
}
}
Client Patterns
Channel Reuse with Client Factory (Recommended)
// Program.cs - Register gRPC client with factory
builder.Services.AddGrpcClient<Greeter.GreeterClient>(options =>
{
options.Address = new Uri("https://localhost:5001");
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new SocketsHttpHandler
{
PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan,
KeepAlivePingDelay = TimeSpan.FromSeconds(60),
KeepAlivePingTimeout = TimeSpan.FromSeconds(30),
EnableMultipleHttp2Connections = true
};
return handler;
})
.AddInterceptor<LoggingInterceptor>();
// Usage in service
public class MyService
{
private readonly Greeter.GreeterClient _client;
public MyService(Greeter.GreeterClient client)
{
_client = client;
}
public async Task<string> GreetAsync(string name, CancellationToken ct)
{
// Always set deadlines
var deadline = DateTime.UtcNow.AddSeconds(5);
var response = await _client.SayHelloAsync(
new HelloRequest { Name = name },
deadline: deadline,
cancellationToken: ct);
return response.Message;
}
}
Manual Channel Creation with Connection Options
// Reuse channels - expensive to create
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
HttpHandler = new SocketsHttpHandler
{
EnableMultipleHttp2Connections = true,
PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan,
KeepAlivePingDelay = TimeSpan.FromSeconds(60),
KeepAlivePingTimeout = TimeSpan.FromSeconds(30)
},
MaxRetryAttempts = 3,
ServiceConfig = new ServiceConfig
{
MethodConfigs =
{
new MethodConfig
{
Names = { MethodName.Default },
RetryPolicy = new RetryPolicy
{
MaxAttempts = 3,
InitialBackoff = TimeSpan.FromSeconds(1),
MaxBackoff = TimeSpan.FromSeconds(5),
BackoffMultiplier = 1.5,
RetryableStatusCodes = { StatusCode.Unavailable }
}
}
}
}
});
// Create multiple clients from same channel
var greeterClient = new Greeter.GreeterClient(channel);
var orderClient = new Orders.OrdersClient(channel);
Consuming Server Streaming
public async Task ProcessStreamAsync(CancellationToken ct)
{
using var call = _client.StreamData(new DataRequest { Count = 100 });
try
{
await foreach (var chunk in call.ResponseStream.ReadAllAsync(ct))
{
await ProcessChunkAsync(chunk);
}
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled)
{
_logger.LogInformation("Stream cancelled");
}
}
Bidirectional Streaming Client
public async Task ChatAsync(CancellationToken ct)
{
using var call = _client.ChatStream();
// Read responses in background
var readTask = Task.Run(async () =>
{
await foreach (var response in call.ResponseStream.ReadAllAsync(ct))
{
Console.WriteLine($"Received: {response.Text}");
}
}, ct);
// Send messages
foreach (var message in GetMessages())
{
if (ct.IsCancellationRequested) break;
await call.RequestStream.WriteAsync(new ChatMessage { Text = message });
}
// Signal completion and wait for responses
await call.RequestStream.CompleteAsync();
await readTask;
}
Interceptor Patterns
Logging Interceptor
public class LoggingInterceptor : Interceptor
{
private readonly ILogger<LoggingInterceptor> _logger;
public LoggingInterceptor(ILogger<LoggingInterceptor> logger)
{
_logger = logger;
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
var sw = Stopwatch.StartNew();
var call = continuation(request, context);
return new AsyncUnaryCall<TResponse>(
HandleResponse(call.ResponseAsync, context.Method.FullName, sw),
call.ResponseHeadersAsync,
call.GetStatus,
call.GetTrailers,
call.Dispose);
}
private async Task<TResponse> HandleResponse<TResponse>(
Task<TResponse> responseTask, string method, Stopwatch sw)
{
try
{
var response = await responseTask;
_logger.LogInformation("{Method} completed in {Elapsed}ms",
method, sw.ElapsedMilliseconds);
return response;
}
catch (RpcException ex)
{
_logger.LogError(ex, "{Method} failed with {Status} in {Elapsed}ms",
method, ex.StatusCode, sw.ElapsedMilliseconds);
throw;
}
}
}
Server Exception Interceptor
public class ExceptionInterceptor : Interceptor
{
private readonly ILogger<ExceptionInterceptor> _logger;
public ExceptionInterceptor(ILogger<ExceptionInterceptor> logger)
{
_logger = logger;
}
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
try
{
return await continuation(request, context);
}
catch (RpcException)
{
throw; // Let gRPC exceptions pass through
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled exception in {Method}", context.Method);
throw new RpcException(new Status(StatusCode.Internal, "An error occurred"));
}
}
}
Server Configuration
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc(options =>
{
options.EnableDetailedErrors = builder.Environment.IsDevelopment();
options.MaxReceiveMessageSize = 4 * 1024 * 1024; // 4 MB
options.MaxSendMessageSize = 4 * 1024 * 1024;
options.Interceptors.Add<ExceptionInterceptor>();
});
// Configure Kestrel for HTTP/2
builder.WebHost.ConfigureKestrel(options =>
{
options.Limits.Http2.MaxStreamsPerConnection = 100;
options.Limits.Http2.InitialConnectionWindowSize = 1024 * 1024; // 1 MB
options.Limits.Http2.InitialStreamWindowSize = 512 * 1024; // 512 KB
});
var app = builder.Build();
app.MapGrpcService<GreeterService>();
app.MapGet("/", () => "gRPC endpoint");
app.Run();
Anti-Patterns to Avoid
| Anti-Pattern | Why It's Bad | Better Approach |
|---|---|---|
| Creating new channel per call | Connection overhead kills performance | Reuse channels, use client factory |
| Missing deadlines | Calls can hang indefinitely | Always set deadline on client calls |
| Ignoring cancellation in streams | Wastes server resources | Check CancellationToken periodically |
| Using gRPC for browser clients | Limited browser support | Use gRPC-Web with Envoy or REST |
| Large messages (>1MB) | Memory pressure, LOH allocations | Stream in chunks or use HTTP for files |
Sync blocking (Task.Result) | Thread pool starvation | Use async/await consistently |
| Swallowing exceptions in interceptors | Hides failures from clients | Rethrow or convert to RpcException |
| Not aligning client/server deadlines | Mismatched timeout behavior | Coordinate deadline budgets |
Blocking AsyncUnaryCall with BlockingUnaryCall interceptor | Interceptors are method-specific | Implement both interceptor methods |
| Missing retry configuration | Single failures cause request failure | Configure retry policy on channel |
Best Practices
Channel and Connection Management
- Reuse channels across the application lifetime
- Enable multiple HTTP/2 connections with
EnableMultipleHttp2Connections = true - Configure keep-alive pings to maintain connections through idle periods
- Use client factory (
AddGrpcClient) for centralized channel management - Set
PooledConnectionIdleTimeoutto prevent premature connection closure
Deadlines and Cancellation
- Always set deadlines on client calls to prevent indefinite hangs
- Propagate cancellation through the call chain
- Check cancellation in long-running streaming handlers
- Coordinate deadline budgets between client and server
Performance
- Avoid large messages (>85KB to stay off Large Object Heap)
- Use streaming for large data transfers instead of single messages
- Enable server GC for high-throughput client applications
- Complete streams gracefully to allow connection reuse
- Dispose streaming calls when done to release resources
Error Handling
- Use appropriate status codes (not just
Internalfor everything) - Let
RpcExceptionpropagate through interceptors - Convert domain exceptions to gRPC status codes at service boundaries
- Include meaningful error details in development mode only
Contract Design
- Use custom objects in proto to enable backward-compatible evolution
- Reserve field numbers you remove instead of reusing
- Version service names for breaking changes (
GreeterV2) - Keep proto files as the single source of truth
Observability
- Add logging interceptors for request/response timing
- Track error rates by status code
- Monitor connection pool health and reuse rates
- Integrate with distributed tracing (OpenTelemetry)
Deliver
- stable protobuf contracts and generated code flow
- service and client code that match the RPC shape
- tests or smoke checks for serialization and call behavior
- proper deadline and cancellation handling
Validate
- gRPC is chosen for the right problem
- streaming semantics and deadlines are explicit
- browser constraints are acknowledged when relevant
- channels are reused appropriately
- error handling converts exceptions to proper status codes
- interceptors are ordered correctly (logging before auth before validation)
Frequently asked questions
What to verify before installation and use
What does the dotnet-grpc source document cover?
Build or review gRPC services and clients in . NET with correct contract-first design, streaming behavior, transport assumptions, and backend service integration.
How do I install dotnet-grpc?
The source record exposes this install command: npx skills add https://github.com/Postpartum-genushyacinthus29/dotnet-skills --skill "skills/dotnet-grpc". Inspect the command and pinned source before running it.
Which permission-related actions were detected?
Static rules flagged network in the source; the page lists the matching lines and excerpts.
Alternatives
Compare before choosing
oaustegard/claude-skills
featuring
Generate hierarchical _FEATURES.md files that describe what a codebase DOES from a user/consumer perspective, anchored to source symbols via tree-sitting. Supports large complex codebases through feature-driven decomposition into sub-feature files. Uses a multi-pass synthesis: orientation → detail → overview rewrite. Use when someone says "what does this do", "document features", "feature inventory", "_FEATURES.md", or needs to understand a codebase's purpose before modifying it. Complements tre
NintendaDev/unikit-ai
unikit-docs
Generate and maintain the project's TECHNICAL documentation from its codebase — scans the project structure, tech stack, and module boundaries, then writes a lean README landing page plus detailed topic pages (architecture, modules, setup, build, APIs), only the docs that are relevant. Use whenever the user wants to create, update, or validate documentation of the CODE or the project itself, e.g. "generate documentation", "create docs", "write the README", "update the project docs", "document th
mgiovani/cc-arsenal
team-review
Multi-agent review team: architecture, security, performance, testing, style, docs/UX, plus an adversary that cross-examines the other 6, for security-sensitive, architectural, or large PRs (15+ files) where a single-agent pass risks missing cross-cutting issues. Use for auth/payments/PII changes, schema/pattern changes, compliance sign-off, or when asked to 'get the review team on this' / 'multi-agent review' / 'thorough review before merge'. For a standard PR or a quick pre-merge check, use /r
open-edge-platform/edge-ai-libraries
chatqna-helm-deploy
Deploy Chat Question-and-Answer Core to Kubernetes using Helm (OpenVINO CPU, OpenVINO GPU, or Ollama), including values.yaml configuration, helm install/upgrade, deployment verification, uninstall, and translation from Docker Compose setup_env.sh variables into Helm override values. Use this skill when the user says "deploy chatqna core to kubernetes", "helm install chatqna-core", "configure values.yaml", "convert compose config to helm", or "translate setup_env.sh to chart values".