add_comment
Add comments to Bitbucket pull requests with options for general feedback, replies, inline code comments, and code suggestions. Supports line numbers, code snippets, and multi-line replacements for precise feedback.
Instructions
Add a comment to a pull request. Supports: 1) General PR comments, 2) Replies to existing comments, 3) Inline comments on specific code lines (using line_number OR code_snippet), 4) Code suggestions for single or multi-line replacements. For inline comments, you can either provide exact line_number or use code_snippet to auto-detect the line.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| code_snippet | No | Exact code text from the diff to find and comment on. Use this instead of line_number for auto-detection. Must match exactly including whitespace (optional) | |
| comment_text | Yes | The main comment text. For suggestions, this is the explanation before the code suggestion. | |
| file_path | No | File path for inline comment. Required for inline comments. Example: "src/components/Button.js" (optional) | |
| line_number | No | Exact line number in the file. Use this OR code_snippet, not both. Required with file_path unless using code_snippet (optional) | |
| line_type | No | Type of line: ADDED (green/new lines), REMOVED (red/deleted lines), or CONTEXT (unchanged lines). Default: CONTEXT | |
| match_strategy | No | How to handle multiple matches when using code_snippet. "strict": fail with detailed error showing all matches. "best": automatically pick the highest confidence match. Default: "strict" | |
| parent_comment_id | No | ID of comment to reply to. Use this to create threaded conversations (optional) | |
| pull_request_id | Yes | Pull request ID | |
| repository | Yes | Repository slug (e.g., "my-repo") | |
| search_context | No | Additional context lines to help locate the exact position when using code_snippet. Useful when the same code appears multiple times (optional) | |
| suggestion | No | Replacement code for a suggestion. Creates a suggestion block that can be applied in Bitbucket UI. Requires file_path and line_number. For multi-line, include newlines in the string (optional) | |
| suggestion_end_line | No | For multi-line suggestions: the last line number to replace. If not provided, only replaces the single line at line_number (optional) | |
| workspace | Yes | Bitbucket workspace/project key (e.g., "PROJ") |
Implementation Reference
- Main execution logic for the add_comment tool: argument validation, line resolution from code snippets, suggestion formatting, API request construction for Bitbucket Server/Cloud, and posting the comment with response formatting.async handleAddComment(args: any) { if (!isAddCommentArgs(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for add_comment' ); } let { workspace, repository, pull_request_id, comment_text, parent_comment_id, file_path, line_number, line_type, suggestion, suggestion_end_line, code_snippet, search_context, match_strategy = 'strict' } = args; let sequentialPosition: number | undefined; if (code_snippet && !line_number && file_path) { try { const resolved = await this.resolveLineFromCode( workspace, repository, pull_request_id, file_path, code_snippet, search_context, match_strategy ); line_number = resolved.line_number; line_type = resolved.line_type; sequentialPosition = resolved.sequential_position; } catch (error) { throw error; } } if (suggestion && (!file_path || !line_number)) { throw new McpError( ErrorCode.InvalidParams, 'Suggestions require file_path and line_number to be specified' ); } const isInlineComment = file_path !== undefined && line_number !== undefined; let finalCommentText = comment_text; if (suggestion) { finalCommentText = formatSuggestionComment( comment_text, suggestion, line_number, suggestion_end_line || line_number ); } try { let apiPath: string; let requestBody: any; if (this.apiClient.getIsServer()) { // Bitbucket Server API apiPath = `/rest/api/1.0/projects/${workspace}/repos/${repository}/pull-requests/${pull_request_id}/comments`; requestBody = { text: finalCommentText }; if (parent_comment_id !== undefined) { requestBody.parent = { id: parent_comment_id }; } if (isInlineComment) { requestBody.anchor = { line: line_number, lineType: line_type || 'CONTEXT', fileType: line_type === 'REMOVED' ? 'FROM' : 'TO', path: file_path, diffType: 'EFFECTIVE' }; } } else { // Bitbucket Cloud API apiPath = `/repositories/${workspace}/${repository}/pullrequests/${pull_request_id}/comments`; requestBody = { content: { raw: finalCommentText } }; if (parent_comment_id !== undefined) { requestBody.parent = { id: parent_comment_id }; } if (isInlineComment) { requestBody.inline = { to: line_number, path: file_path }; } } const comment = await this.apiClient.makeRequest<any>('post', apiPath, requestBody); const responseMessage = suggestion ? 'Comment with code suggestion added successfully' : (isInlineComment ? 'Inline comment added successfully' : 'Comment added successfully'); return { content: [ { type: 'text', text: JSON.stringify({ message: responseMessage, comment: { id: comment.id, text: this.apiClient.getIsServer() ? comment.text : comment.content.raw, author: this.apiClient.getIsServer() ? comment.author.displayName : comment.user.display_name, created_on: this.apiClient.getIsServer() ? new Date(comment.createdDate).toLocaleString() : comment.created_on, file_path: isInlineComment ? file_path : undefined, line_number: isInlineComment ? line_number : undefined, line_type: isInlineComment ? (line_type || 'CONTEXT') : undefined, has_suggestion: !!suggestion, suggestion_lines: suggestion ? (suggestion_end_line ? `${line_number}-${suggestion_end_line}` : `${line_number}`) : undefined } }, null, 2), }, ], }; } catch (error) { return this.apiClient.handleApiError(error, `adding ${isInlineComment ? 'inline ' : ''}comment to pull request ${pull_request_id} in ${workspace}/${repository}`); } }
- src/tools/definitions.ts:142-216 (schema)JSON Schema definition for the add_comment tool input parameters, including descriptions and validation rules for all fields like workspace, comment_text, file_path, line_number, suggestion, code_snippet, etc.name: 'add_comment', description: 'Add a comment to a pull request. Supports: 1) General PR comments, 2) Replies to existing comments, 3) Inline comments on specific code lines (using line_number OR code_snippet), 4) Code suggestions for single or multi-line replacements. For inline comments, you can either provide exact line_number or use code_snippet to auto-detect the line.', inputSchema: { type: 'object', properties: { workspace: { type: 'string', description: 'Bitbucket workspace/project key (e.g., "PROJ")', }, repository: { type: 'string', description: 'Repository slug (e.g., "my-repo")', }, pull_request_id: { type: 'number', description: 'Pull request ID', }, comment_text: { type: 'string', description: 'The main comment text. For suggestions, this is the explanation before the code suggestion.', }, parent_comment_id: { type: 'number', description: 'ID of comment to reply to. Use this to create threaded conversations (optional)', }, file_path: { type: 'string', description: 'File path for inline comment. Required for inline comments. Example: "src/components/Button.js" (optional)', }, line_number: { type: 'number', description: 'Exact line number in the file. Use this OR code_snippet, not both. Required with file_path unless using code_snippet (optional)', }, line_type: { type: 'string', description: 'Type of line: ADDED (green/new lines), REMOVED (red/deleted lines), or CONTEXT (unchanged lines). Default: CONTEXT', enum: ['ADDED', 'REMOVED', 'CONTEXT'], }, suggestion: { type: 'string', description: 'Replacement code for a suggestion. Creates a suggestion block that can be applied in Bitbucket UI. Requires file_path and line_number. For multi-line, include newlines in the string (optional)', }, suggestion_end_line: { type: 'number', description: 'For multi-line suggestions: the last line number to replace. If not provided, only replaces the single line at line_number (optional)', }, code_snippet: { type: 'string', description: 'Exact code text from the diff to find and comment on. Use this instead of line_number for auto-detection. Must match exactly including whitespace (optional)', }, search_context: { type: 'object', properties: { before: { type: 'array', items: { type: 'string' }, description: 'Array of code lines that appear BEFORE the target line. Helps disambiguate when code_snippet appears multiple times', }, after: { type: 'array', items: { type: 'string' }, description: 'Array of code lines that appear AFTER the target line. Helps disambiguate when code_snippet appears multiple times', }, }, description: 'Additional context lines to help locate the exact position when using code_snippet. Useful when the same code appears multiple times (optional)', }, match_strategy: { type: 'string', enum: ['strict', 'best'], description: 'How to handle multiple matches when using code_snippet. "strict": fail with detailed error showing all matches. "best": automatically pick the highest confidence match. Default: "strict"', }, }, required: ['workspace', 'repository', 'pull_request_id', 'comment_text'], }, },
- src/index.ts:104-105 (registration)Tool registration and dispatch: routes 'add_comment' calls to the PullRequestHandlers.handleAddComment method.case 'add_comment': return this.pullRequestHandlers.handleAddComment(request.params.arguments);
- src/types/guards.ts:74-112 (schema)Type guard function isAddCommentArgs used in the handler for runtime input validation matching the tool schema.export const isAddCommentArgs = ( args: any ): args is { workspace: string; repository: string; pull_request_id: number; comment_text: string; parent_comment_id?: number; file_path?: string; line_number?: number; line_type?: 'ADDED' | 'REMOVED' | 'CONTEXT'; suggestion?: string; suggestion_end_line?: number; code_snippet?: string; search_context?: { before?: string[]; after?: string[]; }; match_strategy?: 'strict' | 'best'; } => typeof args === 'object' && args !== null && typeof args.workspace === 'string' && typeof args.repository === 'string' && typeof args.pull_request_id === 'number' && typeof args.comment_text === 'string' && (args.parent_comment_id === undefined || typeof args.parent_comment_id === 'number') && (args.file_path === undefined || typeof args.file_path === 'string') && (args.line_number === undefined || typeof args.line_number === 'number') && (args.line_type === undefined || ['ADDED', 'REMOVED', 'CONTEXT'].includes(args.line_type)) && (args.suggestion === undefined || typeof args.suggestion === 'string') && (args.suggestion_end_line === undefined || typeof args.suggestion_end_line === 'number') && (args.code_snippet === undefined || typeof args.code_snippet === 'string') && (args.search_context === undefined || ( typeof args.search_context === 'object' && (args.search_context.before === undefined || Array.isArray(args.search_context.before)) && (args.search_context.after === undefined || Array.isArray(args.search_context.after)) )) && (args.match_strategy === undefined || ['strict', 'best'].includes(args.match_strategy));