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>
This commit is contained in:
parent
56ed3872f3
commit
ffa7599a10
5 changed files with 117 additions and 16 deletions
67
mcp/mcp.go
67
mcp/mcp.go
|
|
@ -4,18 +4,69 @@
|
|||
|
||||
// The mcp package provides an SDK for writing model context protocol clients
|
||||
// and servers. It is a work-in-progress. As of writing, it is a prototype to
|
||||
// explore the design space of client/server lifecycle and binding.
|
||||
// explore the design space of client/server transport and binding.
|
||||
//
|
||||
// To get started, create an MCP client or server with [NewClient] or
|
||||
// [NewServer], then add features to your client or server using Add<feature>
|
||||
// methods, then connect to a peer using a [Transport] instance and a call to
|
||||
// [Client.Connect] or [Server.Connect].
|
||||
// To get started, create either a [Client] or [Server], and connect it to a
|
||||
// peer using a [Transport]. The diagram below illustrates how this works:
|
||||
//
|
||||
// Client Server
|
||||
// ⇅ (jsonrpc2) ⇅
|
||||
// ServerConnection ⇄ Client Transport ⇄ Server Transport ⇄ ClientConnection
|
||||
//
|
||||
// A [Client] is an MCP client, which can be configured with various client
|
||||
// capabilities. Clients may be connected to one or more [Server] instances
|
||||
// using the [Client.Connect] method, which creates a [ServerConnection].
|
||||
//
|
||||
// Similarly, a [Server] is an MCP server, which can be configured with various
|
||||
// server capabilities. Servers may be connected to one or more [Client]
|
||||
// instances using the [Server.Connect] method, which creates a
|
||||
// [ClientConnection].
|
||||
//
|
||||
// A [Transport] connects a bidirectional [Stream] of jsonrpc2 messages. In
|
||||
// practice, transports in the MCP spec are are either client transports or
|
||||
// server transports. For example, the [StdIOTransport] is a server transport
|
||||
// that communicates over stdin/stdout, and its counterpart is a
|
||||
// [CommandTransport] that communicates with a subprocess over its
|
||||
// stdin/stdout.
|
||||
//
|
||||
// Some transports may hide more complicated details, such as an
|
||||
// [SSEClientTransport], which reads messages via server-sent events on a
|
||||
// hanging GET request, and writes them to a POST endpoint. Users of this SDK
|
||||
// may define their own custom Transports by implementing the [Transport]
|
||||
// interface.
|
||||
//
|
||||
// Here's an example that creates a client that talks to an MCP server running
|
||||
// as a sidecar process:
|
||||
//
|
||||
// import "github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
// ...
|
||||
// // Create a new client, with no features.
|
||||
// client := mcp.NewClient("mcp-client", "v1.0.0", nil)
|
||||
// // Connect to a server over stdin/stdout
|
||||
// transport := mcp.NewCommandTransport(exec.Command("myserver"))
|
||||
// serverConn, err := client.Connect(ctx, transport, nil)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// // Call a tool on the server.
|
||||
// content, err := serverConn.CallTool(ctx, "greet", map[string]any{"name": "you"})
|
||||
//
|
||||
// Here is an example of the corresponding server, connected over stdin/stdout:
|
||||
//
|
||||
// import "github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
// ...
|
||||
// // Create a server with a single tool.
|
||||
// server := mcp.NewServer("greeter", "v1.0.0", nil)
|
||||
// server.AddTool(mcp.MakeTool("greet", "say hi", SayHi))
|
||||
// // Run the server over stdin/stdout, until the client diconnects
|
||||
// _ = server.Run(ctx, mcp.NewStdIOTransport(), nil)
|
||||
//
|
||||
// # TODO
|
||||
//
|
||||
// TODO:
|
||||
// - Support pagination.
|
||||
// - Support all client/server operations.
|
||||
// - Support Streamable HTTP transport.
|
||||
// - Support streamable HTTP transport.
|
||||
// - Support multiple versions of the spec.
|
||||
// - Implement proper JSON schema support, with both client-side and
|
||||
// server-side validation..
|
||||
// server-side validation.
|
||||
package mcp
|
||||
|
|
|
|||
53
mcp/server_example_test.go
Normal file
53
mcp/server_example_test.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mcp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
type SayHiParams struct {
|
||||
Name string `json:"name" mcp:"the name to say hi to"`
|
||||
}
|
||||
|
||||
func SayHi(ctx context.Context, cc *mcp.ClientConnection, params *SayHiParams) ([]mcp.Content, error) {
|
||||
return []mcp.Content{
|
||||
mcp.TextContent{Text: "Hi " + params.Name},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ExampleServer() {
|
||||
ctx := context.Background()
|
||||
clientTransport, serverTransport := mcp.NewLocalTransport()
|
||||
|
||||
server := mcp.NewServer("greeter", "v0.0.1", nil)
|
||||
server.AddTools(mcp.MakeTool("greet", "say hi", SayHi))
|
||||
|
||||
clientConnection, err := server.Connect(ctx, serverTransport, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
client := mcp.NewClient("client", "v0.0.1", nil)
|
||||
serverConnection, err := client.Connect(ctx, clientTransport, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
content, err := serverConnection.CallTool(ctx, "greet", SayHiParams{Name: "user"})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(content[0].(mcp.TextContent).Text)
|
||||
|
||||
serverConnection.Close()
|
||||
clientConnection.Wait()
|
||||
|
||||
// Output: Hi user
|
||||
}
|
||||
10
mcp/sse.go
10
mcp/sse.go
|
|
@ -277,14 +277,16 @@ type SSEClientTransport struct {
|
|||
|
||||
// NewSSEClientTransport returns a new client transport that connects to the
|
||||
// SSE server at the provided URL.
|
||||
func NewSSEClientTransport(rawURL string) (*SSEClientTransport, error) {
|
||||
url, err := url.Parse(rawURL)
|
||||
//
|
||||
// NewSSEClientTransport panics if the given URL is invalid.
|
||||
func NewSSEClientTransport(baseURL string) *SSEClientTransport {
|
||||
url, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
panic(fmt.Sprintf("invalid base url: %v", err))
|
||||
}
|
||||
return &SSEClientTransport{
|
||||
sseEndpoint: url,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Connect connects through the client endpoint.
|
||||
|
|
|
|||
50
mcp/sse_example_test.go
Normal file
50
mcp/sse_example_test.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mcp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
type AddParams struct {
|
||||
X, Y int
|
||||
}
|
||||
|
||||
func Add(ctx context.Context, cc *mcp.ClientConnection, params *AddParams) ([]mcp.Content, error) {
|
||||
return []mcp.Content{
|
||||
mcp.TextContent{Text: fmt.Sprintf("%d", params.X+params.Y)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ExampleSSEHandler() {
|
||||
server := mcp.NewServer("adder", "v0.0.1", nil)
|
||||
server.AddTools(mcp.MakeTool("add", "add two numbers", Add))
|
||||
|
||||
handler := mcp.NewSSEHandler(func(*http.Request) *mcp.Server { return server })
|
||||
httpServer := httptest.NewServer(handler)
|
||||
defer httpServer.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
transport := mcp.NewSSEClientTransport(httpServer.URL)
|
||||
serverConn, err := mcp.NewClient("test", "v1.0.0", nil).Connect(ctx, transport, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer serverConn.Close()
|
||||
|
||||
content, err := serverConn.CallTool(ctx, "add", AddParams{1, 2})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(content[0].(mcp.TextContent).Text)
|
||||
|
||||
// Output: 3
|
||||
}
|
||||
|
|
@ -31,11 +31,9 @@ func TestSSEServer(t *testing.T) {
|
|||
}
|
||||
}
|
||||
httpServer := httptest.NewServer(sseHandler)
|
||||
defer httpServer.Close()
|
||||
|
||||
clientTransport, err := NewSSEClientTransport(httpServer.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
clientTransport := NewSSEClientTransport(httpServer.URL)
|
||||
|
||||
client := NewClient("testClient", "v1.0.0", nil)
|
||||
sc, err := client.Connect(ctx, clientTransport, nil)
|
||||
|
|
|
|||
Loading…
Reference in a new issue