UNPKG

@gohcltech/bitbucket-mcp

Version:

Bitbucket integration for Claude via Model Context Protocol

461 lines 18.3 kB
/** * @fileoverview Consolidated pull request management tool for v2.1. * * Replaces v2.0 tools: list_pull_requests, get_pull_request, create_pull_request, * approve_pull_request, unapprove_pull_request, merge_pull_request, decline_pull_request * Consolidates 7 tools into 1 with action-based routing. */ import { ConsolidatedBaseTool } from './consolidated-base-tool.js'; import { ToolCategory } from '../auth-capabilities.js'; import { ManagePullRequestsArgsSchema, } from '../types/consolidated-tools.js'; import { validateWithSchemaOrThrow, validateRequired } from '../utils/index.js'; /** * Consolidated pull request management tool for v2.1. * * Provides unified pull request operations with action-based routing. * * **Replaces v2.0 tools:** * - list_pull_requests * - get_pull_request * - create_pull_request * - approve_pull_request * - unapprove_pull_request * - merge_pull_request * - decline_pull_request * * **Actions:** * - `list`: List pull requests with optional state filtering * - `get`: Get detailed pull request information * - `create`: Create a new pull request * - `approve`: Approve a pull request * - `unapprove`: Remove approval from a pull request * - `merge`: Merge an approved pull request * - `decline`: Decline a pull request * * @example * ```typescript * // List open pull requests * const response = await tool.execute({ * action: 'list', * workspaceSlug: 'my-workspace', * repoSlug: 'my-repo', * state: 'OPEN' * }); * * // Create a new pull request * const response = await tool.execute({ * action: 'create', * workspaceSlug: 'my-workspace', * repoSlug: 'my-repo', * title: 'Add feature X', * sourceBranch: 'feature/x', * destinationBranch: 'develop' * }); * * // Approve and merge a PR * await tool.execute({ * action: 'approve', * workspaceSlug: 'my-workspace', * repoSlug: 'my-repo', * pullRequestId: 42 * }); * * await tool.execute({ * action: 'merge', * workspaceSlug: 'my-workspace', * repoSlug: 'my-repo', * pullRequestId: 42 * }); * ``` */ export class ManagePullRequestsTool extends ConsolidatedBaseTool { getTools() { return [ { name: 'manage_pull_requests', description: 'Manage pull requests: list, get, create, update, approve, unapprove, merge, decline, or comment', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['list', 'get', 'create', 'update', 'approve', 'unapprove', 'merge', 'decline', 'list_comments', 'add_comment'], description: 'Action to perform: list, get, create, update, approve, unapprove, merge, decline, list_comments, or add_comment', }, workspaceSlug: { type: 'string', description: 'Workspace slug (required)', }, repoSlug: { type: 'string', description: 'Repository slug (required)', }, state: { type: 'string', enum: ['OPEN', 'MERGED', 'DECLINED', 'SUPERSEDED'], description: 'PR state filter (optional for list)', }, pullRequestId: { type: 'number', description: 'Pull request ID (required for get/update/approve/unapprove/merge/decline)', }, title: { type: 'string', description: 'PR title (required for create)', }, sourceBranch: { type: 'string', description: 'Source branch (required for create)', }, destinationBranch: { type: 'string', description: 'Destination branch (optional for create, defaults to main)', }, description: { type: 'string', description: 'PR description (optional for create)', }, closeSourceBranch: { type: 'boolean', description: 'Auto-close source branch on merge (optional for create)', }, content: { type: 'string', description: 'Comment content (required for add_comment)', }, }, required: ['action', 'workspaceSlug', 'repoSlug'], }, }, ]; } getToolCategory() { return ToolCategory.PULL_REQUEST; } /** * Gets the handler function for a specific action. * * Maps action names to their corresponding handler functions. * This is used internally by handleAction() for action-based routing. * * @param action - The action to get a handler for * @returns The handler function for this action, or null if action is unknown */ getActionHandler(action) { const actionHandlers = { 'list': this.handleListPullRequests.bind(this), 'get': this.handleGetPullRequest.bind(this), 'create': this.handleCreatePullRequest.bind(this), 'update': this.handleUpdatePullRequest.bind(this), 'approve': this.handleApprovePullRequest.bind(this), 'unapprove': this.handleUnapprovePullRequest.bind(this), 'merge': this.handleMergePullRequest.bind(this), 'decline': this.handleDeclinePullRequest.bind(this), 'list_comments': this.handleListPRComments.bind(this), 'add_comment': this.handleAddPRComment.bind(this), }; return actionHandlers[action] || null; } validateActionParams(action, args) { // All actions require workspace and repo validateRequired(args, ['workspaceSlug', 'repoSlug'], `manage_pull_requests.${action}`); switch (action) { case 'list': case 'list_comments': // Only requires workspace and repo (and pullRequestId for list_comments) if (action === 'list_comments') { validateRequired(args, ['pullRequestId'], `manage_pull_requests.${action}`); } break; case 'get': case 'approve': case 'unapprove': case 'merge': case 'decline': validateRequired(args, ['pullRequestId'], `manage_pull_requests.${action}`); break; case 'create': validateRequired(args, ['title', 'sourceBranch'], `manage_pull_requests.${action}`); break; case 'update': validateRequired(args, ['pullRequestId'], `manage_pull_requests.${action}`); // At least title or description must be provided if (!args.title && !args.description) { throw new Error('At least one of title or description must be provided for update action'); } break; case 'add_comment': validateRequired(args, ['pullRequestId', 'content'], `manage_pull_requests.${action}`); break; } } /** * Handler for listing pull requests in a repository. * * @param args - Tool arguments (workspace, repo, optional state filter) * @returns List of pull requests with metadata */ async handleListPullRequests(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.list'); const typedArgs = args; // Build query parameters from optional filters const params = {}; if (typedArgs.state) { params.state = typedArgs.state; } // Fetch pull requests const result = await this.client.getPullRequests(typedArgs.workspaceSlug || '', typedArgs.repoSlug || '', params); return { pullRequests: result.values, count: result.values.length, page: result.page || 1, pageLen: result.pagelen || result.values.length, nextUrl: result.next, }; }, { toolName: 'manage_pull_requests', operationName: 'list_pull_requests', args, }); } /** * Handler for getting detailed information about a pull request. * * @param args - Tool arguments (must include pullRequestId) * @returns Detailed PR information */ async handleGetPullRequest(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.get'); const typedArgs = args; // Fetch PR details const pr = await this.client.getPullRequest(typedArgs.workspaceSlug, typedArgs.repoSlug, typedArgs.pullRequestId); return pr; }, { toolName: 'manage_pull_requests', operationName: 'get_pull_request', args, }); } /** * Handler for creating a new pull request. * * @param args - Tool arguments (must include title and sourceBranch) * @returns Created PR information */ async handleCreatePullRequest(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.create'); const typedArgs = args; // Build PR creation payload const payload = { title: typedArgs.title, source: { branch: { name: typedArgs.sourceBranch, }, }, }; if (typedArgs.destinationBranch) { payload.destination = { branch: { name: typedArgs.destinationBranch, }, }; } if (typedArgs.description) { payload.description = typedArgs.description; } if (typedArgs.closeSourceBranch !== undefined) { payload.close_source_on_merge = typedArgs.closeSourceBranch; } // Create PR const pr = await this.client.createPullRequest(typedArgs.workspaceSlug, typedArgs.repoSlug, payload); return pr; }, { toolName: 'manage_pull_requests', operationName: 'create_pull_request', args, }); } /** * Handler for updating a pull request. * * @param args - Tool arguments (must include pullRequestId, and title or description) * @returns Updated PR information */ async handleUpdatePullRequest(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.update'); const typedArgs = args; // Build update payload with provided fields const payload = {}; if (typedArgs.title) { payload.title = typedArgs.title; } if (typedArgs.description) { payload.description = typedArgs.description; } // Update PR const pr = await this.client.updatePullRequest(typedArgs.workspaceSlug, typedArgs.repoSlug, typedArgs.pullRequestId, payload); return pr; }, { toolName: 'manage_pull_requests', operationName: 'update_pull_request', args, }); } /** * Handler for approving a pull request. * * @param args - Tool arguments (must include pullRequestId) * @returns Approval confirmation */ async handleApprovePullRequest(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.approve'); const typedArgs = args; // Approve PR await this.client.approvePullRequest(typedArgs.workspaceSlug, typedArgs.repoSlug, typedArgs.pullRequestId); return { status: 'approved', pullRequestId: typedArgs.pullRequestId, message: `Pull request ${typedArgs.pullRequestId} approved successfully`, }; }, { toolName: 'manage_pull_requests', operationName: 'approve_pull_request', args, }); } /** * Handler for removing approval from a pull request. * * @param args - Tool arguments (must include pullRequestId) * @returns Unapproval confirmation */ async handleUnapprovePullRequest(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.unapprove'); const typedArgs = args; // Unapprove PR await this.client.unapprovePullRequest(typedArgs.workspaceSlug, typedArgs.repoSlug, typedArgs.pullRequestId); return { status: 'unapproved', pullRequestId: typedArgs.pullRequestId, message: `Approval removed from pull request ${typedArgs.pullRequestId}`, }; }, { toolName: 'manage_pull_requests', operationName: 'unapprove_pull_request', args, }); } /** * Handler for merging a pull request. * * @param args - Tool arguments (must include pullRequestId) * @returns Merge confirmation */ async handleMergePullRequest(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.merge'); const typedArgs = args; // Merge PR await this.client.mergePullRequest(typedArgs.workspaceSlug, typedArgs.repoSlug, typedArgs.pullRequestId); return { status: 'merged', pullRequestId: typedArgs.pullRequestId, message: `Pull request ${typedArgs.pullRequestId} merged successfully`, }; }, { toolName: 'manage_pull_requests', operationName: 'merge_pull_request', args, }); } /** * Handler for declining a pull request. * * @param args - Tool arguments (must include pullRequestId) * @returns Decline confirmation */ async handleDeclinePullRequest(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.decline'); const typedArgs = args; // Decline PR await this.client.declinePullRequest(typedArgs.workspaceSlug, typedArgs.repoSlug, typedArgs.pullRequestId); return { status: 'declined', pullRequestId: typedArgs.pullRequestId, message: `Pull request ${typedArgs.pullRequestId} declined`, }; }, { toolName: 'manage_pull_requests', operationName: 'decline_pull_request', args, }); } /** * Handler for listing comments on a pull request. * * @param args - Tool arguments (must include pullRequestId) * @returns List of comments on the PR */ async handleListPRComments(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.list_comments'); const typedArgs = args; // Fetch PR comments const result = await this.client.getComments(typedArgs.workspaceSlug, typedArgs.repoSlug, typedArgs.pullRequestId); return { comments: result.values || [], count: (result.values || []).length, page: result.page || 1, pageLen: result.pagelen || (result.values || []).length, nextUrl: result.next, }; }, { toolName: 'manage_pull_requests', operationName: 'list_pr_comments', args, }); } /** * Handler for adding a comment to a pull request. * * @param args - Tool arguments (must include pullRequestId and content) * @returns Created comment information */ async handleAddPRComment(args) { return this.createToolResponse(async () => { // Validate arguments validateWithSchemaOrThrow(ManagePullRequestsArgsSchema, args, 'manage_pull_requests.add_comment'); const typedArgs = args; // Add comment with proper payload format const comment = await this.client.addComment(typedArgs.workspaceSlug, typedArgs.repoSlug, typedArgs.pullRequestId, { content: { raw: typedArgs.content, markup: 'markdown' } }); return comment; }, { toolName: 'manage_pull_requests', operationName: 'add_pr_comment', args, }); } } //# sourceMappingURL=pull-request-tools.js.map