Skip to main content
Glama

Atlassian Bitbucket MCP Server

by aashari
atlassian.search.formatter.ts6.86 kB
import { ContentType } from '../utils/atlassian.util.js'; import { CodeSearchResult } from '../services/vendor.atlassian.search.service.js'; import { formatSeparator, formatDate, formatUrl, } from '../utils/formatter.util.js'; import path from 'path'; import { getContentTypeDisplay } from '../utils/atlassian.util.js'; /** * Try to guess the language from the file path */ function getLanguageHint(filePath: string): string { const ext = path.extname(filePath).toLowerCase(); const langMap: Record<string, string> = { '.js': 'javascript', '.jsx': 'jsx', '.ts': 'typescript', '.tsx': 'tsx', '.py': 'python', '.java': 'java', '.rb': 'ruby', '.php': 'php', '.cs': 'csharp', '.go': 'go', '.rs': 'rust', '.c': 'c', '.cpp': 'cpp', '.h': 'c', '.hpp': 'cpp', '.tf': 'terraform', '.hcl': 'hcl', '.sh': 'bash', '.zsh': 'zsh', '.json': 'json', '.yaml': 'yaml', '.yml': 'yaml', '.xml': 'xml', '.md': 'markdown', '.sql': 'sql', '.dockerfile': 'dockerfile', dockerfile: 'dockerfile', '.gitignore': 'gitignore', }; return langMap[ext] || ''; } /** * Format a single code search result into markdown * * @param result The code search result to format * @returns Formatted markdown string */ function formatCodeSearchResult(result: CodeSearchResult): string { // Format the file path - No highlighting needed here const filePath = result.file.path || 'Unknown File'; // <-- Use direct path // Fix the link text const fileLink = result.file.links?.self?.href ? formatUrl(result.file.links.self.href, filePath) // Use filePath for link text : filePath; // Build markdown output let markdown = `### ${fileLink}\n\n`; // Use fixed fileLink // Add match summary markdown += `${result.content_match_count} ${ result.content_match_count === 1 ? 'match' : 'matches' } found\n\n`; // Get language hint for code block const langHint = getLanguageHint(filePath); markdown += '```' + langHint + '\n'; // Add language hint // Process each content match result.content_matches.forEach((contentMatch) => { // Process each line in the content match contentMatch.lines.forEach((line) => { // Add line number markdown += `${line.line}: `; // Process segments (some may be highlighted matches) if (line.segments.length) { line.segments.forEach((segment) => { // Use standard bold markdown for highlighting markdown += segment.match ? `\\\`${segment.text}\\\`` // <-- Changed highlighting to backticks : segment.text; }); } markdown += '\n'; }); // Add space between match groups only if there are multiple lines shown if (contentMatch.lines.length > 1) { markdown += '\n'; } }); markdown += '```\n\n'; return markdown; } /** * Format code search results into markdown * * @param response The code search response from the API * @returns Markdown formatted string of code search results */ export function formatCodeSearchResults(searchResponse: { values?: CodeSearchResult[]; size: number; }): string { const results = searchResponse.values || []; if (!results || results.length === 0) { // Add standard footer even for empty state return ( '**No code matches found.**\n\n' + '\n\n' + formatSeparator() + '\n' + `*Information retrieved at: ${formatDate(new Date())}*` ); } // Start with a summary let markdown = `## Code Search Results\n\nFound ${searchResponse.size} matches for the code search query.\n\n`; // Format each result results.forEach((result: CodeSearchResult) => { markdown += formatCodeSearchResult(result); }); // Add standard footer with timestamp markdown += '\n\n' + formatSeparator(); markdown += `\n*Information retrieved at: ${formatDate(new Date())}*`; return markdown; } /** * Format content search results into markdown * * @param response The content search response from the API * @param contentType Optional content type filter that was applied * @returns Markdown formatted string of content search results */ export function formatContentSearchResults( response: { values?: unknown[]; size: number }, contentType?: ContentType, ): string { const results = response.values || []; if (!results || results.length === 0) { // Add standard footer even for empty state return ( '**No content matches found.**\n\n' + '\n\n' + formatSeparator() + '\n' + `*Information retrieved at: ${formatDate(new Date())}*` ); } // Start with a summary const typeStr = contentType ? getContentTypeDisplay(contentType) : 'Content'; let markdown = `## ${typeStr} Search Results\n\nFound ${response.size} matches for the content search query.\n\n`; // Format each result - this is generic as content results can vary widely results.forEach((result) => { // We need to handle result as a generic object since content types vary const typedResult = result as Record<string, unknown>; // Try to determine the type from the result const type = (typedResult.type as string) || 'Unknown'; // Try to get a title/name let title = 'Untitled'; if (typedResult.title) { title = String(typedResult.title); } else if (typedResult.name) { title = String(typedResult.name); } else if (typedResult.summary) { const summary = String(typedResult.summary); title = summary.slice(0, 80) + (summary.length > 80 ? '...' : ''); } // Try to get a link let link = ''; const links = typedResult.links as | Record<string, { href?: string }> | undefined; if (links?.html?.href) { link = links.html.href; } else if (links?.self?.href) { link = links.self.href; } markdown += '### '; if (link) { markdown += formatUrl(link, title); } else { markdown += title; } markdown += '\n\n'; // Add type information markdown += `**Type**: ${type}\n`; // Add update/created date if available if (typedResult.updated_on) { markdown += `**Updated**: ${formatDate(typedResult.updated_on as string | Date)}\n`; } else if (typedResult.created_on) { markdown += `**Created**: ${formatDate(typedResult.created_on as string | Date)}\n`; } // Add description/content if available (limited to preserve readability) if (typedResult.description) { const description = String(typedResult.description); const limitedDesc = description.length > 500 ? description.slice(0, 500) + '...' : description; markdown += `\n${limitedDesc}\n\n`; } else if (typedResult.content) { const content = String(typedResult.content); const limitedContent = content.length > 500 ? content.slice(0, 500) + '...' : content; markdown += `\n${limitedContent}\n\n`; } markdown += '\n'; }); // Add standard footer with timestamp markdown += '\n' + formatSeparator(); markdown += `\n*Information retrieved at: ${formatDate(new Date())}*`; return markdown; }

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