use rmcp::ErrorData as McpError;
use rmcp::model::{CallToolRequestParams, CallToolResponse, CreateTaskResult};
use rmcp::service::{RequestContext, RoleServer};
use rmcp::task_manager::{TaskExit, TaskOptions};
use super::BasemindServer;
pub(super) const SLOW_TOOLS: &[&str] = &[
"rescan",
#[cfg(feature = "documents")]
"search_documents",
#[cfg(feature = "crawl")]
"web_scrape",
#[cfg(feature = "crawl")]
"web_crawl",
#[cfg(feature = "crawl")]
"web_map",
];
pub(super) fn is_slow_tool(name: &str) -> bool {
SLOW_TOOLS.contains(&name)
}
pub(super) fn spawn_slow_tool(
server: &BasemindServer,
request: CallToolRequestParams,
context: RequestContext<RoleServer>,
) -> CreateTaskResult {
let server = server.clone();
let manager = server.tasks.clone();
let task = manager.spawn(TaskOptions::new(), move |ctx| {
Box::pin(async move {
let mut work = tokio::spawn(async move {
let tcc = rmcp::handler::server::tool::ToolCallContext::new(&server, request, context);
server.tool_router.call(tcc).await
});
let outcome = tokio::select! {
biased;
() = ctx.cancelled() => return Err(TaskExit::Cancelled),
joined = &mut work => joined,
};
match outcome {
Ok(Ok(CallToolResponse::Complete(result))) => Ok(result),
Ok(Ok(_)) => Err(TaskExit::Error(McpError::internal_error(
"tool returned a non-terminal response inside a task",
None,
))),
Ok(Err(error)) => Err(TaskExit::Error(error)),
Err(join_error) => Err(TaskExit::Error(McpError::internal_error(
format!("slow tool task failed to complete: {join_error}"),
None,
))),
}
})
});
CreateTaskResult::new(task)
}