Files
DevFlow/crates/df-mcp/src/protocol.rs
T
lxy a91e950874 新增: MCP HTTP transport + 系统托盘 + 实体解析(会话前基线收尾)
- df-mcp 加 streamable HTTP transport 层(axum 0.7 + tower,server_http.rs),lib.rs pub mod 接线

- src-tauri 加 mcp.rs(spawn_mcp_http + mcp_get_status IPC + mcp-server CLI)+ tray.rs(系统托盘 show_main/setup_tray),lib.rs 集成 + main.rs mcp-server 参数路由

- AI 工具加 entity_resolve.rs(实体解析,tools/mod.rs pub mod 接线)
2026-08-05 22:14:27 +08:00

205 lines
6.3 KiB
Rust

//! JSON-RPC 2.0 over stdio 协议层
//!
//! 职责:
//! - 按行读 stdin,解析 JSON-RPC Request/Notification
//! - 构造 Response / Error 对象并序列化为单行 JSON 写 stdout
//! - MCP 三类方法路由(initialize / tools/list / tools/call)由上层 dispatch
//!
//! 不解析 batch 请求(MCP 客户端实测逐条发,单条足够)。
//! 错误码遵循 JSON-RPC 2.0 + MCP 自定义段(-32xxx 协议层 / 0xffff 工具层)。
use serde::{Deserialize, Serialize};
use serde_json::Value;
// ============================================================
// JSON-RPC 2.0 错误码
// ============================================================
/// 协议层错误(JSON-RPC 2.0 预留段 -32000..-32099)
pub const PARSE_ERROR: i32 = -32700;
pub const INVALID_REQUEST: i32 = -32600;
pub const METHOD_NOT_FOUND: i32 = -32601;
pub const INVALID_PARAMS: i32 = -32602;
pub const INTERNAL_ERROR: i32 = -32603;
// ============================================================
// 请求 / 响应数据结构
// ============================================================
/// JSON-RPC 2.0 请求(请求与通知共用,Notification 时 id=None)
#[derive(Debug, Clone, Deserialize)]
pub struct Request {
pub jsonrpc: Option<String>,
pub id: Option<Value>,
pub method: String,
#[serde(default)]
pub params: Value,
}
/// JSON-RPC 2.0 错误对象
#[derive(Debug, Clone, Serialize)]
pub struct ErrorObject {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
/// JSON-RPC 2.0 响应
#[derive(Debug, Clone, Serialize)]
pub struct Response {
pub jsonrpc: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<ErrorObject>,
}
impl Response {
/// 成功响应
pub fn ok(id: Option<Value>, result: Value) -> Self {
Self {
jsonrpc: "2.0",
id,
result: Some(result),
error: None,
}
}
/// 错误响应
pub fn err(id: Option<Value>, code: i32, message: impl Into<String>, data: Option<Value>) -> Self {
Self {
jsonrpc: "2.0",
id,
result: None,
error: Some(ErrorObject {
code,
message: message.into(),
data,
}),
}
}
}
// ============================================================
// MCP 方法路由 — 暴露给上层 dispatch 的 enum
// ============================================================
/// MCP 协议层方法(已剥离 JSON-RPC 包装)。未知方法返回 `Unknown`,由 dispatch 回 METHOD_NOT_FOUND。
#[derive(Debug, Clone)]
pub enum McpMethod {
/// initialize 握手:返 server info + capabilities
Initialize { params: Value },
/// initialized 通知:客户端确认握手完成,无需响应
Initialized,
/// tools/list:列出所有工具(MCP Tool schema)
ToolsList,
/// tools/call:调用某工具
ToolsCall { name: String, arguments: Value },
/// ping 心跳(MCP 2025 规范),回空 result
Ping,
/// 未识别的方法(回 METHOD_NOT_FOUND)
Unknown(String),
}
impl McpMethod {
/// 从 Request 路由出 MCP 语义方法。
pub fn from_request(req: &Request) -> Self {
match req.method.as_str() {
"initialize" => McpMethod::Initialize { params: req.params.clone() },
"notifications/initialized" => McpMethod::Initialized,
"tools/list" => McpMethod::ToolsList,
"tools/call" => {
let name = req
.params
.get("name")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_owned();
if name.is_empty() {
return McpMethod::Unknown("tools/call missing required 'name' parameter".to_owned());
}
let arguments = req.params.get("arguments").cloned().unwrap_or(Value::Null);
McpMethod::ToolsCall { name, arguments }
}
"ping" => McpMethod::Ping,
other => McpMethod::Unknown(other.to_owned()),
}
}
}
// ============================================================
// MCP 协议数据结构(initialize 握手 / Tool schema / CallToolResult)
// ============================================================
/// MCP initialize 握手响应结构(server info + capabilities)
#[derive(Debug, Clone, Serialize)]
pub struct InitializeResult {
#[serde(rename = "protocolVersion")]
pub protocol_version: &'static str,
pub capabilities: Capabilities,
#[serde(rename = "serverInfo")]
pub server_info: ServerInfo,
}
#[derive(Debug, Clone, Serialize)]
pub struct Capabilities {
pub tools: ToolsCapability,
}
#[derive(Debug, Clone, Serialize)]
pub struct ToolsCapability {
#[serde(rename = "listChanged")]
pub list_changed: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct ServerInfo {
pub name: &'static str,
pub version: &'static str,
}
/// MCP Tool 描述符(tools/list 返回数组元素)
#[derive(Debug, Clone, Serialize)]
pub struct Tool {
pub name: &'static str,
pub description: &'static str,
#[serde(rename = "inputSchema")]
pub input_schema: Value,
}
/// MCP tools/call 成功响应(CallToolResult)
#[derive(Debug, Clone, Serialize)]
pub struct CallToolResult {
pub content: Vec<ContentBlock>,
#[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
pub is_error: Option<bool>,
}
/// MCP 内容块(本 server 仅用 text)
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum ContentBlock {
Text { text: String },
}
impl CallToolResult {
/// 文本成功响应
pub fn text(text: impl Into<String>) -> Self {
Self {
content: vec![ContentBlock::Text { text: text.into() }],
is_error: None,
}
}
/// 文本错误响应(is_error=true 让客户端区分「工具业务错」与「协议错」)
pub fn error(text: impl Into<String>) -> Self {
Self {
content: vec![ContentBlock::Text { text: text.into() }],
is_error: Some(true),
}
}
}