diff --git a/crates/rmcp/src/service/server.rs b/crates/rmcp/src/service/server.rs index 8efbd34b..c2f57a2a 100644 --- a/crates/rmcp/src/service/server.rs +++ b/crates/rmcp/src/service/server.rs @@ -483,6 +483,16 @@ pub(crate) fn negotiate_protocol_version( } } +fn missing_request_metadata_error(missing: &[&str]) -> ErrorData { + ErrorData::invalid_params( + format!( + "request _meta is missing or has malformed required fields: {}", + missing.join(", ") + ), + None, + ) +} + async fn serve_server_with_ct_inner( service: S, transport: T, @@ -528,11 +538,22 @@ where let initialize_request = match request { ClientRequest::InitializeRequest(request) => request, mut request => { - if !request + let missing_metadata = request .get_meta() - .missing_required_keys(&ProtocolVersion::V_2026_07_28) - .is_empty() - { + .missing_required_keys(&ProtocolVersion::V_2026_07_28); + if !missing_metadata.is_empty() { + transport + .send(ServerJsonRpcMessage::error( + missing_request_metadata_error(&missing_metadata), + Some(id.clone()), + )) + .await + .map_err(|error| { + ServerInitializeError::transport::( + error, + "sending pre-init metadata error response", + ) + })?; return Err(ServerInitializeError::ExpectedInitializeRequest(Some( ClientJsonRpcMessage::request(request, id), ))); diff --git a/crates/rmcp/tests/test_stateless_server_requests.rs b/crates/rmcp/tests/test_stateless_server_requests.rs index 4404e08c..ae418e39 100644 --- a/crates/rmcp/tests/test_stateless_server_requests.rs +++ b/crates/rmcp/tests/test_stateless_server_requests.rs @@ -162,7 +162,7 @@ async fn stateless_server_uses_each_requests_client_context() { } #[tokio::test] -async fn stateless_server_rejects_malformed_metadata_opener() { +async fn stateless_server_rejects_malformed_metadata_opener_with_error_response() { let (server_transport, client_transport) = tokio::io::duplex(4096); let server_task = tokio::spawn(async move { StatelessServer.serve(server_transport).await }); let mut client = IntoTransport::::into_transport(client_transport); @@ -186,6 +186,24 @@ async fn stateless_server_rejects_malformed_metadata_opener() { )) .await .expect("send list tools"); + let Some(ServerJsonRpcMessage::Error(error)) = client.receive().await else { + panic!("expected invalid params"); + }; + assert_eq!(error.id, Some(RequestId::Number(1))); + assert_eq!(error.error.code, ErrorCode::INVALID_PARAMS); + assert!( + error + .error + .message + .contains("request _meta is missing or has malformed required fields") + ); + assert!( + error + .error + .message + .contains("io.modelcontextprotocol/clientCapabilities") + ); + let Err(error) = server_task.await.expect("server task") else { panic!("malformed opener should not start a session"); };