@aaronsb/jira-cloud-mcp 0.2.7 → 0.3.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.
- 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 +5 -4
|
@@ -1,31 +1,7 @@
|
|
|
1
1
|
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
import { MarkdownRenderer } from '../mcp/markdown-renderer.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const normalized = {};
|
|
6
|
-
for (const [key, value] of Object.entries(args)) {
|
|
7
|
-
// Convert snake_case to camelCase
|
|
8
|
-
if (key === 'board_id') {
|
|
9
|
-
normalized['boardId'] = value;
|
|
10
|
-
}
|
|
11
|
-
else if (key === 'include_sprints') {
|
|
12
|
-
normalized['includeSprints'] = value;
|
|
13
|
-
}
|
|
14
|
-
else if (key === 'project_key') {
|
|
15
|
-
normalized['projectKey'] = value;
|
|
16
|
-
}
|
|
17
|
-
else if (key === 'start_at') {
|
|
18
|
-
normalized['startAt'] = value;
|
|
19
|
-
}
|
|
20
|
-
else if (key === 'max_results') {
|
|
21
|
-
normalized['maxResults'] = value;
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
normalized[key] = value;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return normalized;
|
|
28
|
-
}
|
|
3
|
+
import { boardNextSteps } from '../utils/next-steps.js';
|
|
4
|
+
import { normalizeArgs } from '../utils/normalize-args.js';
|
|
29
5
|
// Validate the consolidated board management arguments
|
|
30
6
|
function validateManageJiraBoardArgs(args) {
|
|
31
7
|
if (typeof args !== 'object' || args === null) {
|
|
@@ -34,28 +10,14 @@ function validateManageJiraBoardArgs(args) {
|
|
|
34
10
|
const normalizedArgs = normalizeArgs(args);
|
|
35
11
|
// Validate operation parameter
|
|
36
12
|
if (typeof normalizedArgs.operation !== 'string' ||
|
|
37
|
-
!['get', 'list'
|
|
38
|
-
throw new McpError(ErrorCode.InvalidParams, 'Invalid operation parameter. Valid values are: get, list
|
|
13
|
+
!['get', 'list'].includes(normalizedArgs.operation)) {
|
|
14
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid operation parameter. Valid values are: get, list');
|
|
39
15
|
}
|
|
40
16
|
// Validate parameters based on operation
|
|
41
17
|
switch (normalizedArgs.operation) {
|
|
42
18
|
case 'get':
|
|
43
|
-
case 'update':
|
|
44
|
-
case 'delete':
|
|
45
|
-
case 'get_configuration':
|
|
46
19
|
if (typeof normalizedArgs.boardId !== 'number') {
|
|
47
|
-
throw new McpError(ErrorCode.InvalidParams,
|
|
48
|
-
}
|
|
49
|
-
break;
|
|
50
|
-
case 'create':
|
|
51
|
-
if (typeof normalizedArgs.name !== 'string' || normalizedArgs.name.trim() === '') {
|
|
52
|
-
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid name parameter. Please provide a valid board name for the create operation.');
|
|
53
|
-
}
|
|
54
|
-
if (typeof normalizedArgs.type !== 'string' || !['scrum', 'kanban'].includes(normalizedArgs.type)) {
|
|
55
|
-
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid type parameter. Please provide a valid board type (scrum or kanban) for the create operation.');
|
|
56
|
-
}
|
|
57
|
-
if (typeof normalizedArgs.projectKey !== 'string' || normalizedArgs.projectKey.trim() === '') {
|
|
58
|
-
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid projectKey parameter. Please provide a valid project key for the create operation.');
|
|
20
|
+
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid boardId parameter. Please provide a valid board ID as a number for the get operation.');
|
|
59
21
|
}
|
|
60
22
|
break;
|
|
61
23
|
}
|
|
@@ -97,7 +59,7 @@ async function handleGetBoard(jiraClient, args) {
|
|
|
97
59
|
}
|
|
98
60
|
}
|
|
99
61
|
// If include_sprints is true, add sprints to expansions
|
|
100
|
-
if (args.
|
|
62
|
+
if (args.includeSprints === true) {
|
|
101
63
|
expansionOptions.sprints = true;
|
|
102
64
|
}
|
|
103
65
|
// Get all boards and find the requested one
|
|
@@ -143,7 +105,7 @@ async function handleGetBoard(jiraClient, args) {
|
|
|
143
105
|
content: [
|
|
144
106
|
{
|
|
145
107
|
type: 'text',
|
|
146
|
-
text: markdown,
|
|
108
|
+
text: markdown + boardNextSteps('get', boardId),
|
|
147
109
|
},
|
|
148
110
|
],
|
|
149
111
|
};
|
|
@@ -152,7 +114,7 @@ async function handleListBoards(jiraClient, args) {
|
|
|
152
114
|
// Set default pagination values
|
|
153
115
|
const startAt = args.startAt !== undefined ? args.startAt : 0;
|
|
154
116
|
const maxResults = args.maxResults !== undefined ? args.maxResults : 50;
|
|
155
|
-
const includeSprints = args.
|
|
117
|
+
const includeSprints = args.includeSprints === true;
|
|
156
118
|
// Get all boards
|
|
157
119
|
const boards = await jiraClient.listBoards();
|
|
158
120
|
// Apply pagination
|
|
@@ -204,6 +166,7 @@ async function handleListBoards(jiraClient, args) {
|
|
|
204
166
|
else {
|
|
205
167
|
markdown += `Showing all ${boardDataList.length} board${boardDataList.length !== 1 ? 's' : ''}`;
|
|
206
168
|
}
|
|
169
|
+
markdown += boardNextSteps('list');
|
|
207
170
|
return {
|
|
208
171
|
content: [
|
|
209
172
|
{
|
|
@@ -213,98 +176,8 @@ async function handleListBoards(jiraClient, args) {
|
|
|
213
176
|
],
|
|
214
177
|
};
|
|
215
178
|
}
|
|
216
|
-
async function handleCreateBoard(_jiraClient, _args) {
|
|
217
|
-
// Note: This is a placeholder. The current JiraClient doesn't have a createBoard method.
|
|
218
|
-
// You would need to implement this in the JiraClient class.
|
|
219
|
-
throw new McpError(ErrorCode.InternalError, 'Create board operation is not yet implemented');
|
|
220
|
-
// When implemented, it would look something like this:
|
|
221
|
-
/*
|
|
222
|
-
const result = await _jiraClient.createBoard({
|
|
223
|
-
name: _args.name!,
|
|
224
|
-
type: _args.type!,
|
|
225
|
-
projectKey: _args.projectKey!
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Get the created board to return
|
|
229
|
-
const createdBoard = await _jiraClient.getBoard(result.id);
|
|
230
|
-
const formattedResponse = BoardFormatter.formatBoard(createdBoard);
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
content: [
|
|
234
|
-
{
|
|
235
|
-
type: 'text',
|
|
236
|
-
text: JSON.stringify(formattedResponse, null, 2),
|
|
237
|
-
},
|
|
238
|
-
],
|
|
239
|
-
};
|
|
240
|
-
*/
|
|
241
|
-
}
|
|
242
|
-
async function handleUpdateBoard(_jiraClient, _args) {
|
|
243
|
-
// Note: This is a placeholder. The current JiraClient doesn't have an updateBoard method.
|
|
244
|
-
// You would need to implement this in the JiraClient class.
|
|
245
|
-
throw new McpError(ErrorCode.InternalError, 'Update board operation is not yet implemented');
|
|
246
|
-
// When implemented, it would look something like this:
|
|
247
|
-
/*
|
|
248
|
-
await _jiraClient.updateBoard(
|
|
249
|
-
_args.boardId!,
|
|
250
|
-
_args.name
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
// Get the updated board to return
|
|
254
|
-
const updatedBoard = await _jiraClient.getBoard(_args.boardId!);
|
|
255
|
-
const formattedResponse = BoardFormatter.formatBoard(updatedBoard);
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
content: [
|
|
259
|
-
{
|
|
260
|
-
type: 'text',
|
|
261
|
-
text: JSON.stringify(formattedResponse, null, 2),
|
|
262
|
-
},
|
|
263
|
-
],
|
|
264
|
-
};
|
|
265
|
-
*/
|
|
266
|
-
}
|
|
267
|
-
async function handleDeleteBoard(_jiraClient, _args) {
|
|
268
|
-
// Note: This is a placeholder. The current JiraClient doesn't have a deleteBoard method.
|
|
269
|
-
// You would need to implement this in the JiraClient class.
|
|
270
|
-
throw new McpError(ErrorCode.InternalError, 'Delete board operation is not yet implemented');
|
|
271
|
-
// When implemented, it would look something like this:
|
|
272
|
-
/*
|
|
273
|
-
await _jiraClient.deleteBoard(_args.boardId!);
|
|
274
|
-
|
|
275
|
-
return {
|
|
276
|
-
content: [
|
|
277
|
-
{
|
|
278
|
-
type: 'text',
|
|
279
|
-
text: JSON.stringify({
|
|
280
|
-
success: true,
|
|
281
|
-
message: `Board ${_args.boardId} has been deleted successfully.`,
|
|
282
|
-
}, null, 2),
|
|
283
|
-
},
|
|
284
|
-
],
|
|
285
|
-
};
|
|
286
|
-
*/
|
|
287
|
-
}
|
|
288
|
-
async function handleGetBoardConfiguration(_jiraClient, _args) {
|
|
289
|
-
// Note: This is a placeholder. The current JiraClient doesn't have a getBoardConfiguration method.
|
|
290
|
-
// You would need to implement this in the JiraClient class.
|
|
291
|
-
throw new McpError(ErrorCode.InternalError, 'Get board configuration operation is not yet implemented');
|
|
292
|
-
// When implemented, it would look something like this:
|
|
293
|
-
/*
|
|
294
|
-
const configuration = await _jiraClient.getBoardConfiguration(_args.boardId!);
|
|
295
|
-
|
|
296
|
-
return {
|
|
297
|
-
content: [
|
|
298
|
-
{
|
|
299
|
-
type: 'text',
|
|
300
|
-
text: JSON.stringify(configuration, null, 2),
|
|
301
|
-
},
|
|
302
|
-
],
|
|
303
|
-
};
|
|
304
|
-
*/
|
|
305
|
-
}
|
|
306
179
|
// Main handler function
|
|
307
|
-
export async function
|
|
180
|
+
export async function handleBoardRequest(jiraClient, request) {
|
|
308
181
|
console.error('Handling board request...');
|
|
309
182
|
const { name } = request.params;
|
|
310
183
|
const args = request.params.arguments || {};
|
|
@@ -326,22 +199,6 @@ export async function setupBoardHandlers(server, jiraClient, request) {
|
|
|
326
199
|
console.error('Processing list boards operation');
|
|
327
200
|
return await handleListBoards(jiraClient, normalizedArgs);
|
|
328
201
|
}
|
|
329
|
-
case 'create': {
|
|
330
|
-
console.error('Processing create board operation');
|
|
331
|
-
return await handleCreateBoard(jiraClient, normalizedArgs);
|
|
332
|
-
}
|
|
333
|
-
case 'update': {
|
|
334
|
-
console.error('Processing update board operation');
|
|
335
|
-
return await handleUpdateBoard(jiraClient, normalizedArgs);
|
|
336
|
-
}
|
|
337
|
-
case 'delete': {
|
|
338
|
-
console.error('Processing delete board operation');
|
|
339
|
-
return await handleDeleteBoard(jiraClient, normalizedArgs);
|
|
340
|
-
}
|
|
341
|
-
case 'get_configuration': {
|
|
342
|
-
console.error('Processing get board configuration operation');
|
|
343
|
-
return await handleGetBoardConfiguration(jiraClient, normalizedArgs);
|
|
344
|
-
}
|
|
345
202
|
default: {
|
|
346
203
|
console.error(`Unknown operation: ${normalizedArgs.operation}`);
|
|
347
204
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown operation: ${normalizedArgs.operation}`);
|
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
import { MarkdownRenderer } from '../mcp/markdown-renderer.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const normalized = {};
|
|
6
|
-
for (const [key, value] of Object.entries(args)) {
|
|
7
|
-
// Convert snake_case to camelCase
|
|
8
|
-
if (key === 'filter_id') {
|
|
9
|
-
normalized['filterId'] = value;
|
|
10
|
-
}
|
|
11
|
-
else if (key === 'share_permissions') {
|
|
12
|
-
normalized['sharePermissions'] = value;
|
|
13
|
-
}
|
|
14
|
-
else if (key === 'start_at') {
|
|
15
|
-
normalized['startAt'] = value;
|
|
16
|
-
}
|
|
17
|
-
else if (key === 'max_results') {
|
|
18
|
-
normalized['maxResults'] = value;
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
normalized[key] = value;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return normalized;
|
|
25
|
-
}
|
|
3
|
+
import { filterNextSteps } from '../utils/next-steps.js';
|
|
4
|
+
import { normalizeArgs } from '../utils/normalize-args.js';
|
|
26
5
|
// Validate the consolidated filter management arguments
|
|
27
6
|
function validateManageJiraFilterArgs(args) {
|
|
28
7
|
if (typeof args !== 'object' || args === null) {
|
|
@@ -157,7 +136,7 @@ async function handleGetFilter(jiraClient, args) {
|
|
|
157
136
|
content: [
|
|
158
137
|
{
|
|
159
138
|
type: 'text',
|
|
160
|
-
text: markdown,
|
|
139
|
+
text: markdown + filterNextSteps('get', filterId),
|
|
161
140
|
},
|
|
162
141
|
],
|
|
163
142
|
};
|
|
@@ -218,6 +197,7 @@ async function handleListFilters(jiraClient, args) {
|
|
|
218
197
|
else {
|
|
219
198
|
markdown += `Showing all ${filterDataList.length} filter${filterDataList.length !== 1 ? 's' : ''}`;
|
|
220
199
|
}
|
|
200
|
+
markdown += filterNextSteps('list');
|
|
221
201
|
return {
|
|
222
202
|
content: [
|
|
223
203
|
{
|
|
@@ -228,84 +208,13 @@ async function handleListFilters(jiraClient, args) {
|
|
|
228
208
|
};
|
|
229
209
|
}
|
|
230
210
|
async function handleCreateFilter(_jiraClient, _args) {
|
|
231
|
-
// Note: This is a placeholder. The current JiraClient doesn't have a createFilter method.
|
|
232
|
-
// You would need to implement this in the JiraClient class.
|
|
233
211
|
throw new McpError(ErrorCode.InternalError, 'Create filter operation is not yet implemented');
|
|
234
|
-
// When implemented, it would look something like this:
|
|
235
|
-
/*
|
|
236
|
-
const result = await _jiraClient.createFilter({
|
|
237
|
-
name: _args.name!,
|
|
238
|
-
jql: _args.jql!,
|
|
239
|
-
description: _args.description,
|
|
240
|
-
favourite: _args.favourite,
|
|
241
|
-
sharePermissions: _args.sharePermissions
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// Get the created filter to return
|
|
245
|
-
const createdFilter = await _jiraClient.getFilter(result.id);
|
|
246
|
-
const formattedResponse = FilterFormatter.formatFilter(createdFilter);
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
content: [
|
|
250
|
-
{
|
|
251
|
-
type: 'text',
|
|
252
|
-
text: JSON.stringify(formattedResponse, null, 2),
|
|
253
|
-
},
|
|
254
|
-
],
|
|
255
|
-
};
|
|
256
|
-
*/
|
|
257
212
|
}
|
|
258
213
|
async function handleUpdateFilter(_jiraClient, _args) {
|
|
259
|
-
// Note: This is a placeholder. The current JiraClient doesn't have an updateFilter method.
|
|
260
|
-
// You would need to implement this in the JiraClient class.
|
|
261
214
|
throw new McpError(ErrorCode.InternalError, 'Update filter operation is not yet implemented');
|
|
262
|
-
// When implemented, it would look something like this:
|
|
263
|
-
/*
|
|
264
|
-
await _jiraClient.updateFilter(
|
|
265
|
-
_args.filterId!,
|
|
266
|
-
{
|
|
267
|
-
name: _args.name,
|
|
268
|
-
jql: _args.jql,
|
|
269
|
-
description: _args.description,
|
|
270
|
-
favourite: _args.favourite,
|
|
271
|
-
sharePermissions: _args.sharePermissions
|
|
272
|
-
}
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
// Get the updated filter to return
|
|
276
|
-
const updatedFilter = await _jiraClient.getFilter(_args.filterId!);
|
|
277
|
-
const formattedResponse = FilterFormatter.formatFilter(updatedFilter);
|
|
278
|
-
|
|
279
|
-
return {
|
|
280
|
-
content: [
|
|
281
|
-
{
|
|
282
|
-
type: 'text',
|
|
283
|
-
text: JSON.stringify(formattedResponse, null, 2),
|
|
284
|
-
},
|
|
285
|
-
],
|
|
286
|
-
};
|
|
287
|
-
*/
|
|
288
215
|
}
|
|
289
216
|
async function handleDeleteFilter(_jiraClient, _args) {
|
|
290
|
-
// Note: This is a placeholder. The current JiraClient doesn't have a deleteFilter method.
|
|
291
|
-
// You would need to implement this in the JiraClient class.
|
|
292
217
|
throw new McpError(ErrorCode.InternalError, 'Delete filter operation is not yet implemented');
|
|
293
|
-
// When implemented, it would look something like this:
|
|
294
|
-
/*
|
|
295
|
-
await _jiraClient.deleteFilter(_args.filterId!);
|
|
296
|
-
|
|
297
|
-
return {
|
|
298
|
-
content: [
|
|
299
|
-
{
|
|
300
|
-
type: 'text',
|
|
301
|
-
text: JSON.stringify({
|
|
302
|
-
success: true,
|
|
303
|
-
message: `Filter ${_args.filterId} has been deleted successfully.`,
|
|
304
|
-
}, null, 2),
|
|
305
|
-
},
|
|
306
|
-
],
|
|
307
|
-
};
|
|
308
|
-
*/
|
|
309
218
|
}
|
|
310
219
|
async function handleExecuteFilter(jiraClient, _args) {
|
|
311
220
|
const filterId = _args.filterId;
|
|
@@ -322,7 +231,7 @@ async function handleExecuteFilter(jiraClient, _args) {
|
|
|
322
231
|
content: [
|
|
323
232
|
{
|
|
324
233
|
type: 'text',
|
|
325
|
-
text: markdown,
|
|
234
|
+
text: markdown + filterNextSteps('execute_filter', filterId),
|
|
326
235
|
},
|
|
327
236
|
],
|
|
328
237
|
};
|
|
@@ -353,7 +262,7 @@ async function handleExecuteJql(jiraClient, args) {
|
|
|
353
262
|
content: [
|
|
354
263
|
{
|
|
355
264
|
type: 'text',
|
|
356
|
-
text: markdown,
|
|
265
|
+
text: markdown + filterNextSteps('execute_jql', undefined, args.jql),
|
|
357
266
|
},
|
|
358
267
|
],
|
|
359
268
|
};
|
|
@@ -367,7 +276,7 @@ async function handleExecuteJql(jiraClient, args) {
|
|
|
367
276
|
}
|
|
368
277
|
}
|
|
369
278
|
// Main handler function
|
|
370
|
-
export async function
|
|
279
|
+
export async function handleFilterRequest(jiraClient, request) {
|
|
371
280
|
console.error('Handling filter request...');
|
|
372
281
|
const { name } = request.params;
|
|
373
282
|
const args = request.params.arguments || {};
|