@rui.branco/jira-mcp 1.6.21 → 1.7.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.
Files changed (2) hide show
  1. package/index.js +64 -3
  2. 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"],
@@ -2941,7 +2949,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2941
2949
  }
2942
2950
  }
2943
2951
 
2944
- const newInstance = { name: instName, email, token, baseUrl, projects, auth: authStr, defaultTeam };
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 };
2945
2975
 
2946
2976
  // Update in-memory instances
2947
2977
  if (isUpdate) {
@@ -2974,6 +3004,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2974
3004
  // Save without the computed auth field
2975
3005
  const toSave = { name: instName, email, token, baseUrl, projects };
2976
3006
  if (defaultTeam) toSave.defaultTeam = defaultTeam;
3007
+ if (Object.keys(projectTeams).length > 0) toSave.projectTeams = projectTeams;
2977
3008
  const savedIdx = savedConfig.instances.findIndex((i) => i.name === instName);
2978
3009
  if (savedIdx >= 0) {
2979
3010
  savedConfig.instances[savedIdx] = toSave;
@@ -2989,6 +3020,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2989
3020
  let text = `${action} instance "${instName}" (${baseUrl}).`;
2990
3021
  if (projects.length > 0) text += ` Projects: ${projects.join(", ")}.`;
2991
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
+ }
2992
3034
  if (defaultTeam === "none") {
2993
3035
  text += " Default team: None (disabled).";
2994
3036
  } else if (defaultTeam) {
@@ -3052,7 +3094,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3052
3094
  const isDefault = inst.name === currentDefault ? " **(default)**" : "";
3053
3095
  const projs = inst.projects?.length > 0 ? `\n Projects: ${inst.projects.join(", ")}` : "";
3054
3096
  const team = inst.defaultTeam === "none" ? "\n Default team: None (disabled)" : inst.defaultTeam ? `\n Default team: ${inst.defaultTeam.name}` : "";
3055
- text += `- **${inst.name}**${isDefault}: ${inst.baseUrl} (${inst.email})${projs}${team}\n`;
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`;
3056
3105
  if (!inst.defaultTeam) missingTeam.push(inst);
3057
3106
  }
3058
3107
  if (missingTeam.length > 0) {
@@ -3115,8 +3164,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3115
3164
  fields.parent = { key: args.parentKey };
3116
3165
  }
3117
3166
  let teamPrompt = "";
3167
+ const projKey = args.projectKey.toUpperCase();
3168
+ const projTeam = inst.projectTeams?.[projKey];
3118
3169
  if (args.team) {
3119
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
3120
3176
  } else if (inst.defaultTeam && inst.defaultTeam !== "none") {
3121
3177
  fields.customfield_10001 = inst.defaultTeam.id;
3122
3178
  } else if (!inst.defaultTeam) {
@@ -3537,8 +3593,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3537
3593
  fields.components = of.components.map((c) => ({ name: c.name }));
3538
3594
  }
3539
3595
  let teamPrompt = "";
3596
+ const cloneProjTeam = inst.projectTeams?.[targetProject?.toUpperCase()];
3540
3597
  if (of.customfield_10001) {
3541
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
3542
3603
  } else if (inst.defaultTeam && inst.defaultTeam !== "none") {
3543
3604
  fields.customfield_10001 = inst.defaultTeam.id;
3544
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.21",
3
+ "version": "1.7.0",
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": {