toolchains/golang/grpc/SKILL.md
Production gRPC in Go: protobuf layout, codegen, interceptors, deadlines, error codes, streaming, health checks, TLS, and testing with bufconn
npx skillsauth add bobmatnyc/claude-mpm-skills golang-grpcInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
gRPC provides strongly-typed RPC APIs backed by Protocol Buffers, with first-class streaming support and excellent performance for service-to-service communication. This skill focuses on production defaults: versioned protos, deadlines, error codes, interceptors, health checks, TLS, and testability.
✅ Correct: versioned package
// proto/users/v1/users.proto
syntax = "proto3";
package users.v1;
option go_package = "example.com/myapp/gen/users/v1;usersv1";
service UsersService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (stream User);
}
message GetUserRequest { string id = 1; }
message GetUserResponse { User user = 1; }
message ListUsersRequest { int32 page_size = 1; string page_token = 2; }
message User {
string id = 1;
string email = 2;
string display_name = 3;
}
❌ Wrong: unversioned package (hard to evolve)
package users;
Install generators:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Generate:
protoc -I proto \
--go_out=./gen --go_opt=paths=source_relative \
--go-grpc_out=./gen --go-grpc_opt=paths=source_relative \
proto/users/v1/users.proto
✅ Correct: validate + map errors to gRPC codes
package usersvc
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
usersv1 "example.com/myapp/gen/users/v1"
)
type Service struct {
usersv1.UnimplementedUsersServiceServer
Repo Repo
}
type Repo interface {
GetUser(ctx context.Context, id string) (User, error)
}
type User struct {
ID, Email, DisplayName string
}
func (s *Service) GetUser(ctx context.Context, req *usersv1.GetUserRequest) (*usersv1.GetUserResponse, error) {
if req.GetId() == "" {
return nil, status.Error(codes.InvalidArgument, "id is required")
}
u, err := s.Repo.GetUser(ctx, req.GetId())
if err != nil {
if err == ErrNotFound {
return nil, status.Error(codes.NotFound, "user not found")
}
return nil, status.Error(codes.Internal, "internal error")
}
return &usersv1.GetUserResponse{
User: &usersv1.User{
Id: u.ID,
Email: u.Email,
DisplayName: u.DisplayName,
},
}, nil
}
❌ Wrong: return raw errors (clients lose code semantics)
return nil, errors.New("user not found")
Make every call bounded; enforce server-side timeouts for expensive handlers.
✅ Correct: require deadline
if _, ok := ctx.Deadline(); !ok {
return nil, status.Error(codes.InvalidArgument, "deadline required")
}
Use metadata for auth/session correlation, not for primary request data.
✅ Correct: read auth token from metadata
md, _ := metadata.FromIncomingContext(ctx)
auth := ""
if vals := md.Get("authorization"); len(vals) > 0 {
auth = vals[0]
}
Use interceptors for cross-cutting concerns: auth, logging, metrics, tracing, request IDs.
✅ Correct: unary interceptor with request ID
func unaryRequestID() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
id := uuid.NewString()
ctx = context.WithValue(ctx, requestIDKey{}, id)
resp, err := handler(ctx, req)
return resp, err
}
}
✅ Correct: stop on ctx.Done()
func (s *Service) ListUsers(req *usersv1.ListUsersRequest, stream usersv1.UsersService_ListUsersServer) error {
users, err := s.Repo.ListUsers(stream.Context(), int(req.GetPageSize()))
if err != nil {
return status.Error(codes.Internal, "internal error")
}
for _, u := range users {
select {
case <-stream.Context().Done():
return stream.Context().Err()
default:
}
if err := stream.Send(&usersv1.User{
Id: u.ID,
Email: u.Email,
DisplayName: u.DisplayName,
}); err != nil {
return err
}
}
return nil
}
Add health service; enable reflection only in non-production environments.
✅ Correct: health + conditional reflection
hs := health.NewServer()
grpc_health_v1.RegisterHealthServer(s, hs)
if env != "production" {
reflection.Register(s)
}
Prefer GracefulStop with a deadline.
✅ Correct: graceful stop
stopped := make(chan struct{})
go func() {
grpcServer.GracefulStop()
close(stopped)
}()
select {
case <-stopped:
case <-time.After(10 * time.Second):
grpcServer.Stop()
}
Use TLS (or mTLS) in production; avoid insecure credentials outside local dev.
✅ Correct: server TLS
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
if err != nil { return err }
grpcServer := grpc.NewServer(grpc.Creds(creds))
Test gRPC handlers without opening real sockets using bufconn.
✅ Correct: in-memory gRPC test server
const bufSize = 1024 * 1024
lis := bufconn.Listen(bufSize)
srv := grpc.NewServer()
usersv1.RegisterUsersServiceServer(srv, &Service{Repo: repo})
go func() { _ = srv.Serve(lis) }()
ctx := context.Background()
conn, err := grpc.DialContext(
ctx,
"bufnet",
grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return lis.Dial() }),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil { t.Fatal(err) }
defer conn.Close()
client := usersv1.NewUsersServiceClient(conn)
resp, err := client.GetUser(ctx, &usersv1.GetUserRequest{Id: "1"})
_ = resp
_ = err
Ignore deadlines: unbounded handlers cause tail latency and resource exhaustion.
Return string errors: map domain errors to codes.* with status.Error or status.Errorf.
Stream without backpressure: stop on ctx.Done() and handle Send errors.
Expose reflection in production: treat reflection as a discovery surface.
UNKNOWN errorsActions:
status.Error(codes.X, "...") instead of raw errors.Actions:
ctx to downstream calls.Actions:
ctx.Done() and handle stream.Send errors.tools
Xquik X data automation API - Use REST or MCP for tweet search, user lookup, follower exports, media downloads, monitors, webhooks, giveaway draws, and confirmation-gated X actions.
tools
LinkedIn automation via the Linked API CLI - fetch profiles, search people and companies, send messages, manage connections, create posts, react, comment, and run Sales Navigator and custom workflows. Use when the user wants to interact with LinkedIn.
tools
MCP (Model Context Protocol) server build and evaluation guide, including local conventions for tool surfaces, config, and testing
tools
MCP (Model Context Protocol) - Build AI-native servers with tools, resources, and prompts. TypeScript/Python SDKs for Claude Desktop integration.