a3b046c68f
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
144 lines
5.3 KiB
Rust
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
|
|
}))
|
|
}
|
|
}
|