mcp: NewClient and NewServer take Implementation (#113)
Change the name and version arguments to NewClient and NewServer to a `*Implementation`, to future-proof against the spec. Fixes #109.
This commit is contained in:
parent
b4febf129b
commit
6c6243c0b0
19 changed files with 76 additions and 69 deletions
|
|
@ -72,7 +72,7 @@ func main() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Create a new client, with no features.
|
// 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
|
// Connect to a server over stdin/stdout
|
||||||
transport := mcp.NewCommandTransport(exec.Command("myserver"))
|
transport := mcp.NewCommandTransport(exec.Command("myserver"))
|
||||||
|
|
@ -125,7 +125,7 @@ func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParam
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Create a server with a single tool.
|
// 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)
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
// Run the server over stdin/stdout, until the client disconnects
|
// Run the server over stdin/stdout, until the client disconnects
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ Sessions are created from either `Client` or `Server` using the `Connect` method
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Client struct { /* ... */ }
|
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) Connect(context.Context, Transport) (*ClientSession, error)
|
||||||
func (*Client) Sessions() iter.Seq[*ClientSession]
|
func (*Client) Sessions() iter.Seq[*ClientSession]
|
||||||
// Methods for adding/removing client features are described below.
|
// Methods for adding/removing client features are described below.
|
||||||
|
|
@ -338,7 +338,7 @@ func (*ClientSession) Wait() error
|
||||||
// For example: ClientSession.ListTools.
|
// For example: ClientSession.ListTools.
|
||||||
|
|
||||||
type Server struct { /* ... */ }
|
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) Connect(context.Context, Transport) (*ServerSession, error)
|
||||||
func (*Server) Sessions() iter.Seq[*ServerSession]
|
func (*Server) Sessions() iter.Seq[*ServerSession]
|
||||||
// Methods for adding/removing server features are described below.
|
// Methods for adding/removing server features are described below.
|
||||||
|
|
@ -356,7 +356,7 @@ func (*ServerSession) Wait() error
|
||||||
Here's an example of these APIs from the client side:
|
Here's an example of these APIs from the client side:
|
||||||
|
|
||||||
```go
|
```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
|
// Connect to a server over stdin/stdout
|
||||||
transport := mcp.NewCommandTransport(exec.Command("myserver"))
|
transport := mcp.NewCommandTransport(exec.Command("myserver"))
|
||||||
session, err := client.Connect(ctx, transport)
|
session, err := client.Connect(ctx, transport)
|
||||||
|
|
@ -371,7 +371,7 @@ A server that can handle that client call would look like this:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Create a server with a single tool.
|
// 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)
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
// Run the server over stdin/stdout, until the client disconnects.
|
// Run the server over stdin/stdout, until the client disconnects.
|
||||||
if err := server.Run(context.Background(), mcp.NewStdioTransport()); err != nil {
|
if err := server.Run(context.Background(), mcp.NewStdioTransport()); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ func main() {
|
||||||
|
|
||||||
// Create the MCP Server instance and assign the handler.
|
// Create the MCP Server instance and assign the handler.
|
||||||
// No server running, just showing the configuration.
|
// 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,
|
CompletionHandler: myCompletionHandler,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ func PromptHi(ctx context.Context, ss *mcp.ServerSession, params *mcp.GetPromptP
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
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)
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
server.AddPrompt(&mcp.Prompt{Name: "greet"}, PromptHi)
|
server.AddPrompt(&mcp.Prompt{Name: "greet"}, PromptHi)
|
||||||
server.AddResource(&mcp.Resource{
|
server.AddResource(&mcp.Resource{
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ func main() {
|
||||||
kb := knowledgeBase{s: kbStore}
|
kb := knowledgeBase{s: kbStore}
|
||||||
|
|
||||||
// Setup MCP server with knowledge base tools
|
// 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{
|
mcp.AddTool(server, &mcp.Tool{
|
||||||
Name: "create_entities",
|
Name: "create_entities",
|
||||||
Description: "Create multiple new entities in the knowledge graph",
|
Description: "Create multiple new entities in the knowledge graph",
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ func main() {
|
||||||
log.Fatal("http address not set")
|
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)
|
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)
|
mcp.AddTool(server2, &mcp.Tool{Name: "greet2", Description: "say hello"}, SayHi)
|
||||||
|
|
||||||
log.Printf("MCP servers serving at %s", *httpAddr)
|
log.Printf("MCP servers serving at %s", *httpAddr)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ func main() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Create a new client, with no features.
|
// 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
|
// Connect to a server over stdin/stdout
|
||||||
transport := mcp.NewCommandTransport(exec.Command("myserver"))
|
transport := mcp.NewCommandTransport(exec.Command("myserver"))
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParam
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Create a server with a single tool.
|
// 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)
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
// Run the server over stdin/stdout, until the client disconnects
|
// Run the server over stdin/stdout, until the client disconnects
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ import (
|
||||||
// A Client is an MCP client, which may be connected to an MCP server
|
// A Client is an MCP client, which may be connected to an MCP server
|
||||||
// using the [Client.Connect] method.
|
// using the [Client.Connect] method.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
name string
|
impl *Implementation
|
||||||
version string
|
|
||||||
opts ClientOptions
|
opts ClientOptions
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
roots *featureSet[*Root]
|
roots *featureSet[*Root]
|
||||||
|
|
@ -29,15 +28,19 @@ type Client struct {
|
||||||
receivingMethodHandler_ MethodHandler[*ClientSession]
|
receivingMethodHandler_ MethodHandler[*ClientSession]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new Client.
|
// NewClient creates a new [Client].
|
||||||
//
|
//
|
||||||
// Use [Client.Connect] to connect it to an MCP server.
|
// 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.
|
// 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{
|
c := &Client{
|
||||||
name: name,
|
impl: impl,
|
||||||
version: version,
|
|
||||||
roots: newFeatureSet(func(r *Root) string { return r.URI }),
|
roots: newFeatureSet(func(r *Root) string { return r.URI }),
|
||||||
sendingMethodHandler_: defaultSendingMethodHandler[*ClientSession],
|
sendingMethodHandler_: defaultSendingMethodHandler[*ClientSession],
|
||||||
receivingMethodHandler_: defaultReceivingMethodHandler[*ClientSession],
|
receivingMethodHandler_: defaultReceivingMethodHandler[*ClientSession],
|
||||||
|
|
@ -118,7 +121,7 @@ func (c *Client) Connect(ctx context.Context, t Transport) (cs *ClientSession, e
|
||||||
|
|
||||||
params := &InitializeParams{
|
params := &InitializeParams{
|
||||||
ProtocolVersion: latestProtocolVersion,
|
ProtocolVersion: latestProtocolVersion,
|
||||||
ClientInfo: &implementation{Name: c.name, Version: c.version},
|
ClientInfo: c.impl,
|
||||||
Capabilities: caps,
|
Capabilities: caps,
|
||||||
}
|
}
|
||||||
res, err := handleSend[*InitializeResult](ctx, cs, methodInitialize, params)
|
res, err := handleSend[*InitializeResult](ctx, cs, methodInitialize, params)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func TestMain(m *testing.M) {
|
||||||
func runServer() {
|
func runServer() {
|
||||||
ctx := context.Background()
|
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)
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
if err := server.Run(ctx, mcp.NewStdioTransport()); err != nil {
|
if err := server.Run(ctx, mcp.NewStdioTransport()); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
@ -40,7 +40,7 @@ func runServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerRunContextCancel(t *testing.T) {
|
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)
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
@ -55,7 +55,7 @@ func TestServerRunContextCancel(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// send a ping to the server to ensure it's running
|
// 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)
|
session, err := client.Connect(ctx, clientTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -87,7 +87,7 @@ func TestServerInterrupt(t *testing.T) {
|
||||||
|
|
||||||
cmd := createServerCommand(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))
|
session, err := client.Connect(ctx, mcp.NewCommandTransport(cmd))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -125,7 +125,7 @@ func TestCmdTransport(t *testing.T) {
|
||||||
|
|
||||||
cmd := createServerCommand(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))
|
session, err := client.Connect(ctx, mcp.NewCommandTransport(cmd))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -174,3 +174,5 @@ func requireExec(t *testing.T) {
|
||||||
t.Skip("unsupported OS")
|
t.Skip("unsupported OS")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testImpl = &mcp.Implementation{Name: "test", Version: "v1.0.0"}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ var nextProgressToken atomic.Int64
|
||||||
// This middleware function adds a progress token to every outgoing request
|
// This middleware function adds a progress token to every outgoing request
|
||||||
// from the client.
|
// from the client.
|
||||||
func Example_progressMiddleware() {
|
func Example_progressMiddleware() {
|
||||||
c := mcp.NewClient("test", "v1", nil)
|
c := mcp.NewClient(testImpl, nil)
|
||||||
c.AddSendingMiddleware(addProgressToken[*mcp.ClientSession])
|
c.AddSendingMiddleware(addProgressToken[*mcp.ClientSession])
|
||||||
_ = c
|
_ = c
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ func TestEndToEnd(t *testing.T) {
|
||||||
notificationChans["progress_server"] <- 0
|
notificationChans["progress_server"] <- 0
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
s := NewServer("testServer", "v1.0.0", sopts)
|
s := NewServer(testImpl, sopts)
|
||||||
AddTool(s, &Tool{
|
AddTool(s, &Tool{
|
||||||
Name: "greet",
|
Name: "greet",
|
||||||
Description: "say hi",
|
Description: "say hi",
|
||||||
|
|
@ -125,7 +125,7 @@ func TestEndToEnd(t *testing.T) {
|
||||||
notificationChans["progress_client"] <- 0
|
notificationChans["progress_client"] <- 0
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
c := NewClient("testClient", "v1.0.0", opts)
|
c := NewClient(testImpl, opts)
|
||||||
rootAbs, err := filepath.Abs(filepath.FromSlash("testdata/files"))
|
rootAbs, err := filepath.Abs(filepath.FromSlash("testdata/files"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -510,7 +510,7 @@ func basicConnection(t *testing.T, config func(*Server)) (*ServerSession, *Clien
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ct, st := NewInMemoryTransports()
|
ct, st := NewInMemoryTransports()
|
||||||
|
|
||||||
s := NewServer("testServer", "v1.0.0", nil)
|
s := NewServer(testImpl, nil)
|
||||||
if config != nil {
|
if config != nil {
|
||||||
config(s)
|
config(s)
|
||||||
}
|
}
|
||||||
|
|
@ -519,7 +519,7 @@ func basicConnection(t *testing.T, config func(*Server)) (*ServerSession, *Clien
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := NewClient("testClient", "v1.0.0", nil)
|
c := NewClient(testImpl, nil)
|
||||||
cs, err := c.Connect(ctx, ct)
|
cs, err := c.Connect(ctx, ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -562,13 +562,13 @@ func TestBatching(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ct, st := NewInMemoryTransports()
|
ct, st := NewInMemoryTransports()
|
||||||
|
|
||||||
s := NewServer("testServer", "v1.0.0", nil)
|
s := NewServer(testImpl, nil)
|
||||||
_, err := s.Connect(ctx, st)
|
_, err := s.Connect(ctx, st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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
|
// 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.
|
// 'initialize' to block. Therefore, we can only test with a size of 1.
|
||||||
// Since batching is being removed, we can probably just delete this.
|
// Since batching is being removed, we can probably just delete this.
|
||||||
|
|
@ -632,7 +632,7 @@ func TestMiddleware(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ct, st := NewInMemoryTransports()
|
ct, st := NewInMemoryTransports()
|
||||||
|
|
||||||
s := NewServer("testServer", "v1.0.0", nil)
|
s := NewServer(testImpl, nil)
|
||||||
ss, err := s.Connect(ctx, st)
|
ss, err := s.Connect(ctx, st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -656,7 +656,7 @@ func TestMiddleware(t *testing.T) {
|
||||||
s.AddSendingMiddleware(traceCalls[*ServerSession](&sbuf, "S1"), traceCalls[*ServerSession](&sbuf, "S2"))
|
s.AddSendingMiddleware(traceCalls[*ServerSession](&sbuf, "S1"), traceCalls[*ServerSession](&sbuf, "S2"))
|
||||||
s.AddReceivingMiddleware(traceCalls[*ServerSession](&sbuf, "R1"), traceCalls[*ServerSession](&sbuf, "R2"))
|
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.AddSendingMiddleware(traceCalls[*ClientSession](&cbuf, "S1"), traceCalls[*ClientSession](&cbuf, "S2"))
|
||||||
c.AddReceivingMiddleware(traceCalls[*ClientSession](&cbuf, "R1"), traceCalls[*ClientSession](&cbuf, "R2"))
|
c.AddReceivingMiddleware(traceCalls[*ClientSession](&cbuf, "R1"), traceCalls[*ClientSession](&cbuf, "R2"))
|
||||||
|
|
||||||
|
|
@ -741,13 +741,13 @@ func TestNoJSONNull(t *testing.T) {
|
||||||
var logbuf safeBuffer
|
var logbuf safeBuffer
|
||||||
ct = NewLoggingTransport(ct, &logbuf)
|
ct = NewLoggingTransport(ct, &logbuf)
|
||||||
|
|
||||||
s := NewServer("testServer", "v1.0.0", nil)
|
s := NewServer(testImpl, nil)
|
||||||
ss, err := s.Connect(ctx, st)
|
ss, err := s.Connect(ctx, st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := NewClient("testClient", "v1.0.0", nil)
|
c := NewClient(testImpl, nil)
|
||||||
cs, err := c.Connect(ctx, ct)
|
cs, err := c.Connect(ctx, ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -810,7 +810,7 @@ func TestKeepAlive(t *testing.T) {
|
||||||
serverOpts := &ServerOptions{
|
serverOpts := &ServerOptions{
|
||||||
KeepAlive: 100 * time.Millisecond,
|
KeepAlive: 100 * time.Millisecond,
|
||||||
}
|
}
|
||||||
s := NewServer("testServer", "v1.0.0", serverOpts)
|
s := NewServer(testImpl, serverOpts)
|
||||||
AddTool(s, greetTool(), sayHi)
|
AddTool(s, greetTool(), sayHi)
|
||||||
|
|
||||||
ss, err := s.Connect(ctx, st)
|
ss, err := s.Connect(ctx, st)
|
||||||
|
|
@ -822,7 +822,7 @@ func TestKeepAlive(t *testing.T) {
|
||||||
clientOpts := &ClientOptions{
|
clientOpts := &ClientOptions{
|
||||||
KeepAlive: 100 * time.Millisecond,
|
KeepAlive: 100 * time.Millisecond,
|
||||||
}
|
}
|
||||||
c := NewClient("testClient", "v1.0.0", clientOpts)
|
c := NewClient(testImpl, clientOpts)
|
||||||
cs, err := c.Connect(ctx, ct)
|
cs, err := c.Connect(ctx, ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -855,7 +855,7 @@ func TestKeepAliveFailure(t *testing.T) {
|
||||||
ct, st := NewInMemoryTransports()
|
ct, st := NewInMemoryTransports()
|
||||||
|
|
||||||
// Server without keepalive (to test one-sided keepalive)
|
// Server without keepalive (to test one-sided keepalive)
|
||||||
s := NewServer("testServer", "v1.0.0", nil)
|
s := NewServer(testImpl, nil)
|
||||||
AddTool(s, greetTool(), sayHi)
|
AddTool(s, greetTool(), sayHi)
|
||||||
ss, err := s.Connect(ctx, st)
|
ss, err := s.Connect(ctx, st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -866,7 +866,7 @@ func TestKeepAliveFailure(t *testing.T) {
|
||||||
clientOpts := &ClientOptions{
|
clientOpts := &ClientOptions{
|
||||||
KeepAlive: 50 * time.Millisecond,
|
KeepAlive: 50 * time.Millisecond,
|
||||||
}
|
}
|
||||||
c := NewClient("testClient", "v1.0.0", clientOpts)
|
c := NewClient(testImpl, clientOpts)
|
||||||
cs, err := c.Connect(ctx, ct)
|
cs, err := c.Connect(ctx, ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -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)
|
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"}
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@ type InitializeParams struct {
|
||||||
// attach additional metadata to their responses.
|
// attach additional metadata to their responses.
|
||||||
Meta `json:"_meta,omitempty"`
|
Meta `json:"_meta,omitempty"`
|
||||||
Capabilities *ClientCapabilities `json:"capabilities"`
|
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 latest version of the Model Context Protocol that the client supports.
|
||||||
// The client may decide to support older versions as well.
|
// The client may decide to support older versions as well.
|
||||||
ProtocolVersion string `json:"protocolVersion"`
|
ProtocolVersion string `json:"protocolVersion"`
|
||||||
|
|
@ -318,7 +318,7 @@ type InitializeResult struct {
|
||||||
// may not match the version that the client requested. If the client cannot
|
// may not match the version that the client requested. If the client cannot
|
||||||
// support this version, it must disconnect.
|
// support this version, it must disconnect.
|
||||||
ProtocolVersion string `json:"protocolVersion"`
|
ProtocolVersion string `json:"protocolVersion"`
|
||||||
ServerInfo *implementation `json:"serverInfo"`
|
ServerInfo *Implementation `json:"serverInfo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InitializedParams struct {
|
type InitializedParams struct {
|
||||||
|
|
@ -863,9 +863,9 @@ func (x *ToolListChangedParams) SetProgressToken(t any) { setProgressToken(x, t)
|
||||||
|
|
||||||
// TODO(jba): add ElicitRequest and related types.
|
// 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.
|
// title for UI representation.
|
||||||
type implementation struct {
|
type Implementation struct {
|
||||||
// Intended for programmatic or logical use, but used as a display name in past
|
// Intended for programmatic or logical use, but used as a display name in past
|
||||||
// specs or fallback (if title isn't present).
|
// specs or fallback (if title isn't present).
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,8 @@ const DefaultPageSize = 1000
|
||||||
// sessions by using [Server.Start] or [Server.Run].
|
// sessions by using [Server.Start] or [Server.Run].
|
||||||
type Server struct {
|
type Server struct {
|
||||||
// fixed at creation
|
// fixed at creation
|
||||||
name string
|
impl *Implementation
|
||||||
version string
|
opts ServerOptions
|
||||||
opts ServerOptions
|
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
prompts *featureSet[*serverPrompt]
|
prompts *featureSet[*serverPrompt]
|
||||||
|
|
@ -68,13 +67,18 @@ type ServerOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new MCP server. The resulting server has no features:
|
// NewServer creates a new MCP server. The resulting server has no features:
|
||||||
// add features using [Server.AddTools], [Server.AddPrompts] and [Server.AddResources].
|
// add features using the various Server.AddXXX methods, and the [AddTool] function.
|
||||||
//
|
//
|
||||||
// The server can be connected to one or more MCP clients using [Server.Start]
|
// The server can be connected to one or more MCP clients using [Server.Start]
|
||||||
// or [Server.Run].
|
// or [Server.Run].
|
||||||
//
|
//
|
||||||
// If non-nil, the provided options is used to configure the server.
|
// The first argument must not be nil.
|
||||||
func NewServer(name, version string, opts *ServerOptions) *Server {
|
//
|
||||||
|
// If non-nil, the provided options are used to configure the server.
|
||||||
|
func NewServer(impl *Implementation, opts *ServerOptions) *Server {
|
||||||
|
if impl == nil {
|
||||||
|
panic("nil Implementation")
|
||||||
|
}
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = new(ServerOptions)
|
opts = new(ServerOptions)
|
||||||
}
|
}
|
||||||
|
|
@ -85,8 +89,7 @@ func NewServer(name, version string, opts *ServerOptions) *Server {
|
||||||
opts.PageSize = DefaultPageSize
|
opts.PageSize = DefaultPageSize
|
||||||
}
|
}
|
||||||
return &Server{
|
return &Server{
|
||||||
name: name,
|
impl: impl,
|
||||||
version: version,
|
|
||||||
opts: *opts,
|
opts: *opts,
|
||||||
prompts: newFeatureSet(func(p *serverPrompt) string { return p.prompt.Name }),
|
prompts: newFeatureSet(func(p *serverPrompt) string { return p.prompt.Name }),
|
||||||
tools: newFeatureSet(func(t *serverTool) string { return t.tool.Name }),
|
tools: newFeatureSet(func(t *serverTool) string { return t.tool.Name }),
|
||||||
|
|
@ -686,10 +689,7 @@ func (ss *ServerSession) initialize(ctx context.Context, params *InitializeParam
|
||||||
ProtocolVersion: version,
|
ProtocolVersion: version,
|
||||||
Capabilities: ss.server.capabilities(),
|
Capabilities: ss.server.capabilities(),
|
||||||
Instructions: ss.server.opts.Instructions,
|
Instructions: ss.server.opts.Instructions,
|
||||||
ServerInfo: &implementation{
|
ServerInfo: ss.server.impl,
|
||||||
Name: ss.server.name,
|
|
||||||
Version: ss.server.version,
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func ExampleServer() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
clientTransport, serverTransport := mcp.NewInMemoryTransports()
|
clientTransport, serverTransport := mcp.NewInMemoryTransports()
|
||||||
|
|
||||||
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)
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
|
|
||||||
serverSession, err := server.Connect(ctx, serverTransport)
|
serverSession, err := server.Connect(ctx, serverTransport)
|
||||||
|
|
@ -36,7 +36,7 @@ func ExampleServer() {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
client := mcp.NewClient("client", "v0.0.1", nil)
|
client := mcp.NewClient(&mcp.Implementation{Name: "client"}, nil)
|
||||||
clientSession, err := client.Connect(ctx, clientTransport)
|
clientSession, err := client.Connect(ctx, clientTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
@ -59,8 +59,8 @@ func ExampleServer() {
|
||||||
|
|
||||||
// createSessions creates and connects an in-memory client and server session for testing purposes.
|
// createSessions creates and connects an in-memory client and server session for testing purposes.
|
||||||
func createSessions(ctx context.Context) (*mcp.ClientSession, *mcp.ServerSession, *mcp.Server) {
|
func createSessions(ctx context.Context) (*mcp.ClientSession, *mcp.ServerSession, *mcp.Server) {
|
||||||
server := mcp.NewServer("server", "v0.0.1", nil)
|
server := mcp.NewServer(testImpl, nil)
|
||||||
client := mcp.NewClient("client", "v0.0.1", nil)
|
client := mcp.NewClient(testImpl, nil)
|
||||||
serverTransport, clientTransport := mcp.NewInMemoryTransports()
|
serverTransport, clientTransport := mcp.NewInMemoryTransports()
|
||||||
serverSession, err := server.Connect(ctx, serverTransport)
|
serverSession, err := server.Connect(ctx, serverTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,7 @@ func TestServerCapabilities(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
server := NewServer("", "", nil)
|
server := NewServer(testImpl, nil)
|
||||||
tc.configureServer(server)
|
tc.configureServer(server)
|
||||||
gotCapabilities := server.capabilities()
|
gotCapabilities := server.capabilities()
|
||||||
if diff := cmp.Diff(tc.wantCapabilities, gotCapabilities); diff != "" {
|
if diff := cmp.Diff(tc.wantCapabilities, gotCapabilities); diff != "" {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ func Add(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsF
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleSSEHandler() {
|
func ExampleSSEHandler() {
|
||||||
server := mcp.NewServer("adder", "v0.0.1", nil)
|
server := mcp.NewServer(&mcp.Implementation{Name: "adder", Version: "v0.0.1"}, nil)
|
||||||
mcp.AddTool(server, &mcp.Tool{Name: "add", Description: "add two numbers"}, Add)
|
mcp.AddTool(server, &mcp.Tool{Name: "add", Description: "add two numbers"}, Add)
|
||||||
|
|
||||||
handler := mcp.NewSSEHandler(func(*http.Request) *mcp.Server { return server })
|
handler := mcp.NewSSEHandler(func(*http.Request) *mcp.Server { return server })
|
||||||
|
|
@ -36,7 +36,7 @@ func ExampleSSEHandler() {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
transport := mcp.NewSSEClientTransport(httpServer.URL, nil)
|
transport := mcp.NewSSEClientTransport(httpServer.URL, nil)
|
||||||
client := mcp.NewClient("test", "v1.0.0", nil)
|
client := mcp.NewClient(&mcp.Implementation{Name: "test", Version: "v1.0.0"}, nil)
|
||||||
cs, err := client.Connect(ctx, transport)
|
cs, err := client.Connect(ctx, transport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ func TestSSEServer(t *testing.T) {
|
||||||
for _, closeServerFirst := range []bool{false, true} {
|
for _, closeServerFirst := range []bool{false, true} {
|
||||||
t.Run(fmt.Sprintf("closeServerFirst=%t", closeServerFirst), func(t *testing.T) {
|
t.Run(fmt.Sprintf("closeServerFirst=%t", closeServerFirst), func(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := NewServer("testServer", "v1.0.0", nil)
|
server := NewServer(testImpl, nil)
|
||||||
AddTool(server, &Tool{Name: "greet"}, sayHi)
|
AddTool(server, &Tool{Name: "greet"}, sayHi)
|
||||||
|
|
||||||
sseHandler := NewSSEHandler(func(*http.Request) *Server { return server })
|
sseHandler := NewSSEHandler(func(*http.Request) *Server { return server })
|
||||||
|
|
@ -47,7 +47,7 @@ func TestSSEServer(t *testing.T) {
|
||||||
HTTPClient: customClient,
|
HTTPClient: customClient,
|
||||||
})
|
})
|
||||||
|
|
||||||
c := NewClient("testClient", "v1.0.0", nil)
|
c := NewClient(testImpl, nil)
|
||||||
cs, err := c.Connect(ctx, clientTransport)
|
cs, err := c.Connect(ctx, clientTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func TestStreamableTransports(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// 1. Create a server with a simple "greet" tool.
|
// 1. Create a server with a simple "greet" tool.
|
||||||
server := NewServer("testServer", "v1.0.0", nil)
|
server := NewServer(testImpl, nil)
|
||||||
AddTool(server, &Tool{Name: "greet", Description: "say hi"}, sayHi)
|
AddTool(server, &Tool{Name: "greet", Description: "say hi"}, sayHi)
|
||||||
// 2. Start an httptest.Server with the StreamableHTTPHandler, wrapped in a
|
// 2. Start an httptest.Server with the StreamableHTTPHandler, wrapped in a
|
||||||
// cookie-checking middleware.
|
// cookie-checking middleware.
|
||||||
|
|
@ -65,7 +65,7 @@ func TestStreamableTransports(t *testing.T) {
|
||||||
transport := NewStreamableClientTransport(httpServer.URL, &StreamableClientTransportOptions{
|
transport := NewStreamableClientTransport(httpServer.URL, &StreamableClientTransportOptions{
|
||||||
HTTPClient: httpClient,
|
HTTPClient: httpClient,
|
||||||
})
|
})
|
||||||
client := NewClient("testClient", "v1.0.0", nil)
|
client := NewClient(testImpl, nil)
|
||||||
session, err := client.Connect(ctx, transport)
|
session, err := client.Connect(ctx, transport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("client.Connect() failed: %v", err)
|
t.Fatalf("client.Connect() failed: %v", err)
|
||||||
|
|
@ -162,7 +162,7 @@ func TestStreamableServerTransport(t *testing.T) {
|
||||||
Tools: &toolCapabilities{ListChanged: true},
|
Tools: &toolCapabilities{ListChanged: true},
|
||||||
},
|
},
|
||||||
ProtocolVersion: latestProtocolVersion,
|
ProtocolVersion: latestProtocolVersion,
|
||||||
ServerInfo: &implementation{Name: "testServer", Version: "v1.0.0"},
|
ServerInfo: &Implementation{Name: "testServer", Version: "v1.0.0"},
|
||||||
}, nil)
|
}, nil)
|
||||||
initializedMsg := req(0, "initialized", &InitializedParams{})
|
initializedMsg := req(0, "initialized", &InitializedParams{})
|
||||||
initialize := step{
|
initialize := step{
|
||||||
|
|
@ -328,7 +328,7 @@ func TestStreamableServerTransport(t *testing.T) {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
// Create a server containing a single tool, which runs the test tool
|
// Create a server containing a single tool, which runs the test tool
|
||||||
// behavior, if any.
|
// behavior, if any.
|
||||||
server := NewServer("testServer", "v1.0.0", nil)
|
server := NewServer(&Implementation{Name: "testServer", Version: "v1.0.0"}, nil)
|
||||||
AddTool(server, &Tool{Name: "tool"}, func(ctx context.Context, ss *ServerSession, params *CallToolParamsFor[any]) (*CallToolResultFor[any], error) {
|
AddTool(server, &Tool{Name: "tool"}, func(ctx context.Context, ss *ServerSession, params *CallToolParamsFor[any]) (*CallToolResultFor[any], error) {
|
||||||
if test.tool != nil {
|
if test.tool != nil {
|
||||||
test.tool(t, ctx, ss)
|
test.tool(t, ctx, ss)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue