mcpField
P
Protoc Gen Go Mcp

Protoc Gen Go Mcp

ローカル
github.com/redpanda-data開発ツール22713 週間前に更新

Protoc Gen Go Mcp について

protoc-gen-go-mcp は、gRPC または ConnectRPC サービス向けに MCP サーバーを生成する Protocol Buffers コンパイラープラグインです。既存の protobuf サービスを MCP クライアントに公開したい Go 開発者を対象としています。

主な機能

  1. 01proto サービス定義からの MCP ハンドラー生成
  2. 02メソッド入力から生成される JSON Schema
  3. 03gRPC および ConnectRPC クライアントへのツール呼び出し転送
  4. 04go-sdk と mark3labs/mcp-go の両ライブラリに対応
  5. 05実行時に標準 MCP と OpenAI 互換スキーマを選択

活用シーン

  1. 01既存の gRPC サービスを MCP ツールとして公開
  2. 02buf のビルドワークフローでの MCP コード生成
  3. 03同一サービスの複数インスタンスでのツール名プレフィックス付与

MCP 対応

生成されたコードは各 RPC メソッドを MCP サーバー上のツールとして登録し、JSON Schema によってツールの入力を MCP クライアントに伝えます。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

インストール方法は未掲載です

このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。

リポジトリ

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

protoc-gen-go-mcp

Test Go Report Card codecov

protoc-gen-go-mcp は、gRPC または ConnectRPC API 用の Model Context Protocol (MCP) サーバーを生成する、Protocol Buffers コンパイラープラグインです。

各 protobuf サービスの *.pb.mcp.go ファイルを生成し、ハンドラーを gRPC サーバーまたはクライアントに直接委譲できるようにします。MCP は内部でツール入力に JSON Schema を使用します。protoc-gen-go-mcp は、メソッドの入力ディスクリプターからこれらのスキーマを自動生成します。

生成されるコードは MCP ライブラリに依存しません。薄いアダプターを通じて、サーバー作成時に modelcontextprotocol/go-sdk(公式 SDK)または mark3labs/mcp-go を選択できます。再生成は不要です。

機能

  • .proto サービスから MCP ハンドラーを自動生成
  • メソッドの入力に対する JSON Schema を出力
  • gRPC または ConnectRPC サーバー/クライアントに接続
  • buf と簡単に統合
  • 実行時の LLM プロバイダー選択 — 標準の MCP または OpenAI 互換スキーマ
  • MCP ライブラリに依存しません — 公式の go-sdk と mark3labs/mcp-go の両方に対応

使い方

コードの生成

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

標準の *.pb.go ファイルも生成する必要があります。protoc-gen-go-mcp はデフォルトで {$servicename}mcp という別のサブフォルダーを使用し、*pb.go ファイルをインポートします。これは connectrpc-go と同様です。

buf generate を実行すると、protobuf サービス定義を含むパッケージごとに新しいフォルダーが作成されます。

tree pkg/testdata/gen/
gen
  go
    testdata
      test_service.pb.go
      testdataconnect/
        test_service.connect.go
      testdatamcp/
        test_service.pb.mcp.go
MCP サーバーのセットアップ

生成されたコードは runtime.MCPServer インターフェースを対象とします。対応するアダプターパッケージをインポートして、使用する MCP ライブラリを選択します。

公式 go-sdk を使用する場合(推奨)
import (
    "github.com/modelcontextprotocol/go-sdk/mcp"
    "github.com/redpanda-data/protoc-gen-go-mcp/pkg/runtime/gosdk"
)

// Create server -- raw is *mcp.Server for transport, s is runtime.MCPServer for tools.
raw, s := gosdk.NewServer("my-server", "1.0.0")

testdatamcp.RegisterTestServiceHandler(s, &srv)

// Serve over stdio
raw.Run(ctx, &mcp.StdioTransport{})
mark3labs/mcp-go を使用する場合
import (
    "github.com/mark3labs/mcp-go/server"
    "github.com/redpanda-data/protoc-gen-go-mcp/pkg/runtime/mark3labs"
)

// Create server -- raw is *server.MCPServer for transport, s is runtime.MCPServer for tools.
raw, s := mark3labs.NewServer("my-server", "1.0.0")

