mcp: negotiate protocol version (#112)

Negotiate the protocol version properly, and set the header on HTTP
transports.

We follow the logic of the Typescript SDK.

- On initialization, the client sends the latest version it can support,
and accepts the version that the server returns if the client supports
it. If not, the connection fails.

- The server accepts the client's version unless it doesn't support it,
in which case it replies with its latest version.

Fixes #103.
This commit is contained in:
Jonathan Amsterdam 2025-07-10 11:06:55 -04:00 • committed by GitHub
commit c037ba51d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -36,7 +36,9 @@ func TestStreamableTransports(t *testing.T) {
// 2. Start an httptest.Server with the StreamableHTTPHandler, wrapped in a
// cookie-checking middleware.
handler := NewStreamableHTTPHandler(func(req *http.Request) *Server { return server }, nil)
var header http.Header
httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
header = r.Header
cookie, err := r.Cookie("test-cookie")
if err != nil {
t.Errorf("missing cookie: %v", err)
@ -72,6 +74,9 @@ func TestStreamableTransports(t *testing.T) {
if sid == "" {
t.Error("empty session ID")
}
if g, w := session.mcpConn.(*streamableClientConn).protocolVersion, latestProtocolVersion; g != w {
t.Fatalf("got protocol version %q, want %q", g, w)
}
// 4. The client calls the "greet" tool.
params := &CallToolParams{
Name: "greet",
@ -84,6 +89,9 @@ func TestStreamableTransports(t *testing.T) {
if g := session.ID(); g != sid {
t.Errorf("session ID: got %q, want %q", g, sid)
}
if g, w := header.Get(protocolVersionHeader), latestProtocolVersion; g != w {
t.Errorf("got protocol version header %q, want %q", g, w)
}
// 5. Verify that the correct response is received.
want := &CallToolResult{
@ -154,7 +162,7 @@ func TestStreamableServerTransport(t *testing.T) {
Resources: &resourceCapabilities{ListChanged: true},
Tools: &toolCapabilities{ListChanged: true},
},
ProtocolVersion: "2025-03-26",
ProtocolVersion: latestProtocolVersion,
ServerInfo: &implementation{Name: "testServer", Version: "v1.0.0"},
}, nil)
initializedMsg := req(0, "initialized", &InitializedParams{})