git_iris/mcp/tools/
releasenotes.rs1use crate::changes::ReleaseNotesGenerator;
6use crate::config::Config as GitIrisConfig;
7use crate::git::GitRepo;
8use crate::log_debug;
9use crate::mcp::tools::utils::{
10 GitIrisTool, apply_custom_instructions, create_text_result, parse_detail_level,
11 resolve_git_repo, 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#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
24pub struct ReleaseNotesTool {
25 pub from: String,
27
28 #[serde(default)]
30 pub to: String,
31
32 #[serde(default)]
34 pub detail_level: String,
35
36 #[serde(default)]
38 pub custom_instructions: String,
39
40 pub repository: String,
42
43 #[serde(default)]
45 pub version_name: String,
46}
47
48impl ReleaseNotesTool {
49 pub fn get_tool_definition() -> Tool {
51 Tool {
52 name: Cow::Borrowed("git_iris_release_notes"),
53 description: Cow::Borrowed(
54 "Generate comprehensive release notes between two Git references",
55 ),
56 input_schema: cached_schema_for_type::<Self>(),
57 }
58 }
59}
60
61#[async_trait::async_trait]
62impl GitIrisTool for ReleaseNotesTool {
63 async fn execute(
65 &self,
66 git_repo: Arc<GitRepo>,
67 config: GitIrisConfig,
68 ) -> Result<CallToolResult, anyhow::Error> {
69 log_debug!("Generating release notes with: {:?}", self);
70
71 validate_repository_parameter(&self.repository)?;
73 let git_repo = resolve_git_repo(Some(self.repository.as_str()), git_repo)?;
74 log_debug!("Using repository: {}", git_repo.repo_path().display());
75
76 let detail_level = parse_detail_level(&self.detail_level);
78
79 let mut config = config.clone();
81 apply_custom_instructions(&mut config, &self.custom_instructions);
82
83 let to = if self.to.trim().is_empty() {
85 "HEAD".to_string()
86 } else {
87 self.to.clone()
88 };
89
90 let content = ReleaseNotesGenerator::generate(
92 git_repo.clone(),
93 &self.from,
94 &to,
95 &config,
96 detail_level,
97 if self.version_name.is_empty() {
98 None
99 } else {
100 Some(self.version_name.clone())
101 },
102 )
103 .await?;
104
105 Ok(create_text_result(content))
107 }
108}