internal/mcp: update protocol versions

Update protocol versions to reflect that 2025-03-26 is supported, and
add server logic to negotiate the supported version with the client.

Update the client to set the protocol version, fixing what was probably
a spec violation.

+tests

Change-Id: I564b591b3daf018dd54f5c7e5d8f2d2b1e4ff502
Reviewed-on: https://go-review.googlesource.com/c/tools/+/682516
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
This commit is contained in:
Rob Findley 2025-06-18 04:42:52 +00:00 • committed by Robert Findley
commit 5ae2a9f222
8 changed files with 130 additions and 7 deletions

View file

@ -101,9 +101,13 @@ func (c *Client) Connect(ctx context.Context, t Transport) (cs *ClientSession, e
}
params := &InitializeParams{
ClientInfo: &implementation{Name: c.name, Version: c.version},
Capabilities: caps,
ClientInfo: &implementation{Name: c.name, Version: c.version},
Capabilities: caps,
ProtocolVersion: "2025-03-26",
}
// TODO(rfindley): handle protocol negotiation gracefully. If the server
// responds with 2024-11-05, surface that failure to the caller of connect,
// so that they can choose a different transport.
res, err := handleSend[*InitializeResult](ctx, cs, methodInitialize, params)
if err != nil {
_ = cs.Close()

View file

@ -0,0 +1,16 @@
// 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.
//go:build go1.24 && goexperiment.synctest
package mcp
import (
"testing"
"testing/synctest"
)
func runSyncTest(t *testing.T, f func(t *testing.T)) {
synctest.Run(func() { f(t) })
}

View file

@ -0,0 +1,16 @@
// 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.
//go:build go1.25
package mcp
import (
"testing"
"testing/synctest"
)
func runSyncTest(t *testing.T, f func(t *testing.T)) {
synctest.Test(t, f)
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
//go:build go1.24 && goexperiment.synctest
//go:build (go1.24 && goexperiment.synctest) || go1.25
package mcp
@ -85,7 +85,7 @@ func TestServerConformance(t *testing.T) {
// By comparison, gopls has a complicated framework based on progress
// reporting and careful accounting to detect when all 'expected' work
// on the server is complete.
synctest.Run(func() { runServerTest(t, test) })
runSyncTest(t, func(t *testing.T) { runServerTest(t, test) })
// TODO: in 1.25, use the following instead:
// synctest.Test(t, func(t *testing.T) {

View file

@ -634,9 +634,16 @@ func (ss *ServerSession) initialize(ctx context.Context, params *InitializeParam
ss.mu.Unlock()
}()
version := "2025-03-26" // preferred version
switch v := params.ProtocolVersion; v {
case "2024-11-05", "2025-03-26":
version = v
}
return &InitializeResult{
// TODO(rfindley): support multiple protocol versions.
ProtocolVersion: "2024-11-05",
// TODO(rfindley): alter behavior when falling back to an older version:
// reject unsupported features.
ProtocolVersion: version,
Capabilities: &serverCapabilities{
Prompts: &promptCapabilities{
ListChanged: true,

View file

@ -122,7 +122,7 @@ func TestStreamableServerTransport(t *testing.T) {
Resources: &resourceCapabilities{ListChanged: true},
Tools: &toolCapabilities{ListChanged: true},
},
ProtocolVersion: "2024-11-05",
ProtocolVersion: "2025-03-26",
ServerInfo: &implementation{Name: "testServer", Version: "v1.0.0"},
}, nil)
initializedMsg := req(0, "initialized", &InitializedParams{})

View file

@ -0,0 +1,40 @@
Check that when the client sends an arbitrary later version, the server
response with its latest supported version.
-- client --
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2029-01-01",
"capabilities": {},
"clientInfo": { "name": "ExampleClient", "version": "1.0.0" }
}
}
-- server --
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"_meta": {},
"capabilities": {
"completions": {},
"logging": {},
"prompts": {
"listChanged": true
},
"resources": {
"listChanged": true
},
"tools": {
"listChanged": true
}
},
"protocolVersion": "2025-03-26",
"serverInfo": {
"name": "testServer",
"version": "v1.0.0"
}
}
}

View file

@ -0,0 +1,40 @@
Check that when the client sends an older version, the server indicates
support.
-- client --
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "ExampleClient", "version": "1.0.0" }
}
}
-- server --
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"_meta": {},
"capabilities": {
"completions": {},
"logging": {},
"prompts": {
"listChanged": true
},
"resources": {
"listChanged": true
},
"tools": {
"listChanged": true
}
},
"protocolVersion": "2024-11-05",
"serverInfo": {
"name": "testServer",
"version": "v1.0.0"
}
}
}