embacle_server/mcp/
handler.rs1use std::convert::Infallible;
8use std::sync::Arc;
9
10use axum::extract::State;
11use axum::http::HeaderMap;
12use axum::response::sse::{Event, Sse};
13use axum::response::{IntoResponse, Response};
14use axum::Json;
15use futures::stream;
16use tracing::{debug, error};
17
18use super::protocol::{JsonRpcRequest, JsonRpcResponse, PARSE_ERROR};
19use super::server::McpServer;
20
21pub async fn handle_mcp_post(
26 State(server): State<Arc<McpServer>>,
27 headers: HeaderMap,
28 body: String,
29) -> Response {
30 let request: JsonRpcRequest = match serde_json::from_str(&body) {
31 Ok(req) => req,
32 Err(e) => {
33 error!(error = %e, "Failed to parse MCP JSON-RPC body");
34 let resp = JsonRpcResponse::error(None, PARSE_ERROR, format!("Parse error: {e}"));
35 return Json(resp).into_response();
36 }
37 };
38
39 debug!(method = %request.method, "Handling MCP request");
40
41 let Some(response) = server.handle_request(request).await else {
42 return axum::http::StatusCode::NO_CONTENT.into_response();
44 };
45
46 let wants_sse = headers
47 .get("accept")
48 .and_then(|v| v.to_str().ok())
49 .is_some_and(|accept| accept.contains("text/event-stream"));
50
51 if wants_sse {
52 respond_sse(&response)
53 } else {
54 Json(response).into_response()
55 }
56}
57
58fn respond_sse(response: &JsonRpcResponse) -> Response {
60 let data = serde_json::to_string(&response).unwrap_or_else(|e| {
61 format!(
62 r#"{{"jsonrpc":"2.0","error":{{"code":-32603,"message":"Serialization failed: {e}"}}}}"#
63 )
64 });
65
66 let event = Event::default().data(data);
67 let event_stream = stream::once(async { Ok::<_, Infallible>(event) });
68
69 Sse::new(event_stream).into_response()
70}