MCP Go SDK 入門
MCP Go SDK を使ってみる
この記事は、最新版(2025/08/17) の MCP Go SDK で stdio
と Streamable HTTP
のMCPサーバーを実装してみる記事です。
※100%人力でこの記事を書いています
MCP inspector を起動する
これからMCPサーバーを実装してみますが、実装したMCPサーバーの動作確認をするために MCP inspector
というものを使ってデバッグしていきます。
↓ MCP inspector
はこういうGUI上でMCPサーバーをデバッグできるツールです。
MCP inspector
を起動するコマンドは次のとおりで、このコマンドを打つと勝手にブラウザが立ち上がり MCP inspector を使える状態になります。
npx @modelcontextprotocol/inspector
この MCP inspector
は stdio
と Streamable HTTP
の両方に対応しており、 stdio
と Streamable HTTP
の切り替えは画面上からポチポチ変更できますが、次のようにすると、自動で設定された状態で起動することも可能です。
stdio で go の MCP サーバーを起動する例:
npx @modelcontextprotocol/inspector --transport stdio go run ./examples/stdio/main.go
Streamable HTTP で go の MCP サーバーに接続する例:
npx @modelcontextprotocol/inspector --transport http --server-url http://localhost:3001
ちなみにポートを変えたい時はこんな感じにすればよさそう:
CLIENT_PORT=6275 SERVER_PORT=6279 npx @modelcontextprotocol/inspector
stdio 接続を試してみる
これからシンプルなMCPサーバーを実装していきますが、まずはシンプルな stdio
の方から実装してみます。
こんな感じのソースコードを書きます。
package main
import (
"context"
"log"
"os"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
type HiArgs struct {
Name string `json:"name" jsonschema:"the name to say hi to"`
}
func SayHi(ctx context.Context, req *mcp.ServerRequest[*mcp.CallToolParamsFor[HiArgs]]) (*mcp.CallToolResultFor[struct{}], error) {
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: "Hi " + req.Params.Arguments.Name},
},
}, nil
}
func main() {
server := mcp.NewServer(&mcp.Implementation{Name: "greeter"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
t := &mcp.LoggingTransport{Transport: &mcp.StdioTransport{}, Writer: os.Stderr}
if err := server.Run(context.Background(), t); err != nil {
log.Printf("Server failed: %v", err)
}
}
なお現在リリースされている go-sdk v0.2.0 は機能が足りないので最新(2025/08/17時点)のmainブランチのgo-skdを使っています
go get -u github.com/modelcontextprotocol/go-sdk@main
最低限の実装ができたので MCP inspector
で接続してみましょう。次のようなコマンドで MCP inspector
を立ち上げるとよいと思います(実装した main.go
へのパスになっていること)
npx @modelcontextprotocol/inspector --transport stdio go run ./examples/stdio/main.go
左側に Connect
ボタンがあるのでクリックすると MCP サーバーを立ち上がり stdio 接続されます。
接続すると画面上部に 🔨Tools
と表示されるのでクリックしてみましょう。
-
List Tools
をクリックする -
greet
が表示されるのでクリック -
name
に適当に入力してRun Tool
をクリックする
すると "Hi xxx"
と応答が表示されます。これで stdio で無事に動いていることを確認ができました!🎉
Streamable HTTP 接続を試してみる
Streamable HTTP
というのは Model Context Protocol の Transports
という仕様で定義されている HTTP を使った接続方式です。 HTTP を使ってどうにか stdio と同じことを実現させようとしているためちょっと複雑な仕様になっています。
この仕様をざっくり説明すると、クライアント側からサーバー側に問い合わせるときはPOSTメソッドによる一般的なHTTP通信を行いつつ、サーバー側からのPUSH通知を受け取るために事前にGETメソッドで Server-Sent Events(SSE)
を確立しておきサーバー側からPUSH通知を受け取るというものです。
※同じパスで「常時GETの接続」をしつつ「必要になったらPOSTでリクエストを送る」というイメージです。
ちょっとややこしい仕様な気がしますが、 Go SDK を使えば特にこの仕様を意識する必要はないので、ちゃんと理解できる必要はないと思います。
では早速 Streamable HTTP
のサーバーを書いてみましょう。
先ほどの stdio
版のソースを次のように書き換えるだけで http://localhost:3001
で起動する Streamable HTTP
のMCPサーバーになります。
package main
import (
"context"
"log"
+ "net/http"
- "os"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
type HiArgs struct {
Name string `json:"name" jsonschema:"the name to say hi to"`
}
func SayHi(ctx context.Context, req *mcp.ServerRequest[*mcp.CallToolParamsFor[HiArgs]]) (*mcp.CallToolResultFor[struct{}], error) {
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: "Hi " + req.Params.Arguments.Name},
},
}, nil
}
func main() {
server := mcp.NewServer(&mcp.Implementation{Name: "greeter"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
+ handler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server {
+ return server
+ }, nil)
+ if err := http.ListenAndServe(":3001", handler); err != nil {
- t := &mcp.LoggingTransport{Transport: &mcp.StdioTransport{}, Writer: os.Stderr}
- if err := server.Run(context.Background(), t); err != nil {
log.Printf("Server failed: %v", err)
}
}
ソースを書き換えたので go run main.go
でサーバーを立ち上げ、 MCP inspector
から接続してみましょう。次のコマンドで MCP inspector
を立ち上げるとよいと思います。
npx @modelcontextprotocol/inspector --transport http --server-url http://localhost:3001
起動したら stdio
で行ったように Connect
でMCPサーバーと接続し、 greet
ツールを使うことができるはずです!
たったこれだけの変更で Streamable HTTP
として接続できるようになりました!🎉
Streamable HTTP で SSE のレスポンスを確認してみる
先ほどは Streamable HTTP を使って greet
という即座に結果が返ってくる tool を使えることを確認しましたが、実はこれは Server-Sent Events(SSE) は使っていませんでした(厳密には使っているんですが)。
そこで次はGETメソッドによるSSEが動作することを確認してみます。
今度はソースを次のように書き換えます。変更内容はMCPサーバーが起動後10秒経過すると greet2
という tool が増えるようにしました。こうすることでMCPサーバーから15秒後にSSEでnotificationが届くことを確認できるはずです。
package main
import (
"context"
"log"
"net/http"
+ "time"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
type HiArgs struct {
Name string `json:"name" jsonschema:"the name to say hi to"`
}
func SayHi(ctx context.Context, req *mcp.ServerRequest[*mcp.CallToolParamsFor[HiArgs]]) (*mcp.CallToolResultFor[struct{}], error) {
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: "Hi " + req.Params.Arguments.Name},
},
}, nil
}
func main() {
server := mcp.NewServer(&mcp.Implementation{Name: "greeter"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
+
+ go func() {
+ time.Sleep(15 * time.Second)
+ mcp.AddTool(server, &mcp.Tool{Name: "greet2", Description: "say hi2"}, SayHi)
+ }()
+
handler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server {
return server
}, nil)
if err := http.ListenAndServe(":3001", handler); err != nil {
log.Printf("Server failed: %v", err)
}
}
MCPサーバーを起動しつつ MCP inspector
で15秒以内に Connect
ボタンを押して接続すると notification が届くはずです。
npx @modelcontextprotocol/inspector --transport http --server-url http://localhost:3001
Server Notifications
の欄に notifications/tools/list_changed
が届いたことが確認できました。
また devtools の Network タブから EventStream が届いていることを確認できます。
ここで注目したいのは、MCPサーバー側から通知が来るのはGETメソッドを使った接続からであることです。GETメソッドでのリクエストは Connection: keep-alive
で常時接続しながらMCPサーバーからの通知を待ち受けます。
今確認したのは notifications/tools/list_changed
というnotificationでしたが、 tool を次のように書き換えるとtoolの進捗をnotificationすることができます。これは notification/progress
という種類の通知です。
func SayHi(ctx context.Context, req *mcp.ServerRequest[*mcp.CallToolParamsFor[HiArgs]]) (*mcp.CallToolResultFor[struct{}], error) {
+ req.Session.NotifyProgress(ctx, &mcp.ProgressNotificationParams{
+ Message: "Starting...",
+ ProgressToken: req.Params.GetProgressToken(),
+ Progress: 1,
+ Total: 2,
+ })
+
+ // なにか重い処理
+ time.Sleep(2 * time.Second)
+
+ req.Session.NotifyProgress(ctx, &mcp.ProgressNotificationParams{
+ Message: "Finishing up...",
+ ProgressToken: req.Params.GetProgressToken(),
+ Progress: 2,
+ Total: 2,
+ })
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: "Hi " + req.Params.Arguments.Name},
},
}, nil
}
こんな感じで通知が来ていることを確認できます。
おわり
以上 Go SDK でMCPサーバーを動かしてみる記事でした。
最後にあらためて Streamable HTTP
の最小のソースコードを載せておきます。
package main
import (
"context"
"log"
"net/http"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
type HiArgs struct {
Name string `json:"name" jsonschema:"the name to say hi to"`
}
func SayHi(ctx context.Context, req *mcp.ServerRequest[*mcp.CallToolParamsFor[HiArgs]]) (*mcp.CallToolResultFor[struct{}], error) {
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: "Hi " + req.Params.Arguments.Name},
},
}, nil
}
func main() {
server := mcp.NewServer(&mcp.Implementation{Name: "greeter"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
handler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server {
return server
}, nil)
if err := http.ListenAndServe(":3001", handler); err != nil {
log.Printf("Server failed: %v", err)
}
}
以上です
Discussion