Files
mt5-quant/src/mcp_server.rs
T
Devid HW a3b046c68f feat: complete Rust migration with modular architecture
Major changes:
- Migrate Python/shell scripts to Rust modules:
  - analytics/extract.py → src/analytics/extract.rs (ReportExtractor)
  - analytics/analyze.py → src/analytics/analyze.rs (DealAnalyzer)
  - scripts/mqlcompile.sh → src/compile/mql_compiler.rs (MqlCompiler)
  - scripts/backtest_pipeline.sh → src/pipeline/backtest.rs (BacktestPipeline)

- New modular structure:
  - src/models/ - Config, Deal, Metrics, Report structs
  - src/analytics/ - Report parsing and deal analysis
  - src/compile/ - MQL5 compilation via Wine
  - src/pipeline/ - 5-stage backtest orchestration
  - src/tools/ - 27 MCP tool definitions and handlers

- Remove PyInstaller setup (now pure Rust)
- Remove migrated shell scripts (backtest_pipeline.sh, mqlcompile.sh)
- Add GitHub Actions CI/CD for macOS & Linux releases
- Update all documentation for Rust architecture

Binary size: 4.3MB (no Python dependencies)
Tools: 27 MCP tools fully functional
2026-04-18 15:07:08 +07:00

144 lines
5.3 KiB
Rust

use serde_json::{json, Value};
use std::sync::Arc;
use tokio::sync::Mutex;
use crate::{models::Config as ModelsConfig, tools::ToolHandler, McpError, McpRequest, McpResponse};
#[derive(Debug)]
pub struct McpServer {
initialized: Arc<Mutex<bool>>,
tool_handler: Arc<ToolHandler>,
}
impl McpServer {
pub fn new() -> Self {
let config = ModelsConfig::load().unwrap_or_default();
Self {
initialized: Arc::new(Mutex::new(false)),
tool_handler: Arc::new(ToolHandler::new(config)),
}
}
pub async fn handle_request(&self, request: McpRequest) -> McpResponse {
match request.method.as_str() {
"initialize" => {
*self.initialized.lock().await = true;
McpResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(json!(crate::InitializeResult {
protocol_version: "2024-11-05".to_string(),
capabilities: crate::ServerCapabilities {
experimental: json!({}),
tools: crate::ToolCapabilities {
list_changed: false,
},
},
server_info: crate::ServerInfo {
name: "MT5-Quant".to_string(),
version: "1.27.0".to_string(),
},
})),
error: None,
}
}
"tools/list" => {
let initialized = *self.initialized.lock().await;
if !initialized {
return McpResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(McpError {
code: -32600,
message: "Received request before initialization was complete".to_string(),
data: None,
}),
};
}
McpResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(crate::tools::get_tools_list()),
error: None,
}
}
"tools/call" => {
let initialized = *self.initialized.lock().await;
if !initialized {
return McpResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(McpError {
code: -32600,
message: "Received request before initialization was complete".to_string(),
data: None,
}),
};
}
if let Some(params) = request.params {
if let (Some(tool_name), Some(arguments)) = (
params.get("name").and_then(|v| v.as_str()),
params.get("arguments")
) {
let result = self.handle_tool_call(tool_name, arguments).await;
McpResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(result),
error: None,
}
} else {
McpResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(McpError {
code: -32602,
message: "Invalid request parameters".to_string(),
data: None,
}),
}
}
} else {
McpResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(McpError {
code: -32602,
message: "Invalid request parameters".to_string(),
data: None,
}),
}
}
}
_ => {
McpResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(McpError {
code: -32601,
message: format!("Method not found: {}", request.method),
data: None,
}),
}
}
}
}
async fn handle_tool_call(&self, tool_name: &str, arguments: &Value) -> Value {
self.tool_handler.handle(tool_name, arguments).await.unwrap_or_else(|e| json!({
"content": [{
"type": "text",
"text": format!("Tool execution failed: {}", e)
}],
"isError": true
}))
}
}