@rui.branco/jira-mcp 1.6.20 → 1.6.22
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 +96 -3
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1831,7 +1831,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1831
1831
|
},
|
|
1832
1832
|
defaultTeam: {
|
|
1833
1833
|
type: "string",
|
|
1834
|
-
description: "Default team name to auto-assign when creating tickets (e.g., 'Site Surveys (MODS)'). Resolved and validated via Jira Teams API. Pass 'none' to explicitly disable team prompts. Pass empty string to reset.",
|
|
1834
|
+
description: "Default team name to auto-assign when creating tickets (e.g., 'Site Surveys (MODS)'). Resolved and validated via Jira Teams API. Pass 'none' to explicitly disable team prompts. Pass empty string to reset. This is the instance-wide fallback.",
|
|
1835
|
+
},
|
|
1836
|
+
projectKey: {
|
|
1837
|
+
type: "string",
|
|
1838
|
+
description: "Project key to set a project-specific team for (e.g., 'FRFSD'). Must be used together with projectTeam.",
|
|
1839
|
+
},
|
|
1840
|
+
projectTeam: {
|
|
1841
|
+
type: "string",
|
|
1842
|
+
description: "Team name for the specific project (set via projectKey). Overrides the instance defaultTeam for that project. Pass 'none' to disable team for this project. Pass empty string to remove the override.",
|
|
1835
1843
|
},
|
|
1836
1844
|
},
|
|
1837
1845
|
required: ["name"],
|
|
@@ -1862,6 +1870,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1862
1870
|
required: [],
|
|
1863
1871
|
},
|
|
1864
1872
|
},
|
|
1873
|
+
{
|
|
1874
|
+
name: "jira_get_teams",
|
|
1875
|
+
description:
|
|
1876
|
+
"Search for available Jira teams by name. Returns matching teams with their IDs. Use this to find a team before setting it as default or assigning to a ticket. IMPORTANT: You MUST display the results in your chat response so the user can see them.",
|
|
1877
|
+
inputSchema: {
|
|
1878
|
+
type: "object",
|
|
1879
|
+
properties: {
|
|
1880
|
+
query: {
|
|
1881
|
+
type: "string",
|
|
1882
|
+
description: "Search term to filter teams (e.g., 'site', 'mods'). Leave empty to list all available teams (may be partial).",
|
|
1883
|
+
},
|
|
1884
|
+
instance: {
|
|
1885
|
+
type: "string",
|
|
1886
|
+
description: "Instance name. Uses default instance if omitted.",
|
|
1887
|
+
},
|
|
1888
|
+
},
|
|
1889
|
+
required: [],
|
|
1890
|
+
},
|
|
1891
|
+
},
|
|
1865
1892
|
{
|
|
1866
1893
|
name: "jira_create_ticket",
|
|
1867
1894
|
description:
|
|
@@ -2922,7 +2949,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2922
2949
|
}
|
|
2923
2950
|
}
|
|
2924
2951
|
|
|
2925
|
-
|
|
2952
|
+
// Resolve projectTeam if provided
|
|
2953
|
+
let projectTeams = existing.projectTeams || {};
|
|
2954
|
+
if (args.projectKey && args.projectTeam !== undefined) {
|
|
2955
|
+
const pk = args.projectKey.toUpperCase();
|
|
2956
|
+
if (args.projectTeam === "") {
|
|
2957
|
+
delete projectTeams[pk];
|
|
2958
|
+
} else if (args.projectTeam.toLowerCase() === "none") {
|
|
2959
|
+
projectTeams[pk] = "none";
|
|
2960
|
+
} else {
|
|
2961
|
+
const tempInst = { baseUrl, auth: authStr };
|
|
2962
|
+
try {
|
|
2963
|
+
const teamId = await resolveTeamId(args.projectTeam, tempInst);
|
|
2964
|
+
projectTeams[pk] = { name: args.projectTeam, id: teamId };
|
|
2965
|
+
} catch (e) {
|
|
2966
|
+
return {
|
|
2967
|
+
content: [{ type: "text", text: e.message }],
|
|
2968
|
+
isError: true,
|
|
2969
|
+
};
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
const newInstance = { name: instName, email, token, baseUrl, projects, auth: authStr, defaultTeam, projectTeams };
|
|
2926
2975
|
|
|
2927
2976
|
// Update in-memory instances
|
|
2928
2977
|
if (isUpdate) {
|
|
@@ -2955,6 +3004,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2955
3004
|
// Save without the computed auth field
|
|
2956
3005
|
const toSave = { name: instName, email, token, baseUrl, projects };
|
|
2957
3006
|
if (defaultTeam) toSave.defaultTeam = defaultTeam;
|
|
3007
|
+
if (Object.keys(projectTeams).length > 0) toSave.projectTeams = projectTeams;
|
|
2958
3008
|
const savedIdx = savedConfig.instances.findIndex((i) => i.name === instName);
|
|
2959
3009
|
if (savedIdx >= 0) {
|
|
2960
3010
|
savedConfig.instances[savedIdx] = toSave;
|
|
@@ -2970,6 +3020,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2970
3020
|
let text = `${action} instance "${instName}" (${baseUrl}).`;
|
|
2971
3021
|
if (projects.length > 0) text += ` Projects: ${projects.join(", ")}.`;
|
|
2972
3022
|
if (args.setDefault) text += " Set as default.";
|
|
3023
|
+
if (args.projectKey && args.projectTeam !== undefined) {
|
|
3024
|
+
const pk = args.projectKey.toUpperCase();
|
|
3025
|
+
const pt = projectTeams[pk];
|
|
3026
|
+
if (pt === "none") {
|
|
3027
|
+
text += ` Project ${pk} team: None (disabled).`;
|
|
3028
|
+
} else if (pt) {
|
|
3029
|
+
text += ` Project ${pk} team: ${pt.name}.`;
|
|
3030
|
+
} else {
|
|
3031
|
+
text += ` Project ${pk} team override removed.`;
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
2973
3034
|
if (defaultTeam === "none") {
|
|
2974
3035
|
text += " Default team: None (disabled).";
|
|
2975
3036
|
} else if (defaultTeam) {
|
|
@@ -3033,7 +3094,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3033
3094
|
const isDefault = inst.name === currentDefault ? " **(default)**" : "";
|
|
3034
3095
|
const projs = inst.projects?.length > 0 ? `\n Projects: ${inst.projects.join(", ")}` : "";
|
|
3035
3096
|
const team = inst.defaultTeam === "none" ? "\n Default team: None (disabled)" : inst.defaultTeam ? `\n Default team: ${inst.defaultTeam.name}` : "";
|
|
3036
|
-
|
|
3097
|
+
let projTeams = "";
|
|
3098
|
+
if (inst.projectTeams && Object.keys(inst.projectTeams).length > 0) {
|
|
3099
|
+
for (const [pk, pt] of Object.entries(inst.projectTeams)) {
|
|
3100
|
+
const ptName = pt === "none" ? "None (disabled)" : pt.name;
|
|
3101
|
+
projTeams += `\n ${pk} team: ${ptName}`;
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
text += `- **${inst.name}**${isDefault}: ${inst.baseUrl} (${inst.email})${projs}${team}${projTeams}\n`;
|
|
3037
3105
|
if (!inst.defaultTeam) missingTeam.push(inst);
|
|
3038
3106
|
}
|
|
3039
3107
|
if (missingTeam.length > 0) {
|
|
@@ -3042,6 +3110,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3042
3110
|
}
|
|
3043
3111
|
return { content: [{ type: "text", text }] };
|
|
3044
3112
|
|
|
3113
|
+
} else if (name === "jira_get_teams") {
|
|
3114
|
+
const inst = args.instance ? getInstanceByName(args.instance) : defaultInstance;
|
|
3115
|
+
const query = args.query || "";
|
|
3116
|
+
const teams = await searchTeamsViaJql(query, inst);
|
|
3117
|
+
if (teams.length === 0) {
|
|
3118
|
+
return { content: [{ type: "text", text: `No teams found${query ? ` matching "${query}"` : ""}.` }] };
|
|
3119
|
+
}
|
|
3120
|
+
teams.sort((a, b) => a.title.localeCompare(b.title));
|
|
3121
|
+
const list = teams.map((t, i) => `${i + 1}. ${t.title}`).join("\n");
|
|
3122
|
+
return {
|
|
3123
|
+
content: [{ type: "text", text: `Found ${teams.length} team(s)${query ? ` matching "${query}"` : ""}:\n\n0. None\n${list}\n\nIMPORTANT: Display this list in chat so the user can see it. To set as default team, call jira_add_instance with defaultTeam="<team name>". To assign to a ticket, use team parameter on jira_create_ticket or jira_update_ticket.` }],
|
|
3124
|
+
};
|
|
3125
|
+
|
|
3045
3126
|
} else if (name === "jira_create_ticket") {
|
|
3046
3127
|
const inst = args.instance
|
|
3047
3128
|
? getInstanceByName(args.instance)
|
|
@@ -3083,8 +3164,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3083
3164
|
fields.parent = { key: args.parentKey };
|
|
3084
3165
|
}
|
|
3085
3166
|
let teamPrompt = "";
|
|
3167
|
+
const projKey = args.projectKey.toUpperCase();
|
|
3168
|
+
const projTeam = inst.projectTeams?.[projKey];
|
|
3086
3169
|
if (args.team) {
|
|
3087
3170
|
fields.customfield_10001 = await resolveTeamId(args.team, inst);
|
|
3171
|
+
} else if (projTeam && projTeam !== "none") {
|
|
3172
|
+
// Project-specific team override
|
|
3173
|
+
fields.customfield_10001 = projTeam.id;
|
|
3174
|
+
} else if (projTeam === "none") {
|
|
3175
|
+
// Project explicitly set to no team — skip silently
|
|
3088
3176
|
} else if (inst.defaultTeam && inst.defaultTeam !== "none") {
|
|
3089
3177
|
fields.customfield_10001 = inst.defaultTeam.id;
|
|
3090
3178
|
} else if (!inst.defaultTeam) {
|
|
@@ -3505,8 +3593,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3505
3593
|
fields.components = of.components.map((c) => ({ name: c.name }));
|
|
3506
3594
|
}
|
|
3507
3595
|
let teamPrompt = "";
|
|
3596
|
+
const cloneProjTeam = inst.projectTeams?.[targetProject?.toUpperCase()];
|
|
3508
3597
|
if (of.customfield_10001) {
|
|
3509
3598
|
fields.customfield_10001 = of.customfield_10001;
|
|
3599
|
+
} else if (cloneProjTeam && cloneProjTeam !== "none") {
|
|
3600
|
+
fields.customfield_10001 = cloneProjTeam.id;
|
|
3601
|
+
} else if (cloneProjTeam === "none") {
|
|
3602
|
+
// Project explicitly set to no team
|
|
3510
3603
|
} else if (inst.defaultTeam && inst.defaultTeam !== "none") {
|
|
3511
3604
|
fields.customfield_10001 = inst.defaultTeam.id;
|
|
3512
3605
|
} else if (!inst.defaultTeam) {
|
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.22",
|
|
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": {
|