Spaces:
Sleeping
Sleeping
File size: 3,301 Bytes
6d57fe0 2270763 4d0b75e 6d57fe0 fe3d8c6 6d57fe0 098a7fc 6d57fe0 1a755e8 6d57fe0 098a7fc 6d57fe0 1a755e8 098a7fc 6d57fe0 098a7fc 6d57fe0 1a755e8 6d57fe0 098a7fc a34d624 098a7fc 1a755e8 a34d624 1a755e8 098a7fc 6d57fe0 fe3d8c6 6d57fe0 4d0b75e 6d57fe0 098a7fc 6d57fe0 098a7fc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | pub mod middleware;
pub mod paths;
pub mod perms;
pub mod routes_admin;
pub mod routes_auth;
pub mod routes_fs;
pub mod routes_vcs;
pub mod ui;
use axum::body::Body;
use axum::extract::{Request, State};
use axum::http::HeaderMap;
use axum::response::IntoResponse;
use axum::routing::any;
use axum::Router;
use rmcp::transport::streamable_http_server::session::local::LocalSessionManager;
use rmcp::transport::streamable_http_server::tower::{
StreamableHttpServerConfig, StreamableHttpService,
};
use std::sync::Arc;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
use crate::auth::session::Session;
use crate::db::MarkdownDb;
use crate::mcp::McpServer;
use crate::server::middleware::session_from_headers;
#[derive(Clone)]
pub struct ServerState {
pub db: Arc<MarkdownDb>,
}
pub type AppState = Arc<ServerState>;
tokio::task_local! {
pub static MCP_SESSION: Session;
}
type McpService = StreamableHttpService<McpServer, LocalSessionManager>;
#[derive(Clone)]
struct McpRouterState {
app: AppState,
mcp: Arc<McpService>,
}
pub fn build_router(db: MarkdownDb) -> Router {
let mcp_db = db.clone();
let state: AppState = Arc::new(ServerState {
db: Arc::new(db),
});
// Each new MCP session reads MCP_SESSION (set by mcp_handler) to seed
// the McpServer with the right Session. Default to root if absent.
// disable_allowed_hosts: rmcp's default DNS-rebinding protection only
// permits localhost-style hosts; we're served from a public HTTPS host
// (e.g. *.hf.space) where this check is redundant.
let mcp_service: McpService = StreamableHttpService::new(
move || {
let session = MCP_SESSION
.try_with(|s| s.clone())
.unwrap_or_else(|_| Session::root());
Ok(McpServer::with_session(mcp_db.clone(), session))
},
Arc::new(LocalSessionManager::default()),
StreamableHttpServerConfig::default().disable_allowed_hosts(),
);
let mcp_router_state = McpRouterState {
app: state.clone(),
mcp: Arc::new(mcp_service),
};
let mcp_router: Router = Router::new()
.route("/", any(mcp_handler))
.route("/{*rest}", any(mcp_handler))
.with_state(mcp_router_state);
Router::new()
.merge(ui::routes())
.merge(routes_auth::routes())
.merge(routes_admin::routes())
.merge(routes_fs::routes())
.merge(routes_vcs::routes())
.with_state(state)
.nest("/mcp", mcp_router)
.layer(TraceLayer::new_for_http())
.layer(CorsLayer::permissive())
}
async fn mcp_handler(
State(router_state): State<McpRouterState>,
headers: HeaderMap,
request: Request,
) -> axum::response::Response {
let session = match session_from_headers(&router_state.app, &headers).await {
Ok(s) => s,
Err(e) => {
return (
axum::http::StatusCode::UNAUTHORIZED,
axum::Json(serde_json::json!({"error": e.to_string()})),
)
.into_response()
}
};
let mcp = router_state.mcp.clone();
MCP_SESSION
.scope(session, async move {
mcp.handle(request).await.map(Body::new).into_response()
})
.await
}
|