Files
zip/mcp_discover_test.go
zeekay 2ba02d0c15 mcp: answer server/discover, and say 2026-07-28
MCP 2026-07-28 made the protocol stateless, and zip already was — there is no
Mcp-Session-Id anywhere in mcp.go, nothing is remembered between requests, and
tools/list answers without a preceding initialize. So the door met the new
revision's central requirement before the revision existed and went on
announcing the old one, which is the version a client negotiates against.

What was actually missing is one method. The new spec drops the mandatory
handshake, so `initialize` stops being a lifecycle step and becomes one more way
to ask "what are you"; `server/discover` is the name the spec gives that
question. It is optional, which is exactly why it has to be answered — a client
that uses it to inspect a server before committing got `method not found` from a
door that is otherwise fully stateless, and reads the whole thing as pre-2026.
Both names now return the same payload, and `initialize` stays for every client
still speaking the older revision.

Nothing needs a migration window: roots, sampling, logging and the HTTP+SSE
transport are what this revision deprecates, and zip never had any of them.
2026-08-12 19:15:17 -07:00

45 lines
1.3 KiB
Go

package zip
import (
"context"
"encoding/json"
"testing"
zapmcp "github.com/zap-proto/mcp"
)
// 2026-07-28 removed the mandatory handshake, so a client may inspect a server
// with server/discover instead of initialize. Answering one and not the other
// reads as a pre-2026 door however stateless the transport already is.
func TestDiscoverAnswersLikeInitialize(t *testing.T) {
a := New(Config{})
get := func(method string) map[string]any {
f := a.MCP(context.Background(), &zapmcp.Frame{Method: method, Kind: zapmcp.Request, ID: "1"})
if f == nil {
t.Fatalf("%s: nil frame", method)
}
b, err := json.Marshal(f)
if err != nil {
t.Fatalf("%s: marshal: %v", method, err)
}
var env struct {
Result map[string]any `json:"result"`
Error any `json:"error"`
}
if err := json.Unmarshal(b, &env); err != nil {
t.Fatalf("%s: unmarshal %s: %v", method, b, err)
}
if env.Error != nil {
t.Fatalf("%s: error %v", method, env.Error)
}
return env.Result
}
in, disc := get("initialize"), get("server/discover")
if in["protocolVersion"] != "2026-07-28" {
t.Fatalf("protocolVersion = %v, want 2026-07-28", in["protocolVersion"])
}
if disc["protocolVersion"] != in["protocolVersion"] {
t.Fatalf("server/discover %v != initialize %v", disc["protocolVersion"], in["protocolVersion"])
}
}