Skip to content

Commit f62a410

Browse files
committed
fix: reject duplicate initialize requests
1 parent 1253d17 commit f62a410

2 files changed

Lines changed: 96 additions & 1 deletion

File tree

mcp/server.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,9 +1488,17 @@ func (ss *ServerSession) initialize(ctx context.Context, params *InitializeParam
14881488
if params == nil {
14891489
return nil, fmt.Errorf("%w: \"params\" must be be provided", jsonrpc2.ErrInvalidParams)
14901490
}
1491+
var duplicate bool
14911492
ss.updateState(func(state *ServerSessionState) {
1492-
state.InitializeParams = params
1493+
duplicate = state.InitializeParams != nil
1494+
if !duplicate {
1495+
state.InitializeParams = params
1496+
}
14931497
})
1498+
if duplicate {
1499+
ss.server.opts.Logger.Error("duplicate initialize request")
1500+
return nil, fmt.Errorf("%w: duplicate %q received", jsonrpc2.ErrInvalidRequest, methodInitialize)
1501+
}
14941502

14951503
s := ss.server
14961504
return &InitializeResult{

mcp/server_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bytes"
99
"context"
1010
"encoding/json"
11+
"errors"
1112
"fmt"
1213
"log"
1314
"log/slog"
@@ -825,6 +826,92 @@ func TestClientRootCapabilities(t *testing.T) {
825826
}
826827
}
827828

829+
func TestServerRejectsDuplicateInitialize(t *testing.T) {
830+
ctx := context.Background()
831+
832+
server := NewServer(&Implementation{Name: "testServer", Version: "v1.0.0"}, nil)
833+
cTransport, sTransport := NewInMemoryTransports()
834+
ss, err := server.Connect(ctx, sTransport, nil)
835+
if err != nil {
836+
t.Fatal(err)
837+
}
838+
defer ss.Close()
839+
840+
cConn, err := cTransport.Connect(ctx)
841+
if err != nil {
842+
t.Fatal(err)
843+
}
844+
defer cConn.Close()
845+
846+
firstParams := json.RawMessage(`{
847+
"protocolVersion": "2025-11-25",
848+
"clientInfo": {"name": "first-client", "version": "1.0.0"}
849+
}`)
850+
firstReq, err := jsonrpc2.NewCall(jsonrpc2.Int64ID(1), methodInitialize, firstParams)
851+
if err != nil {
852+
t.Fatal(err)
853+
}
854+
if err := cConn.Write(ctx, firstReq); err != nil {
855+
t.Fatalf("first initialize write failed: %v", err)
856+
}
857+
msg, err := cConn.Read(ctx)
858+
if err != nil {
859+
t.Fatalf("first initialize read failed: %v", err)
860+
}
861+
resp, ok := msg.(*jsonrpc2.Response)
862+
if !ok {
863+
t.Fatalf("expected Response, got %T", msg)
864+
}
865+
if resp.Error != nil {
866+
t.Fatalf("first initialize failed: %v", resp.Error)
867+
}
868+
869+
initializedReq, err := jsonrpc2.NewNotification(notificationInitialized, &InitializedParams{})
870+
if err != nil {
871+
t.Fatal(err)
872+
}
873+
if err := cConn.Write(ctx, initializedReq); err != nil {
874+
t.Fatalf("initialized notification write failed: %v", err)
875+
}
876+
877+
secondParams := json.RawMessage(`{
878+
"protocolVersion": "2024-11-05",
879+
"clientInfo": {"name": "second-client", "version": "2.0.0"}
880+
}`)
881+
secondReq, err := jsonrpc2.NewCall(jsonrpc2.Int64ID(2), methodInitialize, secondParams)
882+
if err != nil {
883+
t.Fatal(err)
884+
}
885+
if err := cConn.Write(ctx, secondReq); err != nil {
886+
t.Fatalf("second initialize write failed: %v", err)
887+
}
888+
msg, err = cConn.Read(ctx)
889+
if err != nil {
890+
t.Fatalf("second initialize read failed: %v", err)
891+
}
892+
resp, ok = msg.(*jsonrpc2.Response)
893+
if !ok {
894+
t.Fatalf("expected Response, got %T", msg)
895+
}
896+
if resp.Error == nil {
897+
t.Fatal("second initialize unexpectedly succeeded")
898+
}
899+
if !errors.Is(resp.Error, jsonrpc2.ErrInvalidRequest) {
900+
t.Fatalf("second initialize error = %v, want invalid request", resp.Error)
901+
}
902+
903+
got := ss.InitializeParams()
904+
if got == nil {
905+
t.Fatal("InitializeParams is nil")
906+
}
907+
if got.ProtocolVersion != "2025-11-25" {
908+
t.Fatalf("ProtocolVersion = %q, want first initialize value", got.ProtocolVersion)
909+
}
910+
if got.ClientInfo == nil || got.ClientInfo.Name != "first-client" {
911+
t.Fatalf("ClientInfo = %#v, want first initialize value", got.ClientInfo)
912+
}
913+
}
914+
828915
// TODO: move this to tool_test.go
829916
func TestToolForSchemas(t *testing.T) {
830917
// Validate that toolForErr handles schemas properly.

0 commit comments

Comments
 (0)