@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
162 lines (161 loc) • 8.41 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const atlassian_pullrequests_base_controller_js_1 = require("./atlassian.pullrequests.base.controller.js");
/**
* List comments on a Bitbucket pull request
* @param options - Options including workspace slug, repo slug, and pull request ID
* @returns Promise with formatted pull request comments as Markdown content
*/
async function listComments(options) {
const methodLogger = atlassian_pullrequests_base_controller_js_1.Logger.forContext('controllers/atlassian.pullrequests.comments.controller.ts', 'listComments');
try {
// Create defaults object
const defaults = {
limit: atlassian_pullrequests_base_controller_js_1.DEFAULT_PAGE_SIZE,
};
// Apply defaults
const mergedOptions = (0, atlassian_pullrequests_base_controller_js_1.applyDefaults)(options, defaults);
// Handle optional workspaceSlug - get default if not provided
if (!mergedOptions.workspaceSlug) {
methodLogger.debug('No workspace provided, fetching default workspace');
const defaultWorkspace = await (0, atlassian_pullrequests_base_controller_js_1.getDefaultWorkspace)();
if (!defaultWorkspace) {
throw new Error('Could not determine a default workspace. Please provide a workspaceSlug.');
}
mergedOptions.workspaceSlug = defaultWorkspace;
methodLogger.debug(`Using default workspace: ${mergedOptions.workspaceSlug}`);
}
const { workspaceSlug, repoSlug, prId } = mergedOptions;
// Validate required parameters
if (!workspaceSlug || !repoSlug || !prId) {
throw new Error('Workspace slug, repository slug, and pull request ID are required');
}
methodLogger.debug(`Listing comments for PR ${workspaceSlug}/${repoSlug}/${prId}`, { limit: mergedOptions.limit, cursor: mergedOptions.cursor });
// Map controller options to service parameters
const serviceParams = {
workspace: workspaceSlug,
repo_slug: repoSlug,
pull_request_id: parseInt(prId, 10),
pagelen: mergedOptions.limit,
page: mergedOptions.cursor
? parseInt(mergedOptions.cursor, 10)
: undefined,
};
// Get comments from the service
const commentsData = await atlassian_pullrequests_base_controller_js_1.atlassianPullRequestsService.getComments(serviceParams);
methodLogger.debug(`Retrieved ${commentsData.values?.length || 0} comments`);
// If no comments found, return a simple message
if (!commentsData.values || commentsData.values.length === 0) {
return { content: 'No comments found on this pull request.' };
}
// Extract pagination information
const pagination = (0, atlassian_pullrequests_base_controller_js_1.extractPaginationInfo)(commentsData, atlassian_pullrequests_base_controller_js_1.PaginationType.PAGE);
// Enhance comments with code snippets (for inline comments)
const enhancedComments = await (0, atlassian_pullrequests_base_controller_js_1.enhanceCommentsWithSnippets)(commentsData, 'listComments');
// Format the comments using the formatter
const formattedComments = (0, atlassian_pullrequests_base_controller_js_1.formatPullRequestComments)(enhancedComments, prId);
// Create the final content by combining formatted comments with pagination info
let finalContent = formattedComments;
// Add pagination information if available
if (pagination &&
(pagination.hasMore || pagination.count !== undefined)) {
const paginationString = (0, atlassian_pullrequests_base_controller_js_1.formatPagination)(pagination);
finalContent += '\n\n' + paginationString;
}
return {
content: finalContent,
};
}
catch (error) {
// Use the standardized error handler
throw (0, atlassian_pullrequests_base_controller_js_1.handleControllerError)(error, {
entityType: 'Pull Request Comments',
operation: 'listing',
source: 'controllers/atlassian.pullrequests.comments.controller.ts@listComments',
additionalInfo: { options },
});
}
}
/**
* Add a comment to a Bitbucket pull request
* @param options - Options including workspace slug, repo slug, PR ID, and comment content
* @returns Promise with a success message as content
*/
async function addComment(options) {
const methodLogger = atlassian_pullrequests_base_controller_js_1.Logger.forContext('controllers/atlassian.pullrequests.comments.controller.ts', 'addComment');
try {
// Apply defaults if needed (none for this operation)
const mergedOptions = (0, atlassian_pullrequests_base_controller_js_1.applyDefaults)(options, {});
// Handle optional workspaceSlug - get default if not provided
if (!mergedOptions.workspaceSlug) {
methodLogger.debug('No workspace provided, fetching default workspace');
const defaultWorkspace = await (0, atlassian_pullrequests_base_controller_js_1.getDefaultWorkspace)();
if (!defaultWorkspace) {
throw new Error('Could not determine a default workspace. Please provide a workspaceSlug.');
}
mergedOptions.workspaceSlug = defaultWorkspace;
methodLogger.debug(`Using default workspace: ${mergedOptions.workspaceSlug}`);
}
const { workspaceSlug, repoSlug, prId, content, inline } = mergedOptions;
// Validate required parameters
if (!workspaceSlug || !repoSlug || !prId || !content) {
throw new Error('Workspace slug, repository slug, pull request ID, and comment content are required');
}
// For inline comments, both file path and line number are required
if (inline && (!inline.path || inline.line === undefined)) {
throw new Error('Both file path and line number are required for inline comments');
}
// Prepare the raw content, applying any Bitbucket-specific markdown optimizations
const optimizedContent = (0, atlassian_pullrequests_base_controller_js_1.optimizeBitbucketMarkdown)(content);
methodLogger.debug(`Adding${inline ? ' inline' : ''} comment to PR ${workspaceSlug}/${repoSlug}/${prId}`, {
contentLength: optimizedContent.length,
isInline: !!inline,
inlinePath: inline?.path,
inlineLine: inline?.line,
});
// Map controller options to service parameters
const serviceParams = {
workspace: workspaceSlug,
repo_slug: repoSlug,
pull_request_id: parseInt(prId, 10),
content: {
raw: optimizedContent,
},
};
// For inline comments, add the inline property
if (inline) {
serviceParams.inline = {
path: inline.path,
to: inline.line,
};
}
// For replies, add the parent property
if (mergedOptions.parentId) {
serviceParams.parent = {
id: parseInt(mergedOptions.parentId, 10),
};
}
// Create the comment through the service
const commentResult = await atlassian_pullrequests_base_controller_js_1.atlassianPullRequestsService.createComment(serviceParams);
methodLogger.debug('Comment created successfully', {
commentId: commentResult.id,
isInline: !!inline,
});
// Return a success message
const commentType = inline ? 'inline' : '';
return {
content: `${commentType} Comment successfully added to pull request #${prId}. Comment ID: ${commentResult.id}`,
};
}
catch (error) {
// Use the standardized error handler
throw (0, atlassian_pullrequests_base_controller_js_1.handleControllerError)(error, {
entityType: 'Pull Request Comment',
operation: 'adding',
source: 'controllers/atlassian.pullrequests.comments.controller.ts@addComment',
additionalInfo: { options },
});
}
}
// Export the controller functions
exports.default = { listComments, addComment };