mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
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:
co-authored by
gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
parent
3aa36c8f55
commit
969b559e2a
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user