@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
package/dist/index.js CHANGED
@@ -6,6 +6,13 @@ import { validateConfig } from './config.js';
6
6
  import { ConfluenceService } from './services/confluence.js';
7
7
  import { JiraService } from './services/jira.js';
8
8
  import { BitbucketService } from './services/bitbucket.js';
9
+ // Import modular tool definitions
10
+ import { allTools } from './tools/index.js';
11
+ // Import modular handlers
12
+ import { ConfluenceHandlers } from './handlers/confluence-handlers.js';
13
+ import { JiraHandlers } from './handlers/jira-handlers.js';
14
+ import { BitbucketHandlers } from './handlers/bitbucket-handlers.js';
15
+ import { ERROR_MESSAGES } from './constants.js';
9
16
  const server = new Server({
10
17
  name: 'atlassian-mcp-server',
11
18
  version: '1.0.0',
@@ -14,2554 +21,200 @@ const server = new Server({
14
21
  tools: {},
15
22
  },
16
23
  });
24
+ // Initialize services
17
25
  const confluenceService = new ConfluenceService();
18
26
  const jiraService = new JiraService();
19
27
  const bitbucketService = new BitbucketService();
28
+ // Initialize handlers
29
+ const confluenceHandlers = new ConfluenceHandlers(confluenceService);
30
+ const jiraHandlers = new JiraHandlers(jiraService);
31
+ const bitbucketHandlers = new BitbucketHandlers(bitbucketService);
32
+ /**
33
+ * List all available tools
34
+ */
20
35
  server.setRequestHandler(ListToolsRequestSchema, async () => {
21
- return {
22
- tools: [
23
- // Confluence Tools
24
- {
25
- name: 'confluence_search_pages',
26
- description: 'Search Confluence pages by text query',
27
- inputSchema: {
28
- type: 'object',
29
- properties: {
30
- query: {
31
- type: 'string',
32
- description: 'Text to search for in pages'
33
- },
34
- limit: {
35
- type: 'number',
36
- description: 'Maximum number of results (default: 25)',
37
- default: 25
38
- }
39
- }
40
- }
41
- },
42
- {
43
- name: 'confluence_get_page',
44
- description: 'Get a specific Confluence page by ID',
45
- inputSchema: {
46
- type: 'object',
47
- properties: {
48
- pageId: {
49
- type: 'string',
50
- description: 'The page ID to retrieve'
51
- }
52
- },
53
- required: ['pageId']
54
- }
55
- },
56
- {
57
- name: 'confluence_create_page',
58
- description: 'Create a new Confluence page',
59
- inputSchema: {
60
- type: 'object',
61
- properties: {
62
- spaceKey: {
63
- type: 'string',
64
- description: 'The space key where to create the page'
65
- },
66
- title: {
67
- type: 'string',
68
- description: 'Page title'
69
- },
70
- content: {
71
- type: 'string',
72
- description: 'Page content in Confluence storage format'
73
- },
74
- parentId: {
75
- type: 'string',
76
- description: 'Optional parent page ID'
77
- },
78
- images: {
79
- type: 'array',
80
- description: 'Optional array of images to upload and embed. Use {{IMAGE:filename}} placeholders in content to position images inline, or they will be appended at the end. Provide either filePath or fileContent for each image.',
81
- items: {
82
- type: 'object',
83
- properties: {
84
- filePath: {
85
- type: 'string',
86
- description: 'Local file path (e.g., "/path/to/image.png"). Filename is auto-detected. Use {{IMAGE:image.png}} in content.'
87
- },
88
- filename: {
89
- type: 'string',
90
- description: 'Image filename (optional if filePath provided). Use {{IMAGE:filename}} in content to position it.'
91
- },
92
- fileContent: {
93
- type: 'string',
94
- description: 'Base64 encoded image content (alternative to filePath)'
95
- },
96
- alt: {
97
- type: 'string',
98
- description: 'Optional alt text for the image'
99
- },
100
- caption: {
101
- type: 'string',
102
- description: 'Optional caption to display below the image'
103
- },
104
- width: {
105
- type: 'number',
106
- description: 'Optional width in pixels'
107
- },
108
- align: {
109
- type: 'string',
110
- enum: ['left', 'center', 'right'],
111
- description: 'Optional alignment'
112
- }
113
- },
114
- required: []
115
- }
116
- }
117
- },
118
- required: ['spaceKey', 'title', 'content']
119
- }
120
- },
121
- {
122
- name: 'confluence_update_page',
123
- description: 'Update an existing Confluence page',
124
- inputSchema: {
125
- type: 'object',
126
- properties: {
127
- pageId: {
128
- type: 'string',
129
- description: 'The page ID to update'
130
- },
131
- title: {
132
- type: 'string',
133
- description: 'New page title'
134
- },
135
- content: {
136
- type: 'string',
137
- description: 'New page content in Confluence storage format'
138
- },
139
- version: {
140
- type: 'number',
141
- description: 'Current version number of the page'
142
- }
143
- },
144
- required: ['pageId', 'title', 'content', 'version']
145
- }
146
- },
147
- {
148
- name: 'confluence_get_spaces',
149
- description: 'Get list of Confluence spaces',
150
- inputSchema: {
151
- type: 'object',
152
- properties: {
153
- limit: {
154
- type: 'number',
155
- description: 'Maximum number of spaces to return (default: 25)',
156
- default: 25
157
- }
158
- }
159
- }
160
- },
161
- {
162
- name: 'confluence_get_pages_by_space',
163
- description: 'Get pages from a specific Confluence space',
164
- inputSchema: {
165
- type: 'object',
166
- properties: {
167
- spaceKey: {
168
- type: 'string',
169
- description: 'The space key to get pages from'
170
- },
171
- limit: {
172
- type: 'number',
173
- description: 'Maximum number of pages to return (default: 25)',
174
- default: 25
175
- }
176
- },
177
- required: ['spaceKey']
178
- }
179
- },
180
- {
181
- name: 'confluence_get_attachments',
182
- description: 'Get all attachments for a Confluence page',
183
- inputSchema: {
184
- type: 'object',
185
- properties: {
186
- pageId: {
187
- type: 'string',
188
- description: 'The page ID to get attachments for'
189
- }
190
- },
191
- required: ['pageId']
192
- }
193
- },
194
- {
195
- name: 'confluence_add_attachment',
196
- description: 'Add an attachment to a Confluence page. Provide either filePath (local file) or fileContent (base64).',
197
- inputSchema: {
198
- type: 'object',
199
- properties: {
200
- pageId: {
201
- type: 'string',
202
- description: 'The page ID to attach file to'
203
- },
204
- filePath: {
205
- type: 'string',
206
- description: 'Local file path to attach (e.g., "/path/to/file.svg"). If provided, filename is auto-detected.'
207
- },
208
- filename: {
209
- type: 'string',
210
- description: 'The filename for the attachment (optional if filePath is provided)'
211
- },
212
- fileContent: {
213
- type: 'string',
214
- description: 'Base64 encoded file content (alternative to filePath)'
215
- }
216
- },
217
- required: ['pageId']
218
- }
219
- },
220
- {
221
- name: 'confluence_delete_attachment',
222
- description: 'Delete an attachment from a Confluence page',
223
- inputSchema: {
224
- type: 'object',
225
- properties: {
226
- attachmentId: {
227
- type: 'string',
228
- description: 'The attachment ID to delete'
229
- }
230
- },
231
- required: ['attachmentId']
232
- }
233
- },
234
- {
235
- name: 'confluence_embed_image',
236
- description: 'Attach an image to a Confluence page and embed it in the content. Provide either filePath (local file) or fileContent (base64).',
237
- inputSchema: {
238
- type: 'object',
239
- properties: {
240
- pageId: {
241
- type: 'string',
242
- description: 'The page ID to embed the image in'
243
- },
244
- filePath: {
245
- type: 'string',
246
- description: 'Local file path to the image (e.g., "/path/to/diagram.svg"). If provided, filename is auto-detected.'
247
- },
248
- filename: {
249
- type: 'string',
250
- description: 'The filename for the image (optional if filePath is provided)'
251
- },
252
- fileContent: {
253
- type: 'string',
254
- description: 'Base64 encoded image content (alternative to filePath)'
255
- },
256
- alt: {
257
- type: 'string',
258
- description: 'Alt text for the image (defaults to filename)'
259
- },
260
- caption: {
261
- type: 'string',
262
- description: 'Optional caption to display below the image'
263
- },
264
- width: {
265
- type: 'number',
266
- description: 'Optional width in pixels for the image'
267
- },
268
- align: {
269
- type: 'string',
270
- enum: ['left', 'center', 'right'],
271
- description: 'Image alignment (default: no alignment)'
272
- },
273
- position: {
274
- type: 'string',
275
- enum: ['top', 'bottom', 'after-heading'],
276
- description: 'Where to insert the image (default: bottom)'
277
- },
278
- headingText: {
279
- type: 'string',
280
- description: 'If position is "after-heading", the heading text to insert after'
281
- }
282
- },
283
- required: ['pageId']
284
- }
285
- },
286
- {
287
- name: 'confluence_get_comments',
288
- description: 'Get all comments for a Confluence page',
289
- inputSchema: {
290
- type: 'object',
291
- properties: {
292
- pageId: {
293
- type: 'string',
294
- description: 'The page ID to get comments for'
295
- }
296
- },
297
- required: ['pageId']
298
- }
299
- },
300
- {
301
- name: 'confluence_add_comment',
302
- description: 'Add a comment to a Confluence page',
303
- inputSchema: {
304
- type: 'object',
305
- properties: {
306
- pageId: {
307
- type: 'string',
308
- description: 'The page ID to add comment to'
309
- },
310
- body: {
311
- type: 'string',
312
- description: 'Comment content in Confluence storage format (HTML)'
313
- }
314
- },
315
- required: ['pageId', 'body']
316
- }
317
- },
318
- {
319
- name: 'confluence_update_comment',
320
- description: 'Update an existing comment on a Confluence page',
321
- inputSchema: {
322
- type: 'object',
323
- properties: {
324
- commentId: {
325
- type: 'string',
326
- description: 'The comment ID to update'
327
- },
328
- body: {
329
- type: 'string',
330
- description: 'New comment content in Confluence storage format (HTML)'
331
- },
332
- version: {
333
- type: 'number',
334
- description: 'Current version number of the comment'
335
- }
336
- },
337
- required: ['commentId', 'body', 'version']
338
- }
339
- },
340
- {
341
- name: 'confluence_delete_comment',
342
- description: 'Delete a comment from a Confluence page',
343
- inputSchema: {
344
- type: 'object',
345
- properties: {
346
- commentId: {
347
- type: 'string',
348
- description: 'The comment ID to delete'
349
- }
350
- },
351
- required: ['commentId']
352
- }
353
- },
354
- // Jira Tools
355
- {
356
- name: 'jira_search_issues',
357
- description: 'Search Jira issues by text query',
358
- inputSchema: {
359
- type: 'object',
360
- properties: {
361
- query: {
362
- type: 'string',
363
- description: 'Text to search for in issues'
364
- },
365
- limit: {
366
- type: 'number',
367
- description: 'Maximum number of results (default: 50)',
368
- default: 50
369
- }
370
- }
371
- }
372
- },
373
- {
374
- name: 'jira_get_issue',
375
- description: 'Get a specific Jira issue by key',
376
- inputSchema: {
377
- type: 'object',
378
- properties: {
379
- issueKey: {
380
- type: 'string',
381
- description: 'The issue key (e.g., PROJ-123)'
382
- }
383
- },
384
- required: ['issueKey']
385
- }
386
- },
387
- {
388
- name: 'jira_create_issue',
389
- description: 'Create a new Jira issue with support for custom fields including due dates',
390
- inputSchema: {
391
- type: 'object',
392
- properties: {
393
- projectKey: {
394
- type: 'string',
395
- description: 'The project key where to create the issue'
396
- },
397
- summary: {
398
- type: 'string',
399
- description: 'Issue summary/title'
400
- },
401
- description: {
402
- type: 'string',
403
- description: 'Issue description'
404
- },
405
- issueType: {
406
- type: 'string',
407
- description: 'Issue type (default: Task)',
408
- default: 'Task'
409
- },
410
- duedate: {
411
- type: 'string',
412
- description: 'Due date in YYYY-MM-DD format (e.g., 2024-12-31)'
413
- },
414
- priority: {
415
- type: 'string',
416
- description: 'Priority name (e.g., High, Medium, Low)'
417
- },
418
- labels: {
419
- type: 'array',
420
- items: { type: 'string' },
421
- description: 'Array of label strings'
422
- },
423
- components: {
424
- type: 'array',
425
- items: { type: 'string' },
426
- description: 'Array of component names'
427
- },
428
- assignee: {
429
- type: 'string',
430
- description: 'Account ID of the assignee'
431
- },
432
- customFields: {
433
- type: 'object',
434
- description: 'Additional custom fields as key-value pairs'
435
- }
436
- },
437
- required: ['projectKey', 'summary', 'description']
438
- }
439
- },
440
- {
441
- name: 'jira_update_issue',
442
- description: 'Update an existing Jira issue',
443
- inputSchema: {
444
- type: 'object',
445
- properties: {
446
- issueKey: {
447
- type: 'string',
448
- description: 'The issue key to update'
449
- },
450
- summary: {
451
- type: 'string',
452
- description: 'New issue summary'
453
- },
454
- description: {
455
- type: 'string',
456
- description: 'New issue description'
457
- },
458
- assignee: {
459
- type: 'string',
460
- description: 'Account ID of the assignee'
461
- }
462
- },
463
- required: ['issueKey']
464
- }
465
- },
466
- {
467
- name: 'jira_add_comment',
468
- description: 'Add a comment to a Jira issue',
469
- inputSchema: {
470
- type: 'object',
471
- properties: {
472
- issueKey: {
473
- type: 'string',
474
- description: 'The issue key to comment on'
475
- },
476
- comment: {
477
- type: 'string',
478
- description: 'Comment text'
479
- }
480
- },
481
- required: ['issueKey', 'comment']
482
- }
483
- },
484
- {
485
- name: 'jira_get_comments',
486
- description: 'Get all comments for a Jira issue',
487
- inputSchema: {
488
- type: 'object',
489
- properties: {
490
- issueKey: {
491
- type: 'string',
492
- description: 'The issue key to get comments for'
493
- }
494
- },
495
- required: ['issueKey']
496
- }
497
- },
498
- {
499
- name: 'jira_update_comment',
500
- description: 'Update an existing comment on a Jira issue',
501
- inputSchema: {
502
- type: 'object',
503
- properties: {
504
- issueKey: {
505
- type: 'string',
506
- description: 'The issue key (e.g., PROJ-123)'
507
- },
508
- commentId: {
509
- type: 'string',
510
- description: 'The comment ID to update'
511
- },
512
- comment: {
513
- type: 'string',
514
- description: 'New comment text'
515
- }
516
- },
517
- required: ['issueKey', 'commentId', 'comment']
518
- }
519
- },
520
- {
521
- name: 'jira_delete_comment',
522
- description: 'Delete a comment from a Jira issue',
523
- inputSchema: {
524
- type: 'object',
525
- properties: {
526
- issueKey: {
527
- type: 'string',
528
- description: 'The issue key (e.g., PROJ-123)'
529
- },
530
- commentId: {
531
- type: 'string',
532
- description: 'The comment ID to delete'
533
- }
534
- },
535
- required: ['issueKey', 'commentId']
536
- }
537
- },
538
- {
539
- name: 'jira_get_projects',
540
- description: 'Get list of Jira projects',
541
- inputSchema: {
542
- type: 'object',
543
- properties: {}
544
- }
545
- },
546
- {
547
- name: 'jira_get_issue_transitions',
548
- description: 'Get available transitions for a Jira issue',
549
- inputSchema: {
550
- type: 'object',
551
- properties: {
552
- issueKey: {
553
- type: 'string',
554
- description: 'The issue key (e.g., PROJECT-123)'
555
- }
556
- },
557
- required: ['issueKey']
558
- }
559
- },
560
- {
561
- name: 'jira_transition_issue',
562
- description: 'Transition a Jira issue to a different status with smart field handling',
563
- inputSchema: {
564
- type: 'object',
565
- properties: {
566
- issueKey: {
567
- type: 'string',
568
- description: 'The issue key to transition'
569
- },
570
- transitionId: {
571
- type: 'string',
572
- description: 'The transition ID'
573
- },
574
- fieldValues: {
575
- type: 'object',
576
- description: 'Optional field values for the transition'
577
- }
578
- },
579
- required: ['issueKey', 'transitionId']
580
- }
581
- },
582
- {
583
- name: 'jira_transition_issue_interactive',
584
- description: 'Transition a Jira issue with automatic field suggestion and validation',
585
- inputSchema: {
586
- type: 'object',
587
- properties: {
588
- issueKey: {
589
- type: 'string',
590
- description: 'The issue key to transition'
591
- },
592
- transitionId: {
593
- type: 'string',
594
- description: 'The transition ID'
595
- },
596
- fieldValues: {
597
- type: 'object',
598
- description: 'Optional field values for the transition'
599
- }
600
- },
601
- required: ['issueKey', 'transitionId']
602
- }
603
- },
604
- {
605
- name: 'jira_get_attachments',
606
- description: 'Get all attachments for a Jira issue',
607
- inputSchema: {
608
- type: 'object',
609
- properties: {
610
- issueKey: {
611
- type: 'string',
612
- description: 'The issue key to get attachments for'
613
- }
614
- },
615
- required: ['issueKey']
616
- }
617
- },
618
- {
619
- name: 'jira_add_attachment',
620
- description: 'Add an attachment to a Jira issue',
621
- inputSchema: {
622
- type: 'object',
623
- properties: {
624
- issueKey: {
625
- type: 'string',
626
- description: 'The issue key to attach file to'
627
- },
628
- filename: {
629
- type: 'string',
630
- description: 'The filename for the attachment'
631
- },
632
- fileContent: {
633
- type: 'string',
634
- description: 'Base64 encoded file content'
635
- }
636
- },
637
- required: ['issueKey', 'filename', 'fileContent']
638
- }
639
- },
640
- {
641
- name: 'jira_delete_attachment',
642
- description: 'Delete an attachment from a Jira issue',
643
- inputSchema: {
644
- type: 'object',
645
- properties: {
646
- attachmentId: {
647
- type: 'string',
648
- description: 'The attachment ID to delete'
649
- }
650
- },
651
- required: ['attachmentId']
652
- }
653
- },
654
- {
655
- name: 'jira_get_issue_links',
656
- description: 'Get all issue links for a Jira issue',
657
- inputSchema: {
658
- type: 'object',
659
- properties: {
660
- issueKey: {
661
- type: 'string',
662
- description: 'The issue key to get links for'
663
- }
664
- },
665
- required: ['issueKey']
666
- }
667
- },
668
- {
669
- name: 'jira_create_issue_link',
670
- description: 'Create a link between two Jira issues',
671
- inputSchema: {
672
- type: 'object',
673
- properties: {
674
- inwardIssueKey: {
675
- type: 'string',
676
- description: 'The inward issue key'
677
- },
678
- outwardIssueKey: {
679
- type: 'string',
680
- description: 'The outward issue key'
681
- },
682
- linkType: {
683
- type: 'string',
684
- description: 'Link type name (e.g., "Blocks", "Relates", "Duplicates")'
685
- }
686
- },
687
- required: ['inwardIssueKey', 'outwardIssueKey', 'linkType']
688
- }
689
- },
690
- {
691
- name: 'jira_delete_issue_link',
692
- description: 'Delete a link between Jira issues',
693
- inputSchema: {
694
- type: 'object',
695
- properties: {
696
- linkId: {
697
- type: 'string',
698
- description: 'The link ID to delete'
699
- }
700
- },
701
- required: ['linkId']
702
- }
703
- },
704
- {
705
- name: 'jira_get_link_types',
706
- description: 'Get all available issue link types in Jira',
707
- inputSchema: {
708
- type: 'object',
709
- properties: {}
710
- }
711
- },
712
- {
713
- name: 'jira_get_worklogs',
714
- description: 'Get all work logs (time tracking entries) for a Jira issue',
715
- inputSchema: {
716
- type: 'object',
717
- properties: {
718
- issueKey: {
719
- type: 'string',
720
- description: 'The issue key to get work logs for'
721
- }
722
- },
723
- required: ['issueKey']
724
- }
725
- },
726
- {
727
- name: 'jira_add_worklog',
728
- description: 'Add a work log (time spent) to a Jira issue',
729
- inputSchema: {
730
- type: 'object',
731
- properties: {
732
- issueKey: {
733
- type: 'string',
734
- description: 'The issue key to log work on'
735
- },
736
- timeSpentSeconds: {
737
- type: 'number',
738
- description: 'Time spent in seconds (e.g., 3600 for 1 hour)'
739
- },
740
- comment: {
741
- type: 'string',
742
- description: 'Optional comment describing the work done'
743
- },
744
- started: {
745
- type: 'string',
746
- description: 'Optional start date/time in ISO 8601 format (e.g., 2024-01-15T14:30:00.000+0000)'
747
- }
748
- },
749
- required: ['issueKey', 'timeSpentSeconds']
750
- }
751
- },
752
- {
753
- name: 'jira_update_worklog',
754
- description: 'Update an existing work log entry',
755
- inputSchema: {
756
- type: 'object',
757
- properties: {
758
- issueKey: {
759
- type: 'string',
760
- description: 'The issue key'
761
- },
762
- worklogId: {
763
- type: 'string',
764
- description: 'The work log ID to update'
765
- },
766
- timeSpentSeconds: {
767
- type: 'number',
768
- description: 'Updated time spent in seconds'
769
- },
770
- comment: {
771
- type: 'string',
772
- description: 'Updated comment'
773
- }
774
- },
775
- required: ['issueKey', 'worklogId']
776
- }
777
- },
778
- {
779
- name: 'jira_delete_worklog',
780
- description: 'Delete a work log entry from a Jira issue',
781
- inputSchema: {
782
- type: 'object',
783
- properties: {
784
- issueKey: {
785
- type: 'string',
786
- description: 'The issue key'
787
- },
788
- worklogId: {
789
- type: 'string',
790
- description: 'The work log ID to delete'
791
- }
792
- },
793
- required: ['issueKey', 'worklogId']
794
- }
795
- },
796
- {
797
- name: 'jira_get_watchers',
798
- description: 'Get all watchers for a Jira issue',
799
- inputSchema: {
800
- type: 'object',
801
- properties: {
802
- issueKey: {
803
- type: 'string',
804
- description: 'The issue key to get watchers for'
805
- }
806
- },
807
- required: ['issueKey']
808
- }
809
- },
810
- {
811
- name: 'jira_add_watcher',
812
- description: 'Add a watcher to a Jira issue',
813
- inputSchema: {
814
- type: 'object',
815
- properties: {
816
- issueKey: {
817
- type: 'string',
818
- description: 'The issue key'
819
- },
820
- accountId: {
821
- type: 'string',
822
- description: 'Account ID of the user to add as watcher'
823
- }
824
- },
825
- required: ['issueKey', 'accountId']
826
- }
827
- },
828
- {
829
- name: 'jira_remove_watcher',
830
- description: 'Remove a watcher from a Jira issue',
831
- inputSchema: {
832
- type: 'object',
833
- properties: {
834
- issueKey: {
835
- type: 'string',
836
- description: 'The issue key'
837
- },
838
- accountId: {
839
- type: 'string',
840
- description: 'Account ID of the user to remove as watcher'
841
- }
842
- },
843
- required: ['issueKey', 'accountId']
844
- }
845
- },
846
- {
847
- name: 'jira_get_subtasks',
848
- description: 'Get all sub-tasks for a Jira issue',
849
- inputSchema: {
850
- type: 'object',
851
- properties: {
852
- issueKey: {
853
- type: 'string',
854
- description: 'The parent issue key'
855
- }
856
- },
857
- required: ['issueKey']
858
- }
859
- },
860
- {
861
- name: 'jira_create_subtask',
862
- description: 'Create a sub-task under a parent Jira issue',
863
- inputSchema: {
864
- type: 'object',
865
- properties: {
866
- parentKey: {
867
- type: 'string',
868
- description: 'The parent issue key'
869
- },
870
- summary: {
871
- type: 'string',
872
- description: 'Sub-task summary/title'
873
- },
874
- description: {
875
- type: 'string',
876
- description: 'Sub-task description'
877
- }
878
- },
879
- required: ['parentKey', 'summary', 'description']
880
- }
881
- },
882
- {
883
- name: 'jira_get_issue_history',
884
- description: 'Get complete change history for a Jira issue',
885
- inputSchema: {
886
- type: 'object',
887
- properties: {
888
- issueKey: {
889
- type: 'string',
890
- description: 'The issue key to get history for'
891
- }
892
- },
893
- required: ['issueKey']
894
- }
895
- },
896
- // Bitbucket Tools
897
- {
898
- name: 'bitbucket_get_repositories',
899
- description: 'Get list of Bitbucket repositories in workspace',
900
- inputSchema: {
901
- type: 'object',
902
- properties: {
903
- query: {
904
- type: 'string',
905
- description: 'Search query for repository names'
906
- },
907
- limit: {
908
- type: 'number',
909
- description: 'Maximum number of repositories (default: 10)',
910
- default: 10
911
- }
912
- }
913
- }
914
- },
915
- {
916
- name: 'bitbucket_get_repository',
917
- description: 'Get details of a specific Bitbucket repository',
918
- inputSchema: {
919
- type: 'object',
920
- properties: {
921
- repoName: {
922
- type: 'string',
923
- description: 'Repository name'
924
- }
925
- },
926
- required: ['repoName']
927
- }
928
- },
929
- {
930
- name: 'bitbucket_create_repository',
931
- description: 'Create a new Bitbucket repository',
932
- inputSchema: {
933
- type: 'object',
934
- properties: {
935
- name: {
936
- type: 'string',
937
- description: 'Repository name'
938
- },
939
- description: {
940
- type: 'string',
941
- description: 'Repository description'
942
- },
943
- isPrivate: {
944
- type: 'boolean',
945
- description: 'Whether the repository should be private (default: true)',
946
- default: true
947
- },
948
- language: {
949
- type: 'string',
950
- description: 'Primary programming language'
951
- }
952
- },
953
- required: ['name']
954
- }
955
- },
956
- {
957
- name: 'bitbucket_get_pull_requests',
958
- description: 'Get pull requests for a repository',
959
- inputSchema: {
960
- type: 'object',
961
- properties: {
962
- repoName: {
963
- type: 'string',
964
- description: 'Repository name'
965
- },
966
- state: {
967
- type: 'string',
968
- description: 'Pull request state (OPEN, MERGED, DECLINED)',
969
- default: 'OPEN'
970
- },
971
- limit: {
972
- type: 'number',
973
- description: 'Maximum number of pull requests (default: 10)',
974
- default: 10
975
- }
976
- },
977
- required: ['repoName']
978
- }
979
- },
980
- {
981
- name: 'bitbucket_get_pull_request',
982
- description: 'Get details of a specific pull request',
983
- inputSchema: {
984
- type: 'object',
985
- properties: {
986
- repoName: {
987
- type: 'string',
988
- description: 'Repository name'
989
- },
990
- prId: {
991
- type: 'number',
992
- description: 'Pull request ID'
993
- },
994
- includeDiff: {
995
- type: 'boolean',
996
- description: 'Whether to include the code diff/changes (default: false)',
997
- default: false
998
- }
999
- },
1000
- required: ['repoName', 'prId']
1001
- }
1002
- },
1003
- {
1004
- name: 'bitbucket_create_pull_request',
1005
- description: 'Create a new pull request',
1006
- inputSchema: {
1007
- type: 'object',
1008
- properties: {
1009
- repoName: {
1010
- type: 'string',
1011
- description: 'Repository name'
1012
- },
1013
- title: {
1014
- type: 'string',
1015
- description: 'Pull request title'
1016
- },
1017
- sourceBranch: {
1018
- type: 'string',
1019
- description: 'Source branch name'
1020
- },
1021
- destinationBranch: {
1022
- type: 'string',
1023
- description: 'Destination branch name (default: main)',
1024
- default: 'main'
1025
- },
1026
- description: {
1027
- type: 'string',
1028
- description: 'Pull request description'
1029
- }
1030
- },
1031
- required: ['repoName', 'title', 'sourceBranch']
1032
- }
1033
- },
1034
- {
1035
- name: 'bitbucket_get_branches',
1036
- description: 'Get branches for a repository',
1037
- inputSchema: {
1038
- type: 'object',
1039
- properties: {
1040
- repoName: {
1041
- type: 'string',
1042
- description: 'Repository name'
1043
- },
1044
- limit: {
1045
- type: 'number',
1046
- description: 'Maximum number of branches (default: 10)',
1047
- default: 10
1048
- }
1049
- },
1050
- required: ['repoName']
1051
- }
1052
- },
1053
- {
1054
- name: 'bitbucket_get_commits',
1055
- description: 'Get commits for a repository',
1056
- inputSchema: {
1057
- type: 'object',
1058
- properties: {
1059
- repoName: {
1060
- type: 'string',
1061
- description: 'Repository name'
1062
- },
1063
- branch: {
1064
- type: 'string',
1065
- description: 'Branch name (default: main)',
1066
- default: 'main'
1067
- },
1068
- limit: {
1069
- type: 'number',
1070
- description: 'Maximum number of commits (default: 10)',
1071
- default: 10
1072
- }
1073
- },
1074
- required: ['repoName']
1075
- }
1076
- },
1077
- {
1078
- name: 'bitbucket_get_issues',
1079
- description: 'Get issues for a repository',
1080
- inputSchema: {
1081
- type: 'object',
1082
- properties: {
1083
- repoName: {
1084
- type: 'string',
1085
- description: 'Repository name'
1086
- },
1087
- state: {
1088
- type: 'string',
1089
- description: 'Issue state (open, closed)',
1090
- default: 'open'
1091
- },
1092
- limit: {
1093
- type: 'number',
1094
- description: 'Maximum number of issues (default: 10)',
1095
- default: 10
1096
- }
1097
- },
1098
- required: ['repoName']
1099
- }
1100
- },
1101
- {
1102
- name: 'bitbucket_create_issue',
1103
- description: 'Create a new issue in a repository',
1104
- inputSchema: {
1105
- type: 'object',
1106
- properties: {
1107
- repoName: {
1108
- type: 'string',
1109
- description: 'Repository name'
1110
- },
1111
- title: {
1112
- type: 'string',
1113
- description: 'Issue title'
1114
- },
1115
- content: {
1116
- type: 'string',
1117
- description: 'Issue content/description'
1118
- },
1119
- kind: {
1120
- type: 'string',
1121
- description: 'Issue kind (bug, enhancement, proposal, task)',
1122
- default: 'bug'
1123
- }
1124
- },
1125
- required: ['repoName', 'title']
1126
- }
1127
- }
1128
- ]
1129
- };
36
+ return { tools: allTools };
1130
37
  });
38
+ /**
39
+ * Handle tool calls by routing to appropriate handlers
40
+ */
1131
41
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
1132
42
  const { name, arguments: args } = request.params;
1133
43
  try {
44
+ // Confluence Tools
1134
45
  switch (name) {
1135
- // Confluence Tools
1136
- case 'confluence_search_pages': {
1137
- const { query, limit } = args;
1138
- try {
1139
- const pages = await confluenceService.searchPages({ query, limit });
1140
- return {
1141
- content: [
1142
- {
1143
- type: 'text',
1144
- text: JSON.stringify(pages, null, 2)
1145
- }
1146
- ]
1147
- };
1148
- }
1149
- catch (error) {
1150
- const errorDetails = error.response?.data || error.message;
1151
- return {
1152
- content: [
1153
- {
1154
- type: 'text',
1155
- text: `❌ Failed to search Confluence pages\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Query: ${query || 'none'}\n- Limit: ${limit || 25}\n\n**Tip:** Check your Confluence permissions and that the domain is accessible.`
1156
- }
1157
- ],
1158
- isError: true
1159
- };
1160
- }
1161
- }
1162
- case 'confluence_get_page': {
1163
- const { pageId } = args;
1164
- try {
1165
- const page = await confluenceService.getPage(pageId);
1166
- return {
1167
- content: [
1168
- {
1169
- type: 'text',
1170
- text: JSON.stringify(page, null, 2)
1171
- }
1172
- ]
1173
- };
1174
- }
1175
- catch (error) {
1176
- const errorDetails = error.response?.data || error.message;
1177
- return {
1178
- content: [
1179
- {
1180
- type: 'text',
1181
- text: `❌ Failed to get Confluence page\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Page ID: ${pageId}\n\n**Tip:** Check if the page ID exists and you have permission to view it.`
1182
- }
1183
- ],
1184
- isError: true
1185
- };
1186
- }
1187
- }
1188
- case 'confluence_create_page': {
1189
- const { spaceKey, title, content, parentId, images } = args;
1190
- try {
1191
- const page = await confluenceService.createPageWithImages(spaceKey, title, content, parentId, images);
1192
- const imageCount = images?.length || 0;
1193
- const resultText = imageCount > 0
1194
- ? `✅ Confluence page created successfully with ${imageCount} image(s)\n\n${JSON.stringify(page, null, 2)}`
1195
- : JSON.stringify(page, null, 2);
1196
- return {
1197
- content: [
1198
- {
1199
- type: 'text',
1200
- text: resultText
1201
- }
1202
- ]
1203
- };
1204
- }
1205
- catch (error) {
1206
- const errorDetails = error.response?.data || error.message;
1207
- const imageInfo = images?.length ? `\n- Images: ${images.length} image(s) to upload` : '';
1208
- return {
1209
- content: [
1210
- {
1211
- type: 'text',
1212
- text: `❌ Failed to create Confluence page\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Space Key: ${spaceKey}\n- Title: ${title}\n- Parent ID: ${parentId || 'none'}${imageInfo}\n\n**Tip:** Check if the space exists and you have permission to create pages in it. For images, ensure they are properly base64 encoded.`
1213
- }
1214
- ],
1215
- isError: true
1216
- };
1217
- }
1218
- }
1219
- case 'confluence_update_page': {
1220
- const { pageId, title, content, version } = args;
1221
- try {
1222
- const page = await confluenceService.updatePage(pageId, title, content, version);
1223
- return {
1224
- content: [
1225
- {
1226
- type: 'text',
1227
- text: JSON.stringify(page, null, 2)
1228
- }
1229
- ]
1230
- };
1231
- }
1232
- catch (error) {
1233
- const errorDetails = error.response?.data || error.message;
1234
- return {
1235
- content: [
1236
- {
1237
- type: 'text',
1238
- text: `❌ Failed to update Confluence page\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Page ID: ${pageId}\n- Title: ${title}\n- Version: ${version}\n\n**Tip:** Check if the page exists, version is correct, and you have permission to edit it.`
1239
- }
1240
- ],
1241
- isError: true
1242
- };
1243
- }
1244
- }
1245
- case 'confluence_get_spaces': {
1246
- const { limit } = args;
1247
- try {
1248
- const spaces = await confluenceService.getSpaces(limit);
1249
- return {
1250
- content: [
1251
- {
1252
- type: 'text',
1253
- text: JSON.stringify(spaces, null, 2)
1254
- }
1255
- ]
1256
- };
1257
- }
1258
- catch (error) {
1259
- const errorDetails = error.response?.data || error.message;
1260
- return {
1261
- content: [
1262
- {
1263
- type: 'text',
1264
- text: `❌ Failed to get Confluence spaces\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Limit: ${limit || 25}\n\n**Tip:** Check your Confluence permissions and API access.`
1265
- }
1266
- ],
1267
- isError: true
1268
- };
1269
- }
1270
- }
1271
- case 'confluence_get_pages_by_space': {
1272
- const { spaceKey, limit } = args;
1273
- try {
1274
- const pages = await confluenceService.getPagesBySpace(spaceKey, limit);
1275
- return {
1276
- content: [
1277
- {
1278
- type: 'text',
1279
- text: JSON.stringify(pages, null, 2)
1280
- }
1281
- ]
1282
- };
1283
- }
1284
- catch (error) {
1285
- const errorDetails = error.response?.data || error.message;
1286
- return {
1287
- content: [
1288
- {
1289
- type: 'text',
1290
- text: `❌ Failed to get pages from Confluence space\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Space Key: ${spaceKey}\n- Limit: ${limit || 25}\n\n**Tip:** Check if the space key exists and you have permission to view it.`
1291
- }
1292
- ],
1293
- isError: true
1294
- };
1295
- }
1296
- }
1297
- case 'confluence_get_attachments': {
1298
- const { pageId } = args;
1299
- try {
1300
- const attachments = await confluenceService.getAttachments(pageId);
1301
- return {
1302
- content: [
1303
- {
1304
- type: 'text',
1305
- text: JSON.stringify(attachments, null, 2)
1306
- }
1307
- ]
1308
- };
1309
- }
1310
- catch (error) {
1311
- const errorDetails = error.response?.data || error.message;
1312
- return {
1313
- content: [
1314
- {
1315
- type: 'text',
1316
- text: `❌ Failed to get attachments for Confluence page\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Page ID: ${pageId}\n\n**Tip:** Check if the page exists and you have permission to view it.`
1317
- }
1318
- ],
1319
- isError: true
1320
- };
1321
- }
1322
- }
1323
- case 'confluence_add_attachment': {
1324
- const { pageId, filename, fileContent, filePath } = args;
1325
- // Validate: need either filePath or fileContent
1326
- if (!filePath && !fileContent) {
1327
- return {
1328
- content: [{ type: 'text', text: '❌ Either filePath or fileContent must be provided' }],
1329
- isError: true
1330
- };
1331
- }
1332
- try {
1333
- const result = await confluenceService.addAttachment(pageId, filename, fileContent, filePath);
1334
- const attachedFilename = result.results?.[0]?.title || filename || filePath?.split('/').pop() || 'attachment';
1335
- return {
1336
- content: [
1337
- {
1338
- type: 'text',
1339
- text: `✅ Attachment "${attachedFilename}" added successfully to page ${pageId}\n\n${JSON.stringify(result, null, 2)}`
1340
- }
1341
- ]
1342
- };
1343
- }
1344
- catch (error) {
1345
- const errorDetails = error.response?.data || error.message;
1346
- const source = filePath ? `File Path: ${filePath}` : `Filename: ${filename}`;
1347
- return {
1348
- content: [
1349
- {
1350
- type: 'text',
1351
- text: `❌ Failed to add attachment to Confluence page\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Page ID: ${pageId}\n- ${source}\n\n**Tip:** Check if the page exists, you have permission to attach files, and the file path exists or content is properly base64 encoded.`
1352
- }
1353
- ],
1354
- isError: true
1355
- };
1356
- }
1357
- }
1358
- case 'confluence_delete_attachment': {
1359
- const { attachmentId } = args;
1360
- try {
1361
- await confluenceService.deleteAttachment(attachmentId);
1362
- return {
1363
- content: [
1364
- {
1365
- type: 'text',
1366
- text: `✅ Attachment ${attachmentId} deleted successfully`
1367
- }
1368
- ]
1369
- };
1370
- }
1371
- catch (error) {
1372
- const errorDetails = error.response?.data || error.message;
1373
- return {
1374
- content: [
1375
- {
1376
- type: 'text',
1377
- text: `❌ Failed to delete attachment\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Attachment ID: ${attachmentId}\n\n**Tip:** Check if the attachment exists and you have permission to delete it.`
1378
- }
1379
- ],
1380
- isError: true
1381
- };
1382
- }
1383
- }
1384
- case 'confluence_embed_image': {
1385
- const { pageId, filename, fileContent, filePath, alt, caption, width, align, position, headingText } = args;
1386
- // Validate: need either filePath or fileContent
1387
- if (!filePath && !fileContent) {
1388
- return {
1389
- content: [{ type: 'text', text: '❌ Either filePath or fileContent must be provided' }],
1390
- isError: true
1391
- };
1392
- }
1393
- try {
1394
- const result = await confluenceService.embedImage(pageId, filename, fileContent, filePath, {
1395
- alt,
1396
- caption,
1397
- width,
1398
- align,
1399
- position,
1400
- headingText
1401
- });
1402
- const embeddedFilename = result.attachment.results?.[0]?.title || filename || filePath?.split('/').pop() || 'image';
1403
- return {
1404
- content: [
1405
- {
1406
- type: 'text',
1407
- text: `✅ Image "${embeddedFilename}" attached and embedded successfully!\n\n**Attachment ID:** ${result.attachment.results?.[0]?.id}\n**Page Version:** ${result.page.version}\n**Position:** ${position || 'bottom'}\n${align ? `**Alignment:** ${align}` : ''}\n${width ? `**Width:** ${width}px` : ''}\n${caption ? `**Caption:** ${caption}` : ''}`
1408
- }
1409
- ]
1410
- };
1411
- }
1412
- catch (error) {
1413
- const errorDetails = error.response?.data || error.message;
1414
- const source = filePath ? `File Path: ${filePath}` : `Filename: ${filename}`;
1415
- return {
1416
- content: [
1417
- {
1418
- type: 'text',
1419
- text: `❌ Failed to embed image in Confluence page\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Page ID: ${pageId}\n- ${source}\n- Position: ${position || 'bottom'}\n\n**Tip:** Check if the page exists, you have edit permission, and the file path exists or content is properly base64 encoded.`
1420
- }
1421
- ],
1422
- isError: true
1423
- };
1424
- }
1425
- }
1426
- case 'confluence_get_comments': {
1427
- const { pageId } = args;
1428
- try {
1429
- const comments = await confluenceService.getComments(pageId);
1430
- return {
1431
- content: [
1432
- {
1433
- type: 'text',
1434
- text: JSON.stringify(comments, null, 2)
1435
- }
1436
- ]
1437
- };
1438
- }
1439
- catch (error) {
1440
- const errorDetails = error.response?.data || error.message;
1441
- return {
1442
- content: [
1443
- {
1444
- type: 'text',
1445
- text: `❌ Failed to get comments\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Page ID: ${pageId}\n\n**Tip:** Check if the page exists and you have permission to view it.`
1446
- }
1447
- ],
1448
- isError: true
1449
- };
1450
- }
1451
- }
1452
- case 'confluence_add_comment': {
1453
- const { pageId, body } = args;
1454
- try {
1455
- const comment = await confluenceService.addComment(pageId, body);
1456
- return {
1457
- content: [
1458
- {
1459
- type: 'text',
1460
- text: `✅ Comment added successfully!\n\n**Comment ID:** ${comment.id}\n**Author:** ${comment.author}\n**Created:** ${comment.created}`
1461
- }
1462
- ]
1463
- };
1464
- }
1465
- catch (error) {
1466
- const errorDetails = error.response?.data || error.message;
1467
- return {
1468
- content: [
1469
- {
1470
- type: 'text',
1471
- text: `❌ Failed to add comment\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Page ID: ${pageId}\n\n**Tip:** Check if the page exists and you have permission to comment on it.`
1472
- }
1473
- ],
1474
- isError: true
1475
- };
1476
- }
1477
- }
1478
- case 'confluence_update_comment': {
1479
- const { commentId, body, version } = args;
1480
- try {
1481
- const comment = await confluenceService.updateComment(commentId, body, version);
1482
- return {
1483
- content: [
1484
- {
1485
- type: 'text',
1486
- text: `✅ Comment updated successfully!\n\n**Comment ID:** ${comment.id}\n**New Version:** ${comment.version}\n**Updated:** ${comment.updated}`
1487
- }
1488
- ]
1489
- };
1490
- }
1491
- catch (error) {
1492
- const errorDetails = error.response?.data || error.message;
1493
- return {
1494
- content: [
1495
- {
1496
- type: 'text',
1497
- text: `❌ Failed to update comment\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Comment ID: ${commentId}\n- Version: ${version}\n\n**Tip:** Check if the comment exists, you have permission to edit it, and the version number is correct.`
1498
- }
1499
- ],
1500
- isError: true
1501
- };
1502
- }
1503
- }
1504
- case 'confluence_delete_comment': {
1505
- const { commentId } = args;
1506
- try {
1507
- await confluenceService.deleteComment(commentId);
1508
- return {
1509
- content: [
1510
- {
1511
- type: 'text',
1512
- text: `✅ Comment ${commentId} deleted successfully`
1513
- }
1514
- ]
1515
- };
1516
- }
1517
- catch (error) {
1518
- const errorDetails = error.response?.data || error.message;
1519
- return {
1520
- content: [
1521
- {
1522
- type: 'text',
1523
- text: `❌ Failed to delete comment\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Comment ID: ${commentId}\n\n**Tip:** Check if the comment exists and you have permission to delete it.`
1524
- }
1525
- ],
1526
- isError: true
1527
- };
1528
- }
1529
- }
46
+ case 'confluence_search_pages':
47
+ return confluenceHandlers.searchPages(args);
48
+ case 'confluence_get_page':
49
+ return confluenceHandlers.getPage(args);
50
+ case 'confluence_create_page':
51
+ return confluenceHandlers.createPage(args);
52
+ case 'confluence_update_page':
53
+ return confluenceHandlers.updatePage(args);
54
+ case 'confluence_get_spaces':
55
+ return confluenceHandlers.getSpaces(args);
56
+ case 'confluence_get_pages_by_space':
57
+ return confluenceHandlers.getPagesBySpace(args);
58
+ case 'confluence_get_attachments':
59
+ return confluenceHandlers.getAttachments(args);
60
+ case 'confluence_add_attachment':
61
+ return confluenceHandlers.addAttachment(args);
62
+ case 'confluence_delete_attachment':
63
+ return confluenceHandlers.deleteAttachment(args);
64
+ case 'confluence_embed_image':
65
+ return confluenceHandlers.embedImage(args);
66
+ case 'confluence_get_comments':
67
+ return confluenceHandlers.getComments(args);
68
+ case 'confluence_add_comment':
69
+ return confluenceHandlers.addComment(args);
70
+ case 'confluence_update_comment':
71
+ return confluenceHandlers.updateComment(args);
72
+ case 'confluence_delete_comment':
73
+ return confluenceHandlers.deleteComment(args);
74
+ case 'confluence_get_page_children':
75
+ return confluenceHandlers.getPageChildren(args);
76
+ case 'confluence_get_labels':
77
+ return confluenceHandlers.getLabels(args);
78
+ case 'confluence_add_labels':
79
+ return confluenceHandlers.addLabels(args);
80
+ case 'confluence_remove_label':
81
+ return confluenceHandlers.removeLabel(args);
82
+ case 'confluence_delete_page':
83
+ return confluenceHandlers.deletePage(args);
84
+ case 'confluence_get_page_history':
85
+ return confluenceHandlers.getPageHistory(args);
1530
86
  // Jira Tools
1531
- case 'jira_search_issues': {
1532
- const { query, limit } = args;
1533
- try {
1534
- const issues = await jiraService.searchIssues({ query, limit });
1535
- return {
1536
- content: [
1537
- {
1538
- type: 'text',
1539
- text: JSON.stringify(issues, null, 2)
1540
- }
1541
- ]
1542
- };
1543
- }
1544
- catch (error) {
1545
- const errorDetails = error.response?.data || error.message;
1546
- return {
1547
- content: [
1548
- {
1549
- type: 'text',
1550
- text: `❌ Failed to search Jira issues\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Query: ${query || 'none'}\n- Limit: ${limit || 50}\n\n**Tip:** Check your JQL syntax and Jira permissions.`
1551
- }
1552
- ],
1553
- isError: true
1554
- };
1555
- }
1556
- }
1557
- case 'jira_get_issue': {
1558
- const { issueKey } = args;
1559
- try {
1560
- const issue = await jiraService.getIssue(issueKey);
1561
- return {
1562
- content: [
1563
- {
1564
- type: 'text',
1565
- text: JSON.stringify(issue, null, 2)
1566
- }
1567
- ]
1568
- };
1569
- }
1570
- catch (error) {
1571
- const errorDetails = error.response?.data || error.message;
1572
- return {
1573
- content: [
1574
- {
1575
- type: 'text',
1576
- text: `❌ Failed to get Jira issue\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n\n**Tip:** Check if the issue key exists and you have permission to view it.`
1577
- }
1578
- ],
1579
- isError: true
1580
- };
1581
- }
1582
- }
1583
- case 'jira_create_issue': {
1584
- const { projectKey, summary, description, issueType, duedate, priority, labels, components, assignee, customFields } = args;
1585
- try {
1586
- const additionalFields = {
1587
- duedate,
1588
- priority,
1589
- labels,
1590
- components,
1591
- assignee,
1592
- customFields
1593
- };
1594
- const issue = await jiraService.createIssue(projectKey, summary, description, issueType, additionalFields);
1595
- // Format optional fields display
1596
- const optionalFields = [];
1597
- if (duedate)
1598
- optionalFields.push(`📅 **Due Date:** ${duedate}`);
1599
- if (priority)
1600
- optionalFields.push(`🔥 **Priority:** ${priority}`);
1601
- if (labels && labels.length > 0)
1602
- optionalFields.push(`🏷️ **Labels:** ${labels.join(', ')}`);
1603
- if (components && components.length > 0)
1604
- optionalFields.push(`🧩 **Components:** ${components.join(', ')}`);
1605
- if (assignee)
1606
- optionalFields.push(`👤 **Assignee:** ${issue.assignee || assignee}`);
1607
- const optionalFieldsText = optionalFields.length > 0 ? `\n\n${optionalFields.join('\n')}` : '';
1608
- return {
1609
- content: [
1610
- {
1611
- type: 'text',
1612
- text: `✅ **Jira Issue Created Successfully**
1613
-
1614
- 🎫 **Issue Key:** ${issue.key}
1615
- 📝 **Summary:** ${issue.summary}
1616
- 📋 **Issue Type:** ${issueType || 'Task'}
1617
- 📊 **Status:** ${issue.status}
1618
- 📅 **Created:** ${new Date(issue.created).toLocaleString()}${optionalFieldsText}
1619
-
1620
- 📄 **Description:** Successfully created with provided content
1621
-
1622
- 🔗 **Issue URL:** [View Issue](https://${process.env.JIRA_DOMAIN || process.env.ATLASSIAN_DOMAIN || 'your-domain.atlassian.net'}/browse/${issue.key})`
1623
- }
1624
- ]
1625
- };
1626
- }
1627
- catch (error) {
1628
- const errorDetails = error.response?.data || error.message;
1629
- return {
1630
- content: [
1631
- {
1632
- type: 'text',
1633
- text: `❌ Failed to create Jira issue\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Project: ${projectKey}\n- Summary: ${summary}\n- Issue Type: ${issueType || 'Task'}\n- Due Date: ${duedate || 'none'}\n- Priority: ${priority || 'none'}\n- Labels: ${labels?.join(', ') || 'none'}\n- Components: ${components?.join(', ') || 'none'}\n- Assignee: ${assignee || 'none'}\n\n**Tip:** Check if all required fields are provided and project exists. For custom fields, ensure field IDs are correct.`
1634
- }
1635
- ],
1636
- isError: true
1637
- };
1638
- }
1639
- }
1640
- case 'jira_update_issue': {
1641
- const { issueKey, summary, description, assignee } = args;
1642
- try {
1643
- const issue = await jiraService.updateIssue(issueKey, { summary, description, assignee });
1644
- // Format the updated fields display
1645
- const updatedFields = [];
1646
- if (summary)
1647
- updatedFields.push(`📝 **Summary:** Updated to "${issue.summary}"`);
1648
- if (description)
1649
- updatedFields.push(`📄 **Description:** Updated successfully`);
1650
- if (assignee)
1651
- updatedFields.push(`👤 **Assignee:** ${issue.assignee || assignee}`);
1652
- const updatedFieldsText = updatedFields.length > 0 ? `\n\n**Updated Fields:**\n${updatedFields.join('\n')}` : '';
1653
- return {
1654
- content: [
1655
- {
1656
- type: 'text',
1657
- text: `✅ **Jira Issue Updated Successfully**
1658
-
1659
- 🎫 **Issue Key:** ${issue.key}
1660
- 📊 **Current Status:** ${issue.status}
1661
- 📅 **Last Updated:** ${new Date(issue.updated).toLocaleString()}${updatedFieldsText}
1662
-
1663
- 🔗 **Issue URL:** [View Issue](https://${process.env.JIRA_DOMAIN || process.env.ATLASSIAN_DOMAIN || 'your-domain.atlassian.net'}/browse/${issue.key})`
1664
- }
1665
- ]
1666
- };
1667
- }
1668
- catch (error) {
1669
- const errorDetails = error.response?.data || error.message;
1670
- return {
1671
- content: [
1672
- {
1673
- type: 'text',
1674
- text: `❌ Failed to update Jira issue ${issueKey}\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request Fields:**\n- Summary: ${summary || 'unchanged'}\n- Description: ${description || 'unchanged'}\n- Assignee: ${assignee || 'unchanged'}\n\n**Tip:** Check if the issue exists and you have permission to update it.`
1675
- }
1676
- ],
1677
- isError: true
1678
- };
1679
- }
1680
- }
1681
- case 'jira_add_comment': {
1682
- const { issueKey, comment } = args;
1683
- try {
1684
- await jiraService.addComment(issueKey, comment);
1685
- return {
1686
- content: [
1687
- {
1688
- type: 'text',
1689
- text: `Comment added to issue ${issueKey}`
1690
- }
1691
- ]
1692
- };
1693
- }
1694
- catch (error) {
1695
- const errorDetails = error.response?.data || error.message;
1696
- return {
1697
- content: [
1698
- {
1699
- type: 'text',
1700
- text: `❌ Failed to add comment to Jira issue\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n- Comment: ${comment.substring(0, 100)}...\n\n**Tip:** Check if the issue exists and you have permission to comment on it.`
1701
- }
1702
- ],
1703
- isError: true
1704
- };
1705
- }
1706
- }
1707
- case 'jira_get_comments': {
1708
- const { issueKey } = args;
1709
- try {
1710
- const comments = await jiraService.getComments(issueKey);
1711
- return {
1712
- content: [
1713
- {
1714
- type: 'text',
1715
- text: JSON.stringify(comments, null, 2)
1716
- }
1717
- ]
1718
- };
1719
- }
1720
- catch (error) {
1721
- const errorDetails = error.response?.data || error.message;
1722
- return {
1723
- content: [
1724
- {
1725
- type: 'text',
1726
- text: `❌ Failed to get comments for Jira issue\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n\n**Tip:** Check if the issue exists and you have permission to view it.`
1727
- }
1728
- ],
1729
- isError: true
1730
- };
1731
- }
1732
- }
1733
- case 'jira_update_comment': {
1734
- const { issueKey, commentId, comment } = args;
1735
- try {
1736
- const result = await jiraService.updateComment(issueKey, commentId, comment);
1737
- return {
1738
- content: [
1739
- {
1740
- type: 'text',
1741
- text: `✅ Comment ${commentId} updated successfully on issue ${issueKey}\n\n${JSON.stringify(result, null, 2)}`
1742
- }
1743
- ]
1744
- };
1745
- }
1746
- catch (error) {
1747
- const errorDetails = error.response?.data || error.message;
1748
- return {
1749
- content: [
1750
- {
1751
- type: 'text',
1752
- text: `❌ Failed to update comment on Jira issue\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n- Comment ID: ${commentId}\n\n**Tip:** Check if the comment exists, you are the author, and you have permission to edit it.`
1753
- }
1754
- ],
1755
- isError: true
1756
- };
1757
- }
1758
- }
1759
- case 'jira_delete_comment': {
1760
- const { issueKey, commentId } = args;
1761
- try {
1762
- await jiraService.deleteComment(issueKey, commentId);
1763
- return {
1764
- content: [
1765
- {
1766
- type: 'text',
1767
- text: `✅ Comment ${commentId} deleted successfully from issue ${issueKey}`
1768
- }
1769
- ]
1770
- };
1771
- }
1772
- catch (error) {
1773
- const errorDetails = error.response?.data || error.message;
1774
- return {
1775
- content: [
1776
- {
1777
- type: 'text',
1778
- text: `❌ Failed to delete comment from Jira issue\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n- Comment ID: ${commentId}\n\n**Tip:** Check if the comment exists and you have permission to delete it.`
1779
- }
1780
- ],
1781
- isError: true
1782
- };
1783
- }
1784
- }
1785
- case 'jira_get_projects': {
1786
- try {
1787
- const projects = await jiraService.getProjects();
1788
- return {
1789
- content: [
1790
- {
1791
- type: 'text',
1792
- text: JSON.stringify(projects, null, 2)
1793
- }
1794
- ]
1795
- };
1796
- }
1797
- catch (error) {
1798
- const errorDetails = error.response?.data || error.message;
1799
- return {
1800
- content: [
1801
- {
1802
- type: 'text',
1803
- text: `❌ Failed to get Jira projects\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Tip:** Check your Jira permissions and API access.`
1804
- }
1805
- ],
1806
- isError: true
1807
- };
1808
- }
1809
- }
1810
- case 'jira_get_issue_transitions': {
1811
- const { issueKey } = args;
1812
- try {
1813
- const transitions = await jiraService.getIssueTransitions(issueKey);
1814
- const transitionInfo = transitions.map(t => ({
1815
- id: t.id,
1816
- name: t.name,
1817
- to: t.to?.name || 'Unknown status'
1818
- }));
1819
- return {
1820
- content: [
1821
- {
1822
- type: 'text',
1823
- text: `Available transitions for ${issueKey}:\n\n${transitionInfo.map(t => `• **${t.name}** (ID: ${t.id}) → ${t.to}`).join('\n')}\n\nUse the transition ID with jira_transition_issue_interactive for smart field handling.`
1824
- }
1825
- ]
1826
- };
1827
- }
1828
- catch (error) {
1829
- const errorDetails = error.response?.data || error.message;
1830
- return {
1831
- content: [
1832
- {
1833
- type: 'text',
1834
- text: `❌ Failed to get transitions for Jira issue\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n\n**Tip:** Check if the issue exists and you have permission to view it.`
1835
- }
1836
- ],
1837
- isError: true
1838
- };
1839
- }
1840
- }
1841
- case 'jira_transition_issue': {
1842
- const { issueKey, transitionId, fieldValues } = args;
1843
- try {
1844
- const result = await jiraService.transitionIssue(issueKey, transitionId, fieldValues);
1845
- if (result.success) {
1846
- return {
1847
- content: [
1848
- {
1849
- type: 'text',
1850
- text: result.message || `Issue ${issueKey} transitioned successfully`
1851
- }
1852
- ]
1853
- };
1854
- }
1855
- else if (result.requiresInput && result.requiredFields) {
1856
- // Format required fields information for user
1857
- const fieldInfo = result.requiredFields.map(field => {
1858
- let info = `• **${field.name}** (${field.key})`;
1859
- if (field.options) {
1860
- info += `\n Options: ${field.options.map(opt => `${opt.value} (${opt.id})`).join(', ')}`;
1861
- }
1862
- if (field.suggestion) {
1863
- info += `\n 💡 Suggestion: "${field.suggestion.value}" - ${field.suggestion.reason}`;
1864
- }
1865
- return info;
1866
- }).join('\n\n');
1867
- return {
1868
- content: [
1869
- {
1870
- type: 'text',
1871
- text: `${result.message}\n\n**Required Fields:**\n${fieldInfo}\n\n**Next Steps:**\nUse jira_transition_issue_interactive for automatic field suggestion, or provide fieldValues with the correct field IDs and values.`
1872
- }
1873
- ]
1874
- };
1875
- }
1876
- else {
1877
- return {
1878
- content: [
1879
- {
1880
- type: 'text',
1881
- text: result.message || 'Transition failed with unknown error'
1882
- }
1883
- ],
1884
- isError: true
1885
- };
1886
- }
1887
- }
1888
- catch (error) {
1889
- const errorDetails = error.response?.data || error.message;
1890
- return {
1891
- content: [
1892
- {
1893
- type: 'text',
1894
- text: `❌ Failed to transition Jira issue ${issueKey}\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Transition ID: ${transitionId}\n- Field Values: ${JSON.stringify(fieldValues || {}, null, 2)}\n\n**Tip:** Check if the transition ID is valid for this issue and you have permission to transition it.`
1895
- }
1896
- ],
1897
- isError: true
1898
- };
1899
- }
1900
- }
1901
- case 'jira_transition_issue_interactive': {
1902
- const { issueKey, transitionId, fieldValues } = args;
1903
- try {
1904
- const result = await jiraService.transitionIssueInteractive(issueKey, transitionId, fieldValues);
1905
- if (result.success) {
1906
- return {
1907
- content: [
1908
- {
1909
- type: 'text',
1910
- text: result.message || `Issue ${issueKey} transitioned successfully`
1911
- }
1912
- ]
1913
- };
1914
- }
1915
- else if (result.requiresInput && result.requiredFields) {
1916
- // Still need user input even after smart suggestions
1917
- const fieldInfo = result.requiredFields.map(field => {
1918
- let info = `• **${field.name}** (${field.key})`;
1919
- if (field.options) {
1920
- info += `\n Options: ${field.options.map(opt => `${opt.value} (${opt.id})`).join(', ')}`;
1921
- }
1922
- if (field.suggestion) {
1923
- info += `\n 💡 Suggestion: "${field.suggestion.value}" - ${field.suggestion.reason}`;
1924
- }
1925
- return info;
1926
- }).join('\n\n');
1927
- return {
1928
- content: [
1929
- {
1930
- type: 'text',
1931
- text: `${result.message}\n\n**Fields still requiring input:**\n${fieldInfo}`
1932
- }
1933
- ]
1934
- };
1935
- }
1936
- else {
1937
- return {
1938
- content: [
1939
- {
1940
- type: 'text',
1941
- text: result.message || 'Transition failed with unknown error'
1942
- }
1943
- ],
1944
- isError: true
1945
- };
1946
- }
1947
- }
1948
- catch (error) {
1949
- const errorDetails = error.response?.data || error.message;
1950
- return {
1951
- content: [
1952
- {
1953
- type: 'text',
1954
- text: `❌ Failed to transition Jira issue ${issueKey} (interactive mode)\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Transition ID: ${transitionId}\n- Field Values: ${JSON.stringify(fieldValues || {}, null, 2)}\n\n**Tip:** Check if the transition ID is valid for this issue and you have permission to transition it.`
1955
- }
1956
- ],
1957
- isError: true
1958
- };
1959
- }
1960
- }
1961
- case 'jira_get_attachments': {
1962
- const { issueKey } = args;
1963
- try {
1964
- const attachments = await jiraService.getAttachments(issueKey);
1965
- return {
1966
- content: [
1967
- {
1968
- type: 'text',
1969
- text: JSON.stringify(attachments, null, 2)
1970
- }
1971
- ]
1972
- };
1973
- }
1974
- catch (error) {
1975
- const errorDetails = error.response?.data || error.message;
1976
- return {
1977
- content: [
1978
- {
1979
- type: 'text',
1980
- text: `❌ Failed to get attachments for Jira issue\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n\n**Tip:** Check if the issue exists and you have permission to view it.`
1981
- }
1982
- ],
1983
- isError: true
1984
- };
1985
- }
1986
- }
1987
- case 'jira_add_attachment': {
1988
- const { issueKey, filename, fileContent } = args;
1989
- try {
1990
- const result = await jiraService.addAttachment(issueKey, filename, fileContent);
1991
- return {
1992
- content: [
1993
- {
1994
- type: 'text',
1995
- text: `Attachment "${filename}" added to issue ${issueKey}\n\n${JSON.stringify(result, null, 2)}`
1996
- }
1997
- ]
1998
- };
1999
- }
2000
- catch (error) {
2001
- const errorDetails = error.response?.data || error.message;
2002
- return {
2003
- content: [
2004
- {
2005
- type: 'text',
2006
- text: `❌ Failed to add attachment to Jira issue\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n- Filename: ${filename}\n\n**Tip:** Check if the issue exists, you have permission to attach files, and the file content is properly base64 encoded.`
2007
- }
2008
- ],
2009
- isError: true
2010
- };
2011
- }
2012
- }
2013
- case 'jira_delete_attachment': {
2014
- const { attachmentId } = args;
2015
- try {
2016
- await jiraService.deleteAttachment(attachmentId);
2017
- return {
2018
- content: [
2019
- {
2020
- type: 'text',
2021
- text: `Attachment ${attachmentId} deleted successfully`
2022
- }
2023
- ]
2024
- };
2025
- }
2026
- catch (error) {
2027
- const errorDetails = error.response?.data || error.message;
2028
- return {
2029
- content: [
2030
- {
2031
- type: 'text',
2032
- text: `❌ Failed to delete attachment\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Attachment ID: ${attachmentId}\n\n**Tip:** Check if the attachment exists and you have permission to delete it.`
2033
- }
2034
- ],
2035
- isError: true
2036
- };
2037
- }
2038
- }
2039
- case 'jira_get_issue_links': {
2040
- const { issueKey } = args;
2041
- try {
2042
- const links = await jiraService.getIssueLinks(issueKey);
2043
- return {
2044
- content: [
2045
- {
2046
- type: 'text',
2047
- text: JSON.stringify(links, null, 2)
2048
- }
2049
- ]
2050
- };
2051
- }
2052
- catch (error) {
2053
- const errorDetails = error.response?.data || error.message;
2054
- return {
2055
- content: [
2056
- {
2057
- type: 'text',
2058
- text: `❌ Failed to get issue links\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n\n**Tip:** Check if the issue exists and you have permission to view it.`
2059
- }
2060
- ],
2061
- isError: true
2062
- };
2063
- }
2064
- }
2065
- case 'jira_create_issue_link': {
2066
- const { inwardIssueKey, outwardIssueKey, linkType } = args;
2067
- try {
2068
- await jiraService.createIssueLink(inwardIssueKey, outwardIssueKey, linkType);
2069
- return {
2070
- content: [
2071
- {
2072
- type: 'text',
2073
- text: `Link created: ${inwardIssueKey} ${linkType} ${outwardIssueKey}`
2074
- }
2075
- ]
2076
- };
2077
- }
2078
- catch (error) {
2079
- const errorDetails = error.response?.data || error.message;
2080
- return {
2081
- content: [
2082
- {
2083
- type: 'text',
2084
- text: `❌ Failed to create issue link\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Inward Issue: ${inwardIssueKey}\n- Outward Issue: ${outwardIssueKey}\n- Link Type: ${linkType}\n\n**Tip:** Check if both issues exist, the link type is valid, and you have permission to link issues. Use jira_get_link_types to see available link types.`
2085
- }
2086
- ],
2087
- isError: true
2088
- };
2089
- }
2090
- }
2091
- case 'jira_delete_issue_link': {
2092
- const { linkId } = args;
2093
- try {
2094
- await jiraService.deleteIssueLink(linkId);
2095
- return {
2096
- content: [
2097
- {
2098
- type: 'text',
2099
- text: `Issue link ${linkId} deleted successfully`
2100
- }
2101
- ]
2102
- };
2103
- }
2104
- catch (error) {
2105
- const errorDetails = error.response?.data || error.message;
2106
- return {
2107
- content: [
2108
- {
2109
- type: 'text',
2110
- text: `❌ Failed to delete issue link\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Link ID: ${linkId}\n\n**Tip:** Check if the link exists and you have permission to delete it. Use jira_get_issue_links to get link IDs.`
2111
- }
2112
- ],
2113
- isError: true
2114
- };
2115
- }
2116
- }
2117
- case 'jira_get_link_types': {
2118
- try {
2119
- const linkTypes = await jiraService.getIssueLinkTypes();
2120
- return {
2121
- content: [
2122
- {
2123
- type: 'text',
2124
- text: JSON.stringify(linkTypes, null, 2)
2125
- }
2126
- ]
2127
- };
2128
- }
2129
- catch (error) {
2130
- const errorDetails = error.response?.data || error.message;
2131
- return {
2132
- content: [
2133
- {
2134
- type: 'text',
2135
- text: `❌ Failed to get issue link types\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Tip:** Check your Jira permissions and API access.`
2136
- }
2137
- ],
2138
- isError: true
2139
- };
2140
- }
2141
- }
2142
- case 'jira_get_worklogs': {
2143
- const { issueKey } = args;
2144
- try {
2145
- const worklogs = await jiraService.getWorkLogs(issueKey);
2146
- return {
2147
- content: [
2148
- {
2149
- type: 'text',
2150
- text: JSON.stringify(worklogs, null, 2)
2151
- }
2152
- ]
2153
- };
2154
- }
2155
- catch (error) {
2156
- const errorDetails = error.response?.data || error.message;
2157
- return {
2158
- content: [
2159
- {
2160
- type: 'text',
2161
- text: `❌ Failed to get work logs\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n\n**Tip:** Check if the issue exists and you have permission to view it.`
2162
- }
2163
- ],
2164
- isError: true
2165
- };
2166
- }
2167
- }
2168
- case 'jira_add_worklog': {
2169
- const { issueKey, timeSpentSeconds, comment, started } = args;
2170
- try {
2171
- const result = await jiraService.addWorkLog(issueKey, timeSpentSeconds, comment, started);
2172
- return {
2173
- content: [
2174
- {
2175
- type: 'text',
2176
- text: `Work log added to ${issueKey}\nTime spent: ${Math.floor(timeSpentSeconds / 3600)}h ${Math.floor((timeSpentSeconds % 3600) / 60)}m\n\n${JSON.stringify(result, null, 2)}`
2177
- }
2178
- ]
2179
- };
2180
- }
2181
- catch (error) {
2182
- const errorDetails = error.response?.data || error.message;
2183
- return {
2184
- content: [
2185
- {
2186
- type: 'text',
2187
- text: `❌ Failed to add work log\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n- Time Spent: ${timeSpentSeconds}s\n- Comment: ${comment || 'none'}\n\n**Tip:** Check if the issue exists and you have permission to log work.`
2188
- }
2189
- ],
2190
- isError: true
2191
- };
2192
- }
2193
- }
2194
- case 'jira_update_worklog': {
2195
- const { issueKey, worklogId, timeSpentSeconds, comment } = args;
2196
- try {
2197
- const result = await jiraService.updateWorkLog(issueKey, worklogId, timeSpentSeconds, comment);
2198
- return {
2199
- content: [
2200
- {
2201
- type: 'text',
2202
- text: `Work log ${worklogId} updated\n\n${JSON.stringify(result, null, 2)}`
2203
- }
2204
- ]
2205
- };
2206
- }
2207
- catch (error) {
2208
- const errorDetails = error.response?.data || error.message;
2209
- return {
2210
- content: [
2211
- {
2212
- type: 'text',
2213
- text: `❌ Failed to update work log\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n- Worklog ID: ${worklogId}\n\n**Tip:** Check if the work log exists and you have permission to update it.`
2214
- }
2215
- ],
2216
- isError: true
2217
- };
2218
- }
2219
- }
2220
- case 'jira_delete_worklog': {
2221
- const { issueKey, worklogId } = args;
2222
- try {
2223
- await jiraService.deleteWorkLog(issueKey, worklogId);
2224
- return {
2225
- content: [
2226
- {
2227
- type: 'text',
2228
- text: `Work log ${worklogId} deleted from ${issueKey}`
2229
- }
2230
- ]
2231
- };
2232
- }
2233
- catch (error) {
2234
- const errorDetails = error.response?.data || error.message;
2235
- return {
2236
- content: [
2237
- {
2238
- type: 'text',
2239
- text: `❌ Failed to delete work log\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n- Worklog ID: ${worklogId}\n\n**Tip:** Check if the work log exists and you have permission to delete it.`
2240
- }
2241
- ],
2242
- isError: true
2243
- };
2244
- }
2245
- }
2246
- case 'jira_get_watchers': {
2247
- const { issueKey } = args;
2248
- try {
2249
- const watchers = await jiraService.getWatchers(issueKey);
2250
- return {
2251
- content: [
2252
- {
2253
- type: 'text',
2254
- text: JSON.stringify(watchers, null, 2)
2255
- }
2256
- ]
2257
- };
2258
- }
2259
- catch (error) {
2260
- const errorDetails = error.response?.data || error.message;
2261
- return {
2262
- content: [
2263
- {
2264
- type: 'text',
2265
- text: `❌ Failed to get watchers\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n\n**Tip:** Check if the issue exists and you have permission to view it.`
2266
- }
2267
- ],
2268
- isError: true
2269
- };
2270
- }
2271
- }
2272
- case 'jira_add_watcher': {
2273
- const { issueKey, accountId } = args;
2274
- try {
2275
- await jiraService.addWatcher(issueKey, accountId);
2276
- return {
2277
- content: [
2278
- {
2279
- type: 'text',
2280
- text: `Watcher added to ${issueKey}`
2281
- }
2282
- ]
2283
- };
2284
- }
2285
- catch (error) {
2286
- const errorDetails = error.response?.data || error.message;
2287
- return {
2288
- content: [
2289
- {
2290
- type: 'text',
2291
- text: `❌ Failed to add watcher\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n- Account ID: ${accountId}\n\n**Tip:** Check if the issue and user exist, and you have permission to add watchers.`
2292
- }
2293
- ],
2294
- isError: true
2295
- };
2296
- }
2297
- }
2298
- case 'jira_remove_watcher': {
2299
- const { issueKey, accountId } = args;
2300
- try {
2301
- await jiraService.removeWatcher(issueKey, accountId);
2302
- return {
2303
- content: [
2304
- {
2305
- type: 'text',
2306
- text: `Watcher removed from ${issueKey}`
2307
- }
2308
- ]
2309
- };
2310
- }
2311
- catch (error) {
2312
- const errorDetails = error.response?.data || error.message;
2313
- return {
2314
- content: [
2315
- {
2316
- type: 'text',
2317
- text: `❌ Failed to remove watcher\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n- Account ID: ${accountId}\n\n**Tip:** Check if the issue exists and you have permission to remove watchers.`
2318
- }
2319
- ],
2320
- isError: true
2321
- };
2322
- }
2323
- }
2324
- case 'jira_get_subtasks': {
2325
- const { issueKey } = args;
2326
- try {
2327
- const subtasks = await jiraService.getSubTasks(issueKey);
2328
- return {
2329
- content: [
2330
- {
2331
- type: 'text',
2332
- text: JSON.stringify(subtasks, null, 2)
2333
- }
2334
- ]
2335
- };
2336
- }
2337
- catch (error) {
2338
- const errorDetails = error.response?.data || error.message;
2339
- return {
2340
- content: [
2341
- {
2342
- type: 'text',
2343
- text: `❌ Failed to get sub-tasks\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n\n**Tip:** Check if the issue exists and you have permission to view it.`
2344
- }
2345
- ],
2346
- isError: true
2347
- };
2348
- }
2349
- }
2350
- case 'jira_create_subtask': {
2351
- const { parentKey, summary, description } = args;
2352
- try {
2353
- const subtask = await jiraService.createSubTask(parentKey, summary, description);
2354
- return {
2355
- content: [
2356
- {
2357
- type: 'text',
2358
- text: `Sub-task created: ${subtask.key}\n\n${JSON.stringify(subtask, null, 2)}`
2359
- }
2360
- ]
2361
- };
2362
- }
2363
- catch (error) {
2364
- const errorDetails = error.response?.data || error.message;
2365
- return {
2366
- content: [
2367
- {
2368
- type: 'text',
2369
- text: `❌ Failed to create sub-task\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Parent Key: ${parentKey}\n- Summary: ${summary}\n\n**Tip:** Check if the parent issue exists, the project allows sub-tasks, and you have permission to create issues.`
2370
- }
2371
- ],
2372
- isError: true
2373
- };
2374
- }
2375
- }
2376
- case 'jira_get_issue_history': {
2377
- const { issueKey } = args;
2378
- try {
2379
- const history = await jiraService.getIssueHistory(issueKey);
2380
- return {
2381
- content: [
2382
- {
2383
- type: 'text',
2384
- text: JSON.stringify(history, null, 2)
2385
- }
2386
- ]
2387
- };
2388
- }
2389
- catch (error) {
2390
- const errorDetails = error.response?.data || error.message;
2391
- return {
2392
- content: [
2393
- {
2394
- type: 'text',
2395
- text: `❌ Failed to get issue history\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Issue Key: ${issueKey}\n\n**Tip:** Check if the issue exists and you have permission to view it.`
2396
- }
2397
- ],
2398
- isError: true
2399
- };
2400
- }
2401
- }
87
+ case 'jira_search_issues':
88
+ return jiraHandlers.searchIssues(args);
89
+ case 'jira_get_issue':
90
+ return jiraHandlers.getIssue(args);
91
+ case 'jira_create_issue':
92
+ return jiraHandlers.createIssue(args);
93
+ case 'jira_update_issue':
94
+ return jiraHandlers.updateIssue(args);
95
+ case 'jira_add_comment':
96
+ return jiraHandlers.addComment(args);
97
+ case 'jira_get_comments':
98
+ return jiraHandlers.getComments(args);
99
+ case 'jira_update_comment':
100
+ return jiraHandlers.updateComment(args);
101
+ case 'jira_delete_comment':
102
+ return jiraHandlers.deleteComment(args);
103
+ case 'jira_get_projects':
104
+ return jiraHandlers.getProjects();
105
+ case 'jira_get_issue_transitions':
106
+ return jiraHandlers.getIssueTransitions(args);
107
+ case 'jira_transition_issue':
108
+ return jiraHandlers.transitionIssue(args);
109
+ case 'jira_transition_issue_interactive':
110
+ return jiraHandlers.transitionIssueInteractive(args);
111
+ case 'jira_get_attachments':
112
+ return jiraHandlers.getAttachments(args);
113
+ case 'jira_add_attachment':
114
+ return jiraHandlers.addAttachment(args);
115
+ case 'jira_delete_attachment':
116
+ return jiraHandlers.deleteAttachment(args);
117
+ case 'jira_get_issue_links':
118
+ return jiraHandlers.getIssueLinks(args);
119
+ case 'jira_create_issue_link':
120
+ return jiraHandlers.createIssueLink(args);
121
+ case 'jira_delete_issue_link':
122
+ return jiraHandlers.deleteIssueLink(args);
123
+ case 'jira_get_link_types':
124
+ return jiraHandlers.getLinkTypes();
125
+ case 'jira_get_worklogs':
126
+ return jiraHandlers.getWorklogs(args);
127
+ case 'jira_add_worklog':
128
+ return jiraHandlers.addWorklog(args);
129
+ case 'jira_update_worklog':
130
+ return jiraHandlers.updateWorklog(args);
131
+ case 'jira_delete_worklog':
132
+ return jiraHandlers.deleteWorklog(args);
133
+ case 'jira_get_watchers':
134
+ return jiraHandlers.getWatchers(args);
135
+ case 'jira_add_watcher':
136
+ return jiraHandlers.addWatcher(args);
137
+ case 'jira_remove_watcher':
138
+ return jiraHandlers.removeWatcher(args);
139
+ case 'jira_get_subtasks':
140
+ return jiraHandlers.getSubtasks(args);
141
+ case 'jira_create_subtask':
142
+ return jiraHandlers.createSubtask(args);
143
+ case 'jira_get_issue_history':
144
+ return jiraHandlers.getIssueHistory(args);
145
+ case 'jira_get_fields':
146
+ return jiraHandlers.getFields(args);
147
+ case 'jira_get_labels':
148
+ return jiraHandlers.getLabels(args);
149
+ case 'jira_add_labels':
150
+ return jiraHandlers.addLabels(args);
151
+ case 'jira_remove_labels':
152
+ return jiraHandlers.removeLabels(args);
153
+ case 'jira_get_agile_boards':
154
+ return jiraHandlers.getAgileBoards(args);
155
+ case 'jira_get_board_issues':
156
+ return jiraHandlers.getBoardIssues(args);
157
+ case 'jira_get_sprints':
158
+ return jiraHandlers.getSprints(args);
159
+ case 'jira_get_sprint_issues':
160
+ return jiraHandlers.getSprintIssues(args);
161
+ case 'jira_batch_create_issues':
162
+ return jiraHandlers.batchCreateIssues(args);
163
+ case 'jira_get_dev_status':
164
+ return jiraHandlers.getDevStatus(args);
165
+ case 'jira_delete_issue':
166
+ return jiraHandlers.deleteIssue(args);
167
+ case 'jira_lookup_user':
168
+ return jiraHandlers.lookupUser(args);
169
+ case 'jira_get_issue_types':
170
+ return jiraHandlers.getIssueTypes(args);
171
+ case 'jira_create_sprint':
172
+ return jiraHandlers.createSprint(args);
173
+ case 'jira_update_sprint':
174
+ return jiraHandlers.updateSprint(args);
175
+ case 'jira_get_project_versions':
176
+ return jiraHandlers.getProjectVersions(args);
177
+ case 'jira_create_version':
178
+ return jiraHandlers.createVersion(args);
179
+ case 'jira_update_version':
180
+ return jiraHandlers.updateVersion(args);
181
+ case 'jira_link_to_epic':
182
+ return jiraHandlers.linkToEpic(args);
2402
183
  // Bitbucket Tools
2403
- case 'bitbucket_get_repositories': {
2404
- const { query, limit } = args;
2405
- try {
2406
- const repos = await bitbucketService.getRepositories({ query, limit });
2407
- return {
2408
- content: [
2409
- {
2410
- type: 'text',
2411
- text: JSON.stringify(repos, null, 2)
2412
- }
2413
- ]
2414
- };
2415
- }
2416
- catch (error) {
2417
- const errorDetails = error.response?.data || error.message;
2418
- return {
2419
- content: [
2420
- {
2421
- type: 'text',
2422
- text: `❌ Failed to get Bitbucket repositories\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Query: ${query || 'none'}\n- Limit: ${limit || 10}\n\n**Tip:** Check your Bitbucket permissions and API access.`
2423
- }
2424
- ],
2425
- isError: true
2426
- };
2427
- }
2428
- }
2429
- case 'bitbucket_get_repository': {
2430
- const { repoName } = args;
2431
- try {
2432
- const repo = await bitbucketService.getRepository(repoName);
2433
- return {
2434
- content: [
2435
- {
2436
- type: 'text',
2437
- text: JSON.stringify(repo, null, 2)
2438
- }
2439
- ]
2440
- };
2441
- }
2442
- catch (error) {
2443
- const errorDetails = error.response?.data || error.message;
2444
- return {
2445
- content: [
2446
- {
2447
- type: 'text',
2448
- text: `❌ Failed to get Bitbucket repository\n\n**Error Details:**\n${JSON.stringify(errorDetails, null, 2)}\n\n**Request:**\n- Repository: ${repoName}\n\n**Tip:** Check if the repository exists and you have access to it.`
2449
- }
2450
- ],
2451
- isError: true
2452
- };
2453
- }
2454
- }
2455
- case 'bitbucket_create_repository': {
2456
- const { name, description, isPrivate, language } = args;
2457
- const repo = await bitbucketService.createRepository(name, description, isPrivate, language);
2458
- return {
2459
- content: [
2460
- {
2461
- type: 'text',
2462
- text: JSON.stringify(repo, null, 2)
2463
- }
2464
- ]
2465
- };
2466
- }
2467
- case 'bitbucket_get_pull_requests': {
2468
- const { repoName, state, limit } = args;
2469
- const prs = await bitbucketService.getPullRequests(repoName, state, limit);
2470
- return {
2471
- content: [
2472
- {
2473
- type: 'text',
2474
- text: JSON.stringify(prs, null, 2)
2475
- }
2476
- ]
2477
- };
2478
- }
2479
- case 'bitbucket_get_pull_request': {
2480
- const { repoName, prId, includeDiff = false } = args;
2481
- const pr = await bitbucketService.getPullRequest(repoName, prId, includeDiff);
2482
- if (includeDiff && pr.diff) {
2483
- return {
2484
- content: [
2485
- {
2486
- type: 'text',
2487
- text: `Pull Request #${prId} Details:\n\n${JSON.stringify(pr, null, 2)}\n\n---\n\nCode Diff:\n\n\`\`\`diff\n${pr.diff}\n\`\`\``
2488
- }
2489
- ]
2490
- };
2491
- }
2492
- else {
2493
- return {
2494
- content: [
2495
- {
2496
- type: 'text',
2497
- text: JSON.stringify(pr, null, 2)
2498
- }
2499
- ]
2500
- };
2501
- }
2502
- }
2503
- case 'bitbucket_create_pull_request': {
2504
- const { repoName, title, sourceBranch, destinationBranch, description } = args;
2505
- const pr = await bitbucketService.createPullRequest(repoName, title, sourceBranch, destinationBranch, description);
2506
- return {
2507
- content: [
2508
- {
2509
- type: 'text',
2510
- text: JSON.stringify(pr, null, 2)
2511
- }
2512
- ]
2513
- };
2514
- }
2515
- case 'bitbucket_get_branches': {
2516
- const { repoName, limit } = args;
2517
- const branches = await bitbucketService.getBranches(repoName, limit);
2518
- return {
2519
- content: [
2520
- {
2521
- type: 'text',
2522
- text: JSON.stringify(branches, null, 2)
2523
- }
2524
- ]
2525
- };
2526
- }
2527
- case 'bitbucket_get_commits': {
2528
- const { repoName, branch, limit } = args;
2529
- const commits = await bitbucketService.getCommits(repoName, branch, limit);
2530
- return {
2531
- content: [
2532
- {
2533
- type: 'text',
2534
- text: JSON.stringify(commits, null, 2)
2535
- }
2536
- ]
2537
- };
2538
- }
2539
- case 'bitbucket_get_issues': {
2540
- const { repoName, state, limit } = args;
2541
- const issues = await bitbucketService.getIssues(repoName, state, limit);
2542
- return {
2543
- content: [
2544
- {
2545
- type: 'text',
2546
- text: JSON.stringify(issues, null, 2)
2547
- }
2548
- ]
2549
- };
2550
- }
2551
- case 'bitbucket_create_issue': {
2552
- const { repoName, title, content, kind } = args;
2553
- const issue = await bitbucketService.createIssue(repoName, title, content, kind);
2554
- return {
2555
- content: [
2556
- {
2557
- type: 'text',
2558
- text: JSON.stringify(issue, null, 2)
2559
- }
2560
- ]
2561
- };
2562
- }
184
+ case 'bitbucket_get_repositories':
185
+ return bitbucketHandlers.getRepositories(args);
186
+ case 'bitbucket_get_repository':
187
+ return bitbucketHandlers.getRepository(args);
188
+ case 'bitbucket_create_repository':
189
+ return bitbucketHandlers.createRepository(args);
190
+ case 'bitbucket_get_pull_requests':
191
+ return bitbucketHandlers.getPullRequests(args);
192
+ case 'bitbucket_get_pull_request':
193
+ return bitbucketHandlers.getPullRequest(args);
194
+ case 'bitbucket_create_pull_request':
195
+ return bitbucketHandlers.createPullRequest(args);
196
+ case 'bitbucket_get_branches':
197
+ return bitbucketHandlers.getBranches(args);
198
+ case 'bitbucket_get_commits':
199
+ return bitbucketHandlers.getCommits(args);
200
+ case 'bitbucket_get_issues':
201
+ return bitbucketHandlers.getIssues(args);
202
+ case 'bitbucket_create_issue':
203
+ return bitbucketHandlers.createIssue(args);
204
+ case 'bitbucket_get_pr_comments':
205
+ return bitbucketHandlers.getPRComments(args);
206
+ case 'bitbucket_add_pr_comment':
207
+ return bitbucketHandlers.addPRComment(args);
208
+ case 'bitbucket_update_pr_comment':
209
+ return bitbucketHandlers.updatePRComment(args);
210
+ case 'bitbucket_delete_pr_comment':
211
+ return bitbucketHandlers.deletePRComment(args);
212
+ case 'bitbucket_resolve_pr_comment':
213
+ return bitbucketHandlers.resolvePRComment(args);
214
+ case 'bitbucket_unresolve_pr_comment':
215
+ return bitbucketHandlers.unresolvePRComment(args);
2563
216
  default:
2564
- throw new Error(`Unknown tool: ${name}`);
217
+ throw new Error(ERROR_MESSAGES.UNKNOWN_TOOL(name));
2565
218
  }
2566
219
  }
2567
220
  catch (error) {