@aaronsb/jira-cloud-mcp 0.2.7 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -30
- package/build/client/field-discovery.js +346 -0
- package/build/client/field-type-map.js +106 -0
- package/build/client/jira-client.js +119 -88
- package/build/docs/tool-documentation.js +481 -0
- package/build/handlers/board-handlers.js +10 -153
- package/build/handlers/filter-handlers.js +7 -98
- package/build/handlers/issue-handlers.js +148 -75
- package/build/handlers/project-handlers.js +11 -154
- package/build/handlers/queue-handler.js +252 -0
- package/build/handlers/resource-handlers.js +94 -19
- package/build/handlers/sprint-handlers.js +9 -94
- package/build/handlers/tool-resource-handlers.js +16 -1165
- package/build/index.js +59 -51
- package/build/mcp/markdown-renderer.js +11 -0
- package/build/schemas/tool-schemas.js +98 -197
- package/build/utils/bulk-operation-guard.js +74 -0
- package/build/utils/next-steps.js +124 -0
- package/build/utils/normalize-args.js +12 -0
- package/build/utils/text-processing.js +5 -1
- package/package.json +6 -4
|
@@ -1,67 +1,45 @@
|
|
|
1
1
|
export const toolSchemas = {
|
|
2
|
-
// Filter Management API
|
|
3
2
|
manage_jira_filter: {
|
|
4
3
|
name: 'manage_jira_filter',
|
|
5
|
-
description: '
|
|
4
|
+
description: 'Search for issues using JQL queries, or manage saved filters',
|
|
6
5
|
inputSchema: {
|
|
7
6
|
type: 'object',
|
|
8
7
|
properties: {
|
|
9
8
|
operation: {
|
|
10
9
|
type: 'string',
|
|
11
10
|
enum: ['get', 'create', 'update', 'delete', 'list', 'execute_filter', 'execute_jql'],
|
|
12
|
-
description: 'Operation to perform
|
|
11
|
+
description: 'Operation to perform',
|
|
13
12
|
},
|
|
14
|
-
// Parameters for get, update, delete, execute_filter operations
|
|
15
13
|
filterId: {
|
|
16
14
|
type: 'string',
|
|
17
|
-
description: '
|
|
15
|
+
description: 'Filter ID. Required for get, update, delete, execute_filter.',
|
|
18
16
|
},
|
|
19
|
-
// Parameters for create and update operations
|
|
20
17
|
name: {
|
|
21
18
|
type: 'string',
|
|
22
|
-
description: '
|
|
19
|
+
description: 'Filter name. Required for create.',
|
|
23
20
|
},
|
|
24
21
|
jql: {
|
|
25
22
|
type: 'string',
|
|
26
|
-
description: 'JQL query string.
|
|
27
|
-
'# Portfolio/Plans Queries\n' +
|
|
28
|
-
'- Find child issues: issue in portfolioChildIssuesOf("PROJ-123")\n' +
|
|
29
|
-
'- Combined portfolio search: issue in portfolioChildIssuesOf("PROJ-123") AND status = "In Progress"\n' +
|
|
30
|
-
'- Multiple portfolios: issue in portfolioChildIssuesOf("PROJ-123") OR issue in portfolioChildIssuesOf("PROJ-456")\n\n' +
|
|
31
|
-
'# Common Search Patterns\n' +
|
|
32
|
-
'- Assigned issues: assignee = currentUser()\n' +
|
|
33
|
-
'- Unassigned issues: assignee IS EMPTY\n' +
|
|
34
|
-
'- Recent changes: status CHANGED AFTER -1w\n' +
|
|
35
|
-
'- Multiple statuses: status IN ("In Progress", "Under Review", "Testing")\n' +
|
|
36
|
-
'- Priority tasks: priority = High AND status = Open\n' +
|
|
37
|
-
'- Component search: component = "User Interface" OR component = "API"\n\n' +
|
|
38
|
-
'# Advanced Functions\n' +
|
|
39
|
-
'- Sort results: ORDER BY created DESC\n' +
|
|
40
|
-
'- Track changes: status WAS "Resolved" AND status = "Open"\n' +
|
|
41
|
-
'- Team filters: assignee IN MEMBERSOF("developers")\n\n' +
|
|
42
|
-
'JQL supports complex combinations using AND, OR, NOT operators and parentheses for grouping. ' +
|
|
43
|
-
'All text values are case-sensitive and must be enclosed in quotes when they contain spaces.',
|
|
23
|
+
description: 'JQL query string. Required for create and execute_jql. Read jira://tools/manage_jira_filter/documentation for syntax examples.',
|
|
44
24
|
},
|
|
45
25
|
description: {
|
|
46
26
|
type: 'string',
|
|
47
|
-
description: '
|
|
27
|
+
description: 'Filter description.',
|
|
48
28
|
},
|
|
49
29
|
favourite: {
|
|
50
30
|
type: 'boolean',
|
|
51
|
-
description: '
|
|
31
|
+
description: 'Mark as favorite.',
|
|
52
32
|
},
|
|
53
|
-
// Parameters for list operation
|
|
54
33
|
startAt: {
|
|
55
34
|
type: 'integer',
|
|
56
|
-
description: '
|
|
35
|
+
description: 'Pagination offset (0-based).',
|
|
57
36
|
default: 0,
|
|
58
37
|
},
|
|
59
38
|
maxResults: {
|
|
60
39
|
type: 'integer',
|
|
61
|
-
description: '
|
|
40
|
+
description: 'Max items to return.',
|
|
62
41
|
default: 50,
|
|
63
42
|
},
|
|
64
|
-
// Parameters for sharing
|
|
65
43
|
sharePermissions: {
|
|
66
44
|
type: 'array',
|
|
67
45
|
items: {
|
|
@@ -70,381 +48,304 @@ export const toolSchemas = {
|
|
|
70
48
|
type: {
|
|
71
49
|
type: 'string',
|
|
72
50
|
enum: ['group', 'project', 'global'],
|
|
73
|
-
description: 'Type of share permission',
|
|
74
|
-
},
|
|
75
|
-
group: {
|
|
76
|
-
type: 'string',
|
|
77
|
-
description: 'Group name (required when type is "group")',
|
|
78
|
-
},
|
|
79
|
-
project: {
|
|
80
|
-
type: 'string',
|
|
81
|
-
description: 'Project key (required when type is "project")',
|
|
82
51
|
},
|
|
52
|
+
group: { type: 'string' },
|
|
53
|
+
project: { type: 'string' },
|
|
83
54
|
},
|
|
84
55
|
required: ['type'],
|
|
85
56
|
},
|
|
86
|
-
description: 'Share permissions for the filter.
|
|
57
|
+
description: 'Share permissions for the filter.',
|
|
87
58
|
},
|
|
88
|
-
// Common expansion options
|
|
89
59
|
expand: {
|
|
90
60
|
type: 'array',
|
|
91
61
|
items: {
|
|
92
62
|
type: 'string',
|
|
93
63
|
enum: ['jql', 'description', 'permissions', 'issue_count', 'issue_details', 'transitions', 'comments_preview'],
|
|
94
64
|
},
|
|
95
|
-
description: '
|
|
65
|
+
description: 'Additional fields to include in the response.',
|
|
96
66
|
},
|
|
97
67
|
},
|
|
98
68
|
required: ['operation'],
|
|
99
69
|
},
|
|
100
70
|
},
|
|
101
|
-
// Sprint Management API
|
|
102
71
|
manage_jira_sprint: {
|
|
103
72
|
name: 'manage_jira_sprint',
|
|
104
|
-
description: '
|
|
73
|
+
description: 'Manage sprints: create, start, close, and assign issues to sprints',
|
|
105
74
|
inputSchema: {
|
|
106
75
|
type: 'object',
|
|
107
76
|
properties: {
|
|
108
77
|
operation: {
|
|
109
78
|
type: 'string',
|
|
110
79
|
enum: ['get', 'create', 'update', 'delete', 'list', 'manage_issues'],
|
|
111
|
-
description: 'Operation to perform
|
|
80
|
+
description: 'Operation to perform',
|
|
112
81
|
},
|
|
113
|
-
// Parameters for get operation
|
|
114
82
|
sprintId: {
|
|
115
83
|
type: 'integer',
|
|
116
|
-
description: '
|
|
84
|
+
description: 'Sprint ID. Required for get, update, delete, manage_issues.',
|
|
117
85
|
},
|
|
118
|
-
// Parameters for create operation
|
|
119
86
|
boardId: {
|
|
120
87
|
type: 'integer',
|
|
121
|
-
description: '
|
|
88
|
+
description: 'Board ID. Required for create and list.',
|
|
122
89
|
},
|
|
123
90
|
name: {
|
|
124
91
|
type: 'string',
|
|
125
|
-
description: '
|
|
92
|
+
description: 'Sprint name. Required for create.',
|
|
126
93
|
},
|
|
127
|
-
// Common parameters for create and update
|
|
128
94
|
startDate: {
|
|
129
95
|
type: 'string',
|
|
130
|
-
description: 'Start date
|
|
96
|
+
description: 'Start date in ISO format (e.g., "2025-03-20T00:00:00.000Z").',
|
|
131
97
|
},
|
|
132
98
|
endDate: {
|
|
133
99
|
type: 'string',
|
|
134
|
-
description: 'End date
|
|
100
|
+
description: 'End date in ISO format.',
|
|
135
101
|
},
|
|
136
102
|
goal: {
|
|
137
103
|
type: 'string',
|
|
138
|
-
description: '
|
|
104
|
+
description: 'Sprint goal.',
|
|
139
105
|
},
|
|
140
106
|
state: {
|
|
141
107
|
type: 'string',
|
|
142
108
|
enum: ['future', 'active', 'closed'],
|
|
143
|
-
description: 'Sprint state.
|
|
109
|
+
description: 'Sprint state. Filter for list, or set via update.',
|
|
144
110
|
},
|
|
145
|
-
// Parameters for list operation
|
|
146
111
|
startAt: {
|
|
147
112
|
type: 'integer',
|
|
148
|
-
description: '
|
|
113
|
+
description: 'Pagination offset (0-based).',
|
|
149
114
|
default: 0,
|
|
150
115
|
},
|
|
151
116
|
maxResults: {
|
|
152
117
|
type: 'integer',
|
|
153
|
-
description: '
|
|
118
|
+
description: 'Max items to return.',
|
|
154
119
|
default: 50,
|
|
155
120
|
},
|
|
156
|
-
// Parameters for manage_issues operation
|
|
157
121
|
add: {
|
|
158
122
|
type: 'array',
|
|
159
|
-
items: {
|
|
160
|
-
|
|
161
|
-
},
|
|
162
|
-
description: 'Array of issue keys to add to the sprint. Used for manage_issues operation.',
|
|
123
|
+
items: { type: 'string' },
|
|
124
|
+
description: 'Issue keys to add to sprint (manage_issues).',
|
|
163
125
|
},
|
|
164
126
|
remove: {
|
|
165
127
|
type: 'array',
|
|
166
|
-
items: {
|
|
167
|
-
|
|
168
|
-
},
|
|
169
|
-
description: 'Array of issue keys to remove from the sprint. Used for manage_issues operation.',
|
|
128
|
+
items: { type: 'string' },
|
|
129
|
+
description: 'Issue keys to remove from sprint (manage_issues).',
|
|
170
130
|
},
|
|
171
|
-
// Common expansion options
|
|
172
131
|
expand: {
|
|
173
132
|
type: 'array',
|
|
174
133
|
items: {
|
|
175
134
|
type: 'string',
|
|
176
135
|
enum: ['issues', 'report', 'board'],
|
|
177
136
|
},
|
|
178
|
-
description: '
|
|
137
|
+
description: 'Additional fields to include in the response.',
|
|
179
138
|
},
|
|
180
139
|
},
|
|
181
140
|
required: ['operation'],
|
|
182
141
|
},
|
|
183
142
|
},
|
|
184
|
-
// Issue Management API
|
|
185
143
|
manage_jira_issue: {
|
|
186
144
|
name: 'manage_jira_issue',
|
|
187
|
-
description: '
|
|
145
|
+
description: 'Get, create, update, delete, move, transition, comment on, or link Jira issues',
|
|
188
146
|
inputSchema: {
|
|
189
147
|
type: 'object',
|
|
190
148
|
properties: {
|
|
191
149
|
operation: {
|
|
192
150
|
type: 'string',
|
|
193
|
-
enum: ['create', 'get', 'update', 'delete', 'transition', 'comment', 'link'],
|
|
194
|
-
description: 'Operation to perform
|
|
151
|
+
enum: ['create', 'get', 'update', 'delete', 'move', 'transition', 'comment', 'link'],
|
|
152
|
+
description: 'Operation to perform',
|
|
195
153
|
},
|
|
196
|
-
// Parameters for get, update, delete, transition, comment, and link operations
|
|
197
154
|
issueKey: {
|
|
198
155
|
type: 'string',
|
|
199
|
-
description: '
|
|
156
|
+
description: 'Issue key (e.g., PROJ-123). Required for all operations except create.',
|
|
200
157
|
},
|
|
201
|
-
// Parameters for create operation
|
|
202
158
|
projectKey: {
|
|
203
159
|
type: 'string',
|
|
204
|
-
description: 'Project key (e.g., PROJ). Required for create
|
|
160
|
+
description: 'Project key (e.g., PROJ). Required for create.',
|
|
205
161
|
},
|
|
206
|
-
// Common parameters for create and update
|
|
207
162
|
summary: {
|
|
208
163
|
type: 'string',
|
|
209
|
-
description: 'Issue
|
|
164
|
+
description: 'Issue title. Required for create.',
|
|
210
165
|
},
|
|
211
166
|
description: {
|
|
212
167
|
type: 'string',
|
|
213
|
-
description: '
|
|
168
|
+
description: 'Issue description.',
|
|
214
169
|
},
|
|
215
170
|
issueType: {
|
|
216
171
|
type: 'string',
|
|
217
|
-
description: '
|
|
172
|
+
description: 'Issue type (e.g., Story, Bug, Task). Required for create.',
|
|
218
173
|
},
|
|
219
174
|
priority: {
|
|
220
175
|
type: 'string',
|
|
221
|
-
description: '
|
|
176
|
+
description: 'Priority (e.g., High, Medium, Low).',
|
|
222
177
|
},
|
|
223
178
|
assignee: {
|
|
224
179
|
type: 'string',
|
|
225
|
-
description: '
|
|
180
|
+
description: 'Atlassian accountId of the assignee.',
|
|
226
181
|
},
|
|
227
182
|
labels: {
|
|
228
183
|
type: 'array',
|
|
229
|
-
items: {
|
|
230
|
-
|
|
231
|
-
},
|
|
232
|
-
description: 'Array of labels to apply to the issue. Optional for create/update.',
|
|
184
|
+
items: { type: 'string' },
|
|
185
|
+
description: 'Labels to apply.',
|
|
233
186
|
},
|
|
234
187
|
customFields: {
|
|
235
188
|
type: 'object',
|
|
236
|
-
description: 'Custom field values as key-value pairs.
|
|
189
|
+
description: 'Custom field values as key-value pairs.',
|
|
237
190
|
},
|
|
238
|
-
// Parameters for update operation
|
|
239
191
|
parent: {
|
|
240
192
|
type: ['string', 'null'],
|
|
241
|
-
description: '
|
|
193
|
+
description: 'Parent issue key (e.g., PROJ-100) or null to remove.',
|
|
242
194
|
},
|
|
243
|
-
// Parameters for transition operation
|
|
244
195
|
transitionId: {
|
|
245
196
|
type: 'string',
|
|
246
|
-
description: '
|
|
197
|
+
description: 'Transition ID. Required for transition. Use expand: ["transitions"] on get to discover IDs.',
|
|
247
198
|
},
|
|
248
|
-
// Parameters for comment and transition operations
|
|
249
199
|
comment: {
|
|
250
200
|
type: 'string',
|
|
251
|
-
description: 'Comment text. Required for comment
|
|
201
|
+
description: 'Comment text. Required for comment, optional for transition.',
|
|
252
202
|
},
|
|
253
|
-
// Parameters for link operation
|
|
254
203
|
linkType: {
|
|
255
204
|
type: 'string',
|
|
256
|
-
description: '
|
|
205
|
+
description: 'Link type (e.g., "blocks", "relates to"). Required for link. Read jira://issue-link-types for valid types.',
|
|
257
206
|
},
|
|
258
207
|
linkedIssueKey: {
|
|
259
208
|
type: 'string',
|
|
260
|
-
description: '
|
|
209
|
+
description: 'Issue key to link to. Required for link.',
|
|
210
|
+
},
|
|
211
|
+
targetProjectKey: {
|
|
212
|
+
type: 'string',
|
|
213
|
+
description: 'Target project key for move (e.g., NEWPROJ). Required for move.',
|
|
214
|
+
},
|
|
215
|
+
targetIssueType: {
|
|
216
|
+
type: 'string',
|
|
217
|
+
description: 'Target issue type for move (e.g., Story, Bug). Required for move.',
|
|
261
218
|
},
|
|
262
|
-
// Common expansion options
|
|
263
219
|
expand: {
|
|
264
220
|
type: 'array',
|
|
265
221
|
items: {
|
|
266
222
|
type: 'string',
|
|
267
223
|
enum: ['comments', 'transitions', 'attachments', 'related_issues', 'history'],
|
|
268
224
|
},
|
|
269
|
-
description: '
|
|
225
|
+
description: 'Additional fields to include in the response.',
|
|
270
226
|
},
|
|
271
227
|
},
|
|
272
228
|
required: ['operation'],
|
|
273
229
|
},
|
|
274
230
|
},
|
|
275
|
-
// Project Management API
|
|
276
231
|
manage_jira_project: {
|
|
277
232
|
name: 'manage_jira_project',
|
|
278
|
-
description: '
|
|
233
|
+
description: 'List projects or get project details including status counts',
|
|
279
234
|
inputSchema: {
|
|
280
235
|
type: 'object',
|
|
281
236
|
properties: {
|
|
282
237
|
operation: {
|
|
283
238
|
type: 'string',
|
|
284
|
-
enum: ['get', '
|
|
285
|
-
description: 'Operation to perform
|
|
239
|
+
enum: ['get', 'list'],
|
|
240
|
+
description: 'Operation to perform',
|
|
286
241
|
},
|
|
287
|
-
// Parameters for get, update, delete operations
|
|
288
242
|
projectKey: {
|
|
289
243
|
type: 'string',
|
|
290
|
-
description: '
|
|
291
|
-
},
|
|
292
|
-
// Parameters for create operation
|
|
293
|
-
name: {
|
|
294
|
-
type: 'string',
|
|
295
|
-
description: 'Name of the project. Required for create operation, optional for update.',
|
|
296
|
-
},
|
|
297
|
-
key: {
|
|
298
|
-
type: 'string',
|
|
299
|
-
description: 'Project key. Required for create operation.',
|
|
300
|
-
},
|
|
301
|
-
// Common parameters for create and update
|
|
302
|
-
description: {
|
|
303
|
-
type: 'string',
|
|
304
|
-
description: 'Description of the project. Optional for create/update.',
|
|
305
|
-
},
|
|
306
|
-
lead: {
|
|
307
|
-
type: 'string',
|
|
308
|
-
description: 'Username of the project lead. Optional for create/update.',
|
|
244
|
+
description: 'Project key (e.g., PROJ). Required for get.',
|
|
309
245
|
},
|
|
310
|
-
// Parameters for list operation
|
|
311
246
|
startAt: {
|
|
312
247
|
type: 'integer',
|
|
313
|
-
description: '
|
|
248
|
+
description: 'Pagination offset (0-based).',
|
|
314
249
|
default: 0,
|
|
315
250
|
},
|
|
316
251
|
maxResults: {
|
|
317
252
|
type: 'integer',
|
|
318
|
-
description: '
|
|
253
|
+
description: 'Max items to return.',
|
|
319
254
|
default: 50,
|
|
320
255
|
},
|
|
321
|
-
// Common expansion options
|
|
322
256
|
expand: {
|
|
323
257
|
type: 'array',
|
|
324
258
|
items: {
|
|
325
259
|
type: 'string',
|
|
326
260
|
enum: ['boards', 'components', 'versions', 'recent_issues'],
|
|
327
261
|
},
|
|
328
|
-
description: '
|
|
262
|
+
description: 'Additional fields to include in the response.',
|
|
329
263
|
},
|
|
330
264
|
include_status_counts: {
|
|
331
265
|
type: 'boolean',
|
|
332
|
-
description: '
|
|
266
|
+
description: 'Include issue counts by status.',
|
|
333
267
|
default: true,
|
|
334
268
|
},
|
|
335
269
|
},
|
|
336
270
|
required: ['operation'],
|
|
337
271
|
},
|
|
338
272
|
},
|
|
339
|
-
// Board Management API
|
|
340
273
|
manage_jira_board: {
|
|
341
274
|
name: 'manage_jira_board',
|
|
342
|
-
description: '
|
|
275
|
+
description: 'List boards or get board details and configuration',
|
|
343
276
|
inputSchema: {
|
|
344
277
|
type: 'object',
|
|
345
278
|
properties: {
|
|
346
279
|
operation: {
|
|
347
280
|
type: 'string',
|
|
348
|
-
enum: ['get', 'list'
|
|
349
|
-
description: 'Operation to perform
|
|
281
|
+
enum: ['get', 'list'],
|
|
282
|
+
description: 'Operation to perform',
|
|
350
283
|
},
|
|
351
|
-
// Parameters for get, update, delete, get_configuration operations
|
|
352
284
|
boardId: {
|
|
353
285
|
type: 'integer',
|
|
354
|
-
description: '
|
|
286
|
+
description: 'Board ID. Required for get.',
|
|
355
287
|
},
|
|
356
|
-
// Parameters for create operation
|
|
357
|
-
name: {
|
|
358
|
-
type: 'string',
|
|
359
|
-
description: 'Name of the board. Required for create operation, optional for update.',
|
|
360
|
-
},
|
|
361
|
-
type: {
|
|
362
|
-
type: 'string',
|
|
363
|
-
enum: ['scrum', 'kanban'],
|
|
364
|
-
description: 'Type of board. Required for create operation.',
|
|
365
|
-
},
|
|
366
|
-
projectKey: {
|
|
367
|
-
type: 'string',
|
|
368
|
-
description: 'Project key for the board. Required for create operation. Can also use snake_case "project_key".',
|
|
369
|
-
},
|
|
370
|
-
// Parameters for list operation
|
|
371
288
|
startAt: {
|
|
372
289
|
type: 'integer',
|
|
373
|
-
description: '
|
|
290
|
+
description: 'Pagination offset (0-based).',
|
|
374
291
|
default: 0,
|
|
375
292
|
},
|
|
376
293
|
maxResults: {
|
|
377
294
|
type: 'integer',
|
|
378
|
-
description: '
|
|
295
|
+
description: 'Max items to return.',
|
|
379
296
|
default: 50,
|
|
380
297
|
},
|
|
381
|
-
// Common expansion options
|
|
382
298
|
expand: {
|
|
383
299
|
type: 'array',
|
|
384
300
|
items: {
|
|
385
301
|
type: 'string',
|
|
386
302
|
enum: ['sprints', 'issues', 'configuration'],
|
|
387
303
|
},
|
|
388
|
-
description: '
|
|
304
|
+
description: 'Additional fields to include in the response.',
|
|
389
305
|
},
|
|
390
306
|
include_sprints: {
|
|
391
307
|
type: 'boolean',
|
|
392
|
-
description: '
|
|
308
|
+
description: 'Include active sprints (shorthand for expand: ["sprints"]).',
|
|
393
309
|
default: false,
|
|
394
310
|
},
|
|
395
311
|
},
|
|
396
312
|
required: ['operation'],
|
|
397
313
|
},
|
|
398
314
|
},
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
description: 'Search for issues using JQL with enhanced results and optional expansions',
|
|
315
|
+
queue_jira_operations: {
|
|
316
|
+
name: 'queue_jira_operations',
|
|
317
|
+
description: 'Execute multiple Jira operations in a single call. Operations run sequentially with result references ($0.key) and per-operation error strategies (bail/continue).',
|
|
403
318
|
inputSchema: {
|
|
404
319
|
type: 'object',
|
|
405
320
|
properties: {
|
|
406
|
-
|
|
407
|
-
type: 'string',
|
|
408
|
-
description: 'JQL query string. Supports a wide range of search patterns:\n\n' +
|
|
409
|
-
'# Portfolio/Plans Queries\n' +
|
|
410
|
-
'- Find child issues: issue in portfolioChildIssuesOf("PROJ-123")\n' +
|
|
411
|
-
'- Combined portfolio search: issue in portfolioChildIssuesOf("PROJ-123") AND status = "In Progress"\n' +
|
|
412
|
-
'- Multiple portfolios: issue in portfolioChildIssuesOf("PROJ-123") OR issue in portfolioChildIssuesOf("PROJ-456")\n\n' +
|
|
413
|
-
'# Common Search Patterns\n' +
|
|
414
|
-
'- Assigned issues: assignee = currentUser()\n' +
|
|
415
|
-
'- Unassigned issues: assignee IS EMPTY\n' +
|
|
416
|
-
'- Recent changes: status CHANGED AFTER -1w\n' +
|
|
417
|
-
'- Multiple statuses: status IN ("In Progress", "Under Review", "Testing")\n' +
|
|
418
|
-
'- Priority tasks: priority = High AND status = Open\n' +
|
|
419
|
-
'- Component search: component = "User Interface" OR component = "API"\n\n' +
|
|
420
|
-
'# Advanced Functions\n' +
|
|
421
|
-
'- Sort results: ORDER BY created DESC\n' +
|
|
422
|
-
'- Track changes: status WAS "Resolved" AND status = "Open"\n' +
|
|
423
|
-
'- Team filters: assignee IN MEMBERSOF("developers")\n\n' +
|
|
424
|
-
'JQL supports complex combinations using AND, OR, NOT operators and parentheses for grouping. ' +
|
|
425
|
-
'All text values are case-sensitive and must be enclosed in quotes when they contain spaces.',
|
|
426
|
-
},
|
|
427
|
-
startAt: {
|
|
428
|
-
type: 'number',
|
|
429
|
-
description: 'Index of the first issue to return (0-based). Can also use snake_case "start_at".',
|
|
430
|
-
default: 0,
|
|
431
|
-
},
|
|
432
|
-
maxResults: {
|
|
433
|
-
type: 'number',
|
|
434
|
-
description: 'Maximum number of issues to return (default: 25, max: 100). Can also use snake_case "max_results".',
|
|
435
|
-
default: 25,
|
|
436
|
-
maximum: 100,
|
|
437
|
-
},
|
|
438
|
-
expand: {
|
|
321
|
+
operations: {
|
|
439
322
|
type: 'array',
|
|
440
323
|
items: {
|
|
441
|
-
type: '
|
|
442
|
-
|
|
324
|
+
type: 'object',
|
|
325
|
+
properties: {
|
|
326
|
+
tool: {
|
|
327
|
+
type: 'string',
|
|
328
|
+
enum: ['manage_jira_issue', 'manage_jira_filter', 'manage_jira_sprint', 'manage_jira_project', 'manage_jira_board'],
|
|
329
|
+
description: 'Which tool to call.',
|
|
330
|
+
},
|
|
331
|
+
args: {
|
|
332
|
+
type: 'object',
|
|
333
|
+
description: 'Arguments for the tool call. Use $N.field to reference results from earlier operations (e.g., $0.key for the issue key from operation 0).',
|
|
334
|
+
},
|
|
335
|
+
onError: {
|
|
336
|
+
type: 'string',
|
|
337
|
+
enum: ['bail', 'continue'],
|
|
338
|
+
description: 'Error strategy. bail (default): stop queue. continue: log error, proceed to next.',
|
|
339
|
+
default: 'bail',
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
required: ['tool', 'args'],
|
|
443
343
|
},
|
|
444
|
-
description: '
|
|
344
|
+
description: 'Ordered list of operations to execute (max 10).',
|
|
345
|
+
maxItems: 10,
|
|
445
346
|
},
|
|
446
347
|
},
|
|
447
|
-
required: ['
|
|
348
|
+
required: ['operations'],
|
|
448
349
|
},
|
|
449
350
|
},
|
|
450
351
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sliding-window tracker for bulk-destructive operations (ADR-202 §1.4).
|
|
3
|
+
*
|
|
4
|
+
* Detects when an LLM agent is calling destructive operations (delete, move)
|
|
5
|
+
* in rapid succession and deflects to Jira's bulk-operations UI.
|
|
6
|
+
*/
|
|
7
|
+
const WINDOW_MS = 60_000; // 60-second sliding window
|
|
8
|
+
const DEFAULT_LIMIT = 3;
|
|
9
|
+
/** Session-level operation tracker (singleton) */
|
|
10
|
+
class BulkOperationGuard {
|
|
11
|
+
ops = [];
|
|
12
|
+
limit;
|
|
13
|
+
constructor() {
|
|
14
|
+
const envLimit = process.env.JIRA_BULK_DESTRUCTIVE_LIMIT;
|
|
15
|
+
this.limit = envLimit ? parseInt(envLimit, 10) : DEFAULT_LIMIT;
|
|
16
|
+
if (isNaN(this.limit) || this.limit < 1) {
|
|
17
|
+
this.limit = DEFAULT_LIMIT;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Prune entries outside the sliding window */
|
|
21
|
+
prune() {
|
|
22
|
+
const cutoff = Date.now() - WINDOW_MS;
|
|
23
|
+
this.ops = this.ops.filter(op => op.timestamp >= cutoff);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check whether a destructive operation should be allowed.
|
|
27
|
+
* Returns `null` if allowed, or a deflection message if refused.
|
|
28
|
+
*/
|
|
29
|
+
check(operation, issueKey, jiraHost) {
|
|
30
|
+
this.prune();
|
|
31
|
+
const recentCount = this.ops.length;
|
|
32
|
+
if (recentCount < this.limit) {
|
|
33
|
+
return null; // allowed
|
|
34
|
+
}
|
|
35
|
+
// Build deflection
|
|
36
|
+
const recentKeys = this.ops.map(op => op.issueKey);
|
|
37
|
+
recentKeys.push(issueKey);
|
|
38
|
+
const jql = `key in (${recentKeys.join(', ')})`;
|
|
39
|
+
const encodedJql = encodeURIComponent(jql);
|
|
40
|
+
const lines = [
|
|
41
|
+
`Bulk ${operation} is not supported through this tool — ${operation === 'delete' ? 'deleting' : 'moving'} ${recentKeys.length} issues in quick succession is best done with manual review.`,
|
|
42
|
+
'',
|
|
43
|
+
`**Your JQL query:** \`${jql}\``,
|
|
44
|
+
];
|
|
45
|
+
if (jiraHost) {
|
|
46
|
+
const host = jiraHost.replace(/^https?:\/\//, '');
|
|
47
|
+
lines.push(`**Review in Jira:** https://${host}/issues/?jql=${encodedJql}`);
|
|
48
|
+
}
|
|
49
|
+
lines.push('');
|
|
50
|
+
lines.push(`From Jira's issue list, select the issues and use the bulk operations menu.`);
|
|
51
|
+
lines.push('');
|
|
52
|
+
lines.push(`To ${operation} a single issue, wait a moment and try again with one issue at a time.`);
|
|
53
|
+
return lines.join('\n');
|
|
54
|
+
}
|
|
55
|
+
/** Record a successful destructive operation */
|
|
56
|
+
record(operation, issueKey) {
|
|
57
|
+
this.ops.push({ operation, issueKey, timestamp: Date.now() });
|
|
58
|
+
}
|
|
59
|
+
/** How many more destructive ops are allowed in the current window */
|
|
60
|
+
remainingCapacity() {
|
|
61
|
+
this.prune();
|
|
62
|
+
return Math.max(0, this.limit - this.ops.length);
|
|
63
|
+
}
|
|
64
|
+
/** Reset for testing */
|
|
65
|
+
reset() {
|
|
66
|
+
this.ops = [];
|
|
67
|
+
}
|
|
68
|
+
/** Visible for testing */
|
|
69
|
+
getLimit() {
|
|
70
|
+
return this.limit;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Singleton instance — lives for the MCP server process lifetime */
|
|
74
|
+
export const bulkOperationGuard = new BulkOperationGuard();
|