Claude Media
gRPCサービスをMCP化してClaudeに叩かせる2つの経路

gRPCサービスをMCP化してClaudeに叩かせる2つの経路

protoファイルからMCPサーバーを自動生成するprotoc-gen-go-mcpとprotoc-gen-mcpの違いを比較し、社内gRPCサービス群をClaude Codeから呼び出す手順を解説します。

gRPCサービスは.protoファイルにメソッドとメッセージ型がすでに定義されています。この定義をそのままMCPツールのJSON Schemaに変換できれば、REST APIのように1エンドポイントずつツール定義を手書きする手間がかかりません。この発想を実装したprotocプラグインが2種類公開されており、この記事ではその選び方と導入手順を扱います。

MCPプロトコル自体の仕組みはMCPとは、Go向けのMCPサーバー実装全般はGoでMCPサーバーを書く公式SDKの使い方が対応します。

gRPCをMCP化する2つのアプローチ

.protoファイルからMCPサーバーを生成するプラグインには、既存のgRPCサーバーをそのまま包む方式と、proto定義にMCP用の注釈を足す方式の2種類があります。

protoc-gen-go-mcp(Redpanda Data)protoc-gen-mcp(The Protobuf Project)
対応言語protoc-gen-go-mcp(Redpanda Data)Goprotoc-gen-mcp(The Protobuf Project)Go / Rust / C++
生成対象protoc-gen-go-mcp(Redpanda Data)既存のgRPC/ConnectRPCサービスをラップするMCPハンドラprotoc-gen-mcp(The Protobuf Project)Tools・Prompts・Resources・Elicitationを含むMCPサーバー一式(Elicitationハンドラの生成はGo/Rustのみ、C++は非対応)
proto側の変更protoc-gen-go-mcp(Redpanda Data)不要(既存の.protoをそのまま使える)protoc-gen-mcp(The Protobuf Project)必要((mcp.v1.tool)等のオプションを注釈)
MCPライブラリprotoc-gen-go-mcp(Redpanda Data)公式go-sdkmark3labs/mcp-goをアダプタ経由で選択可protoc-gen-mcp(The Protobuf Project)公式go-sdk(Go)・rmcp(Rust)
向くケースprotoc-gen-go-mcp(Redpanda Data)既にあるgRPCサーバー群を手早くツール化したいprotoc-gen-mcp(The Protobuf Project)プロンプトやリソースまで含めてMCPサーバーを設計したい

既存資産を素早くMCP化するならprotoc-gen-go-mcp、MCPサーバーとしての機能をproto定義から丁寧に設計するならprotoc-gen-mcpという住み分けです。以降、それぞれの生成物と導入手順を見ていきます。

protoc-gen-go-mcp: 既存のgRPCサーバーをそのままツール化

protoc-gen-go-mcpはRedpanda Dataが公開しているOSSのprotocプラグインです。.protoファイルのサービス定義ごとに*.pb.mcp.goを生成し、gRPCサーバーの各RPCメソッドをそのままMCPツールとして登録できるようにします。ツールの入力スキーマは、RPCメソッドの入力メッセージ型からJSON Schemaとして自動導出されます。

buf.gen.yamlにプラグインを追加してコード生成します。

plugins:
  - local:
      - go
      - run
      - github.com/redpanda-data/protoc-gen-go-mcp/cmd/protoc-gen-go-mcp@latest
    out: ./gen/go
    opt: paths=source_relative

生成コードはruntime.MCPServerという共通インターフェース越しに動くため、実際のMCPサーバー実装(公式go-sdkmark3labs/mcp-goか)は生成後に選べます。

import (
    "github.com/modelcontextprotocol/go-sdk/mcp"
    "github.com/redpanda-data/protoc-gen-go-mcp/pkg/runtime/gosdk"
)
 
raw, s := gosdk.NewServer("my-server", "1.0.0")
testdatamcp.RegisterTestServiceHandler(s, &srv)
raw.Run(ctx, &mcp.StdioTransport{})

すでに動いているgRPCクライアントがあるなら、実装を書かずにMCPツール呼び出しをそのまま転送する経路もあります。

testdatamcp.ForwardToTestServiceClient(s, myGrpcClient)

このForwardTo*Client系の関数が、社内マイクロサービス群を機械的にツール化できるこのプラグインを使う最大の利点です。サービスの数だけ.protoがあれば、ハンドラのコードを1行も書かずにMCPサーバー化が完了します。

