From 803537dedaac980fc85ec17beb3c578bd0c36a2d Mon Sep 17 00:00:00 2001 From: JSR Date: Sat, 21 Mar 2026 10:53:23 +0800 Subject: [PATCH] =?UTF-8?q?MCP=E6=96=B0=E5=A2=9E=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=AD=A6=E7=94=9F=E5=88=97=E8=A1=A8=E5=B7=A5=E5=85=B7=E5=B9=B6?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=BD=BF=E7=94=A8=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MCP-使用说明.md | 23 +++++ src-tauri/src/commands/mcp.rs | 175 +++++++++++++++++++++++++++------- 2 files changed, 163 insertions(+), 35 deletions(-) diff --git a/MCP-使用说明.md b/MCP-使用说明.md index 61dfc7c..5e4bfb9 100644 --- a/MCP-使用说明.md +++ b/MCP-使用说明.md @@ -18,6 +18,7 @@ 当前已实现 MCP 工具: - `add_score`:给指定学生加分或扣分,并写入 `score_events` 记录。 +- `list_students`:获取学生列表(姓名、积分、奖励积分、标签)。 ## 2. 启动与停止 MCP 服务 @@ -99,6 +100,28 @@ curl -X POST http://127.0.0.1:3901/mcp \ - `delta`(integer,必填):分值变化,正数加分,负数扣分。 - `reason_content`(string,可选):原因,默认 `MCP 加分`。 +### 3.4 tools/call(调用 list_students) + +```bash +curl -X POST http://127.0.0.1:3901/mcp \ + -H 'Content-Type: application/json' \ + -d '{ + "jsonrpc": "2.0", + "id": 4, + "method": "tools/call", + "params": { + "name": "list_students", + "arguments": { + "limit": 50 + } + } + }' +``` + +`list_students` 参数: + +- `limit`(integer,可选):最多返回多少条,默认返回全部学生。 + ## 4. 返回结果说明 调用 `add_score` 成功时: diff --git a/src-tauri/src/commands/mcp.rs b/src-tauri/src/commands/mcp.rs index 5fc86ec..f08d968 100644 --- a/src-tauri/src/commands/mcp.rs +++ b/src-tauri/src/commands/mcp.rs @@ -6,7 +6,10 @@ use axum::{ Json, Router, }; use parking_lot::RwLock; -use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set, TransactionTrait}; +use sea_orm::{ + ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, Set, + TransactionTrait, +}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::net::SocketAddr; @@ -92,6 +95,12 @@ struct AddScoreArgs { reason_content: Option, } +#[derive(Debug, Deserialize, Default)] +struct ListStudentsArgs { + #[serde(default)] + limit: Option, +} + #[derive(Debug, Serialize)] struct AddScoreResult { event_id: i32, @@ -104,6 +113,21 @@ struct AddScoreResult { event_time: String, } +#[derive(Debug, Serialize)] +struct StudentListItem { + id: i32, + name: String, + score: i32, + reward_points: i32, + tags: Vec, +} + +#[derive(Debug, Serialize)] +struct ListStudentsResult { + total: usize, + students: Vec, +} + static MCP_SERVER_STATE: once_cell::sync::Lazy>> = once_cell::sync::Lazy::new(|| Arc::new(Mutex::new(McpServerState::default()))); @@ -165,6 +189,16 @@ fn tools_list_result() -> Value { }, "required": ["student_name", "delta"] } + }, + { + "name": "list_students", + "description": "获取学生列表,包含姓名、积分、奖励积分和标签。", + "inputSchema": { + "type": "object", + "properties": { + "limit": {"type": "integer", "description": "最多返回多少条,默认全部"} + } + } } ] }) @@ -196,41 +230,75 @@ async fn handle_mcp( match params { None => jsonrpc_error(id, -32602, "Invalid tools/call params"), - Some(params) if params.name != "add_score" => { - jsonrpc_error(id, -32601, &format!("Unknown tool: {}", params.name)) - } - Some(params) => match serde_json::from_value::(params.arguments) { - Ok(args) => match mcp_add_score(&app_state, args).await { - Ok(payload) => jsonrpc_ok( - id, - json!({ - "content": [ - { - "type": "text", - "text": format!( - "已记录:{} {:+} 分({} -> {})", - payload.student_name, payload.delta, payload.val_prev, payload.val_curr - ) - } - ], - "isError": false, - "structuredContent": payload - }), - ), - Err(e) => jsonrpc_ok( - id, - json!({ - "content": [ - { - "type": "text", - "text": format!("加分失败: {}", e) - } - ], - "isError": true - }), - ), + Some(params) => match params.name.as_str() { + "add_score" => match serde_json::from_value::(params.arguments) { + Ok(args) => match mcp_add_score(&app_state, args).await { + Ok(payload) => jsonrpc_ok( + id, + json!({ + "content": [ + { + "type": "text", + "text": format!( + "已记录:{} {:+} 分({} -> {})", + payload.student_name, payload.delta, payload.val_prev, payload.val_curr + ) + } + ], + "isError": false, + "structuredContent": payload + }), + ), + Err(e) => jsonrpc_ok( + id, + json!({ + "content": [ + { + "type": "text", + "text": format!("加分失败: {}", e) + } + ], + "isError": true + }), + ), + }, + Err(e) => jsonrpc_error(id, -32602, &format!("Invalid arguments: {}", e)), }, - Err(e) => jsonrpc_error(id, -32602, &format!("Invalid arguments: {}", e)), + "list_students" => { + match serde_json::from_value::(params.arguments) { + Ok(args) => match mcp_list_students(&app_state, args).await { + Ok(payload) => jsonrpc_ok( + id, + json!({ + "content": [ + { + "type": "text", + "text": format!("已获取 {} 名学生", payload.total) + } + ], + "isError": false, + "structuredContent": payload + }), + ), + Err(e) => jsonrpc_ok( + id, + json!({ + "content": [ + { + "type": "text", + "text": format!("获取学生列表失败: {}", e) + } + ], + "isError": true + }), + ), + }, + Err(e) => { + jsonrpc_error(id, -32602, &format!("Invalid arguments: {}", e)) + } + } + } + _ => jsonrpc_error(id, -32601, &format!("Unknown tool: {}", params.name)), }, } } @@ -320,6 +388,43 @@ async fn mcp_add_score( }) } +async fn mcp_list_students( + app_state: &Arc>, + args: ListStudentsArgs, +) -> Result { + let db_conn = { + let state_guard = app_state.read(); + let db_guard = state_guard.db.read(); + db_guard.clone() + } + .ok_or_else(|| "Database not connected".to_string())?; + + let limit = args.limit.unwrap_or(u64::MAX); + + let rows = students::Entity::find() + .order_by_asc(students::Column::Name) + .limit(limit) + .all(&db_conn) + .await + .map_err(|e| e.to_string())?; + + let students = rows + .into_iter() + .map(|row| StudentListItem { + id: row.id, + name: row.name, + score: row.score, + reward_points: row.reward_points, + tags: serde_json::from_str::>(&row.tags).unwrap_or_default(), + }) + .collect::>(); + + Ok(ListStudentsResult { + total: students.len(), + students, + }) +} + #[tauri::command] pub async fn mcp_server_start( config: Option,