@jagadeesh52423/atlassian-mcp-server 1.0.2 → 1.1.0

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.
Files changed (85) hide show
  1. package/dist/clients/base-client.d.ts +26 -0
  2. package/dist/clients/base-client.d.ts.map +1 -0
  3. package/dist/clients/base-client.js +58 -0
  4. package/dist/clients/base-client.js.map +1 -0
  5. package/dist/clients/bitbucket-client.d.ts +17 -0
  6. package/dist/clients/bitbucket-client.d.ts.map +1 -0
  7. package/dist/clients/bitbucket-client.js +36 -0
  8. package/dist/clients/bitbucket-client.js.map +1 -0
  9. package/dist/clients/confluence-client.d.ts +8 -0
  10. package/dist/clients/confluence-client.d.ts.map +1 -0
  11. package/dist/clients/confluence-client.js +16 -0
  12. package/dist/clients/confluence-client.js.map +1 -0
  13. package/dist/clients/index.d.ts +5 -0
  14. package/dist/clients/index.d.ts.map +1 -0
  15. package/dist/clients/index.js +5 -0
  16. package/dist/clients/index.js.map +1 -0
  17. package/dist/clients/jira-client.d.ts +8 -0
  18. package/dist/clients/jira-client.d.ts.map +1 -0
  19. package/dist/clients/jira-client.js +16 -0
  20. package/dist/clients/jira-client.js.map +1 -0
  21. package/dist/constants.d.ts +67 -0
  22. package/dist/constants.d.ts.map +1 -0
  23. package/dist/constants.js +78 -0
  24. package/dist/constants.js.map +1 -0
  25. package/dist/error-handler.d.ts +83 -0
  26. package/dist/error-handler.d.ts.map +1 -0
  27. package/dist/error-handler.js +141 -0
  28. package/dist/error-handler.js.map +1 -0
  29. package/dist/formatters/adf-formatter.d.ts +25 -0
  30. package/dist/formatters/adf-formatter.d.ts.map +1 -0
  31. package/dist/formatters/adf-formatter.js +609 -0
  32. package/dist/formatters/adf-formatter.js.map +1 -0
  33. package/dist/formatters/index.d.ts +2 -0
  34. package/dist/formatters/index.d.ts.map +1 -0
  35. package/dist/formatters/index.js +2 -0
  36. package/dist/formatters/index.js.map +1 -0
  37. package/dist/handlers/bitbucket-handlers.d.ts +94 -0
  38. package/dist/handlers/bitbucket-handlers.d.ts.map +1 -0
  39. package/dist/handlers/bitbucket-handlers.js +251 -0
  40. package/dist/handlers/bitbucket-handlers.js.map +1 -0
  41. package/dist/handlers/confluence-handlers.d.ts +106 -0
  42. package/dist/handlers/confluence-handlers.d.ts.map +1 -0
  43. package/dist/handlers/confluence-handlers.js +340 -0
  44. package/dist/handlers/confluence-handlers.js.map +1 -0
  45. package/dist/handlers/index.d.ts +4 -0
  46. package/dist/handlers/index.d.ts.map +1 -0
  47. package/dist/handlers/index.js +4 -0
  48. package/dist/handlers/index.js.map +1 -0
  49. package/dist/handlers/jira-handlers.d.ts +237 -0
  50. package/dist/handlers/jira-handlers.d.ts.map +1 -0
  51. package/dist/handlers/jira-handlers.js +816 -0
  52. package/dist/handlers/jira-handlers.js.map +1 -0
  53. package/dist/index.js +189 -2536
  54. package/dist/index.js.map +1 -1
  55. package/dist/services/bitbucket.d.ts +11 -1
  56. package/dist/services/bitbucket.d.ts.map +1 -1
  57. package/dist/services/bitbucket.js +78 -0
  58. package/dist/services/bitbucket.js.map +1 -1
  59. package/dist/services/confluence.d.ts +5 -0
  60. package/dist/services/confluence.d.ts.map +1 -1
  61. package/dist/services/confluence.js +59 -4
  62. package/dist/services/confluence.js.map +1 -1
  63. package/dist/services/jira.d.ts +57 -0
  64. package/dist/services/jira.d.ts.map +1 -1
  65. package/dist/services/jira.js +395 -24
  66. package/dist/services/jira.js.map +1 -1
  67. package/dist/tools/bitbucket-tools.d.ts +6 -0
  68. package/dist/tools/bitbucket-tools.d.ts.map +1 -0
  69. package/dist/tools/bitbucket-tools.js +386 -0
  70. package/dist/tools/bitbucket-tools.js.map +1 -0
  71. package/dist/tools/confluence-tools.d.ts +17 -0
  72. package/dist/tools/confluence-tools.d.ts.map +1 -0
  73. package/dist/tools/confluence-tools.js +440 -0
  74. package/dist/tools/confluence-tools.js.map +1 -0
  75. package/dist/tools/index.d.ts +8 -0
  76. package/dist/tools/index.d.ts.map +1 -0
  77. package/dist/tools/index.js +15 -0
  78. package/dist/tools/index.js.map +1 -0
  79. package/dist/tools/jira-tools.d.ts +6 -0
  80. package/dist/tools/jira-tools.d.ts.map +1 -0
  81. package/dist/tools/jira-tools.js +1032 -0
  82. package/dist/tools/jira-tools.js.map +1 -0
  83. package/dist/types.d.ts +23 -0
  84. package/dist/types.d.ts.map +1 -1
  85. package/package.json +1 -1
