About Grpc MCP Gateway
Grpc MCP Gateway is an MCP server published by machanirobotics in the Developer Tools category: gRPC to MCP proxy generator following the MCP Spec. It has been installed 0 times through Conduid.
The repository has 3 stars and 0 forks, with the last commit 6 months ago. Six months or more without a commit doesn't mean the server is broken, but check the open issues (0) before depending on it in production.
Install
npx grpc-mcp-gatewayThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about Grpc MCP Gateway
Powered by Claude · Grounded in docs
Security checks
- ·README presentNot checked yet.
- ·License declaredNot checked yet.
- ·Tests presentNot checked yet.
- ·Dependencies pinnedNot checked yet.
- ·No dynamic code executionNot checked yet.
- !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.
README
grpc-mcp-gateway
Overview
grpc-mcp-gateway is a protoc plugin and runtime that automatically
converts your gRPC services into MCP-compatible servers.
gRPC → MCP proxy generator following the MCP Specification.
Instead of rewriting APIs for AI systems, you can expose your existing gRPC infrastructure as:
- Tools → callable functions for agents
- Prompts → structured interaction templates
- Resources → retrievable context/data
- Elicitation → dynamic input flows
Built with a spec-first approach, it ensures full compliance with the Model Context Protocol while keeping your system strongly typed and scalable.
Open-sourced by Machani Robotics, this project bridges traditional backend systems with AI-native platforms.
A protoc plugin and runtime that turns any gRPC service into a fully spec-compliant Model Context Protocol server — tools, prompts, resources, and elicitation — in Go, Python, Rust, and C++.
Features
- Multi-language — Generate MCP server code for Go, Python, Rust, and C++ from a single
.protofile - Tools — Every unary RPC becomes an MCP tool with a JSON Schema derived from the protobuf request message
- Prompts — Attach prompt templates to RPCs with schema-validated arguments via
(mcp.protobuf.prompt) - Field descriptions — Add
(mcp.protobuf.field) = { description: "..." }to message fields for schema descriptions - Enum descriptions — Add
(mcp.protobuf.enum)and(mcp.protobuf.enum_value)for enum-level and per-value descriptions in the schema - Progress — Use gRPC server streaming with
mcp.protobuf.MCPProgressfor MCP progress notifications on long-running tools - Resources — Auto-detect MCP resources from
google.api.resourceannotations - Elicitation — Generate confirmation dialogs before tool execution via
(mcp.protobuf.elicitation) - Transports — stdio, SSE, and streamable-http — run multiple concurrently in a single process
- gRPC Gateway — Forward MCP tool calls to a remote gRPC server (Go)
- Published Protos — Import annotations from
buf.build/machanirobotics/grpc-mcp-gateway, or install pre-compiled types from PyPI / crates.io
| Language | Generated File | Example |
|---|---|---|
| Go | *_service.pb.mcp.go |
examples/go |
| Python | *_service_pb2_mcp.py |
examples/python |
| Rust | *_service.mcp.rs |
examples/rust |
| C++ | *_service.mcp.h/cc + Rust bridge |
examples/cpp |
Architecture
graph LR
Proto[".proto + MCP annotations"] -->|buf generate| GenGo["Go MCP stubs"]
Proto -->|buf generate| GenPy["Python MCP stubs"]
Proto -->|buf generate| GenRs["Rust MCP stubs"]
Proto -->|buf generate| GenCpp["C++ MCP bridge"]
GenGo --> GoSrv["Go Server"]
GenPy --> PySrv["Python Server"]
GenRs --> RsSrv["Rust Server"]
GenCpp --> CppSrv["C++ Server"]
GoSrv -->|stdio / SSE / streamable-http| Client["MCP Client / LLM"]
PySrv -->|stdio / SSE / streamable-http| Client
RsSrv -->|stdio / SSE / streamable-http| Client
CppSrv -->|stdio / streamable-http| Client
How It Works
sequenceDiagram
participant LLM as LLM / MCP Client
participant MCP as MCP Server (generated)
participant gRPC as gRPC Service (your impl)
LLM->>MCP: tools/list
MCP-->>LLM: [{name, description, inputSchema}, ...]
LLM->>MCP: tools/call (tool_name, args)
Note over MCP: elicitation (if configured)
MCP->>gRPC: RPC method(request)
gRPC-->>MCP: response
MCP-->>LLM: tool result (JSON)
- Annotate your
.protoservices with MCP options (tools, prompts, resources, elicitation). - Generate MCP server code with
buf generateusingprotoc-gen-mcp. - Implement your gRPC service logic as usual.
- Serve — the generated code starts an MCP server on your chosen transport(s).
- Connect — MCP clients (Claude Desktop, MCP Inspector, custom LLM agents) discover and invoke your tools.
Install
Plugin
go install github.com/machanirobotics/grpc-mcp-gateway/plugin/cmd/protoc-gen-mcp@latest
Or download a binary from GitHub Releases.
Pre-compiled proto types
The MCP annotation types (mcp.protobuf.*) are published as pre-compiled libraries so generated code can resolve its imports at runtime — just like googleapis-common-protos for Google API types.
| Language | Package | Install |
|---|---|---|
| Go | mcp/protobuf/mcppb |
go get github.com/machanirobotics/grpc-mcp-gateway/mcp/protobuf/mcppb |
| Python | grpc-mcp-gateway-protos |
pip install grpc-mcp-gateway-protos==1.5.3 |
| Rust | mcp-protobuf |
cargo add mcp-protobuf@1.5.3 |
Python (PyPI) — Add to your project and import to register proto extensions:
# Required for MCP-annotated protos
import mcp.protobuf.annotations_pb2 # noqa: F401
Rust (crates.io) — Add to Cargo.toml; use the version matching the latest release:
[dependencies]
mcp-protobuf = "1.5.62" # or cargo add mcp-protobuf for latest
Quick Start
1. Add the proto dependency
# buf.yaml
version: v2
deps:
- buf.build/googleapis/googleapis
- buf.build/machanirobotics/grpc-mcp-gateway
buf dep update
2. Annotate your proto
syntax = "proto3";
package todo.v1;
import "mcp/protobuf/annotations.proto";
service TodoService {
option (mcp.protobuf.service) = {
app: {
name: "Todo App"
version: "1.0.0"
description: "A simple todo management application"
}
};
rpc CreateTodo(CreateTodoRequest) returns (Todo) {
option (mcp.protobuf.tool) = {
description: "Creates a new todo item."
};
option (mcp.protobuf.elicitation) = {
message: "Please confirm the todo details before creating."
schema: "todo.v1.CreateTodoConfirmation"
};
}
rpc GetTodo(GetTodoRequest) returns (Todo) {
option (mcp.protobuf.tool) = {
description: "Retrieves a todo by resource name."
};
option (mcp.protobuf.prompt) = {
name: "summarize_todos"
description: "Summarize all pending todo items for a user"
schema: "todo.v1.SummarizeTodosArgs"
};
}
}
// Enum with descriptions for MCP tool schema
enum Priority {
option (mcp.protobuf.enum) = { description: "Priority level for a todo item." };
PRIORITY_UNSPECIFIED = 0 [(mcp.protobuf.enum_value) = { description: "Unspecified; use default priority." }];
PRIORITY_LOW = 1 [(mcp.protobuf.enum_value) = { description: "Low priority; can be done when convenient." }];
PRIORITY_MEDIUM = 2 [(mcp.protobuf.enum_value) = { description: "Normal priority; default for most todos." }];
PRIORITY_HIGH = 3 [(mcp.protobuf.enum_value) = { description: "High priority; should be done soon." }];
PRIORITY_URGENT = 4 [(mcp.protobuf.enum_value) = { description: "Urgent; do first." }];
}
3. Generate code
# buf.gen.yaml
version: v2
plugins:
# --- Go ---
- local: protoc-gen-go
out: generated/go
opt: [module=example/generated/go]
- local: protoc-gen-mcp
out: generated/go
opt: [lang=go, module=example/generated/go]
# --- Python ---
- remote: buf.build/protocolbuffers/python
out: generated/python
- local: protoc-gen-mcp
out: generated/python
opt: [lang=python, paths=source_relative]
# --- Rust ---
- remote: buf.build/community/neoeinstein-prost
out: generated/rust
- local: protoc-gen-mcp
out: generated/rust
opt: [lang=rust, paths=source_relative]
# --- C++ (Rust bridge + C++ gRPC client) ---
- local: protoc-gen-mcp
out: generated/cpp
opt: [lang=cpp, paths=source_relative]
buf generate
4. Run with MCP Inspector
# Go
cd examples/go/stdio && go run .
npx @modelcontextprotocol/inspector -- go run .
# Python
cd examples/python
npx @modelcontextprotocol/inspector -- uv run python stdio/main.py
# Rust
cd examples/rust && cargo build --bin stdio
npx @modelcontextprotocol/inspector -- ./target/debug/stdio
# C++
cd examples/cpp && make
MCP_TRANSPORT=stdio npx @modelcontextprotocol/inspector -- ./server
MCP Annotations
All annotations are imported from mcp/protobuf/annotations.proto (BSR).
Service-level: mcp.protobuf.service
Defines app metadata for the MCP server:
option (mcp.protobuf.service) = {
app: { name: "My App" version: "1.0.0" description: "..." }
};
Tool: mcp.protobuf.tool
Override auto-generated tool name or description:
rpc CreateItem(CreateItemRequest) returns (Item) {
option (mcp.protobuf.tool) = {
name: "custom_tool_name"
description: "Custom description for LLMs."
};
}
Prompt: mcp.protobuf.prompt
Attach a prompt template to an RPC. The schema references a proto message whose fields become prompt arguments:
rpc GetItem(GetItemRequest) returns (Item) {
option (mcp.protobuf.prompt) = {
name: "summarize_items"
description: "Summarize all items"
schema: "mypackage.SummarizeItemsArgs"
};
}
Elicitation: mcp.protobuf.elicitation
Request user confirmation before executing a tool. The schema references a proto message whose fields become the confirmation form:
rpc DeleteItem(DeleteItemRequest) returns (google.protobuf.Empty) {
option (mcp.protobuf.elicitation) = {
message: "Are you sure you want to delete this item?"
schema: "mypackage.DeleteConfirmation"
};
}
Elicitation is supported in all three languages with graceful degradation — if the client doesn't support elicitation, the tool proceeds without confirmation.
Field: mcp.protobuf.field
Add JSON Schema metadata to a message field for the MCP tool inputSchema:
message User {
string name = 1 [
(google.api.field_behavior) = IDENTIFIER,
(mcp.protobuf.field) = {
description: "The resource name of the user. You can parse the user id from the resource name."
examples: "users/alice"
examples: "users/bob"
format: "uri" // optional: override format (uri, email, uuid, etc.)
deprecated: false // optional: mark field as deprecated
}
];
}
- description — Human-readable description (recommended for LLMs)
- examples — Example values to guide LLMs (repeated)
- deprecated — Mark the field as deprecated in the schema
- format — JSON Schema format override (e.g.
uri,email,uuid)
Enum: mcp.protobuf.enum and mcp.protobuf.enum_value
Add descriptions to enum types and individual enum values for the MCP tool inputSchema:
enum Priority {
option (mcp.protobuf.enum) = { description: "Priority level for a todo item." };
PRIORITY_UNSPECIFIED = 0 [(mcp.protobuf.enum_value) = { description: "Unspecified; use default priority." }];
PRIORITY_LOW = 1 [(mcp.protobuf.enum_value) = { description: "Low priority; can be done when convenient." }];
PRIORITY_MEDIUM = 2 [(mcp.protobuf.enum_value) = { description: "Normal priority; default for most todos." }];
PRIORITY_HIGH = 3 [(mcp.protobuf.enum_value) = { description: "High priority; should be done soon." }];
PRIORITY_URGENT = 4 [(mcp.protobuf.enum_value) = { description: "Urgent; do first." }];
}
The schema includes:
- description — Combined enum-level and per-value descriptions
- enumDescriptions — Map of value name → description for structured access
For enum fields, enum descriptions take precedence over (mcp.protobuf.field) description when both are present.
Progress (server streaming)
For long-running operations, use gRPC server streaming with mcp.protobuf.MCPProgress to send progress notifications to MCP clients. Define a stream response with a oneof:
import "mcp/protobuf/progress.proto";
message CreateTodoStreamChunk {
oneof payload {
mcp.protobuf.MCPProgress progress = 1;
Todo result = 2;
}
}
rpc CreateTodo(CreateTodoRequest) returns (stream CreateTodoStreamChunk);
The plugin auto-generates tool handlers that send MCP notifications/progress for each progress chunk and return the final result. Progress is supported when using ForwardTo*MCPClient (gRPC forwarding). Clients request progress by including progressToken in params._meta.
Progress and timeouts: Long-running requests that send progress must not time out. The gateway uses ReadTimeout: 0 and WriteTimeout: 0 by default so streaming progress is never interrupted. If you set WriteTimeout in MCPServerConfig, use 0 or a very high value for progress-enabled tools. MCP clients (e.g. Inspector) may have their own timeout; enable timeout reset on progress when available (MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS). If you see "MCP error -32001: Maximum total timeout exceeded", the client has a hard cap on total request time (Inspector default: 60s). Increase it, e.g. MCP_REQUEST_MAX_TOTAL_TIMEOUT=300000 (5 min, in ms).
Resources
Resources are auto-detected from google.api.resource annotations on proto messages. No additional MCP annotation is needed.
Project Structure
grpc-mcp-gateway/
├── go.mod # Single Go module
├── go.work # Workspace (root + examples)
├── proto/ # Publishable buf module (BSR)
│ └── mcp/protobuf/ # MCP annotation .proto source files
├── mcp/protobuf/ # Pre-compiled proto libraries
│ ├── mcppb/ # Go (.pb.go) — see [mcp/protobuf/README.md](mcp/protobuf/README.md)
│ ├── python/ # Python (PyPI: grpc-mcp-gateway-protos)
│ └── rust/ # Rust (crates.io: mcp-protobuf)
├── runtime/ # Go runtime — [README](runtime/README.md)
├── plugin/
│ ├── cmd/protoc-gen-mcp/ # Plugin binary (go install target)
│ └── generator/ # Code generation (Go, Python, Rust, C++)
│ └── templates/ # go.tpl, python.tpl, rust.tpl, cpp/*.tpl
├── examples/ # Separate module with replace directive
│ ├── proto/ # TodoService + CounterService definitions
│ ├── go/ # Go examples (http, stdio, sse, grpc-gateway, counter)
│ ├── python/ # Python examples (http, stdio, sse)
│ ├── rust/ # Rust examples (http, stdio, sse)
│ └── cpp/ # C++ example (Make, gRPC + MCP via Rust bridge)
└── .github/workflows/ # CI + release pipelines
Plugin Options
| Option | Values | Description |
|---|---|---|
lang |
go, python, rust, cpp |
Target language for generated code |
module |
Go module path | Go module prefix for output path resolution |
package_suffix |
any string (Go only) | Sub-package suffix for generated .pb.mcp.go files |
paths |
source_relative |
Place output relative to the proto source (Python, Rust) |
Generated Code
For each proto service, the plugin generates:
| Feature | Go | Python | Rust | C++ |
|---|---|---|---|---|
| Tools (per RPC) | s.AddTool(...) |
@server.call_tool() |
ServerHandler::call_tool() |
TodoServiceMcpImpl (cxx FFI) |
| Prompts | s.AddPrompt(...) |
@server.get_prompt() |
ServerHandler::get_prompt() |
— |
| Resources | s.AddResource(...) / s.AddResourceTemplate(...) |
@server.list_resources() |
ServerHandler::list_resources() |
— |
| Elicitation | runtime.RunElicitation(...) |
session.elicit(...) |
peer.create_elicitation(...) |
— |
| Serve function | ServeTodoServiceMCP() |
serve_todo_service_mcp() |
serve_todo_service_mcp() |
start_*_mcp_http / _stdio |
| gRPC forwarding | ForwardToTodoServiceMCPClient() |
forward_to_todo_service_mcp_client() |
— | In-process (C++ gRPC server) |
| Interface/trait | TodoServiceMCPServer |
TodoServiceMCPServer (Protocol) |
TodoServiceMcpServer (trait) |
TodoServiceMcpImpl (C++ class) |
JSON Schema derivation
The tool's inputSchema is derived from the protobuf request message:
- Field types → JSON Schema types
google.api.field_behaviorREQUIRED → JSON Schemarequiredbuf.validateconstraints →minLength,maxLength,pattern,minimum,maximum, etc.- Well-known types (Timestamp, Duration, FieldMask, Struct, Any, wrappers) → appropriate JSON Schema
- Protobuf
oneof→ JSON SchemaoneOf/anyOf - Enums → JSON Schema
enumwith string values;(mcp.protobuf.enum)/(mcp.protobuf.enum_value)→descriptionandenumDescriptions
Transport Configuration
Supported transports
| Transport | Value | Protocol | Use Case |
|---|---|---|---|
| stdio | stdio |
stdin/stdout pipes | Local tools, IDE integrations |
| SSE (legacy) | sse |
HTTP + Server-Sent Events | Browser clients, legacy MCP clients |
| Streamable HTTP | streamable-http |
HTTP + bidirectional JSON-RPC | Production deployments, modern SDKs |
Multiple transports
Run multiple transports concurrently with comma-separated values:
MCP_TRANSPORT=stdio,streamable-http go run .
MCP_TRANSPORT=stdio,streamable-http uv run python http/main.py
MCP_TRANSPORT=stdio,streamable-http cargo run --bin http
Environment variables
| Variable | Default | Description |
|---|---|---|
MCP_TRANSPORT |
per-example | Comma-separated: stdio, sse, streamable-http |
MCP_HOST |
0.0.0.0 |
Bind address for HTTP transports |
MCP_PORT |
8082 |
Listen port for HTTP transports |
GRPC_PORT |
50051 |
gRPC server listen port |
Go runtime configuration
import "github.com/machanirobotics/grpc-mcp-gateway/runtime"
cfg := &runtime.MCPServerConfig{
Name: "my-service",
Version: "1.0.0",
Transports: []runtime.Transport{runtime.TransportStdio, runtime.TransportStreamableHTTP},
Addr: ":8082",
BasePath: "/todo/v1/todoservice/mcp",
}
todopbv1.ServeTodoServiceMCP(ctx, server, cfg)
Python configuration
from todo.v1.todo_service_pb2_mcp import serve_todo_service_mcp
serve_todo_service_mcp(impl, transport="streamable-http", host="0.0.0.0", port=8082)
Rust configuration
let config = TodoServiceMcpTransportConfig {
transport: "streamable-http".into(),
host: "0.0.0.0".into(),
port: 8082,
..Default::default()
};
serve_todo_service_mcp(server, config).await?;
Examples
The examples/ directory contains TodoService (CRUD, prompts, elicitation) and CounterService (progress streaming) implementations:
| Service | Proto | Description |
|---|---|---|
| TodoService | proto/todo/v1/ |
CRUD, prompts, elicitation, resources |
| CounterService | proto/counter/v1/ |
Server-streaming with MCP progress notifications |
| Language | Directory | Transports | Test |
|---|---|---|---|
| Go | examples/go/ |
http, stdio, sse, grpc-gateway, counter | go test ./examples/go/... |
| Python | examples/python/ |
http, stdio, sse | uv run python -m pytest smoke_test.py |
| Rust | examples/rust/ |
http, stdio, sse | cargo check |
| C++ | examples/cpp/ |
streamable-http, stdio | make |
See each language's README for detailed setup and run instructions.
Testing with MCP Inspector
# stdio (Inspector spawns the process)
npx @modelcontextprotocol/inspector -- <command>
# HTTP (start server first, then open Inspector)
npx @modelcontextprotocol/inspector
# Enter URL, e.g. http://localhost:8082/todo/v1/todoservice/mcp or http://localhost:8083/counter/v1/counterservice/mcp
For long-running tools with progress, increase the Inspector's max total timeout (default 60s):
MCP_REQUEST_MAX_TOTAL_TIMEOUT=300000 npx @modelcontextprotocol/inspector
License
Licensed under the Apache License, Version 2.0.
README mirrored from the source repository 3 months ago. The original is authoritative.