@aaronsb/jira-cloud-mcp 0.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.
- package/README.md +70 -0
- package/build/client/jira-client.js +721 -0
- package/build/handlers/board-handlers.js +326 -0
- package/build/handlers/filter-handlers.js +427 -0
- package/build/handlers/issue-handlers.js +311 -0
- package/build/handlers/project-handlers.js +368 -0
- package/build/handlers/resource-handlers.js +320 -0
- package/build/handlers/search-handlers.js +103 -0
- package/build/handlers/sprint-handlers.js +433 -0
- package/build/handlers/tool-resource-handlers.js +1185 -0
- package/build/health-check.js +67 -0
- package/build/index.js +141 -0
- package/build/schemas/request-schemas.js +187 -0
- package/build/schemas/tool-schemas.js +450 -0
- package/build/types/index.js +1 -0
- package/build/utils/formatters/base-formatter.js +58 -0
- package/build/utils/formatters/board-formatter.js +63 -0
- package/build/utils/formatters/filter-formatter.js +66 -0
- package/build/utils/formatters/index.js +7 -0
- package/build/utils/formatters/issue-formatter.js +84 -0
- package/build/utils/formatters/project-formatter.js +55 -0
- package/build/utils/formatters/search-formatter.js +62 -0
- package/build/utils/formatters/sprint-formatter.js +111 -0
- package/build/utils/text-processing.js +343 -0
- package/package.json +65 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { FilterFormatter, SearchFormatter } from '../utils/formatters/index.js';
|
|
3
|
+
// Helper function to normalize parameter names (support both snake_case and camelCase)
|
|
4
|
+
function normalizeArgs(args) {
|
|
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
|
+
}
|
|
26
|
+
// Validate the consolidated filter management arguments
|
|
27
|
+
function validateManageJiraFilterArgs(args) {
|
|
28
|
+
if (typeof args !== 'object' || args === null) {
|
|
29
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid manage_jira_filter arguments: Expected an object with an operation parameter');
|
|
30
|
+
}
|
|
31
|
+
const normalizedArgs = normalizeArgs(args);
|
|
32
|
+
// Validate operation parameter
|
|
33
|
+
if (typeof normalizedArgs.operation !== 'string' ||
|
|
34
|
+
!['get', 'create', 'update', 'delete', 'list', 'execute_filter', 'execute_jql'].includes(normalizedArgs.operation)) {
|
|
35
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid operation parameter. Valid values are: get, create, update, delete, list, execute_filter, execute_jql');
|
|
36
|
+
}
|
|
37
|
+
// Validate parameters based on operation
|
|
38
|
+
switch (normalizedArgs.operation) {
|
|
39
|
+
case 'get':
|
|
40
|
+
case 'update':
|
|
41
|
+
case 'delete':
|
|
42
|
+
case 'execute_filter':
|
|
43
|
+
if (typeof normalizedArgs.filterId !== 'string' || normalizedArgs.filterId.trim() === '') {
|
|
44
|
+
throw new McpError(ErrorCode.InvalidParams, `Missing or invalid filterId parameter. Please provide a valid filter ID for the ${normalizedArgs.operation} operation.`);
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
case 'create':
|
|
48
|
+
if (typeof normalizedArgs.name !== 'string' || normalizedArgs.name.trim() === '') {
|
|
49
|
+
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid name parameter. Please provide a valid filter name for the create operation.');
|
|
50
|
+
}
|
|
51
|
+
if (typeof normalizedArgs.jql !== 'string' || normalizedArgs.jql.trim() === '') {
|
|
52
|
+
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid jql parameter. Please provide a valid JQL query for the create operation.');
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
case 'execute_jql':
|
|
56
|
+
if (typeof normalizedArgs.jql !== 'string' || normalizedArgs.jql.trim() === '') {
|
|
57
|
+
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid jql parameter. Please provide a valid JQL query for the execute_jql operation.');
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
// Validate pagination parameters for list and execute_jql operations
|
|
62
|
+
if (normalizedArgs.operation === 'list' || normalizedArgs.operation === 'execute_jql') {
|
|
63
|
+
if (normalizedArgs.startAt !== undefined && typeof normalizedArgs.startAt !== 'number') {
|
|
64
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid startAt parameter. Please provide a valid number.');
|
|
65
|
+
}
|
|
66
|
+
if (normalizedArgs.maxResults !== undefined && typeof normalizedArgs.maxResults !== 'number') {
|
|
67
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid maxResults parameter. Please provide a valid number.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Validate expand parameter
|
|
71
|
+
if (normalizedArgs.expand !== undefined) {
|
|
72
|
+
if (!Array.isArray(normalizedArgs.expand)) {
|
|
73
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid expand parameter. Expected an array of strings.');
|
|
74
|
+
}
|
|
75
|
+
// Combined list of valid expansions for both filter and search operations
|
|
76
|
+
const validExpansions = [
|
|
77
|
+
'jql', 'description', 'permissions', 'issue_count', // Filter expansions
|
|
78
|
+
'issue_details', 'transitions', 'comments_preview' // Search expansions
|
|
79
|
+
];
|
|
80
|
+
for (const expansion of normalizedArgs.expand) {
|
|
81
|
+
if (typeof expansion !== 'string' || !validExpansions.includes(expansion)) {
|
|
82
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid expansion: ${expansion}. Valid expansions are: ${validExpansions.join(', ')}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Validate sharePermissions parameter
|
|
87
|
+
if (normalizedArgs.sharePermissions !== undefined) {
|
|
88
|
+
if (!Array.isArray(normalizedArgs.sharePermissions)) {
|
|
89
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid sharePermissions parameter. Expected an array of permission objects.');
|
|
90
|
+
}
|
|
91
|
+
for (const permission of normalizedArgs.sharePermissions) {
|
|
92
|
+
if (typeof permission !== 'object' || permission === null) {
|
|
93
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid permission object in sharePermissions. Expected an object with a type property.');
|
|
94
|
+
}
|
|
95
|
+
if (typeof permission.type !== 'string' || !['group', 'project', 'global'].includes(permission.type)) {
|
|
96
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid permission type. Valid values are: group, project, global.');
|
|
97
|
+
}
|
|
98
|
+
if (permission.type === 'group' && (typeof permission.group !== 'string' || permission.group.trim() === '')) {
|
|
99
|
+
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid group parameter for group permission type.');
|
|
100
|
+
}
|
|
101
|
+
if (permission.type === 'project' && (typeof permission.project !== 'string' || permission.project.trim() === '')) {
|
|
102
|
+
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid project parameter for project permission type.');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
// Handler functions for each operation
|
|
109
|
+
async function handleGetFilter(jiraClient, args) {
|
|
110
|
+
const filterId = args.filterId;
|
|
111
|
+
// Parse expansion options
|
|
112
|
+
const expansionOptions = {};
|
|
113
|
+
if (args.expand) {
|
|
114
|
+
for (const expansion of args.expand) {
|
|
115
|
+
if (['jql', 'description', 'permissions', 'issue_count'].includes(expansion)) {
|
|
116
|
+
expansionOptions[expansion] = true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
// Get the filter by first getting its issues
|
|
122
|
+
// This is a workaround since we don't have direct access to the filter API
|
|
123
|
+
// The getFilterIssues method internally calls the filter API
|
|
124
|
+
await jiraClient.getFilterIssues(filterId);
|
|
125
|
+
// Now get the filter details from the list of all filters
|
|
126
|
+
const allFilters = await jiraClient.listMyFilters(expansionOptions.jql || expansionOptions.description || expansionOptions.permissions);
|
|
127
|
+
const filter = allFilters.find(f => f.id === filterId);
|
|
128
|
+
if (!filter) {
|
|
129
|
+
throw new McpError(ErrorCode.InvalidParams, `Filter with ID ${filterId} not found or not accessible`);
|
|
130
|
+
}
|
|
131
|
+
// Convert to FilterData format
|
|
132
|
+
const filterData = {
|
|
133
|
+
id: filter.id,
|
|
134
|
+
name: filter.name || 'Unnamed Filter',
|
|
135
|
+
owner: filter.owner || 'Unknown',
|
|
136
|
+
favourite: filter.favourite || false,
|
|
137
|
+
viewUrl: filter.viewUrl || '',
|
|
138
|
+
description: filter.description || '',
|
|
139
|
+
jql: filter.jql || '',
|
|
140
|
+
sharePermissions: filter.sharePermissions?.map(perm => ({
|
|
141
|
+
type: perm.type,
|
|
142
|
+
group: perm.group,
|
|
143
|
+
project: perm.project
|
|
144
|
+
})) || []
|
|
145
|
+
};
|
|
146
|
+
// Handle expansions
|
|
147
|
+
if (expansionOptions.issue_count) {
|
|
148
|
+
try {
|
|
149
|
+
// Get issue count for this filter
|
|
150
|
+
const issues = await jiraClient.getFilterIssues(filterId);
|
|
151
|
+
// Add issue count to the response
|
|
152
|
+
filterData.issueCount = issues.length;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.error(`Error getting issue count for filter ${filterId}:`, error);
|
|
156
|
+
// Continue even if issue count fails
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Format the response
|
|
160
|
+
const formattedResponse = FilterFormatter.formatFilter(filterData, expansionOptions);
|
|
161
|
+
return {
|
|
162
|
+
content: [
|
|
163
|
+
{
|
|
164
|
+
type: 'text',
|
|
165
|
+
text: JSON.stringify(formattedResponse, null, 2),
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
if (error instanceof McpError) {
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
console.error(`Error getting filter ${filterId}:`, error);
|
|
175
|
+
throw new McpError(ErrorCode.InternalError, `Failed to get filter: ${error instanceof Error ? error.message : String(error)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function handleListFilters(jiraClient, args) {
|
|
179
|
+
// Set default pagination values
|
|
180
|
+
const startAt = args.startAt !== undefined ? args.startAt : 0;
|
|
181
|
+
const maxResults = args.maxResults !== undefined ? args.maxResults : 50;
|
|
182
|
+
// Parse expansion options
|
|
183
|
+
const expansionOptions = {};
|
|
184
|
+
if (args.expand) {
|
|
185
|
+
for (const expansion of args.expand) {
|
|
186
|
+
if (['jql', 'description', 'permissions', 'issue_count'].includes(expansion)) {
|
|
187
|
+
expansionOptions[expansion] = true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Get all filters
|
|
192
|
+
const filters = await jiraClient.listMyFilters(expansionOptions.jql || expansionOptions.description || expansionOptions.permissions);
|
|
193
|
+
// Apply pagination
|
|
194
|
+
const paginatedFilters = filters.slice(startAt, startAt + maxResults);
|
|
195
|
+
// Convert to FilterData format
|
|
196
|
+
const filterDataList = paginatedFilters.map(filter => ({
|
|
197
|
+
...filter
|
|
198
|
+
}));
|
|
199
|
+
// If issue count is requested, get it for each filter
|
|
200
|
+
if (expansionOptions.issue_count) {
|
|
201
|
+
// This would be more efficient with a batch API call, but for now we'll do it sequentially
|
|
202
|
+
for (const filter of filterDataList) {
|
|
203
|
+
try {
|
|
204
|
+
// Get issues for this filter
|
|
205
|
+
const issues = await jiraClient.getFilterIssues(filter.id);
|
|
206
|
+
// Add issue count to the filter data
|
|
207
|
+
filter.issueCount = issues.length;
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
console.error(`Error getting issues for filter ${filter.id}:`, error);
|
|
211
|
+
// Continue with other filters even if one fails
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// Format the response
|
|
216
|
+
const formattedFilters = filterDataList.map(filter => FilterFormatter.formatFilter(filter, expansionOptions));
|
|
217
|
+
// Create a response with pagination metadata
|
|
218
|
+
const response = {
|
|
219
|
+
data: formattedFilters,
|
|
220
|
+
_metadata: {
|
|
221
|
+
pagination: {
|
|
222
|
+
startAt,
|
|
223
|
+
maxResults,
|
|
224
|
+
total: filters.length,
|
|
225
|
+
hasMore: startAt + maxResults < filters.length,
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
return {
|
|
230
|
+
content: [
|
|
231
|
+
{
|
|
232
|
+
type: 'text',
|
|
233
|
+
text: JSON.stringify(response, null, 2),
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async function handleCreateFilter(_jiraClient, _args) {
|
|
239
|
+
// Note: This is a placeholder. The current JiraClient doesn't have a createFilter method.
|
|
240
|
+
// You would need to implement this in the JiraClient class.
|
|
241
|
+
throw new McpError(ErrorCode.InternalError, 'Create filter operation is not yet implemented');
|
|
242
|
+
// When implemented, it would look something like this:
|
|
243
|
+
/*
|
|
244
|
+
const result = await _jiraClient.createFilter({
|
|
245
|
+
name: _args.name!,
|
|
246
|
+
jql: _args.jql!,
|
|
247
|
+
description: _args.description,
|
|
248
|
+
favourite: _args.favourite,
|
|
249
|
+
sharePermissions: _args.sharePermissions
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Get the created filter to return
|
|
253
|
+
const createdFilter = await _jiraClient.getFilter(result.id);
|
|
254
|
+
const formattedResponse = FilterFormatter.formatFilter(createdFilter);
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
content: [
|
|
258
|
+
{
|
|
259
|
+
type: 'text',
|
|
260
|
+
text: JSON.stringify(formattedResponse, null, 2),
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
};
|
|
264
|
+
*/
|
|
265
|
+
}
|
|
266
|
+
async function handleUpdateFilter(_jiraClient, _args) {
|
|
267
|
+
// Note: This is a placeholder. The current JiraClient doesn't have an updateFilter method.
|
|
268
|
+
// You would need to implement this in the JiraClient class.
|
|
269
|
+
throw new McpError(ErrorCode.InternalError, 'Update filter operation is not yet implemented');
|
|
270
|
+
// When implemented, it would look something like this:
|
|
271
|
+
/*
|
|
272
|
+
await _jiraClient.updateFilter(
|
|
273
|
+
_args.filterId!,
|
|
274
|
+
{
|
|
275
|
+
name: _args.name,
|
|
276
|
+
jql: _args.jql,
|
|
277
|
+
description: _args.description,
|
|
278
|
+
favourite: _args.favourite,
|
|
279
|
+
sharePermissions: _args.sharePermissions
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// Get the updated filter to return
|
|
284
|
+
const updatedFilter = await _jiraClient.getFilter(_args.filterId!);
|
|
285
|
+
const formattedResponse = FilterFormatter.formatFilter(updatedFilter);
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
content: [
|
|
289
|
+
{
|
|
290
|
+
type: 'text',
|
|
291
|
+
text: JSON.stringify(formattedResponse, null, 2),
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
};
|
|
295
|
+
*/
|
|
296
|
+
}
|
|
297
|
+
async function handleDeleteFilter(_jiraClient, _args) {
|
|
298
|
+
// Note: This is a placeholder. The current JiraClient doesn't have a deleteFilter method.
|
|
299
|
+
// You would need to implement this in the JiraClient class.
|
|
300
|
+
throw new McpError(ErrorCode.InternalError, 'Delete filter operation is not yet implemented');
|
|
301
|
+
// When implemented, it would look something like this:
|
|
302
|
+
/*
|
|
303
|
+
await _jiraClient.deleteFilter(_args.filterId!);
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
content: [
|
|
307
|
+
{
|
|
308
|
+
type: 'text',
|
|
309
|
+
text: JSON.stringify({
|
|
310
|
+
success: true,
|
|
311
|
+
message: `Filter ${_args.filterId} has been deleted successfully.`,
|
|
312
|
+
}, null, 2),
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
};
|
|
316
|
+
*/
|
|
317
|
+
}
|
|
318
|
+
async function handleExecuteFilter(jiraClient, _args) {
|
|
319
|
+
const filterId = _args.filterId;
|
|
320
|
+
// Get issues for the filter
|
|
321
|
+
const issues = await jiraClient.getFilterIssues(filterId);
|
|
322
|
+
return {
|
|
323
|
+
content: [
|
|
324
|
+
{
|
|
325
|
+
type: 'text',
|
|
326
|
+
text: JSON.stringify({
|
|
327
|
+
data: issues,
|
|
328
|
+
_metadata: {
|
|
329
|
+
filter_id: filterId,
|
|
330
|
+
issue_count: issues.length
|
|
331
|
+
}
|
|
332
|
+
}, null, 2),
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
async function handleExecuteJql(jiraClient, args) {
|
|
338
|
+
if (typeof args.jql !== 'string' || args.jql.trim() === '') {
|
|
339
|
+
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid jql parameter. Please provide a valid JQL query for the execute_jql operation.');
|
|
340
|
+
}
|
|
341
|
+
// Set default pagination values
|
|
342
|
+
const startAt = args.startAt !== undefined ? args.startAt : 0;
|
|
343
|
+
const maxResults = args.maxResults !== undefined ? args.maxResults : 25;
|
|
344
|
+
try {
|
|
345
|
+
console.error(`Executing JQL search with args:`, JSON.stringify(args, null, 2));
|
|
346
|
+
// Parse search expansion options
|
|
347
|
+
const searchExpansionOptions = {};
|
|
348
|
+
if (args.expand) {
|
|
349
|
+
for (const expansion of args.expand) {
|
|
350
|
+
if (['issue_details', 'transitions', 'comments_preview'].includes(expansion)) {
|
|
351
|
+
searchExpansionOptions[expansion] = true;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Execute the search
|
|
356
|
+
const searchResult = await jiraClient.searchIssues(args.jql, startAt, maxResults);
|
|
357
|
+
// Format the response using the SearchFormatter for enhanced results
|
|
358
|
+
const formattedResponse = SearchFormatter.formatSearchResult(searchResult, searchExpansionOptions);
|
|
359
|
+
return {
|
|
360
|
+
content: [
|
|
361
|
+
{
|
|
362
|
+
type: 'text',
|
|
363
|
+
text: JSON.stringify(formattedResponse, null, 2),
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
console.error('Error in execute_jql:', error);
|
|
370
|
+
if (error instanceof Error) {
|
|
371
|
+
throw new McpError(ErrorCode.InvalidRequest, `Jira API error: ${error.message}`);
|
|
372
|
+
}
|
|
373
|
+
throw new McpError(ErrorCode.InvalidRequest, 'Failed to execute Jira search');
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// Main handler function
|
|
377
|
+
export async function setupFilterHandlers(server, jiraClient, request) {
|
|
378
|
+
console.error('Handling filter request...');
|
|
379
|
+
const { name } = request.params;
|
|
380
|
+
const args = request.params.arguments || {};
|
|
381
|
+
// Handle the consolidated filter management tool
|
|
382
|
+
if (name === 'manage_jira_filter') {
|
|
383
|
+
// Normalize arguments to support both snake_case and camelCase
|
|
384
|
+
const normalizedArgs = normalizeArgs(args);
|
|
385
|
+
// Validate arguments
|
|
386
|
+
if (!validateManageJiraFilterArgs(normalizedArgs)) {
|
|
387
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid manage_jira_filter arguments');
|
|
388
|
+
}
|
|
389
|
+
// Process the operation
|
|
390
|
+
switch (normalizedArgs.operation) {
|
|
391
|
+
case 'get': {
|
|
392
|
+
console.error('Processing get filter operation');
|
|
393
|
+
return await handleGetFilter(jiraClient, normalizedArgs);
|
|
394
|
+
}
|
|
395
|
+
case 'list': {
|
|
396
|
+
console.error('Processing list filters operation');
|
|
397
|
+
return await handleListFilters(jiraClient, normalizedArgs);
|
|
398
|
+
}
|
|
399
|
+
case 'create': {
|
|
400
|
+
console.error('Processing create filter operation');
|
|
401
|
+
return await handleCreateFilter(jiraClient, normalizedArgs);
|
|
402
|
+
}
|
|
403
|
+
case 'update': {
|
|
404
|
+
console.error('Processing update filter operation');
|
|
405
|
+
return await handleUpdateFilter(jiraClient, normalizedArgs);
|
|
406
|
+
}
|
|
407
|
+
case 'delete': {
|
|
408
|
+
console.error('Processing delete filter operation');
|
|
409
|
+
return await handleDeleteFilter(jiraClient, normalizedArgs);
|
|
410
|
+
}
|
|
411
|
+
case 'execute_filter': {
|
|
412
|
+
console.error('Processing execute filter operation');
|
|
413
|
+
return await handleExecuteFilter(jiraClient, normalizedArgs);
|
|
414
|
+
}
|
|
415
|
+
case 'execute_jql': {
|
|
416
|
+
console.error('Processing execute JQL operation');
|
|
417
|
+
return await handleExecuteJql(jiraClient, normalizedArgs);
|
|
418
|
+
}
|
|
419
|
+
default: {
|
|
420
|
+
console.error(`Unknown operation: ${normalizedArgs.operation}`);
|
|
421
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown operation: ${normalizedArgs.operation}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
console.error(`Unknown tool requested: ${name}`);
|
|
426
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
427
|
+
}
|