use std::sync::Arc;
use futures::future::BoxFuture;
use serde_json::Value;
use turbomcp_core::{
CancellationToken, JsonRpcError, JsonRpcMessage, JsonRpcRequest, McpError, RequestContext,
RequestId,
};
use turbomcp_protocol::methods;
use turbomcp_protocol::v2026_07_28::types as v0728;
use turbomcp_service::mcp_to_jsonrpc_error;
use crate::context::CallToolContext;
use crate::extension::{CallAugmentRequest, CallRunner, Extension};
use crate::mrtr::ClientHandle;
use crate::router::MethodRouter;
use crate::traits::McpServerCore;
use super::params::parse_call_tool_params;
use super::{connection_id, context_declares_extension, error_response};
pub(super) async fn try_augment_call<S: McpServerCore>(
server: &S,
router: &MethodRouter<S>,
req: &JsonRpcRequest,
ctx: &RequestContext,
extensions: &[Arc<dyn Extension>],
id: &RequestId,
) -> Option<JsonRpcMessage> {
for ext in extensions {
if !(ext.augments_calls() && context_declares_extension(ctx, ext.id())) {
continue;
}
let run = match build_call_runner(server, router, req, ctx) {
Ok(run) => run,
Err(e) => return Some(error_response(id.clone(), &e)),
};
let connection_id = connection_id(req.params.as_ref()).map(str::to_owned);
if let Some(resp) = ext
.augment_call(CallAugmentRequest {
request: req.clone(),
context: ctx.clone(),
connection_id,
run,
})
.await
{
return Some(resp);
}
}
None
}
fn build_call_runner<S: McpServerCore>(
server: &S,
router: &MethodRouter<S>,
req: &JsonRpcRequest,
ctx: &RequestContext,
) -> Result<CallRunner, McpError> {
let params = parse_call_tool_params(req.params.as_ref())?;
let cancel = CancellationToken::new();
let mut call_ctx = ctx.clone();
call_ctx.cancellation = cancel.clone();
let input_slot = crate::extension::TaskInputSlot::default();
let handle = ClientHandle::task_mediated(ctx.client_capabilities.clone(), input_slot.clone());
let fut = router.dispatch_call_tool(
server.clone(),
CallToolContext::new(call_ctx).with_client(handle),
params,
);
let future: BoxFuture<'static, Result<Value, JsonRpcError>> = Box::pin(async move {
match fut {
None => Err(mcp_to_jsonrpc_error(&McpError::method_not_found(
methods::request::TOOLS_CALL,
))),
Some(f) => match f.await {
Ok(result) => serde_json::to_value(v0728::CallToolResult::from(result))
.map_err(|e| mcp_to_jsonrpc_error(&McpError::internal(e.to_string()))),
Err(e) => Err(mcp_to_jsonrpc_error(&e)),
},
}
});
Ok(CallRunner::new(future, cancel).with_input_slot(input_slot))
}