mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 21:14:21 +08:00
MCP调用链路增加后端日志记录
This commit is contained in:
@@ -140,6 +140,18 @@ fn check_admin_permission(state: &Arc<RwLock<AppState>>) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mcp_log_info(app_state: &Arc<RwLock<AppState>>, message: &str, meta: Value) {
|
||||
let state_guard = app_state.read();
|
||||
let logger = state_guard.logger.read();
|
||||
logger.info_with_meta(message, meta);
|
||||
}
|
||||
|
||||
fn mcp_log_error(app_state: &Arc<RwLock<AppState>>, message: &str, meta: Value) {
|
||||
let state_guard = app_state.read();
|
||||
let logger = state_guard.logger.read();
|
||||
logger.error_with_meta(message, meta);
|
||||
}
|
||||
|
||||
fn jsonrpc_ok(id: Value, result: Value) -> Value {
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
@@ -211,11 +223,25 @@ async fn handle_mcp(
|
||||
let req = match serde_json::from_value::<McpRequest>(payload) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
mcp_log_error(
|
||||
&app_state,
|
||||
"mcp:request_parse_failed",
|
||||
json!({ "error": e.to_string() }),
|
||||
);
|
||||
let body = jsonrpc_error(Value::Null, -32700, &format!("Parse error: {}", e));
|
||||
return (StatusCode::OK, Json(body)).into_response();
|
||||
}
|
||||
};
|
||||
|
||||
mcp_log_info(
|
||||
&app_state,
|
||||
"mcp:request_received",
|
||||
json!({
|
||||
"method": req.method.clone(),
|
||||
"has_id": req.id.is_some()
|
||||
}),
|
||||
);
|
||||
|
||||
let Some(id) = req.id.clone() else {
|
||||
return StatusCode::NO_CONTENT.into_response();
|
||||
};
|
||||
@@ -229,7 +255,14 @@ async fn handle_mcp(
|
||||
.and_then(|p| serde_json::from_value::<ToolCallParams>(p).ok());
|
||||
|
||||
match params {
|
||||
None => jsonrpc_error(id, -32602, "Invalid tools/call params"),
|
||||
None => {
|
||||
mcp_log_error(
|
||||
&app_state,
|
||||
"mcp:tools_call_invalid_params",
|
||||
json!({ "reason": "invalid_tools_call_params" }),
|
||||
);
|
||||
jsonrpc_error(id, -32602, "Invalid tools/call params")
|
||||
}
|
||||
Some(params) => match params.name.as_str() {
|
||||
"add_score" => match serde_json::from_value::<AddScoreArgs>(params.arguments) {
|
||||
Ok(args) => match mcp_add_score(&app_state, args).await {
|
||||
@@ -249,7 +282,16 @@ async fn handle_mcp(
|
||||
"structuredContent": payload
|
||||
}),
|
||||
),
|
||||
Err(e) => jsonrpc_ok(
|
||||
Err(e) => {
|
||||
mcp_log_error(
|
||||
&app_state,
|
||||
"mcp:tool_call_failed",
|
||||
json!({
|
||||
"tool": "add_score",
|
||||
"error": e
|
||||
}),
|
||||
);
|
||||
jsonrpc_ok(
|
||||
id,
|
||||
json!({
|
||||
"content": [
|
||||
@@ -260,9 +302,20 @@ async fn handle_mcp(
|
||||
],
|
||||
"isError": true
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
Err(e) => jsonrpc_error(id, -32602, &format!("Invalid arguments: {}", e)),
|
||||
Err(e) => {
|
||||
mcp_log_error(
|
||||
&app_state,
|
||||
"mcp:tool_call_invalid_arguments",
|
||||
json!({
|
||||
"tool": "add_score",
|
||||
"error": e.to_string()
|
||||
}),
|
||||
);
|
||||
jsonrpc_error(id, -32602, &format!("Invalid arguments: {}", e))
|
||||
}
|
||||
},
|
||||
"list_students" => {
|
||||
match serde_json::from_value::<ListStudentsArgs>(params.arguments) {
|
||||
@@ -280,7 +333,16 @@ async fn handle_mcp(
|
||||
"structuredContent": payload
|
||||
}),
|
||||
),
|
||||
Err(e) => jsonrpc_ok(
|
||||
Err(e) => {
|
||||
mcp_log_error(
|
||||
&app_state,
|
||||
"mcp:tool_call_failed",
|
||||
json!({
|
||||
"tool": "list_students",
|
||||
"error": e
|
||||
}),
|
||||
);
|
||||
jsonrpc_ok(
|
||||
id,
|
||||
json!({
|
||||
"content": [
|
||||
@@ -291,14 +353,32 @@ async fn handle_mcp(
|
||||
],
|
||||
"isError": true
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
mcp_log_error(
|
||||
&app_state,
|
||||
"mcp:tool_call_invalid_arguments",
|
||||
json!({
|
||||
"tool": "list_students",
|
||||
"error": e.to_string()
|
||||
}),
|
||||
);
|
||||
jsonrpc_error(id, -32602, &format!("Invalid arguments: {}", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => jsonrpc_error(id, -32601, &format!("Unknown tool: {}", params.name)),
|
||||
_ => {
|
||||
mcp_log_error(
|
||||
&app_state,
|
||||
"mcp:tool_call_unknown_tool",
|
||||
json!({
|
||||
"tool": params.name
|
||||
}),
|
||||
);
|
||||
jsonrpc_error(id, -32601, &format!("Unknown tool: {}", params.name))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -313,6 +393,16 @@ async fn mcp_add_score(
|
||||
app_state: &Arc<RwLock<AppState>>,
|
||||
args: AddScoreArgs,
|
||||
) -> Result<AddScoreResult, String> {
|
||||
mcp_log_info(
|
||||
app_state,
|
||||
"mcp:tool_call_started",
|
||||
json!({
|
||||
"tool": "add_score",
|
||||
"student_name": args.student_name.clone(),
|
||||
"delta": args.delta
|
||||
}),
|
||||
);
|
||||
|
||||
let student_name = args.student_name.trim();
|
||||
if student_name.is_empty() {
|
||||
return Err("student_name 不能为空".to_string());
|
||||
@@ -376,6 +466,18 @@ async fn mcp_add_score(
|
||||
|
||||
realtime_dual_write_sync(app_state).await?;
|
||||
|
||||
mcp_log_info(
|
||||
app_state,
|
||||
"mcp:tool_call_succeeded",
|
||||
json!({
|
||||
"tool": "add_score",
|
||||
"student_name": student_name,
|
||||
"delta": args.delta,
|
||||
"event_id": inserted.id,
|
||||
"event_uuid": event_uuid
|
||||
}),
|
||||
);
|
||||
|
||||
Ok(AddScoreResult {
|
||||
event_id: inserted.id,
|
||||
event_uuid,
|
||||
@@ -392,6 +494,15 @@ async fn mcp_list_students(
|
||||
app_state: &Arc<RwLock<AppState>>,
|
||||
args: ListStudentsArgs,
|
||||
) -> Result<ListStudentsResult, String> {
|
||||
mcp_log_info(
|
||||
app_state,
|
||||
"mcp:tool_call_started",
|
||||
json!({
|
||||
"tool": "list_students",
|
||||
"limit": args.limit
|
||||
}),
|
||||
);
|
||||
|
||||
let db_conn = {
|
||||
let state_guard = app_state.read();
|
||||
let db_guard = state_guard.db.read();
|
||||
@@ -419,6 +530,15 @@ async fn mcp_list_students(
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
mcp_log_info(
|
||||
app_state,
|
||||
"mcp:tool_call_succeeded",
|
||||
json!({
|
||||
"tool": "list_students",
|
||||
"total": students.len()
|
||||
}),
|
||||
);
|
||||
|
||||
Ok(ListStudentsResult {
|
||||
total: students.len(),
|
||||
students,
|
||||
@@ -431,9 +551,23 @@ pub async fn mcp_server_start(
|
||||
state: State<'_, Arc<RwLock<AppState>>>,
|
||||
) -> Result<IpcResponse<McpServerStartResult>, String> {
|
||||
check_admin_permission(&state)?;
|
||||
mcp_log_info(
|
||||
state.inner(),
|
||||
"mcp:server_start_requested",
|
||||
json!({
|
||||
"config": config.clone()
|
||||
}),
|
||||
);
|
||||
|
||||
let mut server_state = MCP_SERVER_STATE.lock().await;
|
||||
if server_state.is_running {
|
||||
mcp_log_info(
|
||||
state.inner(),
|
||||
"mcp:server_already_running",
|
||||
json!({
|
||||
"url": server_state.url.clone()
|
||||
}),
|
||||
);
|
||||
return Ok(IpcResponse::failure_with_type(
|
||||
"MCP server is already running",
|
||||
));
|
||||
@@ -482,6 +616,16 @@ pub async fn mcp_server_start(
|
||||
server_state.url = Some(url.clone());
|
||||
server_state.shutdown_tx = Some(shutdown_tx);
|
||||
|
||||
mcp_log_info(
|
||||
state.inner(),
|
||||
"mcp:server_started",
|
||||
json!({
|
||||
"url": url.clone(),
|
||||
"host": server_state.config.host.clone(),
|
||||
"port": server_state.config.port
|
||||
}),
|
||||
);
|
||||
|
||||
Ok(IpcResponse::success(McpServerStartResult {
|
||||
url,
|
||||
config: server_state.config.clone(),
|
||||
@@ -493,10 +637,12 @@ pub async fn mcp_server_stop(
|
||||
state: State<'_, Arc<RwLock<AppState>>>,
|
||||
) -> Result<IpcResponse<()>, String> {
|
||||
check_admin_permission(&state)?;
|
||||
mcp_log_info(state.inner(), "mcp:server_stop_requested", json!({}));
|
||||
|
||||
let mut server_state = MCP_SERVER_STATE.lock().await;
|
||||
|
||||
if !server_state.is_running {
|
||||
mcp_log_info(state.inner(), "mcp:server_already_stopped", json!({}));
|
||||
return Ok(IpcResponse::success_empty());
|
||||
}
|
||||
|
||||
@@ -507,14 +653,24 @@ pub async fn mcp_server_stop(
|
||||
server_state.is_running = false;
|
||||
server_state.url = None;
|
||||
|
||||
mcp_log_info(state.inner(), "mcp:server_stopped", json!({}));
|
||||
|
||||
Ok(IpcResponse::success_empty())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mcp_server_status(
|
||||
_state: State<'_, Arc<RwLock<AppState>>>,
|
||||
state: State<'_, Arc<RwLock<AppState>>>,
|
||||
) -> Result<IpcResponse<McpServerStatus>, String> {
|
||||
let server_state = MCP_SERVER_STATE.lock().await;
|
||||
mcp_log_info(
|
||||
state.inner(),
|
||||
"mcp:server_status_requested",
|
||||
json!({
|
||||
"is_running": server_state.is_running,
|
||||
"url": server_state.url.clone()
|
||||
}),
|
||||
);
|
||||
|
||||
Ok(IpcResponse::success(McpServerStatus {
|
||||
is_running: server_state.is_running,
|
||||
|
||||
Reference in New Issue
Block a user