소스 정보
- 저장소
- ffsshhttiikk/opencode-agents-skills
- 최근 소스 활동
- 2026년 2월 28일 22:51
- 감지된 SKILL.md 언어
- 영어
- 스타
- 2
- 포크
- 2
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/ffsshhttiikk/opencode-agents-skills --skill grpc명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
SOC 직업 분류 기준
| name | grpc |
| description | gRPC framework best practices and implementation |
| license | MIT |
| compatibility | opencode |
| metadata | {"audience":"developers","category":"api-design"} |
When implementing gRPC services or working with Protocol Buffers.
syntax = "proto3";
package user.v1;
option go_package = "github.com/company/api/user/v1";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
// Service definition
service UserService {
// Unary RPC
rpc GetUser(GetUserRequest) returns (User);
// Server streaming
rpc ListUsers(ListUsersRequest) returns (stream User);
// Client streaming
rpc CreateUsers(stream CreateUserRequest) returns (CreateUsersResponse);
// Bidirectional streaming
rpc StreamUserUpdates(stream UserUpdateRequest) returns (stream User);
// Health check
rpc HealthCheck(google.protobuf.Empty) returns (HealthCheckResponse);
}
// Message types
message GetUserRequest {
string user_id = 1;
}
message User {
string id = 1;
string email = 2;
string name = 3;
UserStatus status = 4;
google.protobuf.Timestamp created_at = 5;
google.protobuf.Timestamp updated_at = 6;
}
enum UserStatus {
USER_STATUS_UNSPECIFIED = 0;
USER_STATUS_ACTIVE = 1;
USER_STATUS_INACTIVE = 2;
USER_STATUS_SUSPENDED = 3;
}
message ListUsersRequest {
int32 page_size = 1;
string page_token = 2;
UserStatus status_filter = 3;
}
message CreateUserRequest {
string email = 1;
string name = 2;
string password = 3;
}
message CreateUsersResponse {
repeated User users = 1;
int32 failed_count = 2;
}
message UserUpdateRequest {
string user_id = 1;
string name = 2;
}
message HealthCheckResponse {
bool healthy = 1;
string version = 2;
}
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
pb "github.com/company/api/user/v1"
)
type UserServer struct {
pb.UnimplementedUserServiceServer
userStore UserStore
}
type UserStore interface {
GetUser(ctx context.Context, id string) (*pb.User, error)
ListUsers(ctx context.Context, filter *pb.UserStatus, limit int) ([]*pb.User, error)
CreateUser(ctx context.Context, user *pb.User) error
}
func NewUserServer(store UserStore) *UserServer {
return &UserServer{userStore: store}
}
func (s *UserServer) GetUser(
ctx context.Context,
req *pb.GetUserRequest,
) (*pb.User, error) {
// Extract metadata for logging
md, ok := metadata.FromIncomingContext(ctx)
if ok {
log.Printf("GetUser request: user_id=%s, from=%v", req.UserId, md.Get("x-forwarded-for"))
}
// Validate request
if req.UserId == "" {
return nil, status.Error(codes.InvalidArgument, "user_id is required")
}
// Fetch user
user, err := s.userStore.GetUser(ctx, req.UserId)
if err != {
, status.Errorf(codes.NotFound, , err)
}
user,
}
ListUsers(
req *pb.ListUsersRequest,
stream pb.UserService_ListUsersServer,
) {
ctx := stream.Context()
pageSize := (req.PageSize)
pageSize <= {
pageSize =
}
pageSize > {
pageSize =
}
users, err := s.userStore.ListUsers(ctx, req.StatusFilter, pageSize)
err != {
status.Errorf(codes.Internal, , err)
}
_, user := users {
err := stream.Send(user); err != {
status.Errorf(codes.Internal, , err)
}
}
}
CreateUsers(
stream pb.UserService_CreateUsersServer,
) {
ctx := stream.Context()
users []*pb.User
failedCount
{
req, err := stream.Recv()
err != {
err.Error() == {
}
status.Errorf(codes.Internal, , err)
}
user := &pb.User{
Email: req.Email,
Name: req.Name,
Status: pb.UserStatus_USER_STATUS_ACTIVE,
CreatedAt: timestamppb.Now(),
}
err := s.userStore.CreateUser(ctx, user); err != {
failedCount++
log.Printf(, err)
}
users = (users, user)
}
stream.SendAndClose(&pb.CreateUsersResponse{
Users: users,
FailedCount: failedCount,
})
}
StreamUserUpdates(
stream pb.UserService_StreamUserUpdatesServer,
) {
ctx := stream.Context()
{
{
<-ctx.Done():
ctx.Err()
:
req, err := stream.Recv()
err != {
err
}
user, err := s.userStore.GetUser(ctx, req.UserId)
err != {
}
err := stream.Send(user); err != {
err
}
}
}
}
{
}
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
pb "github.com/company/api/user/v1"
)
type UserClient struct {
conn *grpc.ClientConn
client pb.UserServiceClient
}
func NewUserClient(addr string) (*UserClient, error) {
conn, err := grpc.Dial(
addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(loggingInterceptor),
grpc.WithStreamInterceptor(streamLoggingInterceptor),
)
if err != nil {
return nil, err
}
return &UserClient{
conn: conn,
client: pb.NewUserServiceClient(conn),
}, nil
}
func (c *UserClient) GetUser(ctx context.Context, userId string) (*pb.User, error) {
// Add metadata for tracing
ctx = metadata.AppendToOutgoingContext(ctx, "x-request-id", "req-123")
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
return c.client.GetUser(ctx, &pb.GetUserRequest{
UserId: userId,
})
}
func (c *UserClient) ListActiveUsers(ctx context.Context) ([]*pb.User, error) {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
stream, err := c.client.ListUsers(ctx, &pb.ListUsersRequest{
PageSize: 100,
StatusFilter: pb.UserStatus_USER_STATUS_ACTIVE,
})
err != {
, err
}
users []*pb.User
{
user, err := stream.Recv()
err != {
err.Error() == {
}
, err
}
users = (users, user)
}
users,
}
Close() {
c.conn.Close()
}
{
start := time.Now()
err := invoker(ctx, method, req, reply, cc, opts...)
log.Printf(,
method, time.Since(start), err)
err
}
{
start := time.Now()
err := stream.RecvMsg()
log.Printf(,
method, time.Since(start), err)
err
}
package status
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Custom error codes
const (
ErrCodeUserNotFound = "USER_NOT_FOUND"
ErrCodeInvalidInput = "INVALID_INPUT"
ErrCodeUnauthorized = "UNAUTHORIZED"
)
func UserNotFound(id string) error {
return status.Errorf(codes.NotFound, "user not found: %s", id)
}
func InvalidInput(field, reason string) error {
return status.Errorf(codes.InvalidArgument, "invalid %s: %s", field, reason)
}
func Unauthorized(reason string) error {
return status.Errorf(codes.Unauthenticated, "unauthorized: %s", reason)
}
1. Use Protocol Buffers for schema
- Define contracts explicitly
- Enable backwards compatibility
2. Handle errors properly
- Use gRPC status codes
- Include error details
3. Implement interceptors
- Logging, auth, metrics
4. Use streaming appropriately
- Server streaming for lists
- Bidirectional for real-time
5. Set timeouts
- Always use context with timeout
6. Enable reflection
- For debugging and tools
7. Version your APIs
- Include version in package name
- Support multiple versions
8. Use proper authentication
- Token-based or mTLS
9. Monitor and trace
- Add interceptors for metrics
- Use OpenTelemetry
10. Test thoroughly
- Unit tests with mock stores
- Integration tests