testdatamcp.RegisterTestServiceHandler(s, &srv)

// Serve over stdio
server.ServeStdio(raw)
ハンドラーの接続

プロセス内で登録する例:

srv := testServer{} // your gRPC implementation

// Register all RPC methods as tools on the MCP server
testdatamcp.RegisterTestServiceHandler(s, &srv)

protobuf サービス内の各 RPC メソッドが MCP ツールになります。

実行時の LLM プロバイダー選択

コードを再生成せずに、実行時に LLM との互換性を選択できます。

// Option 1: Use convenience function with runtime provider selection
testdatamcp.RegisterTestServiceHandlerWithProvider(s, &srv, runtime.LLMProviderOpenAI)

// Option 2: Register specific handlers directly
testdatamcp.RegisterTestServiceHandler(s, &srv)        // Standard MCP
testdatamcp.RegisterTestServiceHandlerOpenAI(s, &srv)  // OpenAI-compatible
grpc および connectrpc クライアントとの接続

MCP ツールの呼び出しを gRPC クライアントに直接転送します。

testdatamcp.ForwardToTestServiceClient(s, myGrpcClient)

connectrpc でも同様です。

testdatamcp.ForwardToConnectTestServiceClient(s, myConnectClient)

これにより MCP ハンドラーがクライアントに直接接続され、定型コードが一切不要になります。

追加プロパティ

proto に定義されていない追加プロパティを MCP ツールに追加できます。これらはコンテキストに書き込まれます。

// Enable URL override with custom field name and description
option := runtime.WithExtraProperties(
    runtime.ExtraProperty{
        Name:        "base_url",
        Description: "Base URL for the API",
        Required:    true,
        ContextKey:  MyURLOverrideKey{},
    },
)

// Use with any generated function
testdatamcp.RegisterTestServiceHandler(s, &srv, option)
testdatamcp.ForwardToTestServiceClient(s, client, option)
ツール名のプレフィックス付与

同じサービスを複数回登録する場合(例:別々のデータベースインスタンス)、WithNamePrefix を使ってツールを名前空間に分けます。

sqlv1mcp.RegisterSQLServiceHandler(s, postgresHandler, runtime.WithNamePrefix("postgres"))
sqlv1mcp.RegisterSQLServiceHandler(s, clickhouseHandler, runtime.WithNamePrefix("clickhouse"))
// Tools: postgres_SQLService_Query, clickhouse_SQLService_Query, ...
ツールの命名

デフォルトでは、ツール名はメソッドの完全な proto 名のドットをアンダースコアに置き換えたものです(redpanda_mcps_sql_v1_SQLService_Query)。最も厳しいプロバイダーの制限である64文字を超える名前は、完全な名前の10文字のハッシュと、収まる範囲で末尾(最も具体的な部分)を組み合わせて切り詰められます。Gemini は数字で始まる関数名を拒否するため、ハッシュは必ず英字で始まります。

メソッドを、安定した任意の名前で公開するには、(mcp.v1.tool_name) メソッドオプションを設定します。

import "mcp/v1/annotations.proto";

rpc GetEquityResearchReport(GetEquityResearchReportRequest) returns (GetEquityResearchReportResponse) {
  option (mcp.v1.tool_name) = "get_equity_research_report";
}

値は ^[a-z_][a-z0-9_-]{0,63}$ に一致する必要があります。すべてのプロバイダーの関数名ルールに準拠する小文字のスネークケース/ケバブケースである必要があり、小文字にすることで、設定した名前がコンシューマーの名前のデマングリングヒューリスティクス(派生名の UpperCamel Service_ セグメントを基準に判定します)を通過しても変更されません。無効な値や、サービス内のツール名の重複がある場合、コード生成は失敗します(1つのサービスは1つの MCP サーバー登録に相当します。異なるサービス間では同じ名前を正当に再利用できます)。動的登録パス(gen.RegisterService)にも同じオプションが適用されます。無効な名前、または名前が競合する場合は、派生名にフォールバックするため、ツールが暗黙のうちに削除されることはありません。

mark3labs のみを使用する構成からの移行(v0.2 より前)

生成されたコードは mark3labs/mcp-go を直接インポートしなくなりました。runtime.MCPServer インターフェースを使用するため、アダプターパッケージを介して使用する MCP ライブラリを選択します。

