A simple MCP server that will proxy to a grpc backend based on a provided descriptors file or using reflection.
-
Install the binary:
go install .orgo install github.com/adiom-data/grpcmcpEnsure the go bin directory is in your PATH. -
In a terminal, run the example grpc server
go run example/main.go. This will start a grpc health service on port 8090 with server reflection enabled. Note that this runs on the default port that grpcmcp will connect to. -
Streamable HTTP Transport In another terminal, run
grpcmcp --hostport=localhost:3000 --reflect. Specifyinghostportwill use Streamable HTTP by default. The MCP endpoint will be served athttp://localhost:3000/mcp. -
Legacy SSE Transport For older clients, run
grpcmcp --hostport=localhost:3000 --transport=sse --reflect. The SSE endpoint will be served athttp://localhost:3000/sse. -
STDIN Transport Set up the MCP config. e.g.
"grpcmcp": {
"command": "grpcmcp",
"args": ["--reflect"]
}
grpcmcp --help for a full list of options.
-
hostportstring - When set, serve MCP over HTTP, and use this as the server host:port. -
transportstring - Transport to use whenhostportis set. Defaults tohttpfor Streamable HTTP at/mcp. Set tossefor the legacy SSE transport at/sse. -
descriptorsstring - Specify file location of the protobuf definitions generated frombuf build -o protos.pborprotoc --descriptor_set_out=protos.pbinstead of using gRPC reflection. -
reflect- If set, use reflection to retrieve gRPC endpoints instead of descriptor file. -
urlstring - Specify the url of the backend server. -
servicesstring - Comma separated list of fully qualified gRPC service names to filter. -
bearerstring - Token to attach in anAuthorization: Bearerheader. -
bearer-envstring - Environment variable for token to attach in anAuthorization: Bearerheader. Overridesbearer. -
headerstring (repeatable) - Headers to add inKey: Valueformat. -
string64- If set, expose 64-bit protobuf integer fields (int64,uint64,sint64,fixed64,sfixed64) as strings only in MCP JSON schemas. This avoids precision ambiguity for JavaScript-based clients and agents. By default, schemas continue to allow either JSON numbers or strings for compatibility.
grpcmcp currently exposes unary gRPC methods as MCP tools. Backend gRPC client-streaming, server-streaming, and bidi-streaming methods are skipped. This is separate from MCP transports: Streamable HTTP and legacy SSE are supported for client connections to grpcmcp.
grpcmcp can also be embedded as a Go library. The library accepts a descriptor set directly, so applications can decide how to load descriptors and how to wrap the MCP HTTP handler.
descriptors, err := grpcmcp.LoadDescriptorsFromReflection(ctx, backendURL, headers, false)
if err != nil {
return err
}
srv, err := grpcmcp.NewServer(grpcmcp.Config{
ServerName: "gRPC MCP Server",
Version: "1.0.0",
BaseURL: backendURL,
Descriptors: descriptors,
Headers: grpcmcp.StaticHeaders(headers),
})
if err != nil {
return err
}
handler := server.NewStreamableHTTPServer(srv)To route backend calls through a custom transport, provide HTTPClient. For example, an embedded application can use an in-memory HTTP transport such as go.akshayshah.org/memhttp by passing the in-memory server's URL and client:
backend, err := memhttp.New(backendHandler)
if err != nil {
return err
}
defer backend.Close()
srv, err := grpcmcp.NewServer(grpcmcp.Config{
BaseURL: backend.URL(),
HTTPClient: backend.Client(),
Descriptors: descriptors,
})For dynamic backend auth, provide a ToolHeaderProvider. The full mcp.CallToolRequest is available, including inbound HTTP headers supplied by supported MCP transports.
srv, err := grpcmcp.NewServer(grpcmcp.Config{
BaseURL: backendURL,
Descriptors: descriptors,
Headers: func(ctx context.Context, req mcp.CallToolRequest) (http.Header, error) {
h := make(http.Header)
h.Set("Authorization", req.Header.Get("Authorization"))
return h, nil
},
})Inbound MCP authentication should be handled by wrapping the HTTP handler with standard Go middleware. Outbound backend authentication is controlled by the configured header provider.
Join our Discord at https://discord.gg/hDjx3DehwG