#![cfg_attr(coverage_nightly, coverage(off))]
use crate::agents::registry::AgentRegistry;
use crate::mcp_integration::{error_codes, McpError, McpTool, ToolMetadata};
use async_trait::async_trait;
use serde_json::{json, Value};
use std::sync::Arc;
pub struct OrchestrateTool {
pub(super) registry: Arc<AgentRegistry>,
pub(super) executor: Arc<dyn crate::workflow::WorkflowExecutor>,
}
impl OrchestrateTool {
pub fn new(registry: Arc<AgentRegistry>) -> Self {
let executor = Arc::new(crate::workflow::executor::DefaultWorkflowExecutor::new(
registry.clone(),
));
Self { registry, executor }
}
pub fn new_with_executor(
registry: Arc<AgentRegistry>,
executor: Arc<dyn crate::workflow::WorkflowExecutor>,
) -> Self {
Self { registry, executor }
}
}
#[async_trait]
impl McpTool for OrchestrateTool {
fn metadata(&self) -> ToolMetadata {
ToolMetadata {
name: "orchestrate".to_string(),
description: "Orchestrate complex multi-step workflows".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"workflow": {
"type": "object",
"properties": {
"name": {"type": "string"},
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"action": {"type": "string"},
"params": {"type": "object"}
}
}
}
},
"required": ["name", "steps"]
},
"input": {
"type": "object",
"description": "Initial input data"
}
},
"required": ["workflow"]
}),
}
}
async fn execute(&self, params: Value) -> Result<Value, McpError> {
use crate::workflow::{Workflow, WorkflowContext, WorkflowState};
use parking_lot::RwLock;
use std::time::Instant;
use uuid::Uuid;
let workflow_def = params["workflow"].as_object().ok_or_else(|| McpError {
code: error_codes::INVALID_PARAMS,
message: "Missing workflow parameter".to_string(),
data: None,
})?;
let input = params["input"].clone();
let workflow: Workflow = serde_json::from_value(json!({
"id": Uuid::new_v4().to_string(),
"name": workflow_def.get("name")
.and_then(|v| v.as_str())
.unwrap_or("mcp_workflow"),
"description": workflow_def.get("description")
.and_then(|v| v.as_str()),
"version": "1.0.0",
"steps": workflow_def.get("steps")
.ok_or_else(|| McpError {
code: error_codes::INVALID_PARAMS,
message: "Missing steps in workflow".to_string(),
data: None,
})?,
"error_strategy": "fail_fast",
"timeout": null,
"metadata": {}
}))
.map_err(|e| McpError {
code: error_codes::INVALID_PARAMS,
message: format!("Invalid workflow definition: {}", e),
data: None,
})?;
let context = WorkflowContext {
workflow_id: workflow.id,
execution_id: Uuid::new_v4(),
variables: Arc::new(RwLock::new(
input
.as_object()
.map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
.unwrap_or_default(),
)),
step_results: Arc::new(RwLock::new(std::collections::HashMap::new())),
state: Arc::new(RwLock::new(WorkflowState::Running)),
started_at: Instant::now(),
agent_registry: self.registry.clone(),
};
let result = self
.executor
.execute(&workflow, &context)
.await
.map_err(|e| McpError {
code: error_codes::INTERNAL_ERROR,
message: format!("Workflow execution failed: {}", e),
data: None,
})?;
Ok(json!({
"type": "text",
"text": format!(
"Workflow Execution Results:\n\nWorkflow: {}\nExecution ID: {}\n\nResult:\n{}\n",
workflow.name,
context.execution_id,
serde_json::to_string_pretty(&result).unwrap_or_else(|_| "{}".to_string())
)
}))
}
}
pub struct QualityGateTool {
pub(super) _registry: Arc<AgentRegistry>,
}
impl QualityGateTool {
pub fn new(registry: Arc<AgentRegistry>) -> Self {
Self {
_registry: registry,
}
}
}
#[async_trait]
impl McpTool for QualityGateTool {
fn metadata(&self) -> ToolMetadata {
ToolMetadata {
name: "quality_gate".to_string(),
description: "Run quality gate checks with zero tolerance".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Source code to check"
},
"language": {
"type": "string",
"description": "Programming language"
},
"gates": {
"type": "array",
"items": {
"type": "string",
"enum": ["complexity", "satd", "efficiency", "entropy"]
},
"description": "Quality gates to run"
}
},
"required": ["code", "language"]
}),
}
}
async fn execute(&self, params: Value) -> Result<Value, McpError> {
let code = params["code"].as_str().ok_or_else(|| McpError {
code: error_codes::INVALID_PARAMS,
message: "Missing code parameter".to_string(),
data: None,
})?;
let language = params["language"].as_str().ok_or_else(|| McpError {
code: error_codes::INVALID_PARAMS,
message: "Missing language parameter".to_string(),
data: None,
})?;
let gates = params["gates"]
.as_array()
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect::<Vec<_>>()
})
.unwrap_or_else(|| vec!["complexity".to_string(), "satd".to_string()]);
use crate::quality::complexity::ComplexityAnalyzer;
use crate::quality::satd::SatdDetector;
let mut results = json!({});
for gate in gates {
match gate.as_str() {
"complexity" => {
let analyzer = ComplexityAnalyzer::new();
let complexity = if language.to_lowercase() == "rust" {
analyzer.analyze_string(code).unwrap_or_default()
} else {
Default::default()
};
results["complexity"] = json!(complexity);
}
"satd" => {
let detector = SatdDetector::new();
let satd = detector.detect(code);
results["satd"] = json!(satd);
}
_ => {}
}
}
Ok(json!({
"type": "text",
"text": serde_json::to_string_pretty(&results).expect("internal error")
}))
}
}