1. サーバーの作成
// Before
s := server.NewMCPServer("name", "1.0", server.WithToolCapabilities(true))

// After -- mark3labs
raw, s := mark3labs.NewServer("name", "1.0", server.WithToolCapabilities(true))
// raw is *server.MCPServer for transport (ServeStdio, NewStreamableHTTPServer, etc.)
// s is runtime.MCPServer for tool registration

// After -- official go-sdk
raw, s := gosdk.NewServer("name", "1.0")
2. ツールの登録

生成された Register*Handler 関数は、*server.MCPServer ではなく runtime.MCPServer を受け取るようになりました。手順1の s を渡してください。

名前にプレフィックスを付けるため、mark3labs の型を使って s.AddTool() を手動で呼び出していた場合、そのコードを削除し、WithNamePrefix を使用してください。

// Before: 60 lines of manual register.go per service
sql.RegisterTools(s, "postgres", handler) // custom wrapper around s.AddTool(mcp.Tool, ...)

// After: one line
sqlv1mcp.RegisterSQLServiceHandler(s, handler, runtime.WithNamePrefix("postgres"))
3. HandleError の戻り値の型

runtime.HandleError() は、*mcp.CallToolResult ではなく *runtime.CallToolResult を返すようになりました。カスタムハンドラーから呼び出している場合は、戻り値の型を更新してください。

4. トランスポート

トランスポートの設定に変更はありません。手順1の未加工のサーバーを使用してください。

// mark3labs stdio
server.ServeStdio(raw)

// mark3labs streamable HTTP
mcpserver.NewStreamableHTTPServer(raw)

// go-sdk stdio
raw.Run(ctx, &mcp.StdioTransport{})

LLM プロバイダーとの互換性

ジェネレーターは、標準 MCP 用と OpenAI 互換用のハンドラーを自動的に生成します。実行時に使用するものを選択してください。

標準 MCP
  • 完全な JSON Schema サポート(additionalProperties、anyOf、oneOf)
  • マップは JSON オブジェクトとして表現されます
  • 既知の型はネイティブの JSON 表現を使用します
OpenAI 互換
  • 制限付き JSON Schema(additionalProperties、anyOf、oneOf なし)
  • マップはキーと値のペアの配列に変換されます
  • 既知の型(Struct、Value、ListValue)は JSON 文字列としてエンコードされます
  • すべてのフィールドは、nullable ユニオンを使って必須としてマークされます

開発とテスト

コマンド

すべてのコマンドで just を使用します。

just build                # Build everything (Bazel)
just test-unit            # Unit + golden tests (no API keys needed)
just test                 # All tests including conformance/integration (needs API keys)
just test-cover           # Tests with coverage report
just generate             # Regenerate proto code + descriptor set
just lint                 # Run golangci-lint
just fmt                  # Format code
just install              # Install binary to GOPATH/bin
just gazelle              # Sync BUILD files from go.mod

# View all available commands
just --list
開発ワークフロー
# 1. Edit proto or generator code
# 2. Regenerate
just generate
# 3. Run tests
just test-unit
ゴールデンファイルテスト

ジェネレーターは、出力の一貫性を確保するためにゴールデンファイルテストを使用します。テストでは、コンパイル済みディスクリプターからプロセス内でジェネレーターを再実行し、チェックイン済みの *.pb.mcp.go ファイルと比較します。

新しいテストを追加するには: pkg/testdata/proto/testdata/ に .proto ファイルを配置し、just generate を実行します。ゴールデンテストは生成されたすべてのファイルを自動的に検出し、比較します。

Proto から JSON Schema へのマッピング

このプラグインは、protobuf 型を JSON Schema にマッピングします。LLM が目にするのは proto 定義ではなくスキーマであるため、これらのマッピングを理解することが重要です。

スカラー型
Proto 型JSON Schema 型備考
double、floatnumber
int32、uint32、sint32、fixed32、sfixed32integer
int64、uint64、sint64、fixed64、sfixed64stringJSON では64ビット整数を正確に表現できません
boolboolean
stringstring
bytesstringcontentEncoding: base64
enumstringすべての値名を含む enum 配列
メッセージとマップ

