use crate::mcp_pmcp::tool_functions;
use async_trait::async_trait;
use pmcp::{Error, RequestHandlerExtra, Result, ToolHandler};
use serde::Deserialize;
use serde_json::{json, Value};
use std::path::PathBuf;
use tracing::debug;
pub use self::{
ContextGenerateTool as GenerateContextTool, ContextSummaryTool as ScaffoldProjectTool,
GitStatusTool as GitTool,
};
#[derive(Debug, Deserialize)]
struct GitCloneArgs {
url: String,
#[serde(default)]
target_dir: Option<String>,
#[serde(default)]
branch: Option<String>,
#[serde(default)]
depth: Option<u32>,
}
pub struct GitCloneTool;
impl GitCloneTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for GitCloneTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for GitCloneTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling git.clone with args: {}", args);
let params: GitCloneArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let target_dir = params.target_dir.map(PathBuf::from);
let result = tool_functions::git_clone(
¶ms.url,
target_dir.as_deref(),
params.branch.as_deref(),
params.depth,
)
.await
.map_err(|e| Error::internal(format!("Git clone failed: {e}")))?;
Ok(json!({
"status": "success",
"path": result.display().to_string(),
"message": format!("Successfully cloned repository to {}", result.display())
}))
}
}
#[derive(Debug, Deserialize)]
struct GitStatusArgs {
path: String,
}
pub struct GitStatusTool;
impl GitStatusTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for GitStatusTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for GitStatusTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling git.status with args: {}", args);
let params: GitStatusArgs = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
let path = PathBuf::from(params.path);
let status = tool_functions::git_status(path.as_ref())
.await
.map_err(|e| Error::internal(format!("Failed to get git status: {e}")))?;
Ok(status)
}
}
#[derive(Debug, Deserialize)]
struct ContextGenerateArgs {
paths: Vec<String>,
#[serde(default)]
format: Option<String>,
#[serde(default)]
max_depth: Option<usize>,
#[serde(default)]
include_dependencies: bool,
}
pub struct ContextGenerateTool;
impl ContextGenerateTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for ContextGenerateTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for ContextGenerateTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling context.generate with args: {}", args);
let params: ContextGenerateArgs = 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 context =
tool_functions::generate_context(&paths, params.max_depth, params.include_dependencies)
.await
.map_err(|e| Error::internal(format!("Context generation failed: {e}")))?;
match params.format.as_deref() {
Some("markdown") => Ok(json!({
"context": context,
"markdown": "Context in markdown format (not implemented)"
})),
Some("xml") => Ok(json!({
"context": context,
"xml": "Context in XML format (not implemented)"
})),
Some("json") | None => Ok(context),
Some(format) => Err(Error::validation(format!("Unsupported format: {format}"))),
}
}
}
#[derive(Debug, Deserialize)]
struct ContextAnalyzeArgs {
paths: Vec<String>,
#[serde(default)]
analysis_types: Vec<String>,
}
pub struct ContextAnalyzeTool;
impl ContextAnalyzeTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for ContextAnalyzeTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for ContextAnalyzeTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling context.analyze with args: {}", args);
let params: ContextAnalyzeArgs = 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 analyses = tool_functions::analyze_context(&paths, ¶ms.analysis_types)
.await
.map_err(|e| Error::internal(format!("Context analysis failed: {e}")))?;
Ok(analyses)
}
}
#[derive(Debug, Deserialize)]
struct ContextSummaryArgs {
paths: Vec<String>,
#[serde(default)]
level: Option<String>,
}
pub struct ContextSummaryTool;
impl ContextSummaryTool {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for ContextSummaryTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for ContextSummaryTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling context.summary with args: {}", args);
let params: ContextSummaryArgs = 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 summary = tool_functions::context_summary(&paths, params.level.as_deref())
.await
.map_err(|e| Error::internal(format!("Context summary failed: {e}")))?;
Ok(summary)
}
}
#[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);
}
}
}