gitai/server/tools/
changelog.rs

1//! MCP changelog tool implementation
2//!
3//! This module provides the MCP tool for generating changelogs.
4
5use crate::config::Config as GitAIConfig;
6use crate::debug;
7use crate::features::changelog::ChangelogGenerator;
8use crate::git::GitRepo;
9use crate::server::tools::utils::{
10    PilotTool, apply_custom_instructions, create_text_result, parse_detail_level, resolve_git_repo,
11    validate_repository_parameter,
12};
13
14use rmcp::handler::server::tool::cached_schema_for_type;
15use rmcp::model::{CallToolResult, Tool};
16use rmcp::schemars;
17
18use serde::{Deserialize, Serialize};
19use std::borrow::Cow;
20use std::sync::Arc;
21
22/// Changelog tool for generating comprehensive changelog documents
23#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
24pub struct ChangelogTool {
25    /// Starting reference (commit hash, tag, or branch name)
26    pub from: String,
27
28    /// Ending reference (commit hash, tag, or branch name). Defaults to HEAD if not specified.
29    #[serde(default)]
30    pub to: String,
31
32    /// Level of detail for the changelog
33    #[serde(default)]
34    pub detail_level: String,
35
36    /// Custom instructions for the AI
37    #[serde(default)]
38    pub custom_instructions: String,
39
40    /// Repository path (local) or URL (remote). Required.
41    pub repository: String,
42
43    /// Explicit version name to use in the changelog (optional)
44    #[serde(default)]
45    pub version_name: String,
46}
47
48impl ChangelogTool {
49    /// Returns the tool definition for the changelog tool
50    pub fn get_tool_definition() -> Tool {
51        Tool {
52            name: Cow::Borrowed("gitai_changelog"),
53            description: Some(Cow::Borrowed(
54                "Generate a detailed changelog between two Git references",
55            )),
56            input_schema: cached_schema_for_type::<Self>(),
57            annotations: None,
58            icons: None,
59            output_schema: None,
60            title: None,
61        }
62    }
63}
64
65#[async_trait::async_trait]
66impl PilotTool for ChangelogTool {
67    /// Execute the changelog tool with the provided repository and configuration
68    async fn execute(
69        &self,
70        git_repo: Arc<GitRepo>,
71        config: GitAIConfig,
72    ) -> Result<CallToolResult, anyhow::Error> {
73        debug!("Generating changelog with: {:?}", self);
74
75        // Validate repository parameter
76        validate_repository_parameter(&self.repository)?;
77        let git_repo = resolve_git_repo(Some(self.repository.as_str()), git_repo)?;
78        debug!("Using repository: {}", git_repo.repo_path().display());
79
80        // Parse detail level using shared utility
81        let detail_level = parse_detail_level(&self.detail_level);
82
83        // Set up config with custom instructions if provided
84        let mut config = config.clone();
85        apply_custom_instructions(&mut config, &self.custom_instructions);
86
87        // Default to HEAD if to is empty
88        let to = if self.to.trim().is_empty() {
89            "HEAD".to_string()
90        } else {
91            self.to.clone()
92        };
93
94        // Generate the changelog using the generator
95        let content =
96            ChangelogGenerator::generate(git_repo.clone(), &self.from, &to, &config, detail_level)
97                .await?;
98
99        // If version_name is provided, update the changelog content to use that version
100        let version_opt = if self.version_name.is_empty() {
101            None
102        } else {
103            Some(self.version_name.clone())
104        };
105
106        // If version_name is provided, use it when updating the changelog
107        if let Some(version) = &version_opt {
108            debug!("Using custom version name: {}", version);
109        }
110
111        // Create and return the result using shared utility
112        Ok(create_text_result(content))
113    }
114}