git_iris/mcp/tools/
codereview.rs1use crate::commit::service::IrisCommitService;
7use crate::config::Config as GitIrisConfig;
8use crate::git::GitRepo;
9use crate::log_debug;
10use crate::mcp::tools::utils::{
11 GitIrisTool, apply_custom_instructions, create_text_result, resolve_git_repo,
12 validate_repository_parameter,
13};
14
15use rmcp::handler::server::tool::cached_schema_for_type;
16use rmcp::model::{CallToolResult, Tool};
17use rmcp::schemars;
18
19use serde::{Deserialize, Serialize};
20use std::borrow::Cow;
21use std::sync::Arc;
22
23#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
25pub struct CodeReviewTool {
26 #[serde(default)]
28 pub include_unstaged: bool,
29
30 #[serde(default)]
32 pub commit_id: String,
33
34 #[serde(default)]
36 pub preset: String,
37
38 #[serde(default)]
40 pub custom_instructions: String,
41
42 pub repository: String,
44}
45
46impl CodeReviewTool {
47 pub fn get_tool_definition() -> Tool {
49 Tool {
50 name: Cow::Borrowed("git_iris_code_review"),
51 description: Cow::Borrowed(
52 "Generate a comprehensive code review with options for staged changes, unstaged changes, or specific commits",
53 ),
54 input_schema: cached_schema_for_type::<Self>(),
55 }
56 }
57}
58
59#[async_trait::async_trait]
60impl GitIrisTool for CodeReviewTool {
61 async fn execute(
63 &self,
64 git_repo: Arc<GitRepo>,
65 config: GitIrisConfig,
66 ) -> Result<CallToolResult, anyhow::Error> {
67 log_debug!("Generating code review with: {:?}", self);
68
69 validate_repository_parameter(&self.repository)?;
71 let git_repo = resolve_git_repo(Some(self.repository.as_str()), git_repo)?;
72 log_debug!("Using repository: {}", git_repo.repo_path().display());
73
74 if !self.commit_id.trim().is_empty() {
76 } else if git_repo.is_remote()
78 && (self.include_unstaged || self.commit_id.trim().is_empty())
79 {
80 return Err(anyhow::anyhow!(
81 "Cannot review staged/unstaged changes on a remote repository"
82 ));
83 }
84
85 let repo_path = git_repo.repo_path().clone();
87 let provider_name = &config.default_provider;
88
89 let service = IrisCommitService::new(
90 config.clone(),
91 &repo_path,
92 provider_name,
93 false, false, GitRepo::new(&repo_path)?,
96 )?;
97
98 let mut config_clone = config.clone();
100 apply_custom_instructions(&mut config_clone, &self.custom_instructions);
101
102 let preset = if self.preset.trim().is_empty() {
104 "default"
105 } else {
106 &self.preset
107 };
108
109 let review = if !self.commit_id.trim().is_empty() {
111 service
113 .generate_review_for_commit(preset, &self.custom_instructions, &self.commit_id)
114 .await?
115 } else if self.include_unstaged {
116 service
118 .generate_review_with_unstaged(preset, &self.custom_instructions, true)
119 .await?
120 } else {
121 service
123 .generate_review(preset, &self.custom_instructions)
124 .await?
125 };
126
127 let formatted_review = review.format();
129 Ok(create_text_result(formatted_review))
130 }
131}