@@ -0,0 +1,816 @@
1
+ import { successResponse, jsonResponse, errorResponse, ERROR_TIPS, } from '../error-handler.js';
2
+ import { ICONS } from '../constants.js';
3
+ /**
4
+ * Jira tool handlers
5
+ */
6
+ export class JiraHandlers {
7
+ service;
8
+ constructor(service) {
9
+ this.service = service;
10
+ }
11
+ async searchIssues(args) {
12
+ const { query, assignee, status, project, labels, reporter, createdAfter, updatedAfter, jql, fields, limit } = args;
13
+ try {
14
+ const issues = await this.service.searchIssues({ query, assignee, status, project, labels, reporter, createdAfter, updatedAfter, jql, fields, limit });
15
+ return jsonResponse(issues);
16
+ }
17
+ catch (error) {
18
+ return errorResponse(error, {
19
+ operation: 'Failed to search Jira issues',
20
+ params: { Query: query || 'none', Assignee: assignee || 'none', JQL: jql || 'none', Limit: limit || 50 },
21
+ tip: ERROR_TIPS.JIRA_SEARCH,
22
+ });
23
+ }
24
+ }
25
+ async getIssue(args) {
26
+ const { issueKey } = args;
27
+ try {
28
+ const issue = await this.service.getIssue(issueKey);
29
+ return jsonResponse(issue);
30
+ }
31
+ catch (error) {
32
+ return errorResponse(error, {
33
+ operation: 'Failed to get Jira issue',
34
+ params: { 'Issue Key': issueKey },
35
+ tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
36
+ });
37
+ }
38
+ }
39
+ async createIssue(args) {
40
+ const { projectKey, summary, description, issueType, duedate, priority, labels, components, assignee, customFields } = args;
41
+ try {
42
+ const additionalFields = { duedate, priority, labels, components, assignee, customFields };
43
+ const issue = await this.service.createIssue(projectKey, summary, description, issueType, additionalFields);
44
+ const optionalFields = [];
45
+ if (duedate)
46
+ optionalFields.push(`${ICONS.DATE} **Due Date:** ${duedate}`);
47
+ if (priority)
48
+ optionalFields.push(`${ICONS.PRIORITY} **Priority:** ${priority}`);
49
+ if (labels && labels.length > 0)
50
+ optionalFields.push(`${ICONS.LABELS} **Labels:** ${labels.join(', ')}`);
51
+ if (components && components.length > 0)
52
+ optionalFields.push(`${ICONS.COMPONENTS} **Components:** ${components.join(', ')}`);
53
+ if (assignee)
54
+ optionalFields.push(`${ICONS.ASSIGNEE} **Assignee:** ${issue.assignee || assignee}`);
55
+ const optionalFieldsText = optionalFields.length > 0 ? `\n\n${optionalFields.join('\n')}` : '';
56
+ const domain = process.env.JIRA_DOMAIN || process.env.ATLASSIAN_DOMAIN || 'your-domain.atlassian.net';
57
+ return successResponse(`${ICONS.SUCCESS} **Jira Issue Created Successfully**\n\n` +
58
+ `${ICONS.TICKET} **Issue Key:** ${issue.key}\n` +
59
+ `${ICONS.SUMMARY} **Summary:** ${issue.summary}\n` +
60
+ `📋 **Issue Type:** ${issueType || 'Task'}\n` +
61
+ `${ICONS.STATUS} **Status:** ${issue.status}\n` +
62
+ `${ICONS.DATE} **Created:** ${new Date(issue.created).toLocaleString()}${optionalFieldsText}\n\n` +
63
+ `${ICONS.DESCRIPTION} **Description:** Successfully created with provided content\n\n` +
64
+ `${ICONS.LINK} **Issue URL:** [View Issue](https://${domain}/browse/${issue.key})`);
65
+ }
66
+ catch (error) {
67
+ return errorResponse(error, {
68
+ operation: 'Failed to create Jira issue',
69
+ params: {
70
+ Project: projectKey,
71
+ Summary: summary,
72
+ 'Issue Type': issueType || 'Task',
73
+ 'Due Date': duedate || 'none',
74
+ Priority: priority || 'none',
75
+ Labels: labels?.join(', ') || 'none',
76
+ Components: components?.join(', ') || 'none',
77
+ Assignee: assignee || 'none',
78
+ },
79
+ tip: ERROR_TIPS.JIRA_ISSUE_CREATE + ' For custom fields, ensure field IDs are correct.',
80
+ });
81
+ }
82
+ }
83
+ async updateIssue(args) {
84
+ const { issueKey, summary, description, assignee } = args;
85
+ try {
86
+ const issue = await this.service.updateIssue(issueKey, { summary, description, assignee });
87
+ const updatedFields = [];
88
+ if (summary)
89
+ updatedFields.push(`${ICONS.SUMMARY} **Summary:** Updated to "${issue.summary}"`);
90
+ if (description)
91
+ updatedFields.push(`${ICONS.DESCRIPTION} **Description:** Updated successfully`);
92
+ if (assignee)
93
+ updatedFields.push(`${ICONS.ASSIGNEE} **Assignee:** ${issue.assignee || assignee}`);
94
+ const updatedFieldsText = updatedFields.length > 0 ? `\n\n**Updated Fields:**\n${updatedFields.join('\n')}` : '';
95
+ const domain = process.env.JIRA_DOMAIN || process.env.ATLASSIAN_DOMAIN || 'your-domain.atlassian.net';
96
+ return successResponse(`${ICONS.SUCCESS} **Jira Issue Updated Successfully**\n\n` +
97
+ `${ICONS.TICKET} **Issue Key:** ${issue.key}\n` +
98
+ `${ICONS.STATUS} **Current Status:** ${issue.status}\n` +
99
+ `${ICONS.DATE} **Last Updated:** ${new Date(issue.updated).toLocaleString()}${updatedFieldsText}\n\n` +
100
+ `${ICONS.LINK} **Issue URL:** [View Issue](https://${domain}/browse/${issue.key})`);
101
+ }
102
+ catch (error) {
103
+ return errorResponse(error, {
104
+ operation: `Failed to update Jira issue ${issueKey}`,
105
+ params: {
106
+ Summary: summary || 'unchanged',
107
+ Description: description || 'unchanged',
108
+ Assignee: assignee || 'unchanged',
109
+ },
110
+ tip: ERROR_TIPS.JIRA_ISSUE_EDIT,
111
+ });
112
+ }
113
+ }
114
+ async addComment(args) {
115
+ const { issueKey, comment } = args;
116
+ try {
117
+ await this.service.addComment(issueKey, comment);
118
+ return successResponse(`Comment added to issue ${issueKey}`);
119
+ }
120
+ catch (error) {
121
+ return errorResponse(error, {
122
+ operation: 'Failed to add comment to Jira issue',
123
+ params: { 'Issue Key': issueKey, Comment: comment.substring(0, 100) + '...' },
124
+ tip: ERROR_TIPS.JIRA_COMMENT,
125
+ });
126
+ }
127
+ }
128
+ async getComments(args) {
129
+ const { issueKey } = args;
130
+ try {
131
+ const comments = await this.service.getComments(issueKey);
132
+ return jsonResponse(comments);
133
+ }
134
+ catch (error) {
135
+ return errorResponse(error, {
136
+ operation: 'Failed to get comments for Jira issue',
137
+ params: { 'Issue Key': issueKey },
138
+ tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
139
+ });
140
+ }
141
+ }
142
+ async updateComment(args) {
143
+ const { issueKey, commentId, comment } = args;
144
+ try {
145
+ const result = await this.service.updateComment(issueKey, commentId, comment);
146
+ return successResponse(`${ICONS.SUCCESS} Comment ${commentId} updated successfully on issue ${issueKey}\n\n${JSON.stringify(result, null, 2)}`);
147
+ }
148
+ catch (error) {
149
+ return errorResponse(error, {
150
+ operation: 'Failed to update comment on Jira issue',
151
+ params: { 'Issue Key': issueKey, 'Comment ID': commentId },
152
+ tip: 'Check if the comment exists, you are the author, and you have permission to edit it.',
153
+ });
154
+ }
155
+ }
156
+ async deleteComment(args) {
157
+ const { issueKey, commentId } = args;
158
+ try {
159
+ await this.service.deleteComment(issueKey, commentId);
160
+ return successResponse(`${ICONS.SUCCESS} Comment ${commentId} deleted successfully from issue ${issueKey}`);
161
+ }
162
+ catch (error) {
163
+ return errorResponse(error, {
164
+ operation: 'Failed to delete comment from Jira issue',
165
+ params: { 'Issue Key': issueKey, 'Comment ID': commentId },
166
+ tip: 'Check if the comment exists and you have permission to delete it.',
167
+ });
168
+ }
169
+ }
170
+ async getProjects() {
171
+ try {
172
+ const projects = await this.service.getProjects();
173
+ return jsonResponse(projects);
174
+ }
175
+ catch (error) {
176
+ return errorResponse(error, {
177
+ operation: 'Failed to get Jira projects',
178
+ tip: 'Check your Jira permissions and API access.',
179
+ });
180
+ }
181
+ }
182
+ async getIssueTransitions(args) {
183
+ const { issueKey } = args;
184
+ try {
185
+ const transitions = await this.service.getIssueTransitions(issueKey);
186
+ const transitionInfo = transitions.map((t) => ({
187
+ id: t.id,
188
+ name: t.name,
189
+ to: t.to?.name || 'Unknown status',
190
+ }));
191
+ return successResponse(`Available transitions for ${issueKey}:\n\n` +
192
+ `${transitionInfo.map((t) => `• **${t.name}** (ID: ${t.id}) → ${t.to}`).join('\n')}\n\n` +
193
+ `Use the transition ID with jira_transition_issue_interactive for smart field handling.`);
194
+ }
195
+ catch (error) {
196
+ return errorResponse(error, {
197
+ operation: 'Failed to get transitions for Jira issue',
198
+ params: { 'Issue Key': issueKey },
199
+ tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
200
+ });
201
+ }
202
+ }
203
+ async transitionIssue(args) {
204
+ const { issueKey, transitionId, fieldValues } = args;
205
+ try {
206
+ const result = await this.service.transitionIssue(issueKey, transitionId, fieldValues);
207
+ if (result.success) {
208
+ return successResponse(result.message || `Issue ${issueKey} transitioned successfully`);
209
+ }
210
+ else if (result.requiresInput && result.requiredFields) {
211
+ const fieldInfo = result.requiredFields.map((field) => {
212
+ let info = `• **${field.name}** (${field.key})`;
213
+ if (field.options) {
214
+ info += `\n Options: ${field.options.map((opt) => `${opt.value} (${opt.id})`).join(', ')}`;
215
+ }
216
+ if (field.suggestion) {
217
+ info += `\n ${ICONS.SUGGESTION} Suggestion: "${field.suggestion.value}" - ${field.suggestion.reason}`;
218
+ }
219
+ return info;
220
+ }).join('\n\n');
221
+ return successResponse(`${result.message}\n\n**Required Fields:**\n${fieldInfo}\n\n` +
222
+ `**Next Steps:**\nUse jira_transition_issue_interactive for automatic field suggestion, or provide fieldValues with the correct field IDs and values.`);
223
+ }
224
+ else {
225
+ return {
226
+ content: [{ type: 'text', text: result.message || 'Transition failed with unknown error' }],
227
+ isError: true,
228
+ };
229
+ }
230
+ }
231
+ catch (error) {
232
+ return errorResponse(error, {
233
+ operation: `Failed to transition Jira issue ${issueKey}`,
234
+ params: {
235
+ 'Transition ID': transitionId,
236
+ 'Field Values': JSON.stringify(fieldValues || {}, null, 2),
237
+ },
238
+ tip: ERROR_TIPS.JIRA_TRANSITION,
239
+ });
240
+ }
241
+ }
242
+ async transitionIssueInteractive(args) {
243
+ const { issueKey, transitionId, fieldValues } = args;
244
+ try {
245
+ const result = await this.service.transitionIssueInteractive(issueKey, transitionId, fieldValues);
246
+ if (result.success) {
247
+ return successResponse(result.message || `Issue ${issueKey} transitioned successfully`);
248
+ }
249
+ else if (result.requiresInput && result.requiredFields) {
250
+ const fieldInfo = result.requiredFields.map((field) => {
251
+ let info = `• **${field.name}** (${field.key})`;
252
+ if (field.options) {
253
+ info += `\n Options: ${field.options.map((opt) => `${opt.value} (${opt.id})`).join(', ')}`;
254
+ }
255
+ if (field.suggestion) {
256
+ info += `\n ${ICONS.SUGGESTION} Suggestion: "${field.suggestion.value}" - ${field.suggestion.reason}`;
257
+ }
258
+ return info;
259
+ }).join('\n\n');
260
+ return successResponse(`${result.message}\n\n**Fields still requiring input:**\n${fieldInfo}`);
261
+ }
262
+ else {
263
+ return {
264
+ content: [{ type: 'text', text: result.message || 'Transition failed with unknown error' }],
265
+ isError: true,
266
+ };
267
+ }
268
+ }
269
+ catch (error) {
270
+ return errorResponse(error, {
271
+ operation: `Failed to transition Jira issue ${issueKey} (interactive mode)`,
272
+ params: {
273
+ 'Transition ID': transitionId,
274
+ 'Field Values': JSON.stringify(fieldValues || {}, null, 2),
275
+ },
276
+ tip: ERROR_TIPS.JIRA_TRANSITION,
277
+ });
278
+ }
279
+ }
280
+ async getAttachments(args) {
281
+ const { issueKey } = args;
282
+ try {
283
+ const attachments = await this.service.getAttachments(issueKey);
284
+ return jsonResponse(attachments);
285
+ }
286
+ catch (error) {
287
+ return errorResponse(error, {
288
+ operation: 'Failed to get attachments for Jira issue',
289
+ params: { 'Issue Key': issueKey },
290
+ tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
291
+ });
292
+ }
293
+ }
294
+ async addAttachment(args) {
295
+ const { issueKey, filename, fileContent } = args;
296
+ try {
297
+ const result = await this.service.addAttachment(issueKey, filename, fileContent);
298
+ return successResponse(`Attachment "${filename}" added to issue ${issueKey}\n\n${JSON.stringify(result, null, 2)}`);
299
+ }
300
+ catch (error) {
301
+ return errorResponse(error, {
302
+ operation: 'Failed to add attachment to Jira issue',
303
+ params: { 'Issue Key': issueKey, Filename: filename },
304
+ tip: ERROR_TIPS.JIRA_ATTACHMENT,
305
+ });
306
+ }
307
+ }
308
+ async deleteAttachment(args) {
309
+ const { attachmentId } = args;
310
+ try {
311
+ await this.service.deleteAttachment(attachmentId);
312
+ return successResponse(`Attachment ${attachmentId} deleted successfully`);
313
+ }
314
+ catch (error) {
315
+ return errorResponse(error, {
316
+ operation: 'Failed to delete attachment',
317
+ params: { 'Attachment ID': attachmentId },
318
+ tip: 'Check if the attachment exists and you have permission to delete it.',
319
+ });
320
+ }
321
+ }
322
+ async getIssueLinks(args) {
323
+ const { issueKey } = args;
324
+ try {
325
+ const links = await this.service.getIssueLinks(issueKey);
326
+ return jsonResponse(links);
327
+ }
328
+ catch (error) {
329
+ return errorResponse(error, {
330
+ operation: 'Failed to get issue links',
331
+ params: { 'Issue Key': issueKey },
332
+ tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
333
+ });
334
+ }
335
+ }
336
+ async createIssueLink(args) {
337
+ const { inwardIssueKey, outwardIssueKey, linkType } = args;
338
+ try {
339
+ await this.service.createIssueLink(inwardIssueKey, outwardIssueKey, linkType);
340
+ return successResponse(`Link created: ${inwardIssueKey} ${linkType} ${outwardIssueKey}`);
341
+ }
342
+ catch (error) {
343
+ return errorResponse(error, {
344
+ operation: 'Failed to create issue link',
345
+ params: { 'Inward Issue': inwardIssueKey, 'Outward Issue': outwardIssueKey, 'Link Type': linkType },
346
+ tip: ERROR_TIPS.JIRA_LINK,
347
+ });
348
+ }
349
+ }
350
+ async deleteIssueLink(args) {
351
+ const { linkId } = args;
352
+ try {
353
+ await this.service.deleteIssueLink(linkId);
354
+ return successResponse(`Issue link ${linkId} deleted successfully`);
355
+ }
356
+ catch (error) {
357
+ return errorResponse(error, {
358
+ operation: 'Failed to delete issue link',
359
+ params: { 'Link ID': linkId },
360
+ tip: 'Check if the link exists and you have permission to delete it. Use jira_get_issue_links to get link IDs.',
361
+ });
362
+ }
363
+ }
364
+ async getLinkTypes() {
365
+ try {
366
+ const linkTypes = await this.service.getIssueLinkTypes();
367
+ return jsonResponse(linkTypes);
368
+ }
369
+ catch (error) {
370
+ return errorResponse(error, {
371
+ operation: 'Failed to get issue link types',
372
+ tip: 'Check your Jira permissions and API access.',
373
+ });
374
+ }
375
+ }
376
+ async getWorklogs(args) {
377
+ const { issueKey } = args;
378
+ try {
379
+ const worklogs = await this.service.getWorkLogs(issueKey);
380
+ return jsonResponse(worklogs);
381
+ }
382
+ catch (error) {
383
+ return errorResponse(error, {
384
+ operation: 'Failed to get work logs',
385
+ params: { 'Issue Key': issueKey },
386
+ tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
387
+ });
388
+ }
389
+ }
390
+ async addWorklog(args) {
391
+ const { issueKey, timeSpentSeconds, comment, started } = args;
392
+ try {
393
+ const result = await this.service.addWorkLog(issueKey, timeSpentSeconds, comment, started);
394
+ const hours = Math.floor(timeSpentSeconds / 3600);
395
+ const minutes = Math.floor((timeSpentSeconds % 3600) / 60);
396
+ return successResponse(`Work log added to ${issueKey}\nTime spent: ${hours}h ${minutes}m\n\n${JSON.stringify(result, null, 2)}`);
397
+ }
398
+ catch (error) {
399
+ return errorResponse(error, {
400
+ operation: 'Failed to add work log',
401
+ params: { 'Issue Key': issueKey, 'Time Spent': `${timeSpentSeconds}s`, Comment: comment || 'none' },
402
+ tip: ERROR_TIPS.JIRA_WORKLOG,
403
+ });
404
+ }
405
+ }
406
+ async updateWorklog(args) {
407
+ const { issueKey, worklogId, timeSpentSeconds, comment } = args;
408
+ try {
409
+ const result = await this.service.updateWorkLog(issueKey, worklogId, timeSpentSeconds, comment);
410
+ return successResponse(`Work log ${worklogId} updated\n\n${JSON.stringify(result, null, 2)}`);
411
+ }
412
+ catch (error) {
413
+ return errorResponse(error, {
414
+ operation: 'Failed to update work log',
415
+ params: { 'Issue Key': issueKey, 'Worklog ID': worklogId },
416
+ tip: ERROR_TIPS.JIRA_WORKLOG,
417
+ });
418
+ }
419
+ }
420
+ async deleteWorklog(args) {
421
+ const { issueKey, worklogId } = args;
422
+ try {
423
+ await this.service.deleteWorkLog(issueKey, worklogId);
424
+ return successResponse(`Work log ${worklogId} deleted from ${issueKey}`);
425
+ }
426
+ catch (error) {
427
+ return errorResponse(error, {
428
+ operation: 'Failed to delete work log',
429
+ params: { 'Issue Key': issueKey, 'Worklog ID': worklogId },
430
+ tip: ERROR_TIPS.JIRA_WORKLOG,
431
+ });
432
+ }
433
+ }
434
+ async getWatchers(args) {
435
+ const { issueKey } = args;
436
+ try {
437
+ const watchers = await this.service.getWatchers(issueKey);
438
+ return jsonResponse(watchers);
439
+ }
440
+ catch (error) {
441
+ return errorResponse(error, {
442
+ operation: 'Failed to get watchers',
443
+ params: { 'Issue Key': issueKey },
444
+ tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
445
+ });
446
+ }
447
+ }
448
+ async addWatcher(args) {
449
+ const { issueKey, accountId } = args;
450
+ try {
451
+ await this.service.addWatcher(issueKey, accountId);
452
+ return successResponse(`Watcher added to ${issueKey}`);
453
+ }
454
+ catch (error) {
455
+ return errorResponse(error, {
456
+ operation: 'Failed to add watcher',
457
+ params: { 'Issue Key': issueKey, 'Account ID': accountId },
458
+ tip: ERROR_TIPS.JIRA_WATCHER,
459
+ });
460
+ }
461
+ }
462
+ async removeWatcher(args) {
463
+ const { issueKey, accountId } = args;
464
+ try {
465
+ await this.service.removeWatcher(issueKey, accountId);
466
+ return successResponse(`Watcher removed from ${issueKey}`);
467
+ }
468
+ catch (error) {
469
+ return errorResponse(error, {
470
+ operation: 'Failed to remove watcher',
471
+ params: { 'Issue Key': issueKey, 'Account ID': accountId },
472
+ tip: 'Check if the issue exists and you have permission to remove watchers.',
473
+ });
474
+ }
475
+ }
476
+ async getSubtasks(args) {
477
+ const { issueKey } = args;
478
+ try {
479
+ const subtasks = await this.service.getSubTasks(issueKey);
480
+ return jsonResponse(subtasks);
481
+ }
482
+ catch (error) {
483
+ return errorResponse(error, {
484
+ operation: 'Failed to get sub-tasks',
485
+ params: { 'Issue Key': issueKey },
486
+ tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
487
+ });
488
+ }
489
+ }
490
+ async createSubtask(args) {
491
+ const { parentKey, summary, description } = args;
492
+ try {
493
+ const subtask = await this.service.createSubTask(parentKey, summary, description);
494
+ return successResponse(`Sub-task created: ${subtask.key}\n\n${JSON.stringify(subtask, null, 2)}`);
495
+ }
496
+ catch (error) {
497
+ return errorResponse(error, {
498
+ operation: 'Failed to create sub-task',
499
+ params: { 'Parent Key': parentKey, Summary: summary },
500
+ tip: ERROR_TIPS.JIRA_SUBTASK,
501
+ });
502
+ }
503
+ }
504
+ async getIssueHistory(args) {
505
+ const { issueKey } = args;
506
+ try {
507
+ const history = await this.service.getIssueHistory(issueKey);
508
+ return jsonResponse(history);
509
+ }
510
+ catch (error) {
511
+ return errorResponse(error, {
512
+ operation: 'Failed to get issue history',
513
+ params: { 'Issue Key': issueKey },
514
+ tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
515
+ });
516
+ }
517
+ }
518
+ async getFields(args) {
519
+ const { type } = args;
520
+ try {
521
+ const filterType = (type === 'standard' || type === 'custom') ? type : undefined;
522
+ const fields = await this.service.getFields(filterType);
523
+ return jsonResponse(fields);
524
+ }
525
+ catch (error) {
526
+ return errorResponse(error, {
527
+ operation: 'Failed to get Jira fields',
528
+ tip: 'Check your Jira permissions and API access.',
529
+ });
530
+ }
531
+ }
532
+ async getLabels(args) {
533
+ const { query, maxResults } = args;
534
+ try {
535
+ const labels = await this.service.getLabels(query, maxResults);
536
+ return jsonResponse(labels);
537
+ }
538
+ catch (error) {
539
+ return errorResponse(error, {
540
+ operation: 'Failed to get labels',
541
+ params: { Query: query || 'none', 'Max Results': maxResults || 50 },
542
+ tip: 'Check your Jira permissions and API access.',
543
+ });
544
+ }
545
+ }
546
+ async addLabels(args) {
547
+ const { issueKey, labels } = args;
548
+ try {
549
+ await this.service.addLabels(issueKey, labels);
550
+ return successResponse(`${ICONS.SUCCESS} Labels added to ${issueKey}\n\n` +
551
+ `${ICONS.LABELS} **Added:** ${labels.join(', ')}`);
552
+ }
553
+ catch (error) {
554
+ return errorResponse(error, {
555
+ operation: `Failed to add labels to ${issueKey}`,
556
+ params: { 'Issue Key': issueKey, Labels: labels.join(', ') },
557
+ tip: ERROR_TIPS.JIRA_ISSUE_EDIT,
558
+ });
559
+ }
560
+ }
561
+ async removeLabels(args) {
562
+ const { issueKey, labels } = args;
563
+ try {
564
+ await this.service.removeLabels(issueKey, labels);
565
+ return successResponse(`${ICONS.SUCCESS} Labels removed from ${issueKey}\n\n` +
566
+ `${ICONS.LABELS} **Removed:** ${labels.join(', ')}`);
567
+ }
568
+ catch (error) {
569
+ return errorResponse(error, {
570
+ operation: `Failed to remove labels from ${issueKey}`,
571
+ params: { 'Issue Key': issueKey, Labels: labels.join(', ') },
572
+ tip: ERROR_TIPS.JIRA_ISSUE_EDIT,
573
+ });
574
+ }
575
+ }
576
+ async getAgileBoards(args) {
577
+ try {
578
+ const boards = await this.service.getAgileBoards(args);
579
+ return jsonResponse(boards);
580
+ }
581
+ catch (error) {
582
+ return errorResponse(error, {
583
+ operation: 'Failed to get agile boards',
584
+ params: { Name: args.name || 'all', Type: args.type || 'all', Project: args.projectKeyOrId || 'all' },
585
+ tip: ERROR_TIPS.JIRA_AGILE,
586
+ });
587
+ }
588
+ }
589
+ async getBoardIssues(args) {
590
+ const { boardId, jql, maxResults, startAt } = args;
591
+ try {
592
+ const result = await this.service.getBoardIssues(boardId, { jql, maxResults, startAt });
593
+ return jsonResponse(result);
594
+ }
595
+ catch (error) {
596
+ return errorResponse(error, {
597
+ operation: 'Failed to get board issues',
598
+ params: { 'Board ID': boardId, JQL: jql || 'none' },
599
+ tip: ERROR_TIPS.JIRA_AGILE,
600
+ });
601
+ }
602
+ }
603
+ async getSprints(args) {
604
+ const { boardId, state } = args;
605
+ try {
606
+ const sprints = await this.service.getSprints(boardId, state);
607
+ return jsonResponse(sprints);
608
+ }
609
+ catch (error) {
610
+ return errorResponse(error, {
611
+ operation: 'Failed to get sprints',
612
+ params: { 'Board ID': boardId, State: state || 'all' },
613
+ tip: ERROR_TIPS.JIRA_AGILE,
614
+ });
615
+ }
616
+ }
617
+ async getSprintIssues(args) {
618
+ const { sprintId, jql, maxResults, startAt } = args;
619
+ try {
620
+ const result = await this.service.getSprintIssues(sprintId, { jql, maxResults, startAt });
621
+ return jsonResponse(result);
622
+ }
623
+ catch (error) {
624
+ return errorResponse(error, {
625
+ operation: 'Failed to get sprint issues',
626
+ params: { 'Sprint ID': sprintId, JQL: jql || 'none' },
627
+ tip: ERROR_TIPS.JIRA_AGILE,
628
+ });
629
+ }
630
+ }
631
+ async batchCreateIssues(args) {
632
+ const { issues } = args;
633
+ try {
634
+ const result = await this.service.batchCreateIssues(issues);
635
+ const successCount = result.issues?.length || 0;
636
+ const errorCount = result.errors?.length || 0;
637
+ return successResponse(`${ICONS.SUCCESS} Batch create completed: ${successCount} created, ${errorCount} errors\n\n${JSON.stringify(result, null, 2)}`);
638
+ }
639
+ catch (error) {
640
+ return errorResponse(error, {
641
+ operation: 'Failed to batch create issues',
642
+ params: { 'Issue Count': issues.length },
643
+ tip: ERROR_TIPS.JIRA_BATCH,
644
+ });
645
+ }
646
+ }
647
+ async getDevStatus(args) {
648
+ const { issueId, applicationType, dataType } = args;
649
+ try {
650
+ const result = await this.service.getDevStatus(issueId, applicationType, dataType);
651
+ return jsonResponse(result);
652
+ }
653
+ catch (error) {
654
+ return errorResponse(error, {
655
+ operation: 'Failed to get dev status',
656
+ params: { 'Issue ID': issueId, 'Application Type': applicationType || 'all', 'Data Type': dataType || 'all' },
657
+ tip: ERROR_TIPS.JIRA_DEV_STATUS,
658
+ });
659
+ }
660
+ }
661
+ async deleteIssue(args) {
662
+ const { issueKey, deleteSubtasks } = args;
663
+ try {
664
+ await this.service.deleteIssue(issueKey, deleteSubtasks);
665
+ return successResponse(`${ICONS.SUCCESS} Issue ${issueKey} deleted successfully` +
666
+ (deleteSubtasks ? ' (including subtasks)' : ''));
667
+ }
668
+ catch (error) {
669
+ return errorResponse(error, {
670
+ operation: `Failed to delete issue ${issueKey}`,
671
+ params: { 'Issue Key': issueKey, 'Delete Subtasks': deleteSubtasks || false },
672
+ tip: ERROR_TIPS.JIRA_DELETE,
673
+ });
674
+ }
675
+ }
676
+ async lookupUser(args) {
677
+ const { query } = args;
678
+ try {
679
+ const users = await this.service.getUsersByQuery(query);
680
+ return jsonResponse(users.map((u) => ({
681
+ accountId: u.accountId,
682
+ displayName: u.displayName,
683
+ emailAddress: u.emailAddress || null,
684
+ active: u.active,
685
+ })));
686
+ }
687
+ catch (error) {
688
+ return errorResponse(error, {
689
+ operation: 'Failed to look up user',
690
+ params: { Query: query },
691
+ tip: 'Check your Jira permissions. Search by display name or email address.',
692
+ });
693
+ }
694
+ }
695
+ async getIssueTypes(args) {
696
+ const { projectKey } = args;
697
+ try {
698
+ const issueTypes = await this.service.getIssueTypes(projectKey);
699
+ return jsonResponse(issueTypes);
700
+ }
701
+ catch (error) {
702
+ return errorResponse(error, {
703
+ operation: 'Failed to get issue types',
704
+ params: { 'Project Key': projectKey },
705
+ tip: 'Check if the project key exists and you have permission to view it.',
706
+ });
707
+ }
708
+ }
709
+ async createSprint(args) {
710
+ const { boardId, name, startDate, endDate, goal } = args;
711
+ try {
712
+ const sprint = await this.service.createSprint(boardId, name, { startDate, endDate, goal });
713
+ return successResponse(`${ICONS.SUCCESS} Sprint created successfully\n\n` +
714
+ `**ID:** ${sprint.id}\n` +
715
+ `**Name:** ${sprint.name}\n` +
716
+ `**State:** ${sprint.state}\n` +
717
+ (sprint.startDate ? `**Start:** ${sprint.startDate}\n` : '') +
718
+ (sprint.endDate ? `**End:** ${sprint.endDate}\n` : '') +
719
+ (sprint.goal ? `**Goal:** ${sprint.goal}` : ''));
720
+ }
721
+ catch (error) {
722
+ return errorResponse(error, {
723
+ operation: 'Failed to create sprint',
724
+ params: { 'Board ID': boardId, Name: name },
725
+ tip: ERROR_TIPS.JIRA_AGILE,
726
+ });
727
+ }
728
+ }
729
+ async updateSprint(args) {
730
+ const { sprintId, name, state, startDate, endDate, goal } = args;
731
+ try {
732
+ const sprint = await this.service.updateSprint(sprintId, { name, state, startDate, endDate, goal });
733
+ return successResponse(`${ICONS.SUCCESS} Sprint ${sprintId} updated successfully\n\n` +
734
+ `**Name:** ${sprint.name}\n` +
735
+ `**State:** ${sprint.state}\n` +
736
+ (sprint.startDate ? `**Start:** ${sprint.startDate}\n` : '') +
737
+ (sprint.endDate ? `**End:** ${sprint.endDate}\n` : '') +
738
+ (sprint.goal ? `**Goal:** ${sprint.goal}` : ''));
739
+ }
740
+ catch (error) {
741
+ return errorResponse(error, {
742
+ operation: `Failed to update sprint ${sprintId}`,
743
+ params: { 'Sprint ID': sprintId, Name: name || 'unchanged', State: state || 'unchanged' },
744
+ tip: ERROR_TIPS.JIRA_AGILE,
745
+ });
746
+ }
747
+ }
748
+ async getProjectVersions(args) {
749
+ const { projectKey } = args;
750
+ try {
751
+ const versions = await this.service.getProjectVersions(projectKey);
752
+ return jsonResponse(versions);
753
+ }
754
+ catch (error) {
755
+ return errorResponse(error, {
756
+ operation: 'Failed to get project versions',
757
+ params: { 'Project Key': projectKey },
758
+ tip: 'Check if the project key exists and you have permission to view versions.',
759
+ });
760
+ }
761
+ }
762
+ async createVersion(args) {
763
+ const { projectKey, name, description, startDate, releaseDate, released } = args;
764
+ try {
765
+ const version = await this.service.createVersion(projectKey, name, { description, startDate, releaseDate, released });
766
+ return successResponse(`${ICONS.SUCCESS} Version created successfully\n\n` +
767
+ `**ID:** ${version.id}\n` +
768
+ `**Name:** ${version.name}\n` +
769
+ `**Released:** ${version.released}\n` +
770
+ (version.description ? `**Description:** ${version.description}\n` : '') +
771
+ (version.startDate ? `**Start Date:** ${version.startDate}\n` : '') +
772
+ (version.releaseDate ? `**Release Date:** ${version.releaseDate}` : ''));
773
+ }
774
+ catch (error) {
775
+ return errorResponse(error, {
776
+ operation: 'Failed to create version',
777
+ params: { 'Project Key': projectKey, Name: name },
778
+ tip: 'Check if the project exists and you have permission to manage versions.',
779
+ });
780
+ }
781
+ }
782
+ async updateVersion(args) {
783
+ const { versionId, name, description, startDate, releaseDate, released, archived } = args;
784
+ try {
785
+ const version = await this.service.updateVersion(versionId, { name, description, startDate, releaseDate, released, archived });
786
+ return successResponse(`${ICONS.SUCCESS} Version ${versionId} updated successfully\n\n` +
787
+ `**Name:** ${version.name}\n` +
788
+ `**Released:** ${version.released}\n` +
789
+ `**Archived:** ${version.archived}\n` +
790
+ (version.description ? `**Description:** ${version.description}\n` : '') +
791
+ (version.releaseDate ? `**Release Date:** ${version.releaseDate}` : ''));
792
+ }
793
+ catch (error) {
794
+ return errorResponse(error, {
795
+ operation: `Failed to update version ${versionId}`,
796
+ params: { 'Version ID': versionId },
797
+ tip: 'Check if the version exists and you have permission to manage versions.',
798
+ });
799
+ }
800
+ }
801
+ async linkToEpic(args) {
802
+ const { issueKey, epicKey } = args;
803
+ try {
804
+ await this.service.linkToEpic(issueKey, epicKey);
805
+ return successResponse(`${ICONS.SUCCESS} Issue ${issueKey} linked to epic ${epicKey}`);
806
+ }
807
+ catch (error) {
808
+ return errorResponse(error, {
809
+ operation: `Failed to link ${issueKey} to epic ${epicKey}`,
810
+ params: { 'Issue Key': issueKey, 'Epic Key': epicKey },
811
+ tip: 'Check that both issues exist, the epic is actually an Epic type, and you have edit permission.',
812
+ });
813
+ }
814
+ }
815
+ }
816
+ //# sourceMappingURL=jira-handlers.js.map