Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func main() {
ctx := context.Background()

// Create a new client, with no features.
client := mcp.NewClient("mcp-client", "v1.0.0", nil)
client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil)

// Connect to a server over stdin/stdout
transport := mcp.NewCommandTransport(exec.Command("myserver"))
Expand Down Expand Up @@ -125,7 +125,7 @@ func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParam

func main() {
// Create a server with a single tool.
server := mcp.NewServer("greeter", "v1.0.0", nil)
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)

mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
// Run the server over stdin/stdout, until the client disconnects
Expand Down
8 changes: 4 additions & 4 deletions design/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ Sessions are created from either `Client` or `Server` using the `Connect` method
```go
type Client struct { /* ... */ }
func NewClient(name, version string, opts *ClientOptions) *Client
func NewClient(impl *Implementation, opts *ClientOptions) *Client
func (*Client) Connect(context.Context, Transport) (*ClientSession, error)
func (*Client) Sessions() iter.Seq[*ClientSession]
// Methods for adding/removing client features are described below.
Expand All @@ -338,7 +338,7 @@ func (*ClientSession) Wait() error
// For example: ClientSession.ListTools.

type Server struct { /* ... */ }
func NewServer(name, version string, opts *ServerOptions) *Server
func NewServer(impl *Implementation, opts *ServerOptions) *Server
func (*Server) Connect(context.Context, Transport) (*ServerSession, error)
func (*Server) Sessions() iter.Seq[*ServerSession]
// Methods for adding/removing server features are described below.
Expand All @@ -356,7 +356,7 @@ func (*ServerSession) Wait() error
Here's an example of these APIs from the client side:
```go
client := mcp.NewClient("mcp-client", "v1.0.0", nil)
client := mcp.NewClient(&mcp.Implementation{Name:"mcp-client", Version:"v1.0.0"}, nil)
// Connect to a server over stdin/stdout
transport := mcp.NewCommandTransport(exec.Command("myserver"))
session, err := client.Connect(ctx, transport)
Expand All @@ -371,7 +371,7 @@ A server that can handle that client call would look like this:
```go
// Create a server with a single tool.
server := mcp.NewServer("greeter", "v1.0.0", nil)
server := mcp.NewServer(&mcp.Implementation{Name:"greeter", Version:"v1.0.0"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
// Run the server over stdin/stdout, until the client disconnects.
if err := server.Run(context.Background(), mcp.NewStdioTransport()); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion examples/completion/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func main() {

// Create the MCP Server instance and assign the handler.
// No server running, just showing the configuration.
_ = mcp.NewServer("myServer", "v1.0.0", &mcp.ServerOptions{
_ = mcp.NewServer(&mcp.Implementation{Name: "server"}, &mcp.ServerOptions{
CompletionHandler: myCompletionHandler,
})

Expand Down
2 changes: 1 addition & 1 deletion examples/hello/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func PromptHi(ctx context.Context, ss *mcp.ServerSession, params *mcp.GetPromptP
func main() {
flag.Parse()

server := mcp.NewServer("greeter", "v0.0.1", nil)
server := mcp.NewServer(&mcp.Implementation{Name: "greeter"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
server.AddPrompt(&mcp.Prompt{Name: "greet"}, PromptHi)
server.AddResource(&mcp.Resource{
Expand Down
2 changes: 1 addition & 1 deletion examples/memory/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func main() {
kb := knowledgeBase{s: kbStore}

// Setup MCP server with knowledge base tools
server := mcp.NewServer("memory", "v0.0.1", nil)
server := mcp.NewServer(&mcp.Implementation{Name: "memory"}, nil)
mcp.AddTool(server, &mcp.Tool{
Name: "create_entities",
Description: "Create multiple new entities in the knowledge graph",
Expand Down
4 changes: 2 additions & 2 deletions examples/sse/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ func main() {
log.Fatal("http address not set")
}

server1 := mcp.NewServer("greeter1", "v0.0.1", nil)
server1 := mcp.NewServer(&mcp.Implementation{Name: "greeter1"}, nil)
mcp.AddTool(server1, &mcp.Tool{Name: "greet1", Description: "say hi"}, SayHi)

server2 := mcp.NewServer("greeter2", "v0.0.1", nil)
server2 := mcp.NewServer(&mcp.Implementation{Name: "greeter2"}, nil)
mcp.AddTool(server2, &mcp.Tool{Name: "greet2", Description: "say hello"}, SayHi)

log.Printf("MCP servers serving at %s", *httpAddr)
Expand Down
2 changes: 1 addition & 1 deletion internal/readme/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func main() {
ctx := context.Background()

// Create a new client, with no features.
client := mcp.NewClient("mcp-client", "v1.0.0", nil)
client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil)

// Connect to a server over stdin/stdout
transport := mcp.NewCommandTransport(exec.Command("myserver"))
Expand Down
2 changes: 1 addition & 1 deletion internal/readme/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParam

func main() {
// Create a server with a single tool.
server := mcp.NewServer("greeter", "v1.0.0", nil)
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)

mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
// Run the server over stdin/stdout, until the client disconnects
Expand Down
17 changes: 10 additions & 7 deletions mcp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import (
// A Client is an MCP client, which may be connected to an MCP server
// using the [Client.Connect] method.
type Client struct {
name string
version string
impl *Implementation
opts ClientOptions
mu sync.Mutex
roots *featureSet[*Root]
Expand All @@ -29,15 +28,19 @@ type Client struct {
receivingMethodHandler_ MethodHandler[*ClientSession]
}

// NewClient creates a new Client.
// NewClient creates a new [Client].
//
// Use [Client.Connect] to connect it to an MCP server.
//
// The first argument must not be nil.
//
// If non-nil, the provided options configure the Client.
func NewClient(name, version string, opts *ClientOptions) *Client {
func NewClient(impl *Implementation, opts *ClientOptions) *Client {
if impl == nil {
panic("nil Implementation")
}
c := &Client{
name: name,
version: version,
impl: impl,
roots: newFeatureSet(func(r *Root) string { return r.URI }),
sendingMethodHandler_: defaultSendingMethodHandler[*ClientSession],
receivingMethodHandler_: defaultReceivingMethodHandler[*ClientSession],
Expand Down Expand Up @@ -118,7 +121,7 @@ func (c *Client) Connect(ctx context.Context, t Transport) (cs *ClientSession, e

params := &InitializeParams{
ProtocolVersion: latestProtocolVersion,
ClientInfo: &implementation{Name: c.name, Version: c.version},
ClientInfo: c.impl,
Capabilities: caps,
}
res, err := handleSend[*InitializeResult](ctx, cs, methodInitialize, params)
Expand Down
12 changes: 7 additions & 5 deletions mcp/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ func TestMain(m *testing.M) {
func runServer() {
ctx := context.Background()

server := mcp.NewServer("greeter", "v0.0.1", nil)
server := mcp.NewServer(testImpl, nil)
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
if err := server.Run(ctx, mcp.NewStdioTransport()); err != nil {
log.Fatal(err)
}
}

func TestServerRunContextCancel(t *testing.T) {
server := mcp.NewServer("greeter", "v0.0.1", nil)
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v0.0.1"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)

ctx, cancel := context.WithCancel(context.Background())
Expand All @@ -55,7 +55,7 @@ func TestServerRunContextCancel(t *testing.T) {
}()

// send a ping to the server to ensure it's running
client := mcp.NewClient("client", "v0.0.1", nil)
client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
session, err := client.Connect(ctx, clientTransport)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -87,7 +87,7 @@ func TestServerInterrupt(t *testing.T) {

cmd := createServerCommand(t)

client := mcp.NewClient("client", "v0.0.1", nil)
client := mcp.NewClient(testImpl, nil)
session, err := client.Connect(ctx, mcp.NewCommandTransport(cmd))
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -125,7 +125,7 @@ func TestCmdTransport(t *testing.T) {

cmd := createServerCommand(t)

client := mcp.NewClient("client", "v0.0.1", nil)
client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
session, err := client.Connect(ctx, mcp.NewCommandTransport(cmd))
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -174,3 +174,5 @@ func requireExec(t *testing.T) {
t.Skip("unsupported OS")
}
}

var testImpl = &mcp.Implementation{Name: "test", Version: "v1.0.0"}
2 changes: 1 addition & 1 deletion mcp/example_progress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var nextProgressToken atomic.Int64
// This middleware function adds a progress token to every outgoing request
// from the client.
func Example_progressMiddleware() {
c := mcp.NewClient("test", "v1", nil)
c := mcp.NewClient(testImpl, nil)
c.AddSendingMiddleware(addProgressToken[*mcp.ClientSession])
_ = c
}
Expand Down
30 changes: 16 additions & 14 deletions mcp/mcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func TestEndToEnd(t *testing.T) {
notificationChans["progress_server"] <- 0
},
}
s := NewServer("testServer", "v1.0.0", sopts)
s := NewServer(testImpl, sopts)
AddTool(s, &Tool{
Name: "greet",
Description: "say hi",
Expand Down Expand Up @@ -125,7 +125,7 @@ func TestEndToEnd(t *testing.T) {
notificationChans["progress_client"] <- 0
},
}
c := NewClient("testClient", "v1.0.0", opts)
c := NewClient(testImpl, opts)
rootAbs, err := filepath.Abs(filepath.FromSlash("testdata/files"))
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -510,7 +510,7 @@ func basicConnection(t *testing.T, config func(*Server)) (*ServerSession, *Clien
ctx := context.Background()
ct, st := NewInMemoryTransports()

s := NewServer("testServer", "v1.0.0", nil)
s := NewServer(testImpl, nil)
if config != nil {
config(s)
}
Expand All @@ -519,7 +519,7 @@ func basicConnection(t *testing.T, config func(*Server)) (*ServerSession, *Clien
t.Fatal(err)
}

c := NewClient("testClient", "v1.0.0", nil)
c := NewClient(testImpl, nil)
cs, err := c.Connect(ctx, ct)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -562,13 +562,13 @@ func TestBatching(t *testing.T) {
ctx := context.Background()
ct, st := NewInMemoryTransports()

s := NewServer("testServer", "v1.0.0", nil)
s := NewServer(testImpl, nil)
_, err := s.Connect(ctx, st)
if err != nil {
t.Fatal(err)
}

c := NewClient("testClient", "v1.0.0", nil)
c := NewClient(testImpl, nil)
// TODO: this test is broken, because increasing the batch size here causes
// 'initialize' to block. Therefore, we can only test with a size of 1.
// Since batching is being removed, we can probably just delete this.
Expand Down Expand Up @@ -632,7 +632,7 @@ func TestMiddleware(t *testing.T) {
ctx := context.Background()
ct, st := NewInMemoryTransports()

s := NewServer("testServer", "v1.0.0", nil)
s := NewServer(testImpl, nil)
ss, err := s.Connect(ctx, st)
if err != nil {
t.Fatal(err)
Expand All @@ -656,7 +656,7 @@ func TestMiddleware(t *testing.T) {
s.AddSendingMiddleware(traceCalls[*ServerSession](&sbuf, "S1"), traceCalls[*ServerSession](&sbuf, "S2"))
s.AddReceivingMiddleware(traceCalls[*ServerSession](&sbuf, "R1"), traceCalls[*ServerSession](&sbuf, "R2"))

c := NewClient("testClient", "v1.0.0", nil)
c := NewClient(testImpl, nil)
c.AddSendingMiddleware(traceCalls[*ClientSession](&cbuf, "S1"), traceCalls[*ClientSession](&cbuf, "S2"))
c.AddReceivingMiddleware(traceCalls[*ClientSession](&cbuf, "R1"), traceCalls[*ClientSession](&cbuf, "R2"))

Expand Down Expand Up @@ -741,13 +741,13 @@ func TestNoJSONNull(t *testing.T) {
var logbuf safeBuffer
ct = NewLoggingTransport(ct, &logbuf)

s := NewServer("testServer", "v1.0.0", nil)
s := NewServer(testImpl, nil)
ss, err := s.Connect(ctx, st)
if err != nil {
t.Fatal(err)
}

c := NewClient("testClient", "v1.0.0", nil)
c := NewClient(testImpl, nil)
cs, err := c.Connect(ctx, ct)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -810,7 +810,7 @@ func TestKeepAlive(t *testing.T) {
serverOpts := &ServerOptions{
KeepAlive: 100 * time.Millisecond,
}
s := NewServer("testServer", "v1.0.0", serverOpts)
s := NewServer(testImpl, serverOpts)
AddTool(s, greetTool(), sayHi)

ss, err := s.Connect(ctx, st)
Expand All @@ -822,7 +822,7 @@ func TestKeepAlive(t *testing.T) {
clientOpts := &ClientOptions{
KeepAlive: 100 * time.Millisecond,
}
c := NewClient("testClient", "v1.0.0", clientOpts)
c := NewClient(testImpl, clientOpts)
cs, err := c.Connect(ctx, ct)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -855,7 +855,7 @@ func TestKeepAliveFailure(t *testing.T) {
ct, st := NewInMemoryTransports()

// Server without keepalive (to test one-sided keepalive)
s := NewServer("testServer", "v1.0.0", nil)
s := NewServer(testImpl, nil)
AddTool(s, greetTool(), sayHi)
ss, err := s.Connect(ctx, st)
if err != nil {
Expand All @@ -866,7 +866,7 @@ func TestKeepAliveFailure(t *testing.T) {
clientOpts := &ClientOptions{
KeepAlive: 50 * time.Millisecond,
}
c := NewClient("testClient", "v1.0.0", clientOpts)
c := NewClient(testImpl, clientOpts)
cs, err := c.Connect(ctx, ct)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -895,3 +895,5 @@ func TestKeepAliveFailure(t *testing.T) {

t.Errorf("expected connection to be closed by keepalive, but it wasn't. Last error: %v", err)
}

var testImpl = &Implementation{Name: "test", Version: "v1.0.0"}
8 changes: 4 additions & 4 deletions mcp/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ type InitializeParams struct {
// attach additional metadata to their responses.
Meta `json:"_meta,omitempty"`
Capabilities *ClientCapabilities `json:"capabilities"`
ClientInfo *implementation `json:"clientInfo"`
ClientInfo *Implementation `json:"clientInfo"`
// The latest version of the Model Context Protocol that the client supports.
// The client may decide to support older versions as well.
ProtocolVersion string `json:"protocolVersion"`
Expand All @@ -318,7 +318,7 @@ type InitializeResult struct {
// may not match the version that the client requested. If the client cannot
// support this version, it must disconnect.
ProtocolVersion string `json:"protocolVersion"`
ServerInfo *implementation `json:"serverInfo"`
ServerInfo *Implementation `json:"serverInfo"`
}

type InitializedParams struct {
Expand Down Expand Up @@ -863,9 +863,9 @@ func (x *ToolListChangedParams) SetProgressToken(t any) { setProgressToken(x, t)

// TODO(jba): add ElicitRequest and related types.

// Describes the name and version of an MCP implementation, with an optional
// An Implementation describes the name and version of an MCP implementation, with an optional
// title for UI representation.
type implementation struct {
type Implementation struct {
// Intended for programmatic or logical use, but used as a display name in past
// specs or fallback (if title isn't present).
Name string `json:"name"`
Expand Down
Loading
Loading