@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
161 lines (160 loc) • 6.52 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.searchCommits = searchCommits;
exports.searchCode = searchCode;
const transport_util_js_1 = require("../utils/transport.util.js");
const logger_util_js_1 = require("../utils/logger.util.js");
const url_1 = require("url");
const transport_util_js_2 = require("../utils/transport.util.js");
const logger = logger_util_js_1.Logger.forContext('services/vendor.atlassian.search.service.ts');
/**
* Search for commits in a repository using the Bitbucket API
*
* @param {SearchCommitsParams} params - Parameters for the commit search
* @returns {Promise<CommitsResponse>} The search results from the Bitbucket API
*/
async function searchCommits(params) {
// Build the query parameters - the Bitbucket API allows searching commits by message
const queryParams = new url_1.URLSearchParams();
// If search query is provided, add it as a q parameter
if (params.searchQuery) {
queryParams.append('q', `message ~ "${params.searchQuery}"`);
}
// Add optional pagination parameters
if (params.page) {
queryParams.append('page', params.page.toString());
}
if (params.pageLen) {
queryParams.append('pagelen', params.pageLen.toString());
}
// Add optional fields parameter for enhanced responses
if (params.fields) {
queryParams.append('fields', params.fields);
}
// Get credentials for API call
const credentials = (0, transport_util_js_2.getAtlassianCredentials)();
if (!credentials) {
throw new Error('No Atlassian credentials available');
}
// Set useBitbucketAuth to true since we're calling the Bitbucket API
credentials.useBitbucketAuth = true;
// Create API path for Bitbucket commits
const path = `/2.0/repositories/${params.workspaceSlug}/${params.repoSlug}/commits${queryParams.toString() ? '?' + queryParams.toString() : ''}`;
// Track searching commits in repository
logger.debug(`Searching commits in repository: ${params.workspaceSlug}/${params.repoSlug}`, {
searchQuery: params.searchQuery,
path,
});
// Call Bitbucket API with credentials and path
return (0, transport_util_js_1.fetchAtlassian)(credentials, path);
}
/**
* Search for code in a workspace using the Bitbucket API
*
* @param {SearchCodeParams} params - Parameters for the code search
* @returns {Promise<CodeSearchResponse>} The search results from the Bitbucket API
*/
async function searchCode(params) {
// If repoSlug is provided, enhance the search query with repo: syntax
const searchQuery = params.repoSlug
? `${params.searchQuery} repo:${params.repoSlug}`
: params.searchQuery;
// Language mapping to handle common alternative names
const languageMapping = {
hcl: 'terraform',
tf: 'terraform',
typescript: 'ts',
javascript: 'js',
python: 'py',
// Add more mappings as needed
};
// Append language and extension filters if provided
let finalSearchQuery = searchQuery;
if (params.language) {
// Use the mapped language name if available, otherwise use the original
const mappedLanguage = params.language.toLowerCase();
const apiLanguage = languageMapping[mappedLanguage] || mappedLanguage;
logger.debug(`Language mapping: "${mappedLanguage}" -> "${apiLanguage}"`);
finalSearchQuery += ` lang:${apiLanguage}`;
}
if (params.extension) {
finalSearchQuery += ` ext:${params.extension}`;
}
// Build the query parameters
const queryParams = new url_1.URLSearchParams({
search_query: finalSearchQuery,
});
// Add optional pagination parameters
if (params.page) {
queryParams.append('page', params.page.toString());
}
if (params.pageLen) {
queryParams.append('pagelen', params.pageLen.toString());
}
// Add optional fields parameter for enhanced responses
if (params.fields) {
queryParams.append('fields', params.fields);
}
// Get credentials for API call
const credentials = (0, transport_util_js_2.getAtlassianCredentials)();
if (!credentials) {
throw new Error('No Atlassian credentials available');
}
// Set useBitbucketAuth to true since we're calling the Bitbucket API
credentials.useBitbucketAuth = true;
// Create API path for Bitbucket code search
const path = `/2.0/workspaces/${params.workspaceSlug}/search/code?${queryParams.toString()}`;
// Track searching code in workspace
logger.debug(`Searching code in workspace: ${params.workspaceSlug}`, {
searchQuery: finalSearchQuery,
path,
});
// Call Bitbucket API with credentials and path
return (0, transport_util_js_1.fetchAtlassian)(credentials, path);
}
/**
* Search for content in Bitbucket
*
* @param params Search parameters
* @returns Content search response
*/
async function searchContent(params) {
const logger = logger_util_js_1.Logger.forContext('services/vendor.atlassian.search.service.ts', 'searchContent');
try {
const credentials = (0, transport_util_js_2.getAtlassianCredentials)();
if (!credentials) {
throw new Error('Atlassian credentials are required for content search');
}
// Build query parameters
const queryParams = new url_1.URLSearchParams();
// Format the query
queryParams.set('q', params.query);
// Add pagination parameters
queryParams.set('pagelen', String(params.limit || 25));
queryParams.set('page', String(params.page || 1));
// Add content type filter if specified
if (params.contentType) {
queryParams.set('content_type', params.contentType);
}
// Construct URL based on whether a repository is specified
let url = `/2.0/search/${params.workspaceSlug}`;
if (params.repoSlug) {
url = `/2.0/search/${params.workspaceSlug}/${params.repoSlug}`;
}
// Add query parameters
url += `?${queryParams.toString()}`;
logger.debug(`Content search request URL: ${url}`);
// Make the request
const response = await (0, transport_util_js_1.fetchAtlassian)(credentials, url);
return response;
}
catch (error) {
logger.error('Content search failed:', error);
throw error;
}
}
exports.default = {
searchCode,
searchCommits,
searchContent,
};