Describe the bug
After a normal initialization flow, the server accepts a second initialize request and returns a normal initialize result. Repeated initialize requests with changed capabilities, changed clientInfo, and an older protocolVersion were also accepted, and the server continued responding to later requests.
In my tests, the later initialize succeeded but the stored peer state did not change: Peer<RoleServer>::peer_info() continued to reflect the original initialization parameters. This makes the post-initialization semantics unclear, because the client receives a successful initialize result but the server continues operating on the original peer info.
This makes the duplicate-initialize semantics unclear, because the request succeeds but the stored peer_info() appears to remain unchanged.
To Reproduce
Steps to reproduce the behavior:
- Start a Rust SDK MCP server.
- Complete a normal
initialize followed by notifications/initialized.
- Send a second
initialize request on the same stdio 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 duplicate initialization is intentional, the behavior should be documented explicitly, including whether a later successful initialize is intended to be a no-op with respect to Peer<RoleServer>::peer_info().
Logs
Observed with:
- Stable release
rmcp-v1.7.0 (3529c3675ff64db805bd947ca6ece6090809e43d)
main snapshot cc66e3091e1584f48ee1e0058a2a1201a1d35c81 from 2026-05-15
The same transport-level behavior was reproduced on stdio and stateful HTTP:
- Second
initialize accepted
- Duplicate
initialize with an older protocolVersion accepted
- Fve repeated duplicate
initialize requests accepted
- Later
ping succeeded.
Code:
- The initial client initialization parameters are stored as peer info when the server creates the
Peer<RoleServer>.
Peer::peer_info() reads that value from a OnceCell.
Peer::set_peer_info() warns if peer info is already initialized and does not replace it.
- Server-side capability helpers such as sampling/elicitation checks read from
peer_info().
Session state after duplicate initialization
I compared the stored peer_info() before and after a duplicate initialize on the same running session, focusing on the stored client info, client capabilities, and protocol version.
| Description |
stdio |
stateful HTTP |
| Same parameters |
No change |
No change |
Changed clientInfo and capabilities |
No change observed |
No change observed |
Older protocolVersion (2024-11-05) |
No change observed |
No change observed |
In all scenarios, the server accepted the duplicate initialize and returned an initialize result, but Peer<RoleServer>::peer_info() continued to reflect the original initialization state.
The confusing part is that a client receives a successful response to a post-initialization initialize, but the server continues to use the original peer info for capability checks and client identity.
Describe the bug
After a normal initialization flow, the server accepts a second
initializerequest and returns a normalinitializeresult. Repeatedinitializerequests with changedcapabilities, changedclientInfo, and an olderprotocolVersionwere also accepted, and the server continued responding to later requests.In my tests, the later
initializesucceeded but the stored peer state did not change:Peer<RoleServer>::peer_info()continued to reflect the original initialization parameters. This makes the post-initialization semantics unclear, because the client receives a successfulinitializeresult but the server continues operating on the original peer info.This makes the duplicate-
initializesemantics unclear, because the request succeeds but the storedpeer_info()appears to remain unchanged.To Reproduce
Steps to reproduce the behavior:
initializefollowed bynotifications/initialized.initializerequest on the same stdio 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 duplicate initialization is intentional, the behavior should be documented explicitly, including whether a later successful
initializeis intended to be a no-op with respect toPeer<RoleServer>::peer_info().Logs
Observed with:
rmcp-v1.7.0(3529c3675ff64db805bd947ca6ece6090809e43d)mainsnapshotcc66e3091e1584f48ee1e0058a2a1201a1d35c81from 2026-05-15The same transport-level behavior was reproduced on stdio and stateful HTTP:
initializeacceptedinitializewith an olderprotocolVersionacceptedinitializerequests acceptedpingsucceeded.Code:
Peer<RoleServer>.Peer::peer_info()reads that value from aOnceCell.Peer::set_peer_info()warns if peer info is already initialized and does not replace it.peer_info().Session state after duplicate initialization
I compared the stored
peer_info()before and after a duplicateinitializeon the same running session, focusing on the stored client info, client capabilities, and protocol version.clientInfoandcapabilitiesprotocolVersion(2024-11-05)In all scenarios, the server accepted the duplicate
initializeand returned aninitializeresult, butPeer<RoleServer>::peer_info()continued to reflect the original initialization state.The confusing part is that a client receives a successful response to a post-initialization
initialize, but the server continues to use the original peer info for capability checks and client identity.