UNPKG

@aashari/mcp-server-atlassian-bitbucket

Version:

Node.js/TypeScript MCP server for Atlassian Bitbucket. Enables AI systems (LLMs) to interact with workspaces, repositories, and pull requests via tools (list, get, comment, search). Connects AI directly to version control workflows through the standard MC

248 lines (247 loc) 10.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const logger_util_js_1 = require("../utils/logger.util.js"); const error_handler_util_js_1 = require("../utils/error-handler.util.js"); const defaults_util_js_1 = require("../utils/defaults.util.js"); const atlassian_repositories_controller_js_1 = __importDefault(require("./atlassian.repositories.controller.js")); const atlassian_pullrequests_controller_js_1 = __importDefault(require("./atlassian.pullrequests.controller.js")); const atlassian_search_formatter_js_1 = require("./atlassian.search.formatter.js"); const vendor_atlassian_search_service_js_1 = __importDefault(require("../services/vendor.atlassian.search.service.js")); const controllerLogger = logger_util_js_1.Logger.forContext('controllers/atlassian.search.controller.ts'); /** * Search for code in repositories * * @param options Options for code search * @returns Promise with formatted code search results */ async function searchCode(options = {}) { const methodLogger = controllerLogger.forMethod('searchCode'); methodLogger.debug('Searching code with options:', options); try { // Validate required parameters if (!options.workspaceSlug) { throw new Error('workspaceSlug is required for code search'); } if (!options.query) { throw new Error('query is required for code search'); } // Apply defaults const mergedOptions = (0, defaults_util_js_1.applyDefaults)(options, { page: 1, pageLen: options.limit || defaults_util_js_1.DEFAULT_PAGE_SIZE, }); // Call the code search service const response = await vendor_atlassian_search_service_js_1.default.searchCode({ workspaceSlug: mergedOptions.workspaceSlug, searchQuery: mergedOptions.query || '', repoSlug: mergedOptions.repoSlug, page: mergedOptions.page, pageLen: mergedOptions.pageLen, // Add fields to get repository information for better display fields: '+values.file.commit.repository', }); // Format the results into markdown const content = (0, atlassian_search_formatter_js_1.formatCodeSearchResults)(response); // Create pagination information const pagination = { count: response.size || 0, hasMore: response.page * response.pagelen < response.size, // For cursor-based pagination, use the page number as the cursor nextCursor: response.page * response.pagelen < response.size ? (response.page + 1).toString() : undefined, }; methodLogger.debug('Successfully retrieved code search results', { count: response.size, hasMore: pagination.hasMore, }); return { content, pagination, }; } catch (error) { return (0, error_handler_util_js_1.handleControllerError)(error, { source: 'Bitbucket', operation: 'searchCode', entityType: 'code', entityId: options.workspaceSlug, }); } } /** * Search for commits in a repository * * @param options Options for commit search * @returns Promise with formatted commit search results */ async function searchCommits(options = {}) { const methodLogger = controllerLogger.forMethod('searchCommits'); methodLogger.debug('Searching commits with options:', options); try { // Validate required parameters if (!options.workspaceSlug) { throw new Error('workspaceSlug is required for commit search'); } if (!options.repoSlug) { throw new Error('repoSlug is required for commit search'); } // Apply defaults const mergedOptions = (0, defaults_util_js_1.applyDefaults)(options, { page: 1, pageLen: options.limit || defaults_util_js_1.DEFAULT_PAGE_SIZE, }); // Call the commits search service const response = await vendor_atlassian_search_service_js_1.default.searchCommits({ workspaceSlug: mergedOptions.workspaceSlug, repoSlug: mergedOptions.repoSlug, searchQuery: mergedOptions.query || '', page: mergedOptions.page, pageLen: mergedOptions.pageLen, // Add fields to get repository information for better display fields: '', }); // Format the results into markdown const content = (0, atlassian_search_formatter_js_1.formatCommitsResults)(response, mergedOptions.repoSlug, mergedOptions.workspaceSlug); // Create pagination information const pagination = { count: response.size || response.values?.length || 0, hasMore: response.page * response.pagelen < response.size, // For cursor-based pagination, use the page number as the cursor nextCursor: response.page * response.pagelen < response.size ? (response.page + 1).toString() : undefined, }; methodLogger.debug('Successfully retrieved commit search results', { count: pagination.count, hasMore: pagination.hasMore, }); return { content, pagination, }; } catch (error) { return (0, error_handler_util_js_1.handleControllerError)(error, { source: 'Bitbucket', operation: 'searchCommits', entityType: 'commits', entityId: `${options.workspaceSlug}/${options.repoSlug}`, }); } } /** * Search for Bitbucket content across repositories and pull requests * * @param {SearchOptions} options - Options for the search * @returns {Promise<ControllerResponse>} Formatted search results in Markdown */ async function search(options = {}) { const methodLogger = controllerLogger.forMethod('search'); methodLogger.debug('Searching Bitbucket content with options:', options); try { // Validate required parameters if (!options.workspaceSlug) { throw new Error('workspaceSlug is required for Bitbucket search'); } // Apply defaults to options const mergedOptions = (0, defaults_util_js_1.applyDefaults)(options, { limit: defaults_util_js_1.DEFAULT_PAGE_SIZE, scope: 'all', query: '', }); // Determine what to search based on the scope const scope = mergedOptions.scope || 'all'; const workspaceSlug = mergedOptions.workspaceSlug; const query = mergedOptions.query || ''; // If scope is code, use the code search handler if (scope === 'code') { return searchCode(mergedOptions); } // If scope is commits, use the commits search handler if (scope === 'commits') { // Commits search requires a repository slug if (!mergedOptions.repoSlug) { throw new Error('repoSlug is required for commits search'); } return searchCommits(mergedOptions); } let repoResults = { content: '', pagination: { count: 0, hasMore: false }, }; let prResults = { content: '', pagination: { count: 0, hasMore: false }, }; // Search repositories if scope is 'all' or 'repositories' if (scope === 'all' || scope === 'repositories') { repoResults = await atlassian_repositories_controller_js_1.default.list({ workspaceSlug, query, limit: mergedOptions.limit, cursor: mergedOptions.cursor, }); } // Search pull requests if scope is 'all' or 'pullrequests' and a repository slug is provided if ((scope === 'all' || scope === 'pullrequests') && mergedOptions.repoSlug) { prResults = await atlassian_pullrequests_controller_js_1.default.list({ workspaceSlug, repoSlug: mergedOptions.repoSlug, query, limit: mergedOptions.limit, cursor: mergedOptions.cursor, }); } // Combine results with headers let combinedContent = ''; let totalCount = 0; let hasMore = false; if (scope === 'all' || scope === 'repositories') { combinedContent += `# Repository Search Results\n\n${repoResults.content}\n\n`; totalCount += repoResults.pagination?.count || 0; hasMore = hasMore || repoResults.pagination?.hasMore || false; } if ((scope === 'all' || scope === 'pullrequests') && mergedOptions.repoSlug) { combinedContent += `# Pull Request Search Results\n\n${prResults.content}\n\n`; totalCount += prResults.pagination?.count || 0; hasMore = hasMore || prResults.pagination?.hasMore || false; } // Add a summary at the top const summaryContent = `## Search Summary\n\nFound ${totalCount} results for query "${query}" in workspace "${workspaceSlug}".\n\n${combinedContent}`; // Create pagination response const pagination = { count: totalCount, hasMore, // We don't have a proper way to combine cursors from both sources, // so we just use one of them if available (not ideal but functional) nextCursor: repoResults.pagination ?.nextCursor || prResults.pagination ?.nextCursor, }; methodLogger.debug('Successfully retrieved and formatted search results', { totalCount, hasMore }); return { content: summaryContent, pagination, }; } catch (error) { return (0, error_handler_util_js_1.handleControllerError)(error, { source: 'Bitbucket', operation: 'search', entityType: 'content', entityId: options.workspaceSlug, }); } } exports.default = { search, searchCode, searchCommits, };