@rui.branco/jira-mcp 1.2.0 → 1.3.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 (3) hide show
  1. package/README.md +1 -1
  2. package/index.js +55 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -156,7 +156,7 @@ To enable Figma integration:
156
156
  | `jira_reply_comment` | Reply to a specific comment with quote and mention | `issueKey` (required), `commentId` (required), `reply` (required) |
157
157
  | `jira_edit_comment` | Edit an existing comment | `issueKey` (required), `commentId` (required), `comment` (required) |
158
158
  | `jira_delete_comment` | Delete a comment (irreversible) | `issueKey` (required), `commentId` (required) |
159
- | `jira_transition` | Change ticket status (omit `transitionId` to list available transitions) | `issueKey` (required), `transitionId` |
159
+ | `jira_transition` | Change ticket status by name or ID (auto-handles intermediate steps) | `issueKey` (required), `targetStatus` or `transitionId` |
160
160
  | `jira_update_ticket` | Update ticket fields (summary, description, assignee, priority, labels) | `issueKey` (required), plus optional field parameters |
161
161
 
162
162
  ### Configuration
package/index.js CHANGED
@@ -814,12 +814,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
814
814
  },
815
815
  {
816
816
  name: "jira_transition",
817
- description: "Change the status of a Jira ticket. Use without transitionId to list available transitions, or with transitionId to execute one.",
817
+ description: "Change the status of a Jira ticket. Use targetStatus to transition by name (auto-handles intermediate steps like In Progress), transitionId for direct transition, or omit both to list available transitions.",
818
818
  inputSchema: {
819
819
  type: "object",
820
820
  properties: {
821
821
  issueKey: { type: "string", description: "The Jira issue key (e.g., MODS-123)" },
822
822
  transitionId: { type: "string", description: "The transition ID to execute. Omit to list available transitions." },
823
+ targetStatus: { type: "string", description: "Target status name (e.g., 'Review', 'Done'). Will auto-transition through intermediate states if needed." },
823
824
  },
824
825
  required: ["issueKey"],
825
826
  },
@@ -990,7 +991,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
990
991
  return { content: [{ type: "text", text: `Comment ${args.commentId} on ${args.issueKey} deleted.` }] };
991
992
 
992
993
  } else if (name === "jira_transition") {
993
- if (!args.transitionId) {
994
+ if (!args.transitionId && !args.targetStatus) {
994
995
  // List available transitions
995
996
  const result = await fetchJira(`/issue/${args.issueKey}/transitions`);
996
997
  let output = `# Available transitions for ${args.issueKey}\n\n`;
@@ -1002,7 +1003,58 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1002
1003
  }
1003
1004
  return { content: [{ type: "text", text: output }] };
1004
1005
  }
1005
- // Execute transition
1006
+
1007
+ // If targetStatus is provided, find the transition by status name
1008
+ if (args.targetStatus) {
1009
+ const targetLower = args.targetStatus.toLowerCase();
1010
+ const transitions = [];
1011
+
1012
+ // Try to reach target status, with up to 3 intermediate transitions
1013
+ for (let attempt = 0; attempt < 3; attempt++) {
1014
+ const result = await fetchJira(`/issue/${args.issueKey}/transitions`);
1015
+ const available = result.transitions || [];
1016
+
1017
+ // Check if target status is directly available
1018
+ const directMatch = available.find(t =>
1019
+ t.to?.name?.toLowerCase() === targetLower ||
1020
+ t.name?.toLowerCase() === targetLower
1021
+ );
1022
+
1023
+ if (directMatch) {
1024
+ await fetchJira(`/issue/${args.issueKey}/transitions`, {
1025
+ method: "POST",
1026
+ body: { transition: { id: directMatch.id } },
1027
+ });
1028
+ transitions.push(directMatch.to?.name || directMatch.name);
1029
+ return { content: [{ type: "text", text: `Transitioned ${args.issueKey} to ${transitions.join(" → ")}.` }] };
1030
+ }
1031
+
1032
+ // Target not available, try "In Progress" as intermediate step
1033
+ const inProgress = available.find(t =>
1034
+ t.to?.name?.toLowerCase() === "in progress" ||
1035
+ t.name?.toLowerCase() === "in progress"
1036
+ );
1037
+
1038
+ if (inProgress) {
1039
+ await fetchJira(`/issue/${args.issueKey}/transitions`, {
1040
+ method: "POST",
1041
+ body: { transition: { id: inProgress.id } },
1042
+ });
1043
+ transitions.push(inProgress.to?.name || "In Progress");
1044
+ continue; // Try again to find target
1045
+ }
1046
+
1047
+ // No path found
1048
+ break;
1049
+ }
1050
+
1051
+ // Could not reach target status
1052
+ const result = await fetchJira(`/issue/${args.issueKey}/transitions`);
1053
+ const availableNames = (result.transitions || []).map(t => t.to?.name || t.name).join(", ");
1054
+ return { content: [{ type: "text", text: `Could not transition to "${args.targetStatus}". Available: ${availableNames}` }] };
1055
+ }
1056
+
1057
+ // Execute transition by ID
1006
1058
  await fetchJira(`/issue/${args.issueKey}/transitions`, {
1007
1059
  method: "POST",
1008
1060
  body: { transition: { id: args.transitionId } },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rui.branco/jira-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.3.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": {