MCP新增获取学生列表工具并补充使用文档

This commit is contained in:
JSR
2026-03-21 10:53:23 +08:00
parent bd0fa2375d
commit 803537deda
2 changed files with 163 additions and 35 deletions
+23
View File
@@ -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` 成功时:
+140 -35
View File
@@ -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<String>,
}
#[derive(Debug, Deserialize, Default)]
struct ListStudentsArgs {
#[serde(default)]
limit: Option<u64>,
}
#[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<String>,
}
#[derive(Debug, Serialize)]
struct ListStudentsResult {
total: usize,
students: Vec<StudentListItem>,
}
static MCP_SERVER_STATE: once_cell::sync::Lazy<Arc<Mutex<McpServerState>>> =
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::<AddScoreArgs>(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::<AddScoreArgs>(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::<ListStudentsArgs>(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<RwLock<AppState>>,
args: ListStudentsArgs,
) -> Result<ListStudentsResult, String> {
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::<Vec<String>>(&row.tags).unwrap_or_default(),
})
.collect::<Vec<_>>();
Ok(ListStudentsResult {
total: students.len(),
students,
})
}
#[tauri::command]
pub async fn mcp_server_start(
config: Option<McpServerConfig>,