proto定義に無い値をツールに追加で渡したい場合は、WithExtraPropertiesで拡張プロパティを定義できます。たとえば複数のバックエンドを切り替えるbase_urlのようなフィールドを、protoスキーマを変更せずにツールの入力に追加できます。

option := runtime.WithExtraProperties(
    runtime.ExtraProperty{
        Name:        "base_url",
        Description: "Base URL for the API",
        Required:    true,
        ContextKey:  MyURLOverrideKey{},
    },
)
testdatamcp.RegisterTestServiceHandler(s, &srv, option)

渡された値はGoのcontext.Contextに格納され、ハンドラ側の実装からContextKeyで取り出せます。protoファイルを触らずにMCP側だけの都合でパラメータを増やしたいときに使う仕組みです。

claude mcp add --transport stdio grpc-tools -- go run ./cmd/mcpserver

生成したMCPサーバーのstdioバイナリをビルドし、通常のstdio型MCPサーバーとしてClaude Codeに追加します。

protoc-gen-mcp: proto定義にMCPオプションを注釈する

protoc-gen-mcpはThe Protobuf Projectが公開しているプラグインで、.protoファイルに(mcp.v1.tool)等のオプションを注釈し、Go・Rust・C++向けにMCPサーバーのコードを生成します。プロトコルバージョン2026-07-28に準拠しており、Tools(呼び出し可能な関数)に加えてPrompts(構造化された対話テンプレート)・Resources(取得可能なコンテキスト)・Elicitation(動的な入力確認フロー)まで生成対象に含む点が、protoc-gen-go-mcpとの大きな違いです。ただしElicitationハンドラはGo・Rust向けにのみ生成され、C++ジェネレータは非対応です。

import "mcp/v1/annotations.proto";
 
service TodoService {
  option (mcp.v1.service) = {
    app: { display_name: "Todo App" version: "1.0.0" }
  };
 
  rpc CreateTodo(CreateTodoRequest) returns (Todo) {
    option (mcp.v1.tool) = {
      description: "Creates a new todo item."
    };
    option (mcp.v1.elicitation) = {
      message: "Please confirm the todo details before creating."
      schema: "todo.v1.CreateTodoConfirmation"
    };
  }
}

elicitationオプションを付けたRPCは、実行前にクライアント側で確認ダイアログを挟みます。削除やデータ変更を伴うMutation系のメソッドに付けておくと、Claudeが確認なしに実行してしまう事故を防げます。

導入にはprotoファイル側でMCPアノテーションの依存を追加する準備が要ります。

# buf.yaml
version: v2
deps:
  - buf.build/googleapis/googleapis
  - buf.build/the-protobuf-project/mcp
buf dep update
buf generate

生成後は、Go・Rust・C++それぞれの実装にサーバーロジックを書き込みます。RPCの入力メッセージにもgoogle.api.resourceアノテーションを付けておくと、MCPのResourcesとして自動的に検出される点も、protoc-gen-go-mcpには無い特徴です。長時間かかる処理はgRPCのサーバーストリーミングとmcp.v1.MCPProgress型を組み合わせることで、MCPのProgress通知(進捗の逐次報告)としてクライアントに届けられます。

MCP Inspectorで動作確認する

Claude Codeに繋ぐ前に、生成したMCPサーバーが正しくツールを公開できているかをMCP Inspectorで確認しておくと切り分けが早くなります。protoc-gen-mcpの例では、生成したバイナリをstdioで起動し、Inspectorから接続します。

cd examples/go/stdio && go run .
npx @modelcontextprotocol/inspector -- go run .

Inspectorの画面ではツール一覧・入力スキーマ・実際の呼び出し結果をブラウザ上で確認できます。ツール名が想定と違う、入力スキーマのフィールドが抜けている、といった問題はこの段階で見つけたほうが、Claude Code側の権限設定と混同せずに切り分けられます。

ツール名の衝突とプレフィックス

どちらの実装でも、同じgRPCサービスを複数のインスタンス(本番用DBと検証用DBのSQLサービスなど)に対して登録すると、ツール名が衝突します。protoc-gen-go-mcpではWithNamePrefixオプションでツール名に接頭辞を付けられます。

sqlv1mcp.RegisterSQLServiceHandler(s, postgresHandler, runtime.WithNamePrefix("postgres"))
sqlv1mcp.RegisterSQLServiceHandler(s, clickhouseHandler, runtime.WithNamePrefix("clickhouse"))
// postgres_SQLService_Query, clickhouse_SQLService_Query

既定のツール名はプロトコルの完全修飾名(パッケージ名_サービス名_メソッド名)をアンダースコア区切りにしたものです。64文字を超える名前は、10文字のハッシュ値とメソッド名末尾を組み合わせて自動的に短縮されます。これはClaude Desktopが課している64文字上限(対応プロバイダーの中でもっとも厳しい制約)に合わせた仕様です。

よくあるつまずき

インターセプターを迂回してしまう

ForwardTo*Client系の転送や生成ハンドラをgRPCサーバーへ直接登録する経路では、通常のgRPCリクエストに挟んでいる認可・ロギングなどのインターセプターを迂回します。既存のgRPCサーバーに設定済みのインターセプターに依存した認可チェックがある場合、MCP経由の呼び出しではその処理が実行されない前提で設計する必要があります。

WithExtraPropertiesの名前衝突で値が漏れる

WithExtraPropertiesで追加したプロパティ名が、対象のprotoメッセージが持つフィールド名と衝突すると、渡した値がcontext.Contextに格納されると同時にproto側のメッセージにもそのまま渡ってしまいます。拡張プロパティを定義する際は、対象protoの既存フィールド名と重複しないことを確認してから追加します。

64文字制限で意図しないツール名になる

サービス名やパッケージ名が長いprotoでは、自動生成されたツール名が64文字制限にかかりハッシュ付きの読みにくい名前になることがあります。protoc-gen-go-mcpでは(mcp.v1.tool_name)メソッドオプションで人間が読める名前を明示的に指定できます。指定する値は^[a-z_][a-z0-9_-]{0,63}$のパターンに従う必要があり、違反するとコード生成自体が失敗します。

OpenAI互換モードとの違いに気づかない

protoc-gen-go-mcpは標準MCPのJSON Schemaに加えて、OpenAI互換のスキーマも自動生成します。標準MCPはadditionalPropertiesanyOfoneOfをフルに使ったJSON Schemaで、マップ型はJSONオブジェクトとして表現されます。OpenAI互換モードはこれらの表現を使わない制限版で、マップ型はキーと値のペアの配列に変換され、Struct等のwell-knownな型はJSON文字列にエンコードされ、すべてのフィールドがnullable扱いの必須項目になります。

Claude向けに実装したつもりが誤ってOpenAI互換ハンドラを登録していると、複雑な型を持つフィールドの表現がクライアント側の期待と食い違い、パースエラーやスキーマ不一致になることがあります。どちらのハンドラを使うかはコード生成時ではなく登録時に選べます。

// 標準MCPとOpenAI互換を両方登録し、接続時に選ばせる
testdatamcp.RegisterTestServiceHandlerWithProvider(s, &srv, runtime.LLMProviderOpenAI)

登録関数の末尾にOpenAIが付いているか、WithProviderで明示的にプロバイダーを指定しているかを、実装レビュー時に確認する価値があります。

int64フィールドが文字列として返る

protoc-gen-go-mcpのJSON Schemaマッピングでは、int64uint64系の型はJSON Schema上string型になります。これはJSONが64ビット整数を精度を落とさず表現できないための仕様で、バグではありません。Claudeが返ってきた値を数値として扱う前提のコードを書くと、型不一致でエラーになります。

まとめ

既存のgRPCサーバー群を素早くMCP化したいならprotoc-gen-go-mcpForwardTo*Client系関数がもっとも手間が少なく、社内マイクロサービスの棚卸しにも向きます。Prompts・Resources・Elicitationまで含めてMCPサーバーを設計したい、あるいはGo以外の言語も対象にしたいならprotoc-gen-mcpを選びます。どちらもgRPCの資産である.protoを単一の情報源として保ち続けられる点は共通しており、MCPサーバーのためだけに別のスキーマを維持する必要がありません。

どちらを選んだ場合も、Claude Codeに接続する前にMCP Inspectorでツール一覧と入力スキーマを確認しておくと、権限設計の話とツール定義自体の不具合を切り分けやすくなります。Claude Codeへの接続方法自体はclaude mcp addのstdio・HTTPどちらの経路も使え、詳細はClaude Code MCP設定ガイドにまとめています。

この記事を共有:XはてブLinkedIn