From ca1315bfaf9bf196ac118a12ac43b19f4525d4d5 Mon Sep 17 00:00:00 2001 From: ZeroZipp Date: Fri, 22 May 2026 09:47:20 +0200 Subject: [PATCH] sse endpoint --- readme.md | 2 ++ src/api/mcp/mod.rs | 26 +++++++++++++++++++++++++- src/app/mod.rs | 5 ++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index b78714e..b2a2ec4 100644 --- a/readme.md +++ b/readme.md @@ -24,3 +24,5 @@ docker build -t server . - requires `X-Device-ID: ` - `POST /mcp` for MCP/JSON-RPC requests - typically uses `Authorization: Bearer ` +- `GET /mcp` for MCP server-sent events + - provides a minimal SSE stream for MCP clients that probe or require it diff --git a/src/api/mcp/mod.rs b/src/api/mcp/mod.rs index 8561a71..1e1aa36 100644 --- a/src/api/mcp/mod.rs +++ b/src/api/mcp/mod.rs @@ -6,10 +6,34 @@ use protocol::{ ApiResponse, InitializeResult, JSONRPC_VERSION, JsonRpcRequest, jsonrpc_error, jsonrpc_notification_ok, jsonrpc_ok, }; -use rocket::{State, http::Status, post, serde::json::Json}; +use rocket::{ + Shutdown, State, get, + http::Status, + post, + response::stream::{Event, EventStream}, + serde::json::Json, +}; use serde_json::Value; use tools::{handle_tool_call, tool_definitions}; +const MCP_MESSAGE_ENDPOINT_EVENT: &str = "endpoint"; + +#[get("/mcp")] +pub fn sse(shutdown: Shutdown) -> EventStream![] { + EventStream! { + yield Event::json(&serde_json::json!({ "path": "/mcp" })).event(MCP_MESSAGE_ENDPOINT_EVENT); + + loop { + tokio::select! { + _ = shutdown.clone() => break, + _ = tokio::time::sleep(std::time::Duration::from_secs(15)) => { + yield Event::comment("keepalive"); + } + } + } + } +} + #[post("/mcp", data = "")] pub async fn route( body: Json, diff --git a/src/app/mod.rs b/src/app/mod.rs index 15ac86f..6f3d725 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -33,7 +33,10 @@ pub async fn apply_schema(database: &SqlitePool) -> Result<(), sqlx::Error> { pub fn build_rocket(database: SqlitePool) -> rocket::Rocket { rocket::build() .manage(build_state(database)) - .mount("/", routes![transport::socket::connect, api::mcp::route]) + .mount( + "/", + routes![transport::socket::connect, api::mcp::route, api::mcp::sse], + ) .register("/", rocket::catchers![api::catchers::default_catcher]) }