Skip to main content
Glama

Atlassian Bitbucket MCP Server

by aashari
atlassian.diff.formatter.ts6.13 kB
import { DiffstatResponse } from '../services/vendor.atlassian.repositories.diff.types.js'; import { formatHeading, formatSeparator, formatDate, formatDiff, } from '../utils/formatter.util.js'; /** * Format diffstat results into Markdown * * @param diffstat - Diffstat response containing file changes * @param baseBranchOrCommit - Name of the base branch or commit (source of the diff) * @param targetBranchOrCommit - Name of the target branch or commit (destination of the diff) * @returns Formatted Markdown string */ export function formatDiffstat( diffstat: DiffstatResponse, baseBranchOrCommit: string, targetBranchOrCommit: string, ): string { const lines: string[] = []; const files = diffstat.values || []; // Title section lines.push( formatHeading( `Diff: ${baseBranchOrCommit} → ${targetBranchOrCommit}`, 1, ), ); lines.push(''); if (files.length === 0) { lines.push( '*No changes detected in the diffstat response. This might occur when:*', ); lines.push('- The commits or branches are identical'); lines.push( '- The changes are purely structural (e.g., merge commits without content changes)', ); lines.push( '- The parameters need to be specified in a different order', ); lines.push(''); lines.push('**Try the following:**'); lines.push( '1. For branch comparisons: Reverse the source and destination branch parameters', ); lines.push( '2. For commit comparisons: Ensure newer commit is `sinceCommit` and older commit is `untilCommit`', ); lines.push( '3. Check that both references exist and you have access to them', ); lines.push(''); lines.push(formatSeparator()); lines.push(`*Information retrieved at: ${formatDate(new Date())}*`); return lines.join('\n'); } // Summary statistics let totalAdditions = 0; let totalDeletions = 0; let conflictedFiles = 0; // Collect statistics files.forEach((file) => { if (file.lines_added) totalAdditions += file.lines_added; if (file.lines_removed) totalDeletions += file.lines_removed; if (file.status === 'merge conflict') conflictedFiles++; }); lines.push(formatHeading('Summary', 2)); lines.push( `${files.length} file${files.length !== 1 ? 's' : ''} changed with ${totalAdditions} insertion${totalAdditions !== 1 ? 's' : ''} and ${totalDeletions} deletion${totalDeletions !== 1 ? 's' : ''}.`, ); if (conflictedFiles > 0) { lines.push(''); lines.push( `⚠️ **Merge conflicts detected in ${conflictedFiles} file${conflictedFiles !== 1 ? 's' : ''}.**`, ); } lines.push(''); // File changes section (limit to a reasonable number, like 20) const maxFilesToShow = 20; const hasMoreFiles = files.length > maxFilesToShow; const filesToDisplay = files.slice(0, maxFilesToShow); // File list with changes lines.push(formatHeading('Files Changed', 2)); lines.push(''); filesToDisplay.forEach((file) => { const changes = []; if (file.lines_added) changes.push(`+${file.lines_added}`); if (file.lines_removed) changes.push(`-${file.lines_removed}`); const changeStr = changes.length > 0 ? ` (${changes.join(', ')})` : ''; // Handle potentially null old/new paths const filePath = file.new?.path || file.old?.path || '(unnamed file)'; // Show path, changes, and status if it's a conflict let line = `- \`${filePath}\`${changeStr}`; if (file.status === 'merge conflict') { line += ' **CONFLICT**'; } lines.push(line); }); if (hasMoreFiles) { lines.push(''); lines.push(`... and ${files.length - maxFilesToShow} more files`); } // Standard footer lines.push(''); lines.push(formatSeparator()); lines.push(`*Information retrieved at: ${formatDate(new Date())}*`); return lines.join('\n'); } /** * Format complete diff results, including diffstat summary and raw diff * * @param diffstat - Diffstat response containing file changes * @param rawDiff - Raw unified diff text * @param baseBranchOrCommit - Name of the base branch or commit * @param targetBranchOrCommit - Name of the target branch or commit * @returns Formatted Markdown string */ export function formatFullDiff( diffstat: DiffstatResponse, rawDiff: string, baseBranchOrCommit: string, targetBranchOrCommit: string, ): string { const diffstatMd = formatDiffstat( diffstat, baseBranchOrCommit, targetBranchOrCommit, ); // If there's a raw diff but empty diffstat, we should still show the raw diff // This can happen with structural changes like merges if ( rawDiff && rawDiff.trim() !== '' && (diffstat.values || []).length === 0 ) { const lines = diffstatMd.split('\n'); // Replace the "No changes detected" message with a more accurate one const messageStartIndex = lines.findIndex((line) => line.includes('*No changes detected'), ); if (messageStartIndex >= 0) { lines.splice( messageStartIndex, 6, '*No file changes in diffstat but raw diff content was found. This often happens with:*', '- Merge commits or trivial merges', '- Rename-only changes without content modifications', '- Changes to file metadata or permissions without content changes', '', 'If the diff content below is not what you expected, try reversing the parameter order:', '- For branch comparisons: swap source and destination branch values', '- For commit comparisons: swap sinceCommit and untilCommit values', ); } // Insert section heading for the raw diff before the footer const separatorIndex = lines.findIndex((line) => line.includes('---')); if (separatorIndex >= 0) { lines.splice( separatorIndex, 0, '', formatHeading('Raw Diff Content', 2), '', ); lines.splice(separatorIndex + 3, 0, formatDiff(rawDiff)); } return lines.join('\n'); } if (!rawDiff || rawDiff.trim() === '') { return diffstatMd; } const lines = diffstatMd.split('\n'); // Insert section heading for the raw diff // Insert before the standard footer (which is the last 2 lines) lines.splice(lines.length - 3, 0, '', formatHeading('Code Changes', 2), ''); lines.splice(lines.length - 3, 0, formatDiff(rawDiff)); return lines.join('\n'); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aashari/mcp-server-atlassian-bitbucket'

If you have feedback or need assistance with the MCP directory API, please join our Discord server