protoc-gen-mcp
Generate typed MCP tools, prompts, and resources from Protocol Buffers
About protoc-gen-mcp
This large README is shown as a plain-text preview. Read the full README at the source
# protoc-gen-mcp <p align="center"> <a href="https://github.com/easyp-tech/protoc-gen-mcp/actions/workflows/tests.yml"><img alt="CI" src="https://github.com/easyp-tech/protoc-gen-mcp/actions/workflows/tests.yml/badge.svg"></a> <a href="https://coveralls.io/github/easyp-tech/protoc-gen-mcp?branch=master"><img alt="Coverage Status" src="https://coveralls.io/repos/github/easyp-tech/protoc-gen-mcp/badge.svg?branch=master"></a> <a href="https://pkg.go.dev/github.com/easyp-tech/protoc-gen-mcp"><img alt="Go Reference" src="https://pkg.go.dev/badge/github.com/easyp-tech/protoc-gen-mcp.svg"></a> <a href="https://github.com/easyp-tech/protoc-gen-mcp/releases/latest"><img alt="Release" src="https://img.shields.io/github/v/release/easyp-tech/protoc-gen-mcp?sort=semver"></a> <a href="https://github.com/easyp-tech/protoc-gen-mcp/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/github/license/easyp-tech/protoc-gen-mcp"></a> <a href="https://github.com/easyp-tech/protoc-gen-mcp/blob/master/go.mod"><img alt="Go version" src="https://img.shields.io/github/go-mod/go-version/easyp-tech/protoc-gen-mcp?logo=go&logoColor=white"></a> </p> `protoc-gen-mcp` generates Go, Python, Kotlin, Java, and TypeScript MCP bindings from protobuf definitions — covering **tools**, **prompts**, and **resources**. JavaScript users consume the compiled TypeScript output and declarations. ## Supported MCP Primitives | Primitive | Go | Python | Kotlin | Java | TypeScript | |-----------|:--:|:------:|:------:|:----:|:----------:| | **Tools** | ✅ Full | ✅ Full | ✅ Full | ✅ Full | ✅ Full | | **Prompts** | ✅ Full | ✅ Full | ✅ Full | ✅ Full | ✅ Full | | **Resources** | ✅ Full | ⚠️ Interface | ⚠️ Interface | ⚠️ Interface | ⚠️ Interface | ## MVP - protobuf is the source of truth - generator emits typed Go, Python, Kotlin, Java, and TypeScript MCP bindings - **Tools**: generated from `service` RPC methods with `(mcp.options.v1.method)` - **Prompts**: generated from `message` types with `(mcp.options.v1.prompt)` - **Resources**: generated from `message` types with `(mcp.options.v1.resource)` — static (fixed URI) and template (parameterized URI) resources - Go runtime uses the official Go MCP SDK - Python runtime targets the official MCP Python SDK with `google.protobuf` - Kotlin runtime targets the official `io.modelcontextprotocol:kotlin-sdk-server` SDK - Java runtime targets the official `io.modelcontextprotocol.sdk:mcp` SDK - TypeScript runtime targets the official `@modelcontextprotocol/sdk` SDK, Protobuf-ES, and Ajv-backed raw JSON Schema validation - generated Python handlers default to dataclasses and explicit `oneof` wrapper types from `*_mcp.py`; `python_handler=protobuf` opts into raw `*_pb2` handler request/response classes with the same registration helper; `python_handler=dataclass+protobuf` generates both surfaces side by side - generated Kotlin handlers implement `<Service>ToolHandler` and are registered through `register<Service>Tools(server: Server, impl: <Service>ToolHandler, namespace: String? = null)` - generated Java handlers implement nested `<Service>ToolHandler` interfaces inside a generated `<ProtoFile>Mcp` sidecar and are registered through `register<Service>Tools(McpServerTransportProvider transportProvider, <Service>ToolHandler impl, String namespace)` - generated TypeScript handlers implement `<Service>ToolHandler` and are registered through `register<Service>Tools(server, impl, namespace?)` - Direct lang=javascript generation is intentionally deferred; JavaScript projects use compiled TypeScript target `.js` files plus `.d.ts` declarations - request and response JSON follows ProtoJSON rules - runtime validation is driven by generated JSON Schema ## Repository Workflows Use `easyp` for repository generation and checks. The repository is currently aligned to `easyp v0.15.2-rc1`. ```bash easyp --cfg easyp.yaml lint -p mcp -r . easyp --cfg easyp.yaml generate -p mcp -r . easyp --cfg easyp.test.yaml lint -p internal/testproto -r . easyp --cfg easyp.test.yaml generate -p internal/testproto -r . gradle --no-daemon -p examples/jvm :java-server:compileJava :kotlin-server:compileKotlin gradle --no-daemon -p examples/jvm :java-server:installDist :kotlin-server:installDist go test ./internal/examplemcp -run 'Test(Java|Kotlin).*OverStdio' -count=1 (cd examples/node/sdk-spike && npm ci && npm run typecheck && npm run build) (cd examples/10_python_protobuf_standalone && make lint && make clean generate) (cd examples/8_typescript_standalone && make lint && make clean build) (cd examples/9_javascript_standalone && make clean build) go test ./examples -run 'TestStandalonePython(Protobuf)?ExampleOverStdio' -count=1 go test ./examples -run 'TestStandalone(TypeScript|JavaScript)ExampleOverStdio' -count=1 go test ./... goreleaser check ``` `easyp.yaml` is the main config for shipped protobuf APIs. `easyp.test.yaml` is the development and test config for repository fixtures. `mcp/options/v1/options.proto` carries its own explicit `go_package`, so consumers do not need a special Easyp `go_package_prefix` override just to use the MCP options package. CI is implemented in [tests.yml](.github/workflows/tests.yml) and runs config validation, Easyp lint, Easyp generation, a generated-file freshness check, JVM compile/install gates, JVM stdio parity checks, Node compile/build gates, generated Node stdio checks through `go test ./...`, and standalone Python/Node example tests. Releases are implemented in [release.yml](.github/workflows/release.yml) and use [`.goreleaser.yaml`](.goreleaser.yaml) to publish tagged builds of the `protoc-gen-mcp` binary. This repository does not publish separate Java, Kotlin, TypeScript, or JavaScript runtime artifacts; downstream projects compile generated source against their language SDK dependencies. ## TypeScript And JavaScript Support TypeScript support is implemented for Node stdio servers and verified through the official MCP TypeScript SDK, Protobuf-ES, and Ajv. User-owned standalone project layouts are shown in [`examples/8_typescript_standalone`](examples/8_typescript_standalone/) and [`examples/9_javascript_standalone`](examples/9_javascript_standalone/). - Node prerequisites for the in-repo walkthrough are Go 1.24+, Node.js, npm, `easyp v0.15.2-rc1`, and the npm dependencies pinned by the examples. - The Node generator mode is `lang=typescript`. - The tested stack is `@modelcontextprotocol/[email protected]`, `@bufbuild/[email protected]`, `@bufbuild/protoc-gen-es`, and `[email protected]`. - Protobuf message classes are generated by Protobuf-ES as `_pb.ts` files with NodeNext-compatible `.js` import specifiers. - `protoc-gen-mcp` generates `*_mcp.ts` sidecars that install low-level `tools/list` and `tools/call` handlers on the official SDK `Server`. - Generated TypeScript preserves the shared raw JSON Schema and compiles it with Ajv, then maps request/response payloads through Protobuf-ES ProtoJSON. - JavaScript consumption uses the compiled `.js` output and generated `.d.ts` declarations from the TypeScript target. Direct lang=javascript generation is intentionally deferred until compiled TypeScript output proves insufficient for common JavaScript projects. Use the standalone TypeScript example when you want a full user-project generation flow. Use the JavaScript example when you want to inspect how a plain `.js` server imports compiled generated output without a separate renderer. ## JVM Support JVM support is implemented and CI-verified through the runnable [`examples/jvm`](examples/jvm/README.md) workspace. User-owned standalone project layouts are shown in [`examples/6_java_standalone`](examples/6_java_standalone/) and [`examples/7_kotlin_standalone`](examples/7_kotlin_standalone/). - JVM prerequisites for the in-repo walkthrough are Go 1.24+, JDK 17+, and Gradle 9.2+. - The JVM generator modes are `lang=java` and `lang=kotlin`. - The Java path compiles generated protobuf Java output plus a generated `lang=java` MCP sidecar against the official `io.modelcontextprotocol.sdk:mcp` SDK. - The Kotlin path follows the tested dual-generation flow from `examples/jvm/kotlin-server/build.gradle.kts`: Java protobuf output, Kotlin protobuf output, and then the `lang=kotlin` MCP sidecar are all required. - The canonical runnable JVM verification path uses `installDist` plus the installed scripts under `examples/jvm/*/build/install/.../bin/*`, matching `internal/examplemcp/jvm_stdio_test.go` and CI. - Standalone JVM protos can be Java/Kotlin-native and omit `go_package`; the generator synthesizes internal protogen metadata for `lang=java` and `lang=kotlin`. Use the root README for the language matrix and repository workflow, inspect the standalone Java/Kotlin directories for user-project shape, and use [examples/jvm/README.md](examples/jvm/README.md) for the cross-language JVM verification workspace. ## Test MCP Server The repository also includes a runnable MCP server for manual client checks: ```bash # stdio (default) go run ./cmd/example-mcp-server # Streamable HTTP (MCP spec 2025-11-25), localhost only by default go run ./cmd/example-mcp-server -transport=http -addr=127.0.0.1:8080 -path=/mcp python ./cmd/example-python-mcp-server/main.py ``` It serves the generated tools from `internal/testproto/example/v1` and is used by the stdio smoke tests in `internal/examplemcp/stdio_test.go`. The example server currently exposes: - `example_CreateReport` - `example_Health` - `example_DescribeAdvancedShapes` - `example_DescribeScalarShapes` ### Go Streamable HTTP (`mcpruntime`) Go servers can expose the same generated tools over **Streamable HTTP** instead of (or in addition to) stdio: ```go mux := http.NewServeMux() mux.Handle("/mcp", mcpruntime.NewStreamableHTTPHandler(server, mcpruntime.StreamableHTTPOptions{ AllowedOrigins: []string{"http://localhost:3000"}, })) // Prefer binding to 127.0.0.1 for local deployments (DNS-rebinding mitigation). http.ListenAndServe("127.0.0.1:8080", mux) ``` Or: ```go mcpruntime.ServeStreamableHTTP(ctx, "127.0.0.1:8080", server, mcpruntime.StreamableHTTPOptions{ Path: "/mcp", }) ``` Behavior highlights: - Single MCP endpoint: `POST` (JSON-RPC), `GET` (SSE listen), `DELETE` (session end) - Multi-client sessions via `MCP-Session-Id` on `initialize` - Origin validation (localhost Origins allowed by default when the allowlist is empty) - Optional SSE POST responses with `PreferSSE: true` - `Last-Event-ID` replay for GET streams (in-memory buffer) - Legacy HTTP+SSE (2024-11-05) is **not** implemented ## Testing With MCP Inspector Use the official [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) for interactive stdio checks while developing a server. The Inspector runs through `npx` and starts the MCP server command you pass after the Inspector package. For the Go example server: ```bash npx -y @modelcontextprotocol/inspector go run ./cmd/example-mcp-server ``` For the Python example server: ```bash npx -y @modelcontextprotocol/inspector python ./cmd/example-python-mcp-server/main.py ``` For the standalone raw protobuf Python example, create the example virtualenv first and then point the Inspector at the local server: ```bash (cd examples/10_python_protobuf_standalone && make setup) (cd examples/10_python_protobuf_standalone && npx -y @modelcontextprotocol/inspector .venv/bin/python server.py) ``` For installed JVM examples, build the scripts first and then point the Inspector at the installed application: ```bash gradle --no-daemon -p examples/jvm :java-server:installDist :kotlin-server:installDist npx -y @modelcontextprotocol/inspector ./examples/jvm/java-server/build/install/java-server/bin/java-server npx -y @modelcontextprotocol/inspector ./examples/jvm/kotlin-server/build/install/kotlin-server/bin/kotlin-server ``` For standalone Node examples, build first and then point the Inspector at the Node server entrypoint: ```bash (cd examples/8_typescript_standalone && make build) npx -y @modelcontextprotocol/inspector node ./examples/8_typescript_standalone/dist/server.js (cd examples/9_javascript_standalone && make build) npx -y @modelcontextprotocol/inspector node ./examples/9_javascript_standalone/src/server.js ``` When the Inspector UI opens, connect to the server, open the Tools tab, run List Tools, then call a generated tool such as `example_Health` or `example_CreateReport`. This is the fastest manual check that generated tool names, schemas, annotations, ProtoJSON inputs, structured outputs, and server stdio wiring are visible to an MCP client. ## Examples We provide several standalone, runnable examples demonstrating generated MCP tools, protobuf `options`, validation constraints, and integration with the official Go, Python, Kotlin, Java, TypeScript, and JavaScript SDK paths. Check out the [examples/](examples/) directory for: - [1_helloworld](examples/1_helloworld/) - Minimal Quickstart setup. - [2_weather_api](examples/2_weather_api/) - Read-only queries, validation limits, Oneofs. - [3_file_manager](examples/3_file_manager/) - Destructive tools and schema-based string parameter constraints. - [4_crm_system](examples/4_crm_system/) - A full mock system with FieldMask partial updates, custom icons mapping, schemas nested types, and advanced array filters. - [5_python_standalone](examples/5_python_standalone/) - A Python-only user-style project with its own `pyproject.toml`, `easyp.yaml`, generated bindings, and stdio server. - [10_python_protobuf_standalone](examples/10_python_protobuf_standalone/) - A Python-only user-style project that opts into `python_handler=dataclass+protobuf` and implements the raw `*_mcp_pb.py` sidecar with `*_pb2` classes. - [6_java_standalone](examples/6_java_standalone/) - A Java user-style project with its own Gradle build, `easyp.yaml`, protobuf contract, and generated MCP sidecar. - [7_kotlin_standalone](examples/7_kotlin_standalone/) - A Kotlin user-style project with its own Gradle build, `easyp.yaml`, protobuf contract, and generated MCP sidecar. - [8_typescript_standalone](examples/8_typescript_standalone/) - A TypeScript user-style project with its own npm package, `tsconfig.json`, `easyp.yaml`, Protobuf-ES output, generated MCP sidecar, and stdio server. - [9_javascript_standalone](examples/9_javascript_standalone/) - A JavaScript consumption proof that imports compiled generated `.js` output and `.d.ts` metadata from the TypeScript standalone project. - [jvm](examples/jvm/README.md) - A Java/Kotlin official SDK workspace with Gradle-managed protobuf generation, `lang=java` / `lang=kotlin` sidecars, `installDist` scripts, and stdio verification. ## Agent Skill Install the [skills.sh](https://skills.sh/) agent skill to let your AI coding assistant build MCP servers with protoc-gen-mcp: ```bash npx skills add easyp-tech/protoc-gen-mcp-skill ``` The skill source lives in a separate repository: [easyp-tech/protoc-gen-mcp-skill](https://github.com/easyp-tech/protoc-gen-mcp-skill). The skill teaches agents the full workflow: define proto → configure easyp → generate → implement handler → serve. It covers proto options, requiredness policy, ProtoJSON contract, and common patterns. ## Generation With Easyp The intended workflow is `easyp`, not manual `protoc` invocation. For mixed Go/Python/JVM/Node projects, `easyp` can drive `protoc-gen-go`, the standard Python protobuf generator, Protobuf-ES, and `protoc-gen-mcp` from one config. Before running generation, make sure `protoc-gen-go` is installed and available in `PATH` if your config uses the standard Go plugin: ```bash go install google.golang.org/protobuf/cmd/[email protected] ``` Example `easyp.yaml`: ```yaml lint: use: - PACKAGE_DEFINED - PACKAGE_VERSION_SUFFIX - RPC_NO_CLIENT_STREAMING - RPC_NO_SERVER_STREAMING generate: inputs: - directory: path: mcp root: "." plugins: - name: go out: . opts: paths: source_relative - name: python with_imports: true out: . - command: ["go", "run", "github.com/easyp-tech/protoc-gen-mcp/cmd/protoc-gen-mcp@latest"] out: . opts: paths: source_relative - command: ["go", "run", "github.com/easyp-tech/protoc-gen-mcp/cmd/protoc-gen-mcp@latest"] out: . opts: paths: source_relative lang: python python_runtime: google.protobuf # Optional: use raw *_pb2 handler request/response classes instead of # the default generated dataclass API. # python_handler: protobuf # Optional: generate both surfaces in one pass. This keeps *_mcp.py as # the dataclass sidecar and adds *_mcp_pb.py for raw *_pb2 handlers. # python_handler: dataclass+protobuf - command: ["go", "run", "github.com/easyp-tech/protoc-gen-mcp/cmd/protoc-gen-mcp@latest"] out: . opts: paths: source_relative lang: java - command: ["go", "run", "github.com/easyp-tech/protoc-gen-mcp/cmd/protoc-gen-mcp@latest"] out: . opts: paths: source_relative lang: kotlin - command: ["./node_modules/.bin/protoc-gen-es"] with_imports: true out: src/generated opts: target: ts import_extension: js - command: ["go", "run", "github.com/easyp-tech/protoc-gen-mcp/cmd/protoc-gen-mcp@latest"] out: src/generated opts: paths: source_relative lang: typescript ``` Typical commands: ```bash easyp --cfg easyp.yaml validate-config easyp --cfg easyp.yaml lint -p mcp -r . easyp --cfg easyp.yaml generate -p mcp -r . ``` That generates language-specific protobuf output plus MCP sidecars such as `*.pb.go`, `*_pb2.py`, `*.mcp.go`, `*_mcp.py`, Protobuf-ES `_pb.ts`, and TypeScript `*_mcp.ts` files. The Python target currently supports only `python_runtime=google.protobuf`; handler mode defaults to `python_handler=dataclass`, and `python_handler=protobuf` opts into raw `*_pb2` handlers. `python_handler=dataclass+protobuf` generates both surfaces in one pass: `*_mcp.py` remains the dataclass sidecar and `*_mcp_pb.py` exposes the raw protobuf handler sidecar. Generated Python output also emits a small `mcp/__init__.py` bridge so `mcp.options.*` protobuf modules can coexist with the official `mcp` SDK package in one import tree. No special Easyp override is required for `mcp.options.v1`, because the package declares `go_package` directly in `options.proto`. For reproducible builds, prefer pinning a specific tag instead of `@latest`. For JVM consumers, the language selectors are `lang=java` and `lang=kotlin`. The Java path generates a Java sidecar alongside protobuf Java output. The Kotlin path must follow the same rule as the in-repo Gradle example: Java protobuf output, Kotlin protobuf output, and the `lang=kotlin` MCP sidecar are all part of the working build graph. User-authored JVM protos do not need a Go `go_package` option just to satisfy `protoc-gen-mcp`. See [examples/6_java_standalone](examples/6_java_standalone/), [examples/7_kotlin_standalone](examples/7_kotlin_standalone/), and [examples/jvm/README.md](examples/jvm/README.md) for runnable layouts. For TypeScript consumers, generate Protobuf-ES `_pb.ts` files first and then run `protoc-gen-mcp` with `lang=typescript` into the same generated-source tree. Use `import_extension=js` so emitted imports work under Node ESM and `moduleResolution: NodeNext`. The generated MCP sidecar imports the official `@modelcontextprotocol/sdk` low-level `Server`, preserves raw JSON Schemas, validates them through Ajv, and maps payloads through Protobuf-ES ProtoJSON. See [examples/8_typescript_standalone](examples/8_typescript_standalone/) for a complete standalone project with pinned npm dependencies and checked-in generated sources. For JavaScript consumers, do not use `lang=javascript`; direct
Frequently Asked Questions
What is protoc-gen-mcp?
protoc-gen-mcp is a Generators library for the Go programming language. Generate typed MCP tools, prompts, and resources from Protocol Buffers
How do I install protoc-gen-mcp?
Install protoc-gen-mcp with the Go module system using `go get easyp-tech/protoc-gen-mcp`. Check the repository for the current installation instructions.
What category does protoc-gen-mcp belong to?
protoc-gen-mcp is listed under Generators, specifically Generators.