use crate::mcp_pmcp::tool_functions;
use async_trait::async_trait;
use pmcp::{Error, RequestHandlerExtra, Result, ToolHandler};
use serde::Deserialize;
use serde_json::Value;
use std::path::PathBuf;
use tracing::debug;
pub use self::{
ChurnTool as AnalyzeDeepContextTool, ComplexityTool as AnalyzeComplexityTool,
CouplingTool as AnalyzeBigOTool, DeadCodeTool as AnalyzeDeadCodeTool,
LintHotspotTool as AnalyzeDagTool, SatdTool as AnalyzeSatdTool,
TdgCompareTool as AnalyzeTdgCompareTool, TdgTool as AnalyzeTdgTool,
};
#[derive(Debug, Deserialize)]
struct ComplexityArgs {
paths: Vec<String>,
#[serde(default)]
top_files: Option<usize>,
#[serde(default)]
threshold: Option<u64>,
}
pub struct ComplexityTool;
impl ComplexityTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for ComplexityTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for ComplexityTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling analyze.complexity with args: {}", args);
let params: ComplexityArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let paths: Vec<PathBuf> = params.paths.into_iter().map(PathBuf::from).collect();
let results =
tool_functions::analyze_complexity(&paths, params.top_files, params.threshold)
.await
.map_err(|e| Error::internal(format!("Complexity analysis failed: {e}")))?;
Ok(results)
}
}
#[derive(Debug, Deserialize)]
struct SatdArgs {
paths: Vec<String>,
#[serde(default)]
include_resolved: bool,
}
pub struct SatdTool;
impl SatdTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for SatdTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for SatdTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling analyze.satd with args: {}", args);
let params: SatdArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let paths: Vec<PathBuf> = params.paths.into_iter().map(PathBuf::from).collect();
let results = tool_functions::analyze_satd(&paths, params.include_resolved)
.await
.map_err(|e| Error::internal(format!("SATD analysis failed: {e}")))?;
Ok(results)
}
}
#[derive(Debug, Deserialize)]
struct DeadCodeArgs {
paths: Vec<String>,
#[serde(default)]
include_tests: bool,
}
pub struct DeadCodeTool;
impl DeadCodeTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for DeadCodeTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for DeadCodeTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling analyze.dead-code with args: {}", args);
let params: DeadCodeArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let paths: Vec<PathBuf> = params.paths.into_iter().map(PathBuf::from).collect();
let results = tool_functions::analyze_dead_code(&paths, params.include_tests)
.await
.map_err(|e| Error::internal(format!("Dead code analysis failed: {e}")))?;
Ok(results)
}
}
#[derive(Debug, Deserialize)]
struct LintHotspotArgs {
paths: Vec<String>,
#[serde(default)]
top_files: Option<usize>,
}
pub struct LintHotspotTool;
impl LintHotspotTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for LintHotspotTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for LintHotspotTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling analyze.lint-hotspot with args: {}", args);
let params: LintHotspotArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let paths: Vec<PathBuf> = params.paths.into_iter().map(PathBuf::from).collect();
let results = tool_functions::analyze_lint_hotspots(&paths, params.top_files)
.await
.map_err(|e| Error::internal(format!("Lint hotspot analysis failed: {e}")))?;
Ok(results)
}
}
#[derive(Debug, Deserialize)]
struct ChurnArgs {
paths: Vec<String>,
#[serde(default)]
days: Option<u32>,
#[serde(default)]
top_files: Option<usize>,
}
pub struct ChurnTool;
impl ChurnTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for ChurnTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for ChurnTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling analyze.churn with args: {}", args);
let params: ChurnArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let paths: Vec<PathBuf> = params.paths.into_iter().map(PathBuf::from).collect();
let results = tool_functions::analyze_churn(&paths, params.days, params.top_files)
.await
.map_err(|e| Error::internal(format!("Churn analysis failed: {e}")))?;
Ok(results)
}
}
#[derive(Debug, Deserialize)]
struct CouplingArgs {
paths: Vec<String>,
#[serde(default)]
threshold: Option<f64>,
}
pub struct CouplingTool;
impl CouplingTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for CouplingTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for CouplingTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling analyze.coupling with args: {}", args);
let params: CouplingArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let paths: Vec<PathBuf> = params.paths.into_iter().map(PathBuf::from).collect();
let results = tool_functions::analyze_coupling(&paths, params.threshold)
.await
.map_err(|e| Error::internal(format!("Coupling analysis failed: {e}")))?;
Ok(results)
}
}
#[derive(Debug, Deserialize)]
struct TdgArgs {
paths: Vec<String>,
#[serde(default)]
threshold: Option<f64>,
#[serde(default)]
top_files: Option<usize>,
#[serde(default)]
include_components: Option<bool>,
}
pub struct TdgTool;
impl TdgTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for TdgTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for TdgTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling analyze.tdg with args: {}", args);
let params: TdgArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let paths: Vec<PathBuf> = params.paths.into_iter().map(PathBuf::from).collect();
let results = tool_functions::analyze_tdg(
&paths,
params.threshold,
params.top_files,
params.include_components,
)
.await
.map_err(|e| Error::internal(format!("TDG analysis failed: {e}")))?;
Ok(results)
}
}
#[derive(Debug, Deserialize)]
struct TdgCompareArgs {
path1: String,
path2: String,
}
pub struct TdgCompareTool;
impl TdgCompareTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for TdgCompareTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for TdgCompareTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling analyze.tdg_compare with args: {}", args);
let params: TdgCompareArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let path1 = PathBuf::from(params.path1);
let path2 = PathBuf::from(params.path2);
let results = tool_functions::compare_tdg(&path1, &path2)
.await
.map_err(|e| Error::internal(format!("TDG comparison failed: {e}")))?;
Ok(results)
}
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}