UNPKG

@blario/mcp

Version:

Blar Model Context Protocol server

171 lines (170 loc) 7.01 kB
import { z } from 'zod'; import { blarAPIClient, BLAR_API_BASE } from '../api-client.js'; export const getPullRequestIssuesSchema = { pr_number: z.string().describe('Pull request number'), agent_type: z .enum([ 'debugger', 'optimizer', 'cyber_security', 'design_pattern', 'smell_detector', ]) .optional() .describe('Filter by agent type'), severity: z .enum(['error', 'warning', 'info']) .optional() .describe('Filter by severity level'), repository: z .string() .optional() .describe('Filter by repository name in format <org>/<repo_name> (use when multiple open PRs exist with same number)'), issue_source: z .enum(['blar', 'human', 'all']) .optional() .default('all') .describe('Filter by issue source (blar, human, or all)'), limit: z .number() .min(1) .max(100) .optional() .describe('Items per page (max 100, default 5)'), offset: z .number() .min(0) .optional() .describe('Starting position for pagination (default 0)'), }; export async function getPullRequestIssuesHandler({ pr_number, agent_type, severity, repository, issue_source, limit, offset, }) { // Build the API URL with query parameters const params = new URLSearchParams(); if (agent_type) params.append('agent_type', agent_type); if (severity) params.append('severity', severity); if (repository) params.append('repository', repository); if (issue_source && issue_source !== 'all') params.append('issue_source', issue_source); if (limit) params.append('limit', limit.toString()); if (offset) params.append('offset', offset.toString()); const url = `${BLAR_API_BASE}/agents/pull-request/${pr_number}/issues/${params.toString() ? `?${params.toString()}` : ''}`; const { data, error, status } = await blarAPIClient.makeRequestWithErrorHandling(url); // Handle multiple PRs error case if (status === 400 && error?.repositories && Array.isArray(error.repositories)) { const errorData = error; return { content: [ { type: 'text', text: `❌ Multiple Open PRs Found\n` + `═══════════════════════════════════════\n` + `PR Number: ${pr_number}\n` + `Problem: Multiple open PRs found with this number\n\n` + `Available Repositories:\n` + errorData.repositories .map((repo) => ` • ${repo}`) .join('\n') + '\n\n' + `🔧 Solution:\n` + `Please specify which repository using the 'repository' parameter:\n\n` + `Examples:\n` + errorData.repositories .map((repo) => ` get-pull-request-issues({ pr_number: "${pr_number}", repository: "${repo}" })`) .join('\n'), }, ], }; } // Handle other errors if (!data || error) { return { content: [ { type: 'text', text: `Failed to retrieve pull request issues for PR: ${pr_number}`, }, ], }; } if (data.results.length === 0) { return { content: [ { type: 'text', text: `No issues found for pull request: ${pr_number}${repository ? ` in repository: ${repository}` : ''}`, }, ], }; } // Calculate pagination info const currentLimit = limit || 5; // Default limit from API const currentOffset = offset || 0; const hasNext = data.next !== null; const hasPrevious = data.previous !== null; const totalCount = data.count; const currentPage = Math.floor(currentOffset / currentLimit) + 1; const totalPages = Math.ceil(totalCount / currentLimit); // Format the issues with pagination information let formatted = `Pull Request ${pr_number} Issues\n`; formatted += `═══════════════════════════════════════\n`; formatted += `Total Issues: ${totalCount}\n`; formatted += `Current Page: ${currentPage} of ${totalPages}\n`; formatted += `Items per page: ${currentLimit}\n`; formatted += `Current offset: ${currentOffset}\n`; formatted += `Showing: ${Math.min(currentOffset + 1, totalCount)}-${Math.min(currentOffset + data.results.length, totalCount)} of ${totalCount}\n`; if (agent_type) formatted += `Filtered by agent type: ${agent_type}\n`; if (severity) formatted += `Filtered by severity: ${severity}\n`; if (repository) formatted += `Filtered by repository: ${repository}\n`; if (issue_source && issue_source !== 'all') formatted += `Filtered by source: ${issue_source}\n`; formatted += `═══════════════════════════════════════\n\n`; data.results.forEach((issue, idx) => { const issueNumber = currentOffset + idx + 1; formatted += `[${issueNumber}] Issue #${issue.id}:\n`; formatted += ` Text: ${issue.text}\n`; formatted += ` File: ${issue.file_path} (lines ${issue.start_line}-${issue.end_line})\n`; formatted += ` Severity: ${issue.severity}\n`; formatted += ` Source: ${issue.source === 'blar' ? '🤖 Blar' : '👤 Human'}\n`; if (issue.considerations && issue.considerations.length > 0) { formatted += ` Considerations:\n`; issue.considerations.forEach((c, cIdx) => { formatted += ` [${cIdx + 1}] ${c.title}: ${c.description}\n`; if (c.code_snippet) { formatted += ` Code: ${c.code_snippet}\n`; } }); } formatted += ` ---\n`; }); // Add pagination navigation hints formatted += `\nPagination Info:\n`; if (hasPrevious) { const prevOffset = Math.max(0, currentOffset - currentLimit); formatted += ` ← Previous page: Use offset=${prevOffset}\n`; } if (hasNext) { const nextOffset = currentOffset + currentLimit; formatted += ` → Next page: Use offset=${nextOffset}\n`; } if (!hasNext && !hasPrevious) { formatted += ` • All issues are displayed on this page\n`; } return { content: [ { type: 'text', text: formatted, }, ], }; }