@stubbedev/atlassian-mcp 0.1.1 → 0.1.4

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/dist/bitbucket.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { execSync } from 'child_process';
2
+ const EMOJI_RE = /\p{Extended_Pictographic}/u;
2
3
  function safeExec(cmd) {
3
4
  try {
4
5
  return execSync(cmd, { encoding: 'utf-8' }).trim();
@@ -136,6 +137,16 @@ function formatBitbucketError(status, method, path, details) {
136
137
  return `${prefix}. Conflict (often stale version/state). Refresh and retry. ${details}`.trim();
137
138
  return details ? `${prefix}. ${details}` : prefix;
138
139
  }
140
+ function validateCommentText(textValue) {
141
+ const trimmed = textValue.trim();
142
+ if (!trimmed) {
143
+ throw new Error('Bitbucket comment text must not be empty.');
144
+ }
145
+ if (EMOJI_RE.test(trimmed)) {
146
+ throw new Error('Bitbucket comments must not include emoji. Use concise plain text only.');
147
+ }
148
+ return trimmed;
149
+ }
139
150
  export class BitbucketClient {
140
151
  baseUrl;
141
152
  headers;
@@ -510,7 +521,7 @@ export class BitbucketClient {
510
521
  }
511
522
  async addPrComment(args) {
512
523
  const { projectKey, repoSlug } = this.resolveProjectAndRepo(args.projectKey, args.repoSlug);
513
- const body = { text: args.text };
524
+ const body = { text: validateCommentText(args.text) };
514
525
  if (args.parentCommentId)
515
526
  body.parent = { id: args.parentCommentId };
516
527
  const created = await this.request('POST', `/projects/${projectKey}/repos/${repoSlug}/pull-requests/${args.prId}/comments`, body);
@@ -531,7 +542,7 @@ export class BitbucketClient {
531
542
  throw new Error(`Comment #${args.commentId} not found.`);
532
543
  const body = {
533
544
  version: current.version,
534
- text: args.text ?? current.text,
545
+ text: args.text !== undefined ? validateCommentText(args.text) : current.text,
535
546
  };
536
547
  if (args.state)
537
548
  body.state = args.state;
package/dist/index.js CHANGED
@@ -162,12 +162,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
162
162
  },
163
163
  {
164
164
  name: 'jira_add_comment',
165
- description: 'Use when you want to leave a comment on a Jira ticket.',
165
+ description: 'Use when you want to leave a comment on a Jira ticket. Keep comments concise, plain text, and free of filler. Never include emojis.',
166
166
  inputSchema: {
167
167
  type: 'object',
168
168
  properties: {
169
169
  issueKey: { type: 'string', description: 'Jira issue key' },
170
- body: { type: 'string', description: 'Comment text (plain text or Jira wiki markup)' },
170
+ body: { type: 'string', description: 'Concise comment text only. No filler. Do not include emojis.' },
171
171
  },
172
172
  required: ['issueKey', 'body'],
173
173
  },
@@ -406,7 +406,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
406
406
  },
407
407
  {
408
408
  name: 'bitbucket_add_pr_comment',
409
- description: 'Use when you want to add a PR review comment or reply to an existing thread. You can pass projectKey/repoSlug or project/repo.',
409
+ description: 'Use when you want to add a PR review comment or reply to an existing thread. Keep comments concise, plain text, and free of filler. Never include emojis. You can pass projectKey/repoSlug or project/repo.',
410
410
  inputSchema: {
411
411
  type: 'object',
412
412
  properties: {
@@ -416,14 +416,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
416
416
  repo: { type: 'string', description: 'Alias for repoSlug' },
417
417
  prId: { type: 'number', description: 'Pull request number (PR ID)' },
418
418
  parentCommentId: { type: 'number', description: 'Parent comment ID for reply mode (optional)' },
419
- text: { type: 'string', description: 'Comment text' },
419
+ text: { type: 'string', description: 'Concise comment text only. No filler. Do not include emojis.' },
420
420
  },
421
421
  required: ['prId', 'text'],
422
422
  },
423
423
  },
424
424
  {
425
425
  name: 'bitbucket_update_pr_comment',
426
- description: 'Use when you want to edit PR comments, resolve/reopen them, or mark comments as task-style BLOCKER items. You can pass projectKey/repoSlug or project/repo.',
426
+ description: 'Use when you want to edit PR comments, resolve/reopen them, or mark comments as task-style BLOCKER items. Keep comments concise, plain text, and free of filler. Never include emojis. You can pass projectKey/repoSlug or project/repo.',
427
427
  inputSchema: {
428
428
  type: 'object',
429
429
  properties: {
@@ -433,7 +433,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
433
433
  repo: { type: 'string', description: 'Alias for repoSlug' },
434
434
  prId: { type: 'number', description: 'Pull request number (PR ID)' },
435
435
  commentId: { type: 'number', description: 'Comment ID to update' },
436
- text: { type: 'string', description: 'New comment text (optional)' },
436
+ text: { type: 'string', description: 'New concise comment text only. No filler. Do not include emojis. (optional)' },
437
437
  state: { type: 'string', enum: ['OPEN', 'RESOLVED'], description: 'Comment state (optional)' },
438
438
  severity: { type: 'string', enum: ['NORMAL', 'BLOCKER'], description: 'Comment severity (optional). BLOCKER marks it as a task/checklist item.' },
439
439
  },
package/dist/jira.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { execSync } from 'child_process';
2
2
  const JIRA_KEY_IN_BRANCH_RE = /\b([A-Z][A-Z0-9]+)-\d+\b/;
3
+ const EMOJI_RE = /\p{Extended_Pictographic}/u;
3
4
  function text(t) {
4
5
  return { content: [{ type: 'text', text: t }] };
5
6
  }
@@ -76,6 +77,16 @@ function formatJiraError(status, method, path, details) {
76
77
  return `${prefix}. Conflict. Refresh and retry. ${details}`.trim();
77
78
  return details ? `${prefix}. ${details}` : prefix;
78
79
  }
80
+ function validateCommentBody(body) {
81
+ const trimmed = body.trim();
82
+ if (!trimmed) {
83
+ throw new Error('Jira comment body must not be empty.');
84
+ }
85
+ if (EMOJI_RE.test(trimmed)) {
86
+ throw new Error('Jira comments must not include emoji. Use concise plain text only.');
87
+ }
88
+ return trimmed;
89
+ }
79
90
  export class JiraClient {
80
91
  baseUrl;
81
92
  headers;
@@ -246,7 +257,7 @@ export class JiraClient {
246
257
  return text(`${data.total} comment(s) on ${issueKey}${page}:\n\n${blocks.join('\n\n')}`);
247
258
  }
248
259
  async addComment(args) {
249
- await this.request('POST', `/issue/${args.issueKey}/comment`, { body: args.body });
260
+ await this.request('POST', `/issue/${args.issueKey}/comment`, { body: validateCommentBody(args.body) });
250
261
  return text(`Comment added to ${args.issueKey}.`);
251
262
  }
252
263
  async transitionIssue(args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stubbedev/atlassian-mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "MCP server for self-hosted Jira and Bitbucket",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",