cargo-mcp 0.2.0

mcp server for cargo commands
use crate::state::CargoTools;
use anyhow::Result;
use mcplease::{
    traits::{Tool, WithExamples},
    types::Example,
};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

/// Set the working directory for cargo operations
/// 
/// This sets the shared working directory that will be used by all AI tools,
/// not just cargo-mcp. Other MCP servers like fs-mcp will also use this directory.
#[derive(Debug, Serialize, Deserialize, schemars::JsonSchema, clap::Args)]
#[serde(rename = "set_working_directory")]
pub struct SetWorkingDirectory {
    /// Path to set as the working directory
    /// Can be absolute or relative to current directory
    pub path: String,
}

impl WithExamples for SetWorkingDirectory {
    fn examples() -> Vec<Example<Self>> {
        vec![
            Example {
                description: "Set working directory to current directory",
                item: Self {
                    path: ".".into(),
                },
            },
            Example {
                description: "Set working directory to a Rust project",
                item: Self {
                    path: "~/my-rust-project".into(),
                },
            },
            Example {
                description: "Set working directory using absolute path",
                item: Self {
                    path: "/Users/username/projects/my-app".into(),
                },
            },
        ]
    }
}

impl Tool<CargoTools> for SetWorkingDirectory {
    fn execute(self, state: &mut CargoTools) -> Result<String> {
        let expanded_path = PathBuf::from(&*shellexpand::tilde(&self.path));
        let canonical_path = std::fs::canonicalize(&expanded_path)
            .map_err(|e| anyhow::anyhow!("Could not resolve path '{}': {}", self.path, e))?;

        state.set_working_directory(canonical_path.clone(), None)?;

        // Check if it's a Rust project and provide helpful feedback
        let cargo_toml = canonical_path.join("Cargo.toml");
        if cargo_toml.exists() {
            Ok(format!(
                "✅ Working directory set to: {}\n🦀 Rust project detected (Cargo.toml found)",
                canonical_path.display()
            ))
        } else {
            Ok(format!(
                "✅ Working directory set to: {}\n⚠️  No Cargo.toml found - this doesn't appear to be a Rust project",
                canonical_path.display()
            ))
        }
    }
}