coderabbitai-mcp
Version:
MCP server for interacting with CodeRabbit AI reviews on GitHub pull requests. Enables LLMs to analyze, implement, and resolve CodeRabbit suggestions programmatically.
114 lines โข 5.29 kB
JavaScript
import { z } from 'zod';
const GetCoderabbitReviewsSchema = z.object({
owner: z.string().min(1, "Repository owner is required"),
repo: z.string().min(1, "Repository name is required"),
pullNumber: z.number().int().positive("Pull request number must be positive")
});
/**
* Parse CodeRabbit review body to extract structured information
*/
function parseCoderabbitReviewBody(body) {
const lines = body.split('\n');
// Extract actionable comments count
const actionableMatch = body.match(/\*\*Actionable comments posted: (\d+)\*\*/);
const actionableCount = actionableMatch ? parseInt(actionableMatch[1]) : 0;
// Extract duplicate comments count
const duplicateMatch = body.match(/โป๏ธ Duplicate comments \((\d+)\)/);
const duplicateCount = duplicateMatch ? parseInt(duplicateMatch[1]) : 0;
// Extract nitpick comments count
const nitpickMatch = body.match(/๐งน Nitpick comments \((\d+)\)/);
const nitpickCount = nitpickMatch ? parseInt(nitpickMatch[1]) : 0;
// Extract summary from review details section
let summary = "";
const summaryMatch = body.match(/๐ Review details[\s\S]*?(?=\n\n|\n<|$)/);
if (summaryMatch) {
summary = summaryMatch[0].replace(/๐ Review details\n/, '').trim();
}
// Parse embedded comments in the review body
const comments = [];
// Look for comment blocks in details sections
const commentBlocks = body.split('<details>');
for (const block of commentBlocks) {
if (!block.includes('</details>'))
continue;
// Extract category from summary
const summaryMatch = block.match(/<summary>(.*?)<\/summary>/);
const category = summaryMatch ? summaryMatch[1].trim() : 'Unknown';
// Determine severity from emoji/markers
let severity = 'info';
if (block.includes('โ ๏ธ Potential issue') || block.includes('_โ ๏ธ Potential issue_')) {
severity = 'warning';
}
else if (block.includes('๐ ๏ธ Refactor suggestion') || block.includes('_๐ ๏ธ Refactor suggestion_')) {
severity = 'suggestion';
}
else if (block.includes('๐งน Nitpick')) {
severity = 'info';
}
// Extract AI prompt
const aiPromptMatch = block.match(/๐ค Prompt for AI Agents[\s\S]*?```\n([\s\S]*?)\n```/);
const aiPrompt = aiPromptMatch ? aiPromptMatch[1].trim() : undefined;
// Extract committable suggestion
const committableMatch = block.match(/๐ Committable suggestion[\s\S]*?```suggestion\n([\s\S]*?)\n```/);
const committableSuggestion = committableMatch ? committableMatch[1].trim() : undefined;
// Extract file path and line range if present
const filePathMatch = block.match(/`([^`]+\.(js|ts|tsx|jsx|py|java|cpp|c|h|go|rs|rb|php|cs|kt|swift))`/);
const filePath = filePathMatch ? filePathMatch[1] : undefined;
const lineRangeMatch = block.match(/lines? (\d+(?:-\d+)?)/i);
const lineRange = lineRangeMatch ? lineRangeMatch[1] : undefined;
// Extract description (first substantial text block)
const descMatch = block.match(/<\/summary><blockquote>\n\n(.*?)(?=\n\n|\n<)/s);
const description = descMatch ? descMatch[1].replace(/\*\*/g, '').trim() : category;
if (aiPrompt || committableSuggestion || description !== category) {
comments.push({
category,
severity,
description,
ai_prompt: aiPrompt,
committable_suggestion: committableSuggestion,
file_path: filePath,
line_range: lineRange
});
}
}
return {
actionable_comments: actionableCount,
duplicate_comments: duplicateCount,
nitpick_comments: nitpickCount,
summary,
comments
};
}
/**
* Get all CodeRabbit reviews for a specific pull request
*/
export async function getCoderabbitReviews(input, githubClient) {
// Validate input
const validatedInput = GetCoderabbitReviewsSchema.parse(input);
const { owner, repo, pullNumber } = validatedInput;
try {
// Get all reviews for the PR using GitHub API
const reviews = await githubClient.getPullRequestReviews(owner, repo, pullNumber);
// Filter for CodeRabbit reviews only
const coderabbitReviews = reviews.filter(review => review.user.login === 'coderabbitai[bot]');
// Parse and enrich each CodeRabbit review
const enrichedReviews = coderabbitReviews.map(review => {
const parsed = parseCoderabbitReviewBody(review.body);
return {
id: review.id,
submitted_at: review.submitted_at,
html_url: review.html_url,
state: review.state,
actionable_comments: parsed.actionable_comments,
body: review.body,
summary: parsed.summary,
commit_id: review.commit_id
};
});
return enrichedReviews;
}
catch (error) {
throw new Error(`Failed to get CodeRabbit reviews: ${error instanceof Error ? error.message : String(error)}`);
}
}
//# sourceMappingURL=get-reviews.js.map