fix: negotiate protocol version in handler (#930)
* fix: negotiate protocol version in handler (fixes #916) * fix: use server pinned version as fallback in default initialize handler
This commit is contained in:
parent
67a3085944
commit
80a74795e9
1 changed files with 247 additions and 6 deletions
|
|
@ -16,8 +16,9 @@ use super::session::{
|
|||
use crate::{
|
||||
RoleServer,
|
||||
model::{
|
||||
ClientJsonRpcMessage, ClientNotification, ClientRequest, ErrorData, GetExtensions,
|
||||
InitializeRequest, InitializedNotification, JsonRpcError, ProtocolVersion, RequestId,
|
||||
ClientCapabilities, ClientJsonRpcMessage, ClientNotification, ClientRequest, ErrorData,
|
||||
GetExtensions, Implementation, InitializeRequest, InitializeRequestParams,
|
||||
InitializedNotification, JsonRpcError, ProtocolVersion, RequestId,
|
||||
},
|
||||
serve_server,
|
||||
service::serve_directly,
|
||||
|
|
@ -1239,10 +1240,17 @@ where
|
|||
.map_err(internal_error_response("get service"))?;
|
||||
match message {
|
||||
ClientJsonRpcMessage::Request(mut request) => {
|
||||
// Build a peer_info so context.protocol_version() works inside handlers.
|
||||
// serve_directly skips the handshake and receives None by default, making
|
||||
// protocol_version() always return None in stateless mode. We reconstruct it:
|
||||
// - initialize requests: version comes from the request body params
|
||||
// - all other requests: version comes from the MCP-Protocol-Version header
|
||||
// (already validated above; absent header defaults to 2025-03-26)
|
||||
let peer_info = Self::peer_info_for_stateless_request(&request, &part.headers);
|
||||
request.request.extensions_mut().insert(part);
|
||||
let (transport, mut receiver) =
|
||||
OneshotTransport::<RoleServer>::new(ClientJsonRpcMessage::Request(request));
|
||||
let service = serve_directly(service, transport, None);
|
||||
let service = serve_directly(service, transport, peer_info);
|
||||
tokio::spawn(async move {
|
||||
// on service created
|
||||
let _ = service.waiting().await;
|
||||
|
|
@ -1331,4 +1339,34 @@ where
|
|||
}
|
||||
Ok(accepted_response())
|
||||
}
|
||||
|
||||
/// Build a `ClientInfo` (peer_info) for a stateless request so that
|
||||
/// `context.protocol_version()` returns the correct value inside handlers.
|
||||
///
|
||||
/// `serve_directly` skips the MCP handshake and accepts `peer_info = None`,
|
||||
/// which means `context.protocol_version()` is always `None` in stateless mode.
|
||||
/// We reconstruct the protocol version from the available signal per request type:
|
||||
/// - initialize: version is in the request body params (authoritative)
|
||||
/// - all other requests: version is in the MCP-Protocol-Version header
|
||||
/// (validated before this point; absent header defaults to 2025-03-26)
|
||||
fn peer_info_for_stateless_request(
|
||||
request: &crate::model::JsonRpcRequest<ClientRequest>,
|
||||
headers: &HeaderMap,
|
||||
) -> Option<InitializeRequestParams> {
|
||||
let version = if let ClientRequest::InitializeRequest(ref init) = request.request {
|
||||
init.params.protocol_version.clone()
|
||||
} else {
|
||||
headers
|
||||
.get(HEADER_MCP_PROTOCOL_VERSION)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|s| serde_json::from_value(serde_json::Value::String(s.to_owned())).ok())
|
||||
.unwrap_or(ProtocolVersion::V_2025_03_26)
|
||||
};
|
||||
Some(InitializeRequestParams {
|
||||
meta: None,
|
||||
protocol_version: version,
|
||||
capabilities: ClientCapabilities::default(),
|
||||
client_info: Implementation::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue