Skip to content

Commit 37278be

Browse files
findleyrgopherbot
authored andcommitted
internal/mcp: add more package documentation, examples
Add more documentation for the mcp package, explaining how to create and use Clients and Servers. Also add an example of using the SSEHandler to crete an MCP server. Change-Id: Ib327826b2cdb1e3418482551c65569ec6f01af33 Reviewed-on: https://go-review.googlesource.com/c/tools/+/668715 Reviewed-by: Jonathan Amsterdam <jba@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Neal Patel <nealpatel@google.com> Auto-Submit: Robert Findley <rfindley@google.com>
1 parent 7906227 commit 37278be

File tree

5 files changed

+117
-16
lines changed

5 files changed

+117
-16
lines changed

internal/mcp/mcp.go

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,69 @@
44

55
// The mcp package provides an SDK for writing model context protocol clients
66
// and servers. It is a work-in-progress. As of writing, it is a prototype to
7-
// explore the design space of client/server lifecycle and binding.
7+
// explore the design space of client/server transport and binding.
88
//
9-
// To get started, create an MCP client or server with [NewClient] or
10-
// [NewServer], then add features to your client or server using Add<feature>
11-
// methods, then connect to a peer using a [Transport] instance and a call to
12-
// [Client.Connect] or [Server.Connect].
9+
// To get started, create either a [Client] or [Server], and connect it to a
10+
// peer using a [Transport]. The diagram below illustrates how this works:
11+
//
12+
// Client Server
13+
// ⇅ (jsonrpc2) ⇅
14+
// ServerConnection ⇄ Client Transport ⇄ Server Transport ⇄ ClientConnection
15+
//
16+
// A [Client] is an MCP client, which can be configured with various client
17+
// capabilities. Clients may be connected to one or more [Server] instances
18+
// using the [Client.Connect] method, which creates a [ServerConnection].
19+
//
20+
// Similarly, a [Server] is an MCP server, which can be configured with various
21+
// server capabilities. Servers may be connected to one or more [Client]
22+
// instances using the [Server.Connect] method, which creates a
23+
// [ClientConnection].
24+
//
25+
// A [Transport] connects a bidirectional [Stream] of jsonrpc2 messages. In
26+
// practice, transports in the MCP spec are are either client transports or
27+
// server transports. For example, the [StdIOTransport] is a server transport
28+
// that communicates over stdin/stdout, and its counterpart is a
29+
// [CommandTransport] that communicates with a subprocess over its
30+
// stdin/stdout.
31+
//
32+
// Some transports may hide more complicated details, such as an
33+
// [SSEClientTransport], which reads messages via server-sent events on a
34+
// hanging GET request, and writes them to a POST endpoint. Users of this SDK
35+
// may define their own custom Transports by implementing the [Transport]
36+
// interface.
37+
//
38+
// Here's an example that creates a client that talks to an MCP server running
39+
// as a sidecar process:
40+
//
41+
// import "golang.org/x/tools/internal/mcp"
42+
// ...
43+
// // Create a new client, with no features.
44+
// client := mcp.NewClient("mcp-client", "v1.0.0", nil)
45+
// // Connect to a server over stdin/stdout
46+
// transport := mcp.NewCommandTransport(exec.Command("myserver"))
47+
// serverConn, err := client.Connect(ctx, transport, nil)
48+
// if err != nil {
49+
// log.Fatal(err)
50+
// }
51+
// // Call a tool on the server.
52+
// content, err := serverConn.CallTool(ctx, "greet", map[string]any{"name": "you"})
53+
//
54+
// Here is an example of the corresponding server, connected over stdin/stdout:
55+
//
56+
// import "golang.org/x/tools/internal/mcp"
57+
// ...
58+
// // Create a server with a single tool.
59+
// server := mcp.NewServer("greeter", "v1.0.0", nil)
60+
// server.AddTool(mcp.MakeTool("greet", "say hi", SayHi))
61+
// // Run the server over stdin/stdout, until the client diconnects
62+
// _ = server.Run(ctx, mcp.NewStdIOTransport(), nil)
63+
//
64+
// # TODO
1365
//
14-
// TODO:
1566
// - Support pagination.
1667
// - Support all client/server operations.
17-
// - Support Streamable HTTP transport.
68+
// - Support streamable HTTP transport.
1869
// - Support multiple versions of the spec.
1970
// - Implement proper JSON schema support, with both client-side and
20-
// server-side validation..
71+
// server-side validation.
2172
package mcp

internal/mcp/sse.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,14 +277,16 @@ type SSEClientTransport struct {
277277

278278
// NewSSEClientTransport returns a new client transport that connects to the
279279
// SSE server at the provided URL.
280-
func NewSSEClientTransport(rawURL string) (*SSEClientTransport, error) {
281-
url, err := url.Parse(rawURL)
280+
//
281+
// NewSSEClientTransport panics if the given URL is invalid.
282+
func NewSSEClientTransport(baseURL string) *SSEClientTransport {
283+
url, err := url.Parse(baseURL)
282284
if err != nil {
283-
return nil, err
285+
panic(fmt.Sprintf("invalid base url: %v", err))
284286
}
285287
return &SSEClientTransport{
286288
sseEndpoint: url,
287-
}, nil
289+
}
288290
}
289291

290292
// Connect connects through the client endpoint.

internal/mcp/sse_example_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package mcp_test
6+
7+
import (
8+
"context"
9+
"fmt"
10+
"log"
11+
"net/http"
12+
"net/http/httptest"
13+
14+
"golang.org/x/tools/internal/mcp"
15+
)
16+
17+
type AddParams struct {
18+
X, Y int
19+
}
20+
21+
func Add(ctx context.Context, cc *mcp.ClientConnection, params *AddParams) ([]mcp.Content, error) {
22+
return []mcp.Content{
23+
mcp.TextContent{Text: fmt.Sprintf("%d", params.X+params.Y)},
24+
}, nil
25+
}
26+
27+
func ExampleSSEHandler() {
28+
server := mcp.NewServer("adder", "v0.0.1", nil)
29+
server.AddTools(mcp.MakeTool("add", "add two numbers", Add))
30+
31+
handler := mcp.NewSSEHandler(func(*http.Request) *mcp.Server { return server })
32+
httpServer := httptest.NewServer(handler)
33+
defer httpServer.Close()
34+
35+
ctx := context.Background()
36+
transport := mcp.NewSSEClientTransport(httpServer.URL)
37+
serverConn, err := mcp.NewClient("test", "v1.0.0", nil).Connect(ctx, transport, nil)
38+
if err != nil {
39+
log.Fatal(err)
40+
}
41+
defer serverConn.Close()
42+
43+
content, err := serverConn.CallTool(ctx, "add", AddParams{1, 2})
44+
if err != nil {
45+
log.Fatal(err)
46+
}
47+
fmt.Println(content[0].(mcp.TextContent).Text)
48+
49+
// Output: 3
50+
}

internal/mcp/sse_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,9 @@ func TestSSEServer(t *testing.T) {
3131
}
3232
}
3333
httpServer := httptest.NewServer(sseHandler)
34+
defer httpServer.Close()
3435

35-
clientTransport, err := NewSSEClientTransport(httpServer.URL)
36-
if err != nil {
37-
t.Fatal(err)
38-
}
36+
clientTransport := NewSSEClientTransport(httpServer.URL)
3937

4038
client := NewClient("testClient", "v1.0.0", nil)
4139
sc, err := client.Connect(ctx, clientTransport, nil)

0 commit comments

Comments
 (0)