use axum::body::Bytes;
use axum::extract::State;
use axum::http::StatusCode;
use axum::response::sse::{Event, KeepAlive, Sse};
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde_json::Value;
use super::handlers::dispatch;
use super::router::McpState;
use super::types::{JsonRpcError, JsonRpcRequest, JsonRpcResponse};
pub(crate) async fn post_handler(State(state): State<McpState>, body: Bytes) -> Response {
handle_message(&state, &body, None).await
}
pub(crate) async fn handle_message(
state: &McpState,
body: &[u8],
ctx: Option<super::tools::McpContext>,
) -> Response {
let value: Value = match serde_json::from_slice(body) {
Ok(v) => v,
Err(_) => return json_error(Value::Null, JsonRpcError::parse_error()),
};
let recovered_id = value.get("id").cloned().unwrap_or(Value::Null);
let request: JsonRpcRequest = match serde_json::from_value(value) {
Ok(r) => r,
Err(e) => return json_error(recovered_id, JsonRpcError::invalid_request(e.to_string())),
};
if request.is_notification() {
if request.method == "notifications/cancelled" {
if let (Some(ctx), Some(rid)) = (
ctx.as_ref(),
request
.params
.as_ref()
.and_then(|p| p.get("requestId"))
.map(jsonrpc_id_string),
) {
super::progress::cancel(&ctx.agent.tenant, ctx.agent.agent_id, &rid);
}
}
return StatusCode::ACCEPTED.into_response();
}
let id = request.id.clone().unwrap_or(Value::Null);
let request_id = jsonrpc_id_string(&id);
match dispatch(
state,
&request.method,
request.params,
ctx,
Some(request_id.as_str()),
)
.await
{
Ok(result) => Json(JsonRpcResponse::success(id, result)).into_response(),
Err(err) => Json(JsonRpcResponse::failure(id, err)).into_response(),
}
}
pub(crate) async fn sse_handler(
t: crate::extractors::Tenant,
axum::extract::State(state): axum::extract::State<McpState>,
axum::extract::OriginalUri(uri): axum::extract::OriginalUri,
headers: axum::http::HeaderMap,
) -> Response {
let Some(jwt) = state.jwt.as_ref() else {
return (StatusCode::INTERNAL_SERVER_ERROR, "mcp auth not configured").into_response();
};
let Some(token) = super::auth::bearer(&headers) else {
return super::auth::unauthorized(&headers, &uri);
};
let Some(agent) = super::auth::verify_agent_token(jwt, token, &t.org.slug) else {
return super::auth::unauthorized(&headers, &uri);
};
let tenant = agent.tenant.clone();
let agent_id = agent.agent_id;
let mut rx = super::notifications::bus().subscribe();
let stream = async_stream::stream! {
loop {
match rx.recv().await {
Ok(frame) => {
if super::notifications::frame_visible(&frame, &tenant, agent_id) {
yield Ok::<Event, std::convert::Infallible>(Event::default().data(frame.body));
}
}
Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => continue,
Err(tokio::sync::broadcast::error::RecvError::Closed) => break,
}
}
};
Sse::new(stream)
.keep_alive(KeepAlive::default())
.into_response()
}
fn json_error(id: Value, error: JsonRpcError) -> Response {
Json(JsonRpcResponse::failure(id, error)).into_response()
}
fn jsonrpc_id_string(id: &Value) -> String {
match id {
Value::String(s) => s.clone(),
other => other.to_string(),
}
}