@aaronsb/jira-cloud-mcp 0.5.8 → 0.5.9

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.
@@ -1003,6 +1003,52 @@ export class JiraClient {
1003
1003
  const response = await this.client.issues.createIssue({ fields });
1004
1004
  return { key: response.key };
1005
1005
  }
1006
+ async createFilter(name, jql, description, favourite) {
1007
+ const result = await this.client.filters.createFilter({
1008
+ name,
1009
+ jql,
1010
+ description: description || '',
1011
+ favourite: favourite ?? false,
1012
+ });
1013
+ if (!result.id || !result.name) {
1014
+ throw new Error('Invalid filter response from Jira');
1015
+ }
1016
+ return {
1017
+ id: result.id,
1018
+ name: result.name,
1019
+ owner: result.owner?.displayName || 'Unknown',
1020
+ favourite: result.favourite || false,
1021
+ viewUrl: result.viewUrl || '',
1022
+ description: result.description || '',
1023
+ jql: result.jql || '',
1024
+ };
1025
+ }
1026
+ async updateFilter(filterId, updates) {
1027
+ // Fetch existing filter to merge with updates (API requires name)
1028
+ const existing = await this.client.filters.getFilter({ id: parseInt(filterId, 10) });
1029
+ const result = await this.client.filters.updateFilter({
1030
+ id: parseInt(filterId, 10),
1031
+ name: updates.name || existing.name || '',
1032
+ jql: updates.jql ?? existing.jql,
1033
+ description: updates.description ?? existing.description,
1034
+ favourite: updates.favourite ?? existing.favourite,
1035
+ });
1036
+ if (!result.id || !result.name) {
1037
+ throw new Error('Invalid filter response from Jira');
1038
+ }
1039
+ return {
1040
+ id: result.id,
1041
+ name: result.name,
1042
+ owner: result.owner?.displayName || 'Unknown',
1043
+ favourite: result.favourite || false,
1044
+ viewUrl: result.viewUrl || '',
1045
+ description: result.description || '',
1046
+ jql: result.jql || '',
1047
+ };
1048
+ }
1049
+ async deleteFilter(filterId) {
1050
+ await this.client.filters.deleteFilter(filterId);
1051
+ }
1006
1052
  async listMyFilters(expand = false) {
1007
1053
  const filters = await this.client.filters.getMyFilters();
1008
1054
  return Promise.all(filters.map(async (filter) => {
@@ -207,14 +207,62 @@ async function handleListFilters(jiraClient, args) {
207
207
  ],
208
208
  };
209
209
  }
210
- async function handleCreateFilter(_jiraClient, _args) {
211
- throw new McpError(ErrorCode.InternalError, 'Create filter operation is not yet implemented');
210
+ async function handleCreateFilter(jiraClient, args) {
211
+ if (!args.name) {
212
+ throw new McpError(ErrorCode.InvalidParams, 'name is required for create operation');
213
+ }
214
+ if (!args.jql) {
215
+ throw new McpError(ErrorCode.InvalidParams, 'jql is required for create operation');
216
+ }
217
+ const filter = await jiraClient.createFilter(args.name, args.jql, args.description, args.favourite);
218
+ const lines = [
219
+ `# Filter Created: ${filter.name}`,
220
+ '',
221
+ `**ID:** ${filter.id}`,
222
+ `**JQL:** \`${filter.jql}\``,
223
+ `**Owner:** ${filter.owner}`,
224
+ filter.description ? `**Description:** ${filter.description}` : '',
225
+ `**Favourite:** ${filter.favourite ? 'Yes' : 'No'}`,
226
+ '',
227
+ `[View in Jira](${filter.viewUrl})`,
228
+ ].filter(Boolean);
229
+ return {
230
+ content: [{ type: 'text', text: lines.join('\n') + filterNextSteps('create', filter.id) }],
231
+ };
212
232
  }
213
- async function handleUpdateFilter(_jiraClient, _args) {
214
- throw new McpError(ErrorCode.InternalError, 'Update filter operation is not yet implemented');
233
+ async function handleUpdateFilter(jiraClient, args) {
234
+ if (!args.filterId) {
235
+ throw new McpError(ErrorCode.InvalidParams, 'filterId is required for update operation');
236
+ }
237
+ const updates = {};
238
+ if (args.name)
239
+ updates.name = args.name;
240
+ if (args.jql)
241
+ updates.jql = args.jql;
242
+ if (args.description !== undefined)
243
+ updates.description = args.description;
244
+ if (args.favourite !== undefined)
245
+ updates.favourite = args.favourite;
246
+ const filter = await jiraClient.updateFilter(args.filterId, updates);
247
+ const lines = [
248
+ `# Filter Updated: ${filter.name}`,
249
+ '',
250
+ `**ID:** ${filter.id}`,
251
+ `**JQL:** \`${filter.jql}\``,
252
+ `**Owner:** ${filter.owner}`,
253
+ ];
254
+ return {
255
+ content: [{ type: 'text', text: lines.join('\n') + filterNextSteps('get', filter.id) }],
256
+ };
215
257
  }
216
- async function handleDeleteFilter(_jiraClient, _args) {
217
- throw new McpError(ErrorCode.InternalError, 'Delete filter operation is not yet implemented');
258
+ async function handleDeleteFilter(jiraClient, args) {
259
+ if (!args.filterId) {
260
+ throw new McpError(ErrorCode.InvalidParams, 'filterId is required for delete operation');
261
+ }
262
+ await jiraClient.deleteFilter(args.filterId);
263
+ return {
264
+ content: [{ type: 'text', text: `Filter ${args.filterId} deleted.` }],
265
+ };
218
266
  }
219
267
  async function handleExecuteFilter(jiraClient, _args) {
220
268
  const filterId = _args.filterId;
package/build/index.js CHANGED
@@ -95,6 +95,9 @@ class JiraServer {
95
95
  const { name, arguments: args } = request.params;
96
96
  return getPrompt(name, args);
97
97
  });
98
+ // Track consecutive single-issue calls to suggest queue tool
99
+ const QUEUE_HINT_THRESHOLD = 3;
100
+ let consecutiveIssueCalls = 0;
98
101
  // Set up tool handlers
99
102
  this.server.setRequestHandler(CallToolRequestSchema, async (request, _extra) => {
100
103
  console.error('Received request:', JSON.stringify(request, null, 2));
@@ -121,6 +124,17 @@ class JiraServer {
121
124
  if (!response) {
122
125
  throw new McpError(ErrorCode.InternalError, `No response from handler for tool: ${name}`);
123
126
  }
127
+ // Track consecutive manage_jira_issue calls and suggest queue tool
128
+ if (name === 'manage_jira_issue') {
129
+ consecutiveIssueCalls++;
130
+ if (consecutiveIssueCalls >= QUEUE_HINT_THRESHOLD && response.content?.[0]?.text) {
131
+ response.content[0].text += `\n\n---\n**💡 Efficiency tip:** You've made ${consecutiveIssueCalls} consecutive \`manage_jira_issue\` calls. Consider using \`queue_jira_operations\` to batch multiple issue operations into a single call — it's faster and uses less context.`;
132
+ consecutiveIssueCalls = 0;
133
+ }
134
+ }
135
+ else {
136
+ consecutiveIssueCalls = 0;
137
+ }
124
138
  return response;
125
139
  }
126
140
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aaronsb/jira-cloud-mcp",
3
- "version": "0.5.8",
3
+ "version": "0.5.9",
4
4
  "mcpName": "io.github.aaronsb/jira-cloud",
5
5
  "description": "Model Context Protocol (MCP) server for Jira Cloud - enables AI assistants to interact with Jira",
6
6
  "type": "module",