use anyhow;
use kodegen_mcp_schema::github::{CreatePullRequestReviewArgs, CreatePullRequestReviewPromptArgs};
use kodegen_mcp_tool::{Tool, error::McpError};
use octocrab::models::pulls::ReviewAction;
use rmcp::model::{PromptArgument, PromptMessage, PromptMessageContent, PromptMessageRole};
use serde_json::Value;
#[derive(Clone)]
pub struct CreatePullRequestReviewTool;
impl Tool for CreatePullRequestReviewTool {
type Args = CreatePullRequestReviewArgs;
type PromptArgs = CreatePullRequestReviewPromptArgs;
fn name() -> &'static str {
"create_pull_request_review"
}
fn description() -> &'static str {
"Create a review on a pull request (approve, request changes, or comment). \
Requires GITHUB_TOKEN environment variable with repo permissions."
}
fn read_only() -> bool {
false }
fn destructive() -> bool {
false }
fn idempotent() -> bool {
false }
fn open_world() -> bool {
true }
async fn execute(&self, args: Self::Args) -> Result<Value, McpError> {
let token = std::env::var("GITHUB_TOKEN").map_err(|_| {
McpError::Other(anyhow::anyhow!("GITHUB_TOKEN environment variable not set"))
})?;
let client = crate::GitHubClient::builder()
.personal_token(token)
.build()
.map_err(|e| McpError::Other(anyhow::anyhow!("Failed to create GitHub client: {e}")))?;
let event = match args.event.to_uppercase().as_str() {
"APPROVE" => ReviewAction::Approve,
"REQUEST_CHANGES" => ReviewAction::RequestChanges,
"COMMENT" => ReviewAction::Comment,
_ => {
return Err(McpError::InvalidArguments(format!(
"Invalid event '{}'. Must be APPROVE, REQUEST_CHANGES, or COMMENT",
args.event
)));
}
};
let options = crate::CreatePullRequestReviewOptions {
event,
body: args.body,
commit_id: args.commit_id,
comments: None, };
let task_result = client
.create_pull_request_review(args.owner, args.repo, args.pull_number, options)
.await;
let api_result =
task_result.map_err(|e| McpError::Other(anyhow::anyhow!("Task channel error: {e}")))?;
let review =
api_result.map_err(|e| McpError::Other(anyhow::anyhow!("GitHub API error: {e}")))?;
Ok(serde_json::to_value(&review)?)
}
fn prompt_arguments() -> Vec<PromptArgument> {
vec![]
}
async fn prompt(&self, _args: Self::PromptArgs) -> Result<Vec<PromptMessage>, McpError> {
Ok(vec![
PromptMessage {
role: PromptMessageRole::User,
content: PromptMessageContent::text("How do I approve a pull request?"),
},
PromptMessage {
role: PromptMessageRole::Assistant,
content: PromptMessageContent::text(
"Use create_pull_request_review with event \"APPROVE\":\n\n\
create_pull_request_review({\n\
\"owner\": \"octocat\",\n\
\"repo\": \"hello-world\",\n\
\"pull_number\": 42,\n\
\"event\": \"APPROVE\",\n\
\"body\": \"LGTM! Great work.\"\n\
})\n\n\
Event types:\n\
- \"APPROVE\" - Approve the PR (allows merging if required reviews are met)\n\
- \"REQUEST_CHANGES\" - Block PR until changes are made\n\
- \"COMMENT\" - Leave review comments without approval/blocking\n\n\
Optional fields:\n\
- body: Overall review comment (recommended for context)\n\
- commit_id: Specific commit SHA to review (defaults to latest)\n\n\
Examples:\n\n\
# Approve with comment\n\
create_pull_request_review({\n\
\"owner\": \"octocat\",\n\
\"repo\": \"hello-world\",\n\
\"pull_number\": 42,\n\
\"event\": \"APPROVE\",\n\
\"body\": \"Looks good! All tests pass.\"\n\
})\n\n\
# Request changes\n\
create_pull_request_review({\n\
\"owner\": \"octocat\",\n\
\"repo\": \"hello-world\",\n\
\"pull_number\": 42,\n\
\"event\": \"REQUEST_CHANGES\",\n\
\"body\": \"Please address the comments before merging.\"\n\
})\n\n\
# Comment only (no approval/block)\n\
create_pull_request_review({\n\
\"owner\": \"octocat\",\n\
\"repo\": \"hello-world\",\n\
\"pull_number\": 42,\n\
\"event\": \"COMMENT\",\n\
\"body\": \"Some suggestions for improvement.\"\n\
})\n\n\
# Review specific commit\n\
create_pull_request_review({\n\
\"owner\": \"octocat\",\n\
\"repo\": \"hello-world\",\n\
\"pull_number\": 42,\n\
\"event\": \"APPROVE\",\n\
\"body\": \"This commit looks good.\",\n\
\"commit_id\": \"abc123...\"\n\
})\n\n\
Note: This creates a REVIEW, not individual line comments.\n\
Use add_pull_request_review_comment for inline code comments.\n\n\
Requirements:\n\
- GITHUB_TOKEN environment variable must be set\n\
- Token needs 'repo' scope for private repos\n\
- User must have write access to the repository\n\
- For APPROVE: User must be authorized reviewer if required reviews are configured",
),
},
])
}
}