diff --git a/docs/client.md b/docs/client.md index 2cbe082c..8fc75441 100644 --- a/docs/client.md +++ b/docs/client.md @@ -145,7 +145,7 @@ func Example_elicitation() { } defer ss.Close() - c := mcp.NewClient(testImpl, &mcp.ClientOptions{ + c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, &mcp.ClientOptions{ ElicitationHandler: func(context.Context, *mcp.ElicitRequest) (*mcp.ElicitResult, error) { return &mcp.ElicitResult{Action: "accept", Content: map[string]any{"test": "value"}}, nil }, diff --git a/mcp/client_example_test.go b/mcp/client_example_test.go index bba3da44..225dabac 100644 --- a/mcp/client_example_test.go +++ b/mcp/client_example_test.go @@ -112,7 +112,7 @@ func Example_elicitation() { } defer ss.Close() - c := mcp.NewClient(testImpl, &mcp.ClientOptions{ + c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, &mcp.ClientOptions{ ElicitationHandler: func(context.Context, *mcp.ElicitRequest) (*mcp.ElicitResult, error) { return &mcp.ElicitResult{Action: "accept", Content: map[string]any{"test": "value"}}, nil }, diff --git a/mcp/server.go b/mcp/server.go index c80b7d9f..44483af1 100644 --- a/mcp/server.go +++ b/mcp/server.go @@ -380,7 +380,7 @@ func setSchema[T any](sfield *any, rfield **jsonschema.Resolved) (zero any, err // If the tool's input schema is nil, it is set to the schema inferred from the // In type parameter. Types are inferred from Go types, and property // descriptions are read from the 'jsonschema' struct tag. Internally, the SDK -// uses the github.com/google/jsonschema-go package for ineference and +// uses the github.com/google/jsonschema-go package for inference and // validation. The In type argument must be a map or a struct, so that its // inferred JSON Schema has type "object", as required by the spec. As a // special case, if the In type is 'any', the tool's input schema is set to an @@ -498,6 +498,9 @@ func (s *Server) changeAndNotify(notification string, params Params, change func } // Sessions returns an iterator that yields the current set of server sessions. +// +// There is no guarantee that the iterator observes sessions that are added or +// removed during iteration. func (s *Server) Sessions() iter.Seq[*ServerSession] { s.mu.Lock() clients := slices.Clone(s.sessions) diff --git a/mcp/streamable_test.go b/mcp/streamable_test.go index 3576d2b5..f7cda72c 100644 --- a/mcp/streamable_test.go +++ b/mcp/streamable_test.go @@ -675,13 +675,14 @@ func TestStreamableServerTransport(t *testing.T) { headers: http.Header{"MCP-Protocol-Version": {"2025-03-26"}}, // Two messages => batch. Expect OK with two responses in order. messages: []jsonrpc.Message{ + // Note: only include one request here, because responses are not + // necessarily sorted. req(201, "tools/call", &CallToolParams{Name: "tool"}), - req(202, "tools/call", &CallToolParams{Name: "tool"}), + req(0, "notifications/roots/list_changed", &RootsListChangedParams{}), }, wantStatusCode: http.StatusOK, wantMessages: []jsonrpc.Message{ resp(201, &CallToolResult{Content: []Content{}}, nil), - resp(202, &CallToolResult{Content: []Content{}}, nil), }, }, }, diff --git a/mcp/transport.go b/mcp/transport.go index a9cfa371..b13d69c3 100644 --- a/mcp/transport.go +++ b/mcp/transport.go @@ -104,8 +104,12 @@ func (t *InMemoryTransport) Connect(context.Context) (Connection, error) { return newIOConn(t.rwc), nil } -// NewInMemoryTransports returns two [InMemoryTransports] that connect to each -// other. +// NewInMemoryTransports returns two [InMemoryTransport] objects that connect +// to each other. +// +// The resulting transports are symmetrical: use either to connect to a server, +// and then the other to connect to a client. Servers must be connected before +// clients, as the client initializes the MCP session during connection. func NewInMemoryTransports() (*InMemoryTransport, *InMemoryTransport) { c1, c2 := net.Pipe() return &InMemoryTransport{c1}, &InMemoryTransport{c2}