@stubbedev/atlassian-mcp 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,7 +39,7 @@ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for **s
39
39
  | `bitbucket_search` | Discover resources: `pull_requests` (default), `repos`, or `branches` via `resource` param; `mine=true` for your inbox |
40
40
  | `bitbucket_get_pr` | Full PR details: metadata, commits, comments, blockers, build status, and optional diff |
41
41
  | `bitbucket_mutate` | Create/update a PR, or perform lifecycle actions: `approve`, `unapprove`, `merge`, `decline` |
42
- | `bitbucket_comment` | Add, update, or delete a PR comment; supports inline anchors, multiline, suggestions, and BLOCKER severity |
42
+ | `bitbucket_comment` | Add, update, or delete a PR comment; for code changes use `suggestion` so Bitbucket shows Apply suggestion (no trailing text after a suggestion block) |
43
43
  | `bitbucket_get_file` | Raw file content from Bitbucket at a branch, tag, or commit |
44
44
  | `bitbucket_pr_tasks` | Manage PR tasks (checklist items): `list`, `create`, `resolve`, `reopen`, `delete` |
45
45
 
package/dist/bitbucket.js CHANGED
@@ -180,6 +180,18 @@ function validateCommentText(textValue) {
180
180
  }
181
181
  return trimmed;
182
182
  }
183
+ function validateSuggestionPlacement(textValue) {
184
+ if (!textValue.includes('```suggestion'))
185
+ return;
186
+ const match = textValue.match(/```suggestion[^\n]*\n[\s\S]*?\n```/);
187
+ if (!match || match.index === undefined) {
188
+ throw new Error('Invalid suggestion block format. Use the suggestion field to post code suggestions.');
189
+ }
190
+ const trailingText = textValue.slice(match.index + match[0].length).trim();
191
+ if (trailingText.length > 0) {
192
+ throw new Error('When using ```suggestion```, do not add text after the closing code fence. Put any explanation before the suggestion block or use the suggestion field.');
193
+ }
194
+ }
183
195
  export class BitbucketClient {
184
196
  baseUrl;
185
197
  headers;
@@ -810,10 +822,21 @@ export class BitbucketClient {
810
822
  || args.multilineStartLineType !== undefined)) {
811
823
  throw new Error('Replies must target an existing comment thread only. Omit filePath/line and other anchor fields when replying.');
812
824
  }
813
- let commentText = args.text;
825
+ if (args.text === undefined && args.suggestion === undefined) {
826
+ throw new Error('Either text or suggestion is required when adding a comment.');
827
+ }
828
+ let commentText = args.text ?? '';
814
829
  if (args.suggestion !== undefined) {
815
- const suggestionBlock = `\`\`\`suggestion\n${args.suggestion}\n\`\`\``;
816
- commentText = args.text ? `${args.text}\n\n${suggestionBlock}` : suggestionBlock;
830
+ const suggestion = args.suggestion.trim();
831
+ if (!suggestion) {
832
+ throw new Error('suggestion must not be empty.');
833
+ }
834
+ const suggestionBlock = `\`\`\`suggestion\n${suggestion}\n\`\`\``;
835
+ const prefix = (args.text ?? '').trim();
836
+ commentText = prefix ? `${prefix}\n\n${suggestionBlock}` : suggestionBlock;
837
+ }
838
+ else {
839
+ validateSuggestionPlacement(commentText);
817
840
  }
818
841
  const body = { text: validateCommentText(commentText) };
819
842
  if (args.severity)
package/dist/index.js CHANGED
@@ -348,7 +348,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
348
348
  },
349
349
  {
350
350
  name: 'bitbucket_comment',
351
- description: `Add, update, or delete a PR comment. action defaults to "add". For review feedback, prefer inline comments with a code suggestion. Replies MUST use commentId. Keep comments concise, no emojis. Only call proactively (without being asked) when you are a reviewer on the PR (i.e. "Viewing as" says "you are a reviewer") — never post unsolicited comments on PRs you authored.`,
351
+ description: `Add, update, or delete a PR comment. action defaults to "add". For code changes, ALWAYS use inline comments with suggestion when exact replacement code is available. Keep any explanatory text before the suggestion block only (never after), or Bitbucket may hide Apply suggestion. Replies MUST use commentId. Keep comments concise, no emojis. Only call proactively (without being asked) when you are a reviewer on the PR (i.e. "Viewing as" says "you are a reviewer") — never post unsolicited comments on PRs you authored.`,
352
352
  inputSchema: {
353
353
  type: 'object',
354
354
  properties: {
@@ -359,7 +359,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
359
359
  repo: { type: 'string', description: 'Alias for repoSlug' },
360
360
  prId: { type: 'number', description: 'Pull request number' },
361
361
  commentId: { type: 'number', description: 'Comment ID to reply to, update, or delete' },
362
- text: { type: 'string', description: 'Comment text. No filler, no emojis. Required for add/update.' },
362
+ text: { type: 'string', description: 'Comment text for add/update. No filler, no emojis. If suggestion is used, keep this optional and brief; it is placed before the suggestion block.' },
363
363
  filePath: { type: 'string', description: 'File path for inline comment (must pair with line)' },
364
364
  srcPath: { type: 'string', description: 'Source path if file was renamed (optional, defaults to filePath)' },
365
365
  line: { type: 'number', description: 'Line number to anchor inline comment (must pair with filePath)' },
@@ -367,7 +367,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
367
367
  fileType: { type: 'string', enum: ['TO', 'FROM'], description: 'Diff side: TO (new, default) or FROM (old)' },
368
368
  multilineStartLine: { type: 'number', description: 'First line of multiline anchor (pair with line as last line)' },
369
369
  multilineStartLineType: { type: 'string', enum: ['ADDED', 'REMOVED', 'CONTEXT'], description: 'Line type for multilineStartLine' },
370
- suggestion: { type: 'string', description: 'Replacement code to suggest (strongly preferred for code changes). Requires filePath + line.' },
370
+ suggestion: { type: 'string', description: 'Replacement code to suggest. Use whenever proposing a concrete code change. Posted as the final ```suggestion``` block so Apply suggestion appears. Requires filePath + line.' },
371
371
  state: { type: 'string', enum: ['OPEN', 'RESOLVED'], description: 'Task state for BLOCKER comments (update only)' },
372
372
  threadResolved: { type: 'boolean', description: 'Resolve/reopen normal comment thread (update only)' },
373
373
  severity: { type: 'string', enum: ['NORMAL', 'BLOCKER'], description: 'Comment severity. BLOCKER = checklist task.' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stubbedev/atlassian-mcp",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "MCP server for self-hosted Jira and Bitbucket",
5
5
  "license": "MIT",
6
6
  "type": "module",