use anyhow::Result;
use axum::{Json, extract::State, http::uri::Uri};
use log::{debug, error, info};
use tokio_util::sync::CancellationToken;
use tracing::instrument;
use crate::{
AppError, get_proxy_manager,
model::{
AppState, CheckMcpStatusRequestParams, CheckMcpStatusResponseParams,
CheckMcpStatusResponseStatus, GLOBAL_RESTART_TRACKER, HttpResult, McpConfig, McpProtocol,
McpRouterPath, McpServiceStatus, McpType,
},
server::mcp_start_task,
};
fn create_response(
ready: bool,
status: CheckMcpStatusResponseStatus,
message: Option<String>,
) -> Result<HttpResult<CheckMcpStatusResponseParams>, AppError> {
let response = CheckMcpStatusResponseParams::new(ready, status, message);
Ok(HttpResult::success(response, None))
}
#[instrument]
pub async fn check_mcp_status_handler(
State(state): State<AppState>,
uri: Uri,
Json(params): Json<CheckMcpStatusRequestParams>,
mcp_protocol: McpProtocol,
) -> Result<HttpResult<CheckMcpStatusResponseParams>, AppError> {
let proxy_manager = get_proxy_manager();
let status = proxy_manager
.get_mcp_service_status(¶ms.mcp_id)
.map(|mcp_service_status| mcp_service_status.check_mcp_status_response_status.clone());
if let Some(status) = status {
match status {
CheckMcpStatusResponseStatus::Error(error_msg) => {
if let Err(e) = proxy_manager.cleanup_resources(¶ms.mcp_id).await {
error!("Failed to cleanup resources for {}: {}", params.mcp_id, e);
}
return create_response(
false,
CheckMcpStatusResponseStatus::Error(error_msg),
None,
);
}
CheckMcpStatusResponseStatus::Pending => {
debug!(
"[check_mcp_status] mcp_id={} status is Pending and the service is starting",
params.mcp_id
);
proxy_manager.update_last_accessed(¶ms.mcp_id);
return create_response(
false,
CheckMcpStatusResponseStatus::Pending,
Some("服务正在启动中...".to_string()),
);
}
CheckMcpStatusResponseStatus::Ready => {
debug!(
"[check_mcp_status] mcp_id={} status is Ready, check the backend health status",
params.mcp_id
);
}
}
}
let proxy_handler = proxy_manager.get_proxy_handler(¶ms.mcp_id);
if let Some(handler) = proxy_handler {
let ready_status = handler.is_mcp_server_ready().await;
if ready_status {
proxy_manager
.update_mcp_service_status(¶ms.mcp_id, CheckMcpStatusResponseStatus::Ready);
}
proxy_manager.update_last_accessed(¶ms.mcp_id);
let status = if ready_status {
CheckMcpStatusResponseStatus::Ready
} else {
CheckMcpStatusResponseStatus::Pending
};
return create_response(ready_status, status, None);
}
let _startup_guard = match GLOBAL_RESTART_TRACKER.try_acquire_startup_lock(¶ms.mcp_id) {
Some(guard) => {
debug!(
"[check_mcp_status] mcp_id={} Obtained the startup lock successfully and started to start the service",
params.mcp_id
);
guard
}
None => {
debug!(
"[check_mcp_status] mcp_id={} The startup lock is occupied and the service is starting. Return to Pending.",
params.mcp_id
);
return create_response(
false,
CheckMcpStatusResponseStatus::Pending,
Some("服务正在启动中...".to_string()),
);
}
};
if proxy_manager.get_proxy_handler(¶ms.mcp_id).is_some() {
debug!(
"[check_mcp_status] mcp_id={} Double check found that the service already exists, return Ready",
params.mcp_id
);
return create_response(true, CheckMcpStatusResponseStatus::Ready, None);
}
if proxy_manager
.get_mcp_service_status(¶ms.mcp_id)
.is_some()
{
debug!(
"[check_mcp_status] mcp_id={} The service status already exists and may be starting. Return to Pending.",
params.mcp_id
);
return create_response(
false,
CheckMcpStatusResponseStatus::Pending,
Some("服务正在启动中...".to_string()),
);
}
spawn_mcp_service(
¶ms.mcp_id,
params.mcp_json_config,
params.mcp_type,
mcp_protocol.clone(),
)?;
create_response(
false,
CheckMcpStatusResponseStatus::Pending,
Some("服务正在启动中...".to_string()),
)
}
#[instrument]
#[utoipa::path(
post,
path = "/mcp/sse/check_status",
tag = "mcp-sse",
request_body = CheckMcpStatusRequestParams,
responses(
(status = 200, description = "MCP 服务状态:服务不存在时按 mcpJsonConfig 懒启动,未就绪返回 PENDING,启动失败返回 ERROR(message 携带原因)",
body = HttpResult<CheckMcpStatusResponseParams>)
)
)]
pub async fn check_mcp_status_handler_sse(
state: State<AppState>,
uri: Uri,
params: Json<CheckMcpStatusRequestParams>,
) -> Result<HttpResult<CheckMcpStatusResponseParams>, AppError> {
check_mcp_status_handler(state, uri, params, McpProtocol::Sse).await
}
#[instrument]
#[utoipa::path(
post,
path = "/mcp/stream/check_status",
tag = "mcp-stream",
request_body = CheckMcpStatusRequestParams,
responses(
(status = 200, description = "MCP 服务状态:服务不存在时按 mcpJsonConfig 懒启动,未就绪返回 PENDING,启动失败返回 ERROR(message 携带原因)",
body = HttpResult<CheckMcpStatusResponseParams>)
)
)]
pub async fn check_mcp_status_handler_stream(
state: State<AppState>,
uri: Uri,
params: Json<CheckMcpStatusRequestParams>,
) -> Result<HttpResult<CheckMcpStatusResponseParams>, AppError> {
check_mcp_status_handler(state, uri, params, McpProtocol::Stream).await
}
fn spawn_mcp_service(
mcp_id: &str,
mcp_json_config: String,
mcp_type: McpType,
client_protocol: McpProtocol,
) -> Result<(), AppError> {
let mcp_id = mcp_id.to_string();
info!(
"[spawn_mcp_service] mcp_id={} Start starting the service",
mcp_id
);
let proxy_manager = get_proxy_manager();
let mcp_router_path = McpRouterPath::new(mcp_id.clone(), client_protocol.clone())
.map_err(|e| AppError::mcp_server_error(e.to_string()))?;
let mcp_service_status = McpServiceStatus::new(
mcp_id.clone(),
mcp_type.clone(),
mcp_router_path,
CancellationToken::new(),
CheckMcpStatusResponseStatus::Pending,
);
proxy_manager.add_mcp_service_status_and_proxy(mcp_service_status, None);
let mcp_id_clone = mcp_id.clone();
let mcp_config: McpConfig = McpConfig::new(
mcp_id_clone.clone(),
Some(mcp_json_config),
mcp_type,
client_protocol,
);
tokio::spawn(async move {
info!(
"[spawn_mcp_service] mcp_id={} tokio::spawn starts executing mcp_start_task",
mcp_id_clone
);
match mcp_start_task(mcp_config).await {
Ok(_) => {
info!(
"[spawn_mcp_service] mcp_id={} mcp_start_task successful, set status to Ready",
mcp_id_clone
);
get_proxy_manager()
.update_mcp_service_status(&mcp_id_clone, CheckMcpStatusResponseStatus::Ready);
}
Err(e) => {
let error_msg = format!("启动MCP服务失败: {e}");
error!(
"[spawn_mcp_service] mcp_id={} mcp_start_task failed: {}",
mcp_id_clone, e
);
get_proxy_manager().update_mcp_service_status(
&mcp_id_clone,
CheckMcpStatusResponseStatus::Error(error_msg),
);
}
}
});
info!(
"[spawn_mcp_service] mcp_id={} Service startup task has been submitted",
mcp_id
);
Ok(())
}