Skip to content

Commit c8f02a9

Browse files
fix: tolerate duplicate initialize requests on the same session
go-sdk v1.7.0-pre.1 (bumped in #2787) started rejecting a second "initialize" call on an already-initialized session with `duplicate "initialize" received`, fixing modelcontextprotocol/go-sdk#961 (a duplicate initialize with changed params could silently overwrite the session's stored InitializeParams). go-sdk v1.6.1 and earlier (github-mcp-server v1.5.0) accepted repeat calls and returned the same result every time. Some MCP clients resend "initialize" on the same transport/session when a handshake is retried (e.g. a slow first response triggers a client-side retry without tearing down the connection), which relied on that old idempotent behavior and now hard-fails on v1.6.0+. Add a receiving middleware that caches the first successful InitializeResult and replays it for later "initialize" calls on the same server, instead of forwarding to the SDK and hitting its error. The response is otherwise constant for the process's lifetime, so a single cached result (not a per-session cache) is enough and avoids any per-session memory growth. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent eb088df commit c8f02a9

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

internal/ghmcp/server.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"os/signal"
1111
"strings"
12+
"sync"
1213
"syscall"
1314
"time"
1415

@@ -190,10 +191,65 @@ func NewStdioMCPServer(ctx context.Context, cfg github.MCPServerConfig) (*mcp.Se
190191
}
191192

192193
ghServer.AddReceivingMiddleware(addUserAgentsMiddleware(cfg, clients.restUATransp, clients.gqlHTTP))
194+
ghServer.AddReceivingMiddleware(dedupeInitializeMiddleware())
193195

194196
return ghServer, nil
195197
}
196198

199+
// dedupeInitializeMiddleware makes a repeated "initialize" call on an
200+
// already-initialized session return the original result instead of
201+
// erroring.
202+
//
203+
// go-sdk v1.7.0-pre.1 (picked up by github-mcp-server v1.6.0, see go.mod)
204+
// started rejecting a second "initialize" on the same session with
205+
// `duplicate "initialize" received" (upstream: TestServerRejectsDuplicateInitialize
206+
// in mcp/server_test.go, added to fix modelcontextprotocol/go-sdk#961). go-sdk
207+
// v1.6.1 and earlier (github-mcp-server v1.5.0) silently accepted repeat
208+
// calls, returning the same result every time. Some MCP clients resend
209+
// "initialize" on the same transport/session when a handshake is retried
210+
// (e.g. after a slow first response), relying on that old idempotent
211+
// behavior. Restore it here instead of depending on unreleased upstream
212+
// behavior, since the response for "initialize" is otherwise constant for
213+
// the lifetime of this server.
214+
func dedupeInitializeMiddleware() func(next mcp.MethodHandler) mcp.MethodHandler {
215+
var (
216+
mu sync.Mutex
217+
cached *mcp.InitializeResult
218+
)
219+
220+
return func(next mcp.MethodHandler) mcp.MethodHandler {
221+
return func(ctx context.Context, method string, request mcp.Request) (mcp.Result, error) {
222+
if method != "initialize" {
223+
return next(ctx, method, request)
224+
}
225+
226+
if sess, ok := request.GetSession().(*mcp.ServerSession); ok && sess.InitializeParams() != nil {
227+
mu.Lock()
228+
result := cached
229+
mu.Unlock()
230+
if result != nil {
231+
return result, nil
232+
}
233+
// No cached result yet even though the session is already
234+
// initialized: fall through and let the SDK produce its own
235+
// error rather than guess at a response.
236+
}
237+
238+
result, err := next(ctx, method, request)
239+
if err == nil {
240+
if initResult, ok := result.(*mcp.InitializeResult); ok {
241+
mu.Lock()
242+
if cached == nil {
243+
cached = initResult
244+
}
245+
mu.Unlock()
246+
}
247+
}
248+
return result, err
249+
}
250+
}
251+
}
252+
197253
type StdioServerConfig struct {
198254
// Version of the server
199255
Version string

0 commit comments

Comments
 (0)