fix(rmcp-client): accept empty one-way responses

This commit is contained in:
Raymond Li
2026-07-07 00:07:50 -07:00
parent 5d36ff2fa5
commit 0ce24a1317
2 changed files with 80 additions and 1 deletions
@@ -45,6 +45,10 @@ mod www_authenticate;
use self::www_authenticate::insufficient_scope_challenge;
#[cfg(test)]
#[path = "http_client_adapter_tests.rs"]
mod tests;
const EVENT_STREAM_MIME_TYPE: &str = "text/event-stream";
const JSON_MIME_TYPE: &str = "application/json";
const HEADER_SESSION_ID: &str = "Mcp-Session-Id";
@@ -215,7 +219,14 @@ impl StreamableHttpClient for StreamableHttpClientAdapter {
}
Some(content_type) if content_type.starts_with(JSON_MIME_TYPE) => {
let body = collect_body(&mut body_stream).await?;
if body.is_empty() && matches!(message, JsonRpcMessage::Notification(_)) {
if body.is_empty()
&& matches!(
message,
JsonRpcMessage::Response(_)
| JsonRpcMessage::Notification(_)
| JsonRpcMessage::Error(_)
)
{
return Ok(StreamableHttpPostResponse::Accepted);
}
let message: ServerJsonRpcMessage =
@@ -0,0 +1,68 @@
use std::collections::HashMap;
use std::sync::Arc;
use codex_exec_server::Environment;
use reqwest::header::HeaderMap;
use rmcp::ErrorData;
use rmcp::model::ClientJsonRpcMessage;
use rmcp::model::ClientResult;
use rmcp::model::RequestId;
use rmcp::transport::streamable_http_client::StreamableHttpClient;
use rmcp::transport::streamable_http_client::StreamableHttpPostResponse;
use wiremock::Mock;
use wiremock::MockServer;
use wiremock::ResponseTemplate;
use wiremock::matchers::method;
use wiremock::matchers::path;
use super::StreamableHttpClientAdapter;
#[tokio::test]
async fn accepts_empty_ok_for_one_way_client_messages() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/mcp"))
.respond_with(ResponseTemplate::new(200).insert_header("content-type", "application/json"))
.expect(2)
.mount(&server)
.await;
let adapter = StreamableHttpClientAdapter::new(
Environment::default_for_tests().get_http_client(),
HeaderMap::new(),
/*auth_provider*/ None,
);
let messages = [
(
"response",
ClientJsonRpcMessage::response(
ClientResult::empty(()),
RequestId::String("response-id".into()),
),
),
(
"error",
ClientJsonRpcMessage::error(
ErrorData::internal_error("test error", /*data*/ None),
Some(RequestId::String("error-id".into())),
),
),
];
for (message_kind, message) in messages {
let result = adapter
.post_message(
Arc::from(format!("{}/mcp", server.uri())),
message,
/*session_id*/ None,
/*auth_token*/ None,
HashMap::new(),
)
.await;
assert!(
matches!(result, Ok(StreamableHttpPostResponse::Accepted)),
"expected empty response for {message_kind} to be accepted, got {result:?}"
);
}
}