@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.
- package/index.js +52 -26
- 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
|
|
212
|
-
const
|
|
213
|
-
`/
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
217
|
+
return (data.results || []).map((r) => ({
|
|
218
|
+
title: r.displayName.replace(/<[^>]*>/g, "").replace(/&/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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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": {
|