Files
One-KVM/src/web/handlers/mcp.rs
mofeng-git 7ea2936436 feat(mcp): 用 MCP 服务替换内置 AI Agent (#319)
在现有 HTTP 服务挂载 /mcp,让外部 AI 经共享 API Key 调用原分辨率截图、批量键鼠、ATX/WOL 和虚拟介质八个工具。所有调用共用有界 FIFO 和全局限流,复用现有采集管线与硬件控制器。

移除内置模型调用、网页 Agent 面板及旧配置,设置页提供可持续查看的 Key、统一的地址/密钥复制样式。补齐 DEB 的 libudev1 运行依赖。

验证:583 项 Rust 库测试、前端生产构建、ARMv7 设备视觉操作与介质/WOL 实测,以及默认 Release 构建和 DEB 包校验通过。

Refs #319
2026-10-19 12:12:03 +08:00

83 lines
2.3 KiB
Rust

use crate::{
error::{AppError, Result},
state::AppState,
};
use axum::{extract::State, http::header, Json};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Serialize)]
pub struct McpConfigResponse {
pub enabled: bool,
pub requests_per_second: u32,
pub api_key_configured: bool,
pub endpoint: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
pub api_key: Option<String>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub struct McpConfigUpdate {
pub enabled: Option<bool>,
pub requests_per_second: Option<u32>,
#[serde(default)]
pub regenerate_api_key: bool,
}
fn response(state: &AppState) -> McpConfigResponse {
let config = state.config.get();
McpConfigResponse {
enabled: config.mcp.enabled,
requests_per_second: config.mcp.requests_per_second,
api_key_configured: config.mcp.api_key_hash.is_some(),
endpoint: "/mcp",
api_key: config.mcp.api_key.clone(),
}
}
pub async fn mcp_config(
State(state): State<Arc<AppState>>,
) -> (
[(header::HeaderName, &'static str); 1],
Json<McpConfigResponse>,
) {
(
[(header::CACHE_CONTROL, "no-store")],
Json(response(&state)),
)
}
pub async fn mcp_update_config(
State(state): State<Arc<AppState>>,
Json(req): Json<McpConfigUpdate>,
) -> Result<(
[(header::HeaderName, &'static str); 1],
Json<McpConfigResponse>,
)> {
if req
.requests_per_second
.is_some_and(|rate| !(1..=100).contains(&rate))
{
return Err(AppError::BadRequest(
"requests_per_second must be between 1 and 100".into(),
));
}
state
.config
.update(|config| {
if let Some(enabled) = req.enabled {
config.mcp.enabled = enabled;
}
if let Some(rate) = req.requests_per_second {
config.mcp.requests_per_second = rate;
}
if req.regenerate_api_key || (config.mcp.enabled && config.mcp.api_key_hash.is_none()) {
let key = crate::mcp::generate_key();
config.mcp.api_key_hash = Some(crate::mcp::hash_key(&key));
config.mcp.api_key = Some(key);
}
})
.await?;
Ok((
[(header::CACHE_CONTROL, "no-store")],
Json(response(&state)),
))
}