Describe the bug
After a normal initialization flow, the server accepts a second initialize request and returns a normal initialize result. The same session then continues to respond to later requests. Repeated initialize requests with changed client capabilities, changed client identity, and an older protocol version were also accepted.
I understand there is a spec-interpretation question here: initialize being the first interaction can be read as an ordering requirement rather than a cardinality requirement. This is framed as a changed-parameter session-state consistency issue rather than a strict spec-violation claim.
To Reproduce
Steps to reproduce the behavior:
- Start a Go SDK MCP server.
- Complete a normal
initialize followed by notifications/initialized.
- Send a second
initialize request on the same connection or HTTP session.
- Repeat with changed
capabilities, changed clientInfo, or an older protocolVersion.
- Send
ping after the duplicate initialization.
Expected behavior
If duplicate initialization is not intentional, the server should reject the second initialize with a JSON-RPC error such as -32600 Invalid Request. If it is intentional, the behavior should be documented, including whether the second request is allowed to replace ServerSession.InitializeParams() after the session is operational.
Logs
Observed with:
- stable release
v1.6.0 (f5f2015413f17f578fbacc839d2c05d0fdb3f1cc)
main snapshot da3535b6786149733cd8383f9ffb41191f45a9a6 from 2026-05-15
The same transport-level behavior was reproduced on stdio, stateful Streamable HTTP, and stateless Streamable HTTP:
- Second
initialize accepted
- Duplicate
initialize with an older protocolVersion accepted
- Five repeated duplicate
initialize requests accepted
- Later
ping succeeded
Code:
ServerSessionState.InitializeParams stores the parameters from initialize.
ServerSession.InitializeParams() exposes that stored value.
ServerSession.handle allows methodInitialize through even when state.InitializeParams != nil.
ServerSession.initialize assigns state.InitializeParams = params every time it handles an initialize request.
notifications/initialized does have duplicate protection, which makes the behavior asymmetric: duplicate initialized notifications are rejected, but duplicate initialize requests are not.
Session state after duplicate initialization
I compared ServerSession.InitializeParams() and ServerSession.ID() before and after a duplicate initialize on the same running session.
| Description |
stdio |
stateful HTTP |
stateless HTTP |
| Same parameters |
No change |
No change |
No change |
Changed clientInfo and capabilities |
Overwrites ClientInfo and Capabilities |
Overwrites ClientInfo and Capabilities |
No stored client state observed |
Older protocolVersion (2024-11-05) |
Overwrites ProtocolVersion and ClientInfo |
Overwrites ProtocolVersion and ClientInfo |
Response reflects the older version, but stored client state was not observed |
On stdio and stateful Streamable HTTP, the second initialize replaces the server's record of the client identity, client capabilities, and client protocol version. This means a session that was operating with 2025-11-25 can have ServerSession.InitializeParams().ProtocolVersion replaced with 2024-11-05, and capability-gated code that reads InitializeParams().Capabilities can observe the later request rather than the original negotiation.
Describe the bug
After a normal initialization flow, the server accepts a second
initializerequest and returns a normalinitializeresult. The same session then continues to respond to later requests. Repeatedinitializerequests with changed client capabilities, changed client identity, and an older protocol version were also accepted.I understand there is a spec-interpretation question here:
initializebeing the first interaction can be read as an ordering requirement rather than a cardinality requirement. This is framed as a changed-parameter session-state consistency issue rather than a strict spec-violation claim.To Reproduce
Steps to reproduce the behavior:
initializefollowed bynotifications/initialized.initializerequest on the same connection or HTTP session.capabilities, changedclientInfo, or an olderprotocolVersion.pingafter the duplicate initialization.Expected behavior
If duplicate initialization is not intentional, the server should reject the second
initializewith a JSON-RPC error such as-32600 Invalid Request. If it is intentional, the behavior should be documented, including whether the second request is allowed to replaceServerSession.InitializeParams()after the session is operational.Logs
Observed with:
v1.6.0(f5f2015413f17f578fbacc839d2c05d0fdb3f1cc)mainsnapshotda3535b6786149733cd8383f9ffb41191f45a9a6from 2026-05-15The same transport-level behavior was reproduced on
stdio, stateful Streamable HTTP, and stateless Streamable HTTP:initializeacceptedinitializewith an olderprotocolVersionacceptedinitializerequests acceptedpingsucceededCode:
ServerSessionState.InitializeParamsstores the parameters frominitialize.ServerSession.InitializeParams()exposes that stored value.ServerSession.handleallowsmethodInitializethrough even whenstate.InitializeParams != nil.ServerSession.initializeassignsstate.InitializeParams = paramsevery time it handles aninitializerequest.notifications/initializeddoes have duplicate protection, which makes the behavior asymmetric: duplicate initialized notifications are rejected, but duplicate initialize requests are not.Session state after duplicate initialization
I compared
ServerSession.InitializeParams()andServerSession.ID()before and after a duplicateinitializeon the same running session.clientInfoandcapabilitiesClientInfoandCapabilitiesClientInfoandCapabilitiesprotocolVersion(2024-11-05)ProtocolVersionandClientInfoProtocolVersionandClientInfoOn
stdioand stateful Streamable HTTP, the secondinitializereplaces the server's record of the client identity, client capabilities, and client protocol version. This means a session that was operating with2025-11-25can haveServerSession.InitializeParams().ProtocolVersionreplaced with2024-11-05, and capability-gated code that readsInitializeParams().Capabilitiescan observe the later request rather than the original negotiation.