git_iris/mcp/tools/
utils.rs

1//! Common utilities for MCP tools
2//!
3//! This module provides shared functionality used across different MCP tool implementations.
4
5use crate::common::DetailLevel;
6use crate::config::Config as GitIrisConfig;
7use crate::git::GitRepo;
8use rmcp::model::{Annotated, CallToolResult, Content, RawContent, RawTextContent};
9use std::sync::Arc;
10
11/// Common trait for all Git-Iris MCP tools
12///
13/// This trait defines the common interface that all Git-Iris tools must implement.
14#[async_trait::async_trait]
15pub trait GitIrisTool {
16    /// Execute the tool with the provided repository and configuration
17    async fn execute(
18        &self,
19        git_repo: Arc<GitRepo>,
20        config: GitIrisConfig,
21    ) -> Result<CallToolResult, anyhow::Error>;
22}
23
24/// Creates a text result response for tool calls
25///
26/// This is a common utility used by all tools to return a text response.
27pub fn create_text_result(text: String) -> CallToolResult {
28    CallToolResult {
29        content: vec![Content::from(Annotated {
30            raw: RawContent::Text(RawTextContent { text }),
31            annotations: None,
32        })],
33        is_error: None,
34    }
35}
36
37/// Parses a detail level string into the corresponding enum value
38///
39/// This provides consistent handling of detail level across all tools.
40pub fn parse_detail_level(detail_level: &str) -> DetailLevel {
41    if detail_level.trim().is_empty() {
42        return DetailLevel::Standard;
43    }
44
45    match detail_level.trim().to_lowercase().as_str() {
46        "minimal" => DetailLevel::Minimal,
47        "detailed" => DetailLevel::Detailed,
48        _ => DetailLevel::Standard,
49    }
50}
51
52/// Apply custom instructions to config if provided
53pub fn apply_custom_instructions(config: &mut crate::config::Config, custom_instructions: &str) {
54    if !custom_instructions.trim().is_empty() {
55        config.set_temp_instructions(Some(custom_instructions.to_string()));
56    }
57}
58
59/// Validates the repository parameter: must be non-empty, and if local, must exist and be a git repo
60pub fn validate_repository_parameter(repo: &str) -> Result<(), anyhow::Error> {
61    if repo.trim().is_empty() {
62        return Err(anyhow::anyhow!(
63            "The `repository` parameter is required and must be a valid local path or remote URL."
64        ));
65    }
66    if !(repo.starts_with("http://") || repo.starts_with("https://") || repo.starts_with("git@")) {
67        let path = std::path::Path::new(repo);
68        if !path.exists() {
69            return Err(anyhow::anyhow!(format!(
70                "The specified repository path does not exist: {}",
71                repo
72            )));
73        }
74        if !path.join(".git").exists() {
75            return Err(anyhow::anyhow!(format!(
76                "The specified path is not a git repository: {}",
77                repo
78            )));
79        }
80    }
81    Ok(())
82}
83
84/// Resolves a Git repository from a `repo_path` parameter
85///
86/// If `repo_path` is provided, creates a new `GitRepo` for that path/URL.
87/// Assumes the parameter has already been validated.
88pub fn resolve_git_repo(
89    repo_path: Option<&str>,
90    _default_git_repo: Arc<GitRepo>,
91) -> Result<Arc<GitRepo>, anyhow::Error> {
92    match repo_path {
93        Some(path) if !path.trim().is_empty() => {
94            if path.starts_with("http://")
95                || path.starts_with("https://")
96                || path.starts_with("git@")
97            {
98                // Handle remote repository URL
99                Ok(Arc::new(GitRepo::new_from_url(Some(path.to_string()))?))
100            } else {
101                // Handle local repository path
102                let path = std::path::Path::new(path);
103                Ok(Arc::new(GitRepo::new(path)?))
104            }
105        }
106        _ => Err(anyhow::anyhow!(
107            "The `repository` parameter is required and must be a valid local path or remote URL."
108        )),
109    }
110}