mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-10-28 08:20:49 +08:00
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
This commit is contained in:
82
src/web/handlers/mcp.rs
Normal file
82
src/web/handlers/mcp.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
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)),
|
||||
))
|
||||
}
|
||||
Reference in New Issue
Block a user