ネストされたメッセージは、ネストされた object スキーマになります。マップはモードによって異なります。

標準モード: map<K, V> は、propertyNames による制約(たとえば、整数キーには pattern、ブール値キーには enum)を持つ JSON オブジェクトになります。

OpenAI モード: map<K, V> は、{key, value} オブジェクトの配列になります。キーの制約(パターン、列挙値)は key フィールドに保持されます。実行時に FixOpenAI が、proto のアンマーシャリング前にこれらの配列をオブジェクトに戻します。

Well-known 型
Proto 型標準スキーマOpenAI スキーマ
google.protobuf.Timestampformat: date-time を持つ string同じ
google.protobuf.Durationduration パターンを持つ string同じ
google.protobuf.StructadditionalProperties: true を持つ objectstring(JSON エンコード済み)
google.protobuf.Value任意(型の制約なし)string(JSON エンコード済み)
google.protobuf.ListValuearraystring(JSON エンコード済み)
google.protobuf.FieldMaskstring同じ
google.protobuf.Any@type と value を持つ object同じ。ただし additionalProperties: false
ラッパー型(StringValue など)null 許容のスカラー(例:["string", "null"])同じ

OpenAI モードでは、実行時に FixOpenAI が逆変換を処理します。JSON エンコードされた文字列は再度パースされ、ラッパーオブジェクト {"value": X} は X に展開され、マップ配列はオブジェクトに変換されます。

Oneof フィールド

標準モード: JSON Schema の anyOf を使用し、各 oneof グループに対してエントリーを1つ設けます。各エントリーには、許可される選択肢が1つ指定されます。

OpenAI モード: oneof フィールドは、oneof の制約を説明する記述を付けた null 許容フィールド(例:type: ["string", "null"])にフラット化されます。実行時に FixOpenAI が null の oneof 選択肢を取り除き、各グループで最初の null でないフィールドのみを残します。

検証制約

buf.validate アノテーションは、JSON Schema のキーワードにマッピングされます。

検証JSON Schema
string.uuidformat: uuid
string.emailformat: email
string.patternpattern
string.min_len/max_lenminLength/maxLength
int32.gte/lteminimum/maximum
int32.gt/ltminimum+1 / maximum-1
float.gt/ltexclusiveMinimum/exclusiveMaximum
double.gte/lteminimum/maximum
再帰的なメッセージ

自己参照および相互再帰するメッセージ(ツリーノードやリンクリストなど)がサポートされています。スキーマはフィールドの詳細をすべて含めて、深さ3レベルまで展開されます。それより深い再帰フィールドは {"type": "string", "description": "JSON-encoded TreeNode. Provide a JSON object as a string."} になります。これは、OpenAI モードで Struct/Value/ListValue に使用されるものと同じパターンです。

実行時に FixOpenAI が、proto のアンマーシャリング前にこれらの文字列エンコードされたフィールドをパースし、JSON オブジェクトに戻します。実際のデータは任意の深さまでネストできます。深さの制限は LLM が参照するスキーマにのみ適用され、送信できるデータには適用されません。

ツール名の変換

完全修飾 RPC 名(ドットをアンダースコアに置き換えたもの)が64文字を超える場合、名前は切り詰められます。先頭部分を10文字の SHA-256 ハッシュ接頭辞に置き換え、末尾(最も具体的な部分で、通常は ServiceName_MethodName)を保持します。64文字の上限があるのは、Claude デスクトップでこの制限が適用されるためです。

制限事項

  • インターセプターはまだサポートされていません。gRPC サーバーに登録すると、インターセプターを経由しません。
  • 再帰的なメッセージスキーマは、ネストが3レベルを超えるとフィールドレベルの詳細が失われます(上記参照)。LLM は、それより深いレベルを JSON 文字列としてエンコードする必要があります。
  • WithExtraProperties で追加するプロパティは、proto フィールド名と重複しないようにしてください。重複する場合、追加プロパティの値はコンテキストに抽出されますが、proto メッセージにも漏れ込みます。

フィードバック

フィードバック、バグ報告、PR をお待ちしています!ぜひ議論に参加して、Go と Protobuf の MCP ツールの未来づくりにご協力ください。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。