fix(mcp): handle empty 202 notification acknowledgements (#1539)

* fix(mcp): handle empty 202 notification acknowledgements

* test(mcp): tighten accepted response regression coverage

* Update src/tools/mcp/http_transport.rs

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
Nige
2026-03-22 14:41:54 -07:00
committed by GitHub
co-authored by gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
parent 3aa36c8f55
commit 969b559e2a
+61
View File
@@ -130,6 +130,16 @@ impl McpTransport for HttpMcpTransport {
)));
}
// MCP notifications commonly acknowledge with 202 Accepted and no body.
if response.status() == reqwest::StatusCode::ACCEPTED {
return Ok(McpResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: None,
});
}
// Determine response format from Content-Type.
let content_type = response
.headers()
@@ -506,4 +516,55 @@ mod tests {
let echoed = response.result.unwrap();
assert_eq!(echoed["authorization"], "Bearer custom-token");
}
async fn spawn_accepted_server() -> (String, tokio::task::JoinHandle<()>) {
use axum::{Router, routing::post};
use tokio::net::TcpListener;
async fn accepted() -> axum::http::StatusCode {
axum::http::StatusCode::ACCEPTED
}
let app = Router::new().route("/", post(accepted));
let listener = TcpListener::bind("127.0.0.1:0")
.await
.expect("Failed to bind to an ephemeral port");
let addr = listener
.local_addr()
.expect("Failed to get listener's local address");
let url = format!("http://127.0.0.1:{}", addr.port());
let handle = tokio::spawn(async move {
axum::serve(listener, app)
.await
.expect("Test server failed to run");
});
(url, handle)
}
fn notification_request(method: &str) -> McpRequest {
McpRequest {
jsonrpc: "2.0".to_string(),
id: None,
method: method.to_string(),
params: None,
}
}
#[tokio::test]
async fn test_accepted_notification_returns_empty_response() {
let (url, _handle) = spawn_accepted_server().await;
let transport = HttpMcpTransport::new(&url, "accepted-test");
let request = notification_request("notifications/initialized");
let response = transport
.send(&request, &HashMap::new())
.await
.expect("202 notification response");
assert_eq!(response.jsonrpc, "2.0");
assert_eq!(response.id, request.id);
assert!(response.result.is_none());
assert!(response.error.is_none());
}
}