@rui.branco/jira-mcp 1.6.16 → 1.6.17

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 (2) hide show
  1. package/index.js +52 -26
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -208,22 +208,58 @@ async function fetchJiraTeams(endpoint, options = {}, instance = defaultInstance
208
208
  return text ? JSON.parse(text) : {};
209
209
  }
210
210
 
211
- async function resolveTeamId(teamName, instance) {
212
- const teams = await fetchJiraTeams(
213
- `/teams/find?query=${encodeURIComponent(teamName)}&excludeMembers=true`,
211
+ async function searchTeamsViaJql(query, instance) {
212
+ const data = await fetchJira(
213
+ `/jql/autocompletedata/suggestions?fieldName=cf[10001]&fieldValue=${encodeURIComponent(query)}`,
214
214
  {},
215
215
  instance,
216
216
  );
217
- const match = teams.find(
218
- (t) => t.title.toLowerCase() === teamName.toLowerCase(),
219
- );
220
- if (!match) {
221
- const available = teams.map((t) => t.title).join(", ");
222
- throw new Error(
223
- `Team "${teamName}" not found.${available ? ` Similar teams: ${available}` : ""}`,
217
+ return (data.results || []).map((r) => ({
218
+ title: r.displayName.replace(/<[^>]*>/g, "").replace(/&amp;/g, "&"),
219
+ id: r.value,
220
+ }));
221
+ }
222
+
223
+ async function listTeams(instance) {
224
+ // Try Teams API first, fall back to JQL autocomplete
225
+ try {
226
+ const teams = await fetchJiraTeams(
227
+ `/teams/find?query=&excludeMembers=true`, {}, instance,
228
+ );
229
+ return teams.map((t) => ({ title: t.title, id: `${t.organizationId}-${t.id}` }));
230
+ } catch {
231
+ // Teams API not available, use JQL autocomplete with a broad search
232
+ return searchTeamsViaJql("", instance);
233
+ }
234
+ }
235
+
236
+ async function resolveTeamId(teamName, instance) {
237
+ // Try Teams API first
238
+ try {
239
+ const teams = await fetchJiraTeams(
240
+ `/teams/find?query=${encodeURIComponent(teamName)}&excludeMembers=true`,
241
+ {},
242
+ instance,
224
243
  );
244
+ const match = teams.find(
245
+ (t) => t.title.toLowerCase() === teamName.toLowerCase(),
246
+ );
247
+ if (match) return `${match.organizationId}-${match.id}`;
248
+ } catch {
249
+ // Teams API not available, fall through to JQL
225
250
  }
226
- return `${match.organizationId}-${match.id}`;
251
+
252
+ // Fallback: JQL autocomplete
253
+ const jqlTeams = await searchTeamsViaJql(teamName, instance);
254
+ const match = jqlTeams.find(
255
+ (t) => t.title.toLowerCase() === teamName.toLowerCase(),
256
+ );
257
+ if (match) return match.id;
258
+
259
+ const available = jqlTeams.map((t) => t.title).join(", ");
260
+ throw new Error(
261
+ `Team "${teamName}" not found.${available ? ` Similar teams: ${available}` : ""}`,
262
+ );
227
263
  }
228
264
 
229
265
  async function downloadAttachment(url, filename, issueKey, instance) {
@@ -2868,11 +2904,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2868
2904
  } catch (e) {
2869
2905
  // Try to list available teams for a helpful error
2870
2906
  try {
2871
- const teams = await fetchJiraTeams(
2872
- `/teams/find?query=&excludeMembers=true`,
2873
- {},
2874
- tempInst,
2875
- );
2907
+ const teams = await listTeams(tempInst);
2876
2908
  const available = teams.map((t) => t.title).join(", ");
2877
2909
  return {
2878
2910
  content: [{ type: "text", text: `Team "${args.defaultTeam}" not found. Available teams: ${available}` }],
@@ -2942,9 +2974,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2942
2974
  // No default team — fetch available teams and prompt
2943
2975
  try {
2944
2976
  const tempInst = { baseUrl, auth: authStr };
2945
- const teams = await fetchJiraTeams(
2946
- `/teams/find?query=&excludeMembers=true`, {}, tempInst,
2947
- );
2977
+ const teams = await listTeams(tempInst);
2948
2978
  if (teams && teams.length > 0) {
2949
2979
  const list = teams.map((t, i) => `${i + 1}. ${t.title}`).join("\n");
2950
2980
  text += `\n\n⚠ No default team configured. Available teams:\n${list}\n0. None\n\nAsk the user which team to set as default. If they pick one, call jira_add_instance with name="${instName}" and defaultTeam="<team name>".`;
@@ -3050,9 +3080,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3050
3080
  } else {
3051
3081
  // No team param and no default — fetch available teams and prompt user
3052
3082
  try {
3053
- const teams = await fetchJiraTeams(
3054
- `/teams/find?query=&excludeMembers=true`, {}, inst,
3055
- );
3083
+ const teams = await listTeams(inst);
3056
3084
  if (teams && teams.length > 0) {
3057
3085
  const list = teams.map((t, i) => `${i + 1}. ${t.title}`).join("\n");
3058
3086
  teamPrompt = `\n\n⚠ No team assigned and no default team configured for instance "${inst.name}". Available teams:\n${list}\n0. None\n\nTo assign a team to this ticket, call jira_update_ticket with issueKey and team parameter.\nIf a team is selected, ask the user if it should also be saved as the default team for instance "${inst.name}" (via jira_add_instance with defaultTeam).`;
@@ -3473,9 +3501,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3473
3501
  fields.customfield_10001 = inst.defaultTeam.id;
3474
3502
  } else {
3475
3503
  try {
3476
- const teams = await fetchJiraTeams(
3477
- `/teams/find?query=&excludeMembers=true`, {}, inst,
3478
- );
3504
+ const teams = await listTeams(inst);
3479
3505
  if (teams && teams.length > 0) {
3480
3506
  const list = teams.map((t, i) => `${i + 1}. ${t.title}`).join("\n");
3481
3507
  teamPrompt = `\n\n⚠ No team assigned (original had none) and no default team configured for instance "${inst.name}". Available teams:\n${list}\n0. None\n\nTo assign a team to this ticket, call jira_update_ticket with issueKey and team parameter.\nIf a team is selected, ask the user if it should also be saved as the default team for instance "${inst.name}" (via jira_add_instance with defaultTeam).`;
@@ -3694,5 +3720,5 @@ if (require.main === module) {
3694
3720
 
3695
3721
  // Export for testing
3696
3722
  if (typeof module !== "undefined") {
3697
- module.exports = { buildCommentADF, parseInlineFormatting, findJiraTicketKeys, resolveTeamId, fetchJiraTeams };
3723
+ module.exports = { buildCommentADF, parseInlineFormatting, findJiraTicketKeys, resolveTeamId, fetchJiraTeams, listTeams, searchTeamsViaJql };
3698
3724
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rui.branco/jira-mcp",
3
- "version": "1.6.16",
3
+ "version": "1.6.17",
4
4
  "description": "Jira MCP server for Claude Code - fetch tickets, search with JQL, update tickets, manage comments, change status, and get Figma designs",
5
5
  "main": "index.js",
6
6
  "bin": {