--- description: Create a new MCP tool for MT5-Quant with full build, install, and test cycle --- # MCP Tool Development Workflow Complete workflow for adding a new MCP tool to MT5-Quant. ## Step 1: Define Tool Schema Add the tool definition to `src/tools/definitions.rs`: 1. Create a function `tool_()` that returns `serde_json::Value` 2. Add the tool to the `get_tools_list()` vector 3. Follow the JSON schema format: - `name`: Tool identifier (snake_case) - `description`: Clear purpose statement - `inputSchema`: JSON Schema with `required` and `properties` **Template:** ```rust fn tool_() -> Value { json!({ "name": "", "description": "", "inputSchema": { "type": "object", "required": [""], "properties": { "": { "type": "string", "description": "..." }, "": { "type": "integer", "description": "..." } } } }) } ``` ## Step 2: Implement Handler Add the handler implementation in `src/tools/handlers.rs`: 1. Add a match arm in `ToolHandler::handle()` dispatch 2. Implement `handle_()` method 3. Return `Result` with proper MCP response format: ```rust async fn handle_(&self, args: &Value) -> Result { // Extract parameters let param = args.get("param").and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing required parameter: param"))?; // Implementation logic here let result = /* ... */; Ok(json!({ "content": [{ "type": "text", "text": json!({ "success": true, "data": result }).to_string() }], "isError": false })) } ``` ## Step 3: Build Release // turbo Run the release build script: ```bash ./scripts/build-release.sh ``` This creates `dist/mt5-quant-{platform}.tar.gz` with the binary and config. ## Step 4: Install Extract and install the new binary: ```bash tar -xzf dist/mt5-quant-*.tar.gz cp mt5-quant-*/mt5-quant ~/.cargo/bin/mt5-quant ``` Or install directly from cargo: ```bash cargo install --path . --force ``` ## Step 5: Test with MCP Request Test the tool using the MCP server. Start the server and send a request: ```bash # Start the MCP server echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | mt5-quant # List tools to verify registration echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | mt5-quant # Call your new tool echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"","arguments":{"":"value"}}}' | mt5-quant ``` **Expected output format:** ```json { "jsonrpc": "2.0", "id": 3, "result": { "content": [{"type": "text", "text": "{\"success\":true,...}"}], "isError": false } } ``` ## Example: Complete Tool Implementation **In `src/tools/definitions.rs`:** ```rust fn tool_healthcheck() -> Value { json!({ "name": "healthcheck", "description": "System health check with OS detection and configuration validation", "inputSchema": { "type": "object", "properties": { "detailed": { "type": "boolean", "description": "Include detailed system info" } } } }) } ``` **Add to `get_tools_list()`:** ```rust let tools = vec![ // ... existing tools tool_healthcheck(), // Add here ]; ``` **In `src/tools/handlers.rs` - add match arm:** ```rust "healthcheck" => self.handle_healthcheck(args).await, ``` **Add handler method with OS detection:** ```rust async fn handle_healthcheck(&self, args: &Value) -> Result { let detailed = args.get("detailed").and_then(|v| v.as_bool()).unwrap_or(false); // OS Detection let platform = std::env::consts::OS; let arch = std::env::consts::ARCH; // Configuration validation let config_path = Config::writable_config_path(); let config_exists = config_path.exists(); let wine_found = self.config.wine_executable.as_ref() .map(|p| Path::new(p).exists()) .unwrap_or(false); let mt5_dir_found = self.config.terminal_dir.as_ref() .map(|p| Path::new(p).is_dir()) .unwrap_or(false); let healthy = config_exists && wine_found && mt5_dir_found; let mut response = json!({ "success": true, "healthy": healthy, "timestamp": chrono::Utc::now().to_rfc3339(), "os": { "platform": platform, "arch": arch, }, "configuration": { "config_exists": config_exists, "wine_found": wine_found, "mt5_dir_found": mt5_dir_found, }, }); if detailed { response["detailed"] = json!({ "exe_path": std::env::current_exe() .map(|p| p.to_string_lossy().to_string()) .unwrap_or_else(|_| "unknown".to_string()), }); } Ok(json!({ "content": [{ "type": "text", "text": response.to_string() }], "isError": false })) } ``` **Test the tool:** ```bash # Basic healthcheck echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"healthcheck","arguments":{}}}' | mt5-quant # Detailed healthcheck with system info echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"healthcheck","arguments":{"detailed":true}}}' | mt5-quant ``` ## Testing Checklist - [ ] Tool appears in `tools/list` output - [ ] Required parameters are validated - [ ] Error responses have `isError: true` - [ ] Success responses have `isError: false` - [ ] Output is valid JSON in the `text` field - [ ] Handler doesn't panic on invalid input