From protoc-gen-mcp-skill
Build MCP servers from protobuf definitions using protoc-gen-mcp and easyp. Use when: creating an MCP server, generating MCP tools from proto files, building a proto-first MCP server in Go, Python, Java, Kotlin, TypeScript, or JavaScript, configuring easyp for MCP generation, adding MCP tool annotations to protobuf services, implementing MCP tool handlers, setting up ProtoJSON-based MCP tools, or any task involving protobuf-to-MCP code generation. Also use when the user mentions protoc-gen-mcp, mcp proto, proto mcp server, easyp mcp, or wants type-safe MCP bindings from .proto files.
How this skill is triggered — by the user, by Claude, or both
Slash command
/protoc-gen-mcp-skill:protoc-gen-mcpThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate type-safe MCP tool bindings from annotated protobuf services for **Go, Python, Java, Kotlin, and TypeScript**. JavaScript projects consume compiled TypeScript output. Protobuf is the source of truth: define your service once in `.proto`, generate language-specific protobuf types and MCP sidecars, implement the handler interface, and serve.
Generate type-safe MCP tool bindings from annotated protobuf services for Go, Python, Java, Kotlin, and TypeScript. JavaScript projects consume compiled TypeScript output. Protobuf is the source of truth: define your service once in .proto, generate language-specific protobuf types and MCP sidecars, implement the handler interface, and serve.
Install easyp — the recommended way to lint and generate:
brew install easyp-tech/tap/easyp
Or install from source:
go install github.com/easyp-tech/easyp/cmd/easyp@latest
See https://easyp.tech/docs for full documentation.
Create a .proto file with service, methods, and MCP annotations using mcp/options/v1/options.proto:
syntax = "proto3";
package myapi.v1;
option go_package = "github.com/you/myproject/myapi/v1;myapiv1";
import "mcp/options/v1/options.proto";
import "google/protobuf/empty.proto";
service MyServiceAPI {
option (mcp.options.v1.service) = {
namespace: "myapi"
description: "My tools exposed as MCP tools."
};
rpc CreateItem(CreateItemRequest) returns (CreateItemResponse) {
option (mcp.options.v1.method) = {
title: "Create item"
description: "Create a new item with validation."
annotations: { read_only_hint: false }
};
}
rpc Health(google.protobuf.Empty) returns (HealthResponse) {
option (mcp.options.v1.method) = {
title: "Health check"
description: "Verify the server is alive."
annotations: { read_only_hint: true }
};
}
}
message CreateItemRequest {
string name = 1 [(mcp.options.v1.field) = {
description: "Item name."
examples: [{ string_value: "Widget" }]
min_length: 1
max_length: 200
}];
int32 count = 2 [(mcp.options.v1.field) = {
default_value: { integer_value: 1 }
minimum: 1
maximum: 1000
}];
repeated string tags = 3 [(mcp.options.v1.field) = {
max_items: 20
unique_items: true
}];
optional string note = 4;
}
message CreateItemResponse {
string id = 1;
}
message HealthResponse {
string status = 1;
}
Each language has its own easyp.yaml configuration and handler pattern. See the examples:
| Language | Example | Handler Style |
|---|---|---|
| Go | examples/go | <Service>ToolHandler interface |
| Python (dataclass) | examples/python/dataclass | Dataclasses from *_mcp.py |
| Python (protobuf) | examples/python/protobuf | Raw *_pb2 classes |
| Python (dataclass+protobuf) | examples/python/dataclass-protobuf | Both surfaces side by side |
| Java | examples/java | Nested <Service>ToolHandler in *Mcp.java |
| Kotlin | examples/kotlin | <Service>ToolHandler interface |
| TypeScript | examples/typescript | <Service>ToolHandler interface |
| JavaScript | examples/javascript | Compiled TS output + JSDoc types |
easyp validate-config
easyp mod download
easyp lint -p proto -r .
easyp generate -p proto -r .
Follow the language-specific example linked above.
Generated tool names follow the pattern {namespace}_{MethodName}. Dots in the namespace are normalized to underscores. Override the method segment with mcp.options.v1.method.name.
Requiredness in generated MCP JSON Schema is determined by proto3 syntax:
| Proto Pattern | Required? |
|---|---|
string name = 1 (singular, no optional) | YES |
optional string name = 1 | NO |
repeated string names = 1 | NO |
map<string, string> m = 1 | NO |
oneof choice { ... } | NO (unless mcp.options.v1.oneof.required = true) |
Fields that are not required accept explicit JSON null.
MCP tool I/O uses ProtoJSON encoding. Key differences from plain JSON:
int64/uint64 are JSON strings, not numbersfloat/double accept "NaN", "Infinity", "-Infinity" as stringsbytes use base64 encoding"FORECAST_MODE_DAILY")Timestamp → RFC 3339 string, Duration → "3.5s", FieldMask → "field1,field2"oneof, optional$defs/$refAny, Empty, Timestamp, Duration, FieldMask,
Struct, Value, ListValue, and all scalar wrapper typesThe generator rejects at generation time (not runtime):
google.protobuf.* typesimport "mcp/options/v1/options.proto";
// Service: namespace prefix, description, icons
option (mcp.options.v1.service) = {
namespace: "myapi"
description: "My API tools."
};
// Method: tool name, title, description, visibility, agent hints
option (mcp.options.v1.method) = {
name: "CustomName"
title: "Human Title"
description: "What this tool does."
hidden: true
annotations: {
read_only_hint: true
destructive_hint: false
idempotent_hint: true
}
};
// Field: description, examples, defaults, validation constraints
[(mcp.options.v1.field) = {
description: "Field purpose."
examples: [{ string_value: "example" }]
default_value: { integer_value: 42 }
pattern: "^[A-Z]"
min_length: 1
max_length: 255
minimum: 0
maximum: 100
min_items: 1
max_items: 50
unique_items: true
}];
// Oneof: make a oneof group required in the MCP schema
option (mcp.options.v1.oneof) = { required: true };
// Enum: title and description
option (mcp.options.v1.enum) = { title: "Status" };
// Enum value: hide sentinel zero-value
UNSPECIFIED = 0 [(mcp.options.v1.enum_value) = { hidden: true }];
For full options details, read references/options-reference.md in this skill.
rpc InternalDebug(DebugRequest) returns (DebugResponse) {
option (mcp.options.v1.method) = { hidden: true };
};
// Read-only query
option (mcp.options.v1.method) = {
annotations: { read_only_hint: true }
};
// Destructive mutation
option (mcp.options.v1.method) = {
annotations: { destructive_hint: true }
};
# Go
npx -y @modelcontextprotocol/inspector go run ./cmd/myserver
# Python
npx -y @modelcontextprotocol/inspector python server.py
# TypeScript (build first)
npx -y @modelcontextprotocol/inspector node dist/server.js
# Java/Kotlin (after installDist)
npx -y @modelcontextprotocol/inspector ./build/install/myserver/bin/myserver
For detailed lookup tables, read these files from this skill directory:
references/options-reference.md — full MCP proto options with all fields and examplesreferences/schema-mapping.md — proto type → JSON Schema mapping, well-known types, nullability rulesGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub easyp-tech/skills --plugin protoc-gen-mcp-skill