mcp-jira-stdio 1.8.1 → 1.9.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.
- package/dist/index.js +221 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -441,7 +441,7 @@ var require_package2 = __commonJS({
|
|
|
441
441
|
"package.json"(exports$1, module) {
|
|
442
442
|
module.exports = {
|
|
443
443
|
name: "mcp-jira-stdio",
|
|
444
|
-
version: "1.
|
|
444
|
+
version: "1.9.0",
|
|
445
445
|
description: "Model Context Protocol (MCP) server for Jira API integration",
|
|
446
446
|
author: "graslt",
|
|
447
447
|
license: "MIT",
|
|
@@ -497,7 +497,7 @@ var require_package2 = __commonJS({
|
|
|
497
497
|
zod: "^3.22.4"
|
|
498
498
|
},
|
|
499
499
|
devDependencies: {
|
|
500
|
-
"@types/node": "^
|
|
500
|
+
"@types/node": "^25.3.3",
|
|
501
501
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
502
502
|
"@typescript-eslint/parser": "^8.0.0",
|
|
503
503
|
"@vitest/coverage-v8": "^4.0.10",
|
|
@@ -577,7 +577,9 @@ var TOOL_NAMES = {
|
|
|
577
577
|
GET_COMMENTS: "jira_get_comments",
|
|
578
578
|
ADD_ATTACHMENT: "jira_add_attachment",
|
|
579
579
|
GET_ATTACHMENTS: "jira_get_attachments",
|
|
580
|
-
DELETE_ATTACHMENT: "jira_delete_attachment"
|
|
580
|
+
DELETE_ATTACHMENT: "jira_delete_attachment",
|
|
581
|
+
GET_TRANSITIONS: "jira_get_transitions",
|
|
582
|
+
TRANSITION_ISSUE: "jira_transition_issue"
|
|
581
583
|
};
|
|
582
584
|
|
|
583
585
|
// src/utils/logger.ts
|
|
@@ -883,6 +885,9 @@ var UpdateIssueInputSchema = z.object({
|
|
|
883
885
|
assignee: z.string().optional().describe("New assignee account ID"),
|
|
884
886
|
labels: z.array(z.string()).optional().describe("New labels (replaces existing)"),
|
|
885
887
|
components: z.array(z.string()).optional().describe("New components (replaces existing)"),
|
|
888
|
+
parent: z.string().optional().describe(
|
|
889
|
+
"New parent issue key (e.g., PROJECT-100). Set to empty string to remove the parent."
|
|
890
|
+
),
|
|
886
891
|
returnIssue: z.boolean().optional().describe("When false, skip fetching full issue after update"),
|
|
887
892
|
format: z.enum(["markdown", "adf", "plain"]).optional().default("markdown").describe(
|
|
888
893
|
'Description format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"'
|
|
@@ -985,6 +990,27 @@ var GetAttachmentsInputSchema = z.object({
|
|
|
985
990
|
var DeleteAttachmentInputSchema = z.object({
|
|
986
991
|
attachmentId: z.string().min(1).describe("ID of the attachment to delete")
|
|
987
992
|
});
|
|
993
|
+
var GetTransitionsInputSchema = z.object({
|
|
994
|
+
issueKey: z.string().describe("Issue key to get available transitions for (e.g., PROJECT-123)").refine((v) => isValidIssueKey(v), "Invalid issue key format")
|
|
995
|
+
});
|
|
996
|
+
var TransitionIssueInputSchema = z.object({
|
|
997
|
+
issueKey: z.string().describe("Issue key to transition (e.g., PROJECT-123)").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
|
|
998
|
+
transitionId: z.string().optional().describe(
|
|
999
|
+
"ID of the transition to perform. Use jira_get_transitions to find available transition IDs."
|
|
1000
|
+
),
|
|
1001
|
+
transitionName: z.string().optional().describe(
|
|
1002
|
+
'Name of the transition to perform (e.g., "In Progress", "Done"). Case-insensitive. Alternative to transitionId.'
|
|
1003
|
+
),
|
|
1004
|
+
comment: z.string().optional().describe("Optional comment to add when transitioning the issue"),
|
|
1005
|
+
resolution: z.string().optional().describe(
|
|
1006
|
+
'Resolution name when transitioning to a resolved/done status (e.g., "Done", "Fixed")'
|
|
1007
|
+
),
|
|
1008
|
+
format: z.enum(["markdown", "adf", "plain"]).optional().default("markdown").describe(
|
|
1009
|
+
'Comment format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"'
|
|
1010
|
+
)
|
|
1011
|
+
}).refine((data) => data.transitionId || data.transitionName, {
|
|
1012
|
+
message: "Either transitionId or transitionName must be provided"
|
|
1013
|
+
});
|
|
988
1014
|
function ensureAdfDescription(desc, format = "markdown") {
|
|
989
1015
|
if (!desc) return desc;
|
|
990
1016
|
if (typeof desc === "object") return desc;
|
|
@@ -1237,6 +1263,9 @@ async function updateIssue(issueKey, updates) {
|
|
|
1237
1263
|
if (updates.components !== void 0) {
|
|
1238
1264
|
fields.components = updates.components.map((name) => ({ name }));
|
|
1239
1265
|
}
|
|
1266
|
+
if (updates.parent !== void 0) {
|
|
1267
|
+
fields.parent = updates.parent ? { key: updates.parent } : null;
|
|
1268
|
+
}
|
|
1240
1269
|
const config = {
|
|
1241
1270
|
method: "PUT",
|
|
1242
1271
|
url: `/issue/${issueKey}`,
|
|
@@ -1521,6 +1550,42 @@ async function deleteAttachment(attachmentId) {
|
|
|
1521
1550
|
};
|
|
1522
1551
|
await makeJiraRequest(config);
|
|
1523
1552
|
}
|
|
1553
|
+
async function getTransitions(issueKey) {
|
|
1554
|
+
const config = {
|
|
1555
|
+
method: "GET",
|
|
1556
|
+
url: `/issue/${issueKey}/transitions`
|
|
1557
|
+
};
|
|
1558
|
+
const response = await makeJiraRequest(config);
|
|
1559
|
+
return response.transitions;
|
|
1560
|
+
}
|
|
1561
|
+
async function transitionIssue(issueKey, transitionId, options = {}) {
|
|
1562
|
+
const data = {
|
|
1563
|
+
transition: { id: transitionId }
|
|
1564
|
+
};
|
|
1565
|
+
if (options.resolution) {
|
|
1566
|
+
data.fields = {
|
|
1567
|
+
resolution: { name: options.resolution }
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
if (options.comment) {
|
|
1571
|
+
const adfBody = ensureAdfDescription(options.comment, options.format || "markdown");
|
|
1572
|
+
data.update = {
|
|
1573
|
+
comment: [
|
|
1574
|
+
{
|
|
1575
|
+
add: {
|
|
1576
|
+
body: adfBody
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
]
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
const config = {
|
|
1583
|
+
method: "POST",
|
|
1584
|
+
url: `/issue/${issueKey}/transitions`,
|
|
1585
|
+
data
|
|
1586
|
+
};
|
|
1587
|
+
await makeJiraRequest(config);
|
|
1588
|
+
}
|
|
1524
1589
|
|
|
1525
1590
|
// src/utils/redaction.ts
|
|
1526
1591
|
function isRedactionEnabled() {
|
|
@@ -1608,6 +1673,8 @@ function formatIssueResponse(issue) {
|
|
|
1608
1673
|
}
|
|
1609
1674
|
const createdText = fields?.created ? new Date(fields.created).toISOString() : "N/A";
|
|
1610
1675
|
const updatedText = fields?.updated ? new Date(fields.updated).toISOString() : "N/A";
|
|
1676
|
+
const parentText = fields?.parent ? `
|
|
1677
|
+
**Parent:** ${fields.parent.key}${fields.parent.fields?.summary ? ` - ${fields.parent.fields.summary}` : ""}${fields.parent.fields?.status ? ` (${fields.parent.fields.status.name})` : ""}` : "";
|
|
1611
1678
|
return {
|
|
1612
1679
|
content: [
|
|
1613
1680
|
{
|
|
@@ -1621,7 +1688,7 @@ function formatIssueResponse(issue) {
|
|
|
1621
1688
|
**Project:** ${fields?.project?.name || "Unknown"} (${fields?.project?.key || "N/A"})
|
|
1622
1689
|
**Issue Type:** ${fields?.issuetype?.name || "Unknown"}
|
|
1623
1690
|
**Labels:** ${labelsText}
|
|
1624
|
-
**Components:** ${componentsText}
|
|
1691
|
+
**Components:** ${componentsText}${parentText}
|
|
1625
1692
|
**Created:** ${createdText}
|
|
1626
1693
|
**Updated:** ${updatedText}
|
|
1627
1694
|
|
|
@@ -2062,6 +2129,34 @@ ${attachmentsList}
|
|
|
2062
2129
|
]
|
|
2063
2130
|
};
|
|
2064
2131
|
}
|
|
2132
|
+
function formatTransitionsResponse(transitions, issueKey) {
|
|
2133
|
+
if (transitions.length === 0) {
|
|
2134
|
+
return {
|
|
2135
|
+
content: [
|
|
2136
|
+
{
|
|
2137
|
+
type: "text",
|
|
2138
|
+
text: `No transitions available for issue ${issueKey}. The issue may be in a final state or you may lack permission.`
|
|
2139
|
+
}
|
|
2140
|
+
]
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
const transitionsList = transitions.map(
|
|
2144
|
+
(t) => `\u2022 **${t.name}** (ID: ${t.id})
|
|
2145
|
+
Target status: ${t.to.name} (Category: ${t.to.statusCategory?.name || "Unknown"})`
|
|
2146
|
+
).join("\n\n");
|
|
2147
|
+
return {
|
|
2148
|
+
content: [
|
|
2149
|
+
{
|
|
2150
|
+
type: "text",
|
|
2151
|
+
text: `**Available transitions for ${issueKey}** (${transitions.length}):
|
|
2152
|
+
|
|
2153
|
+
${transitionsList}
|
|
2154
|
+
|
|
2155
|
+
\u{1F4A1} **Tip:** Use \`jira_transition_issue\` with the transition ID or name to move this issue.`
|
|
2156
|
+
}
|
|
2157
|
+
]
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2065
2160
|
|
|
2066
2161
|
// src/utils/error-handler.ts
|
|
2067
2162
|
var log2 = createLogger("error-handler");
|
|
@@ -2612,6 +2707,10 @@ var updateIssueTool = {
|
|
|
2612
2707
|
items: { type: "string" },
|
|
2613
2708
|
description: "New components array (replaces existing components) - optional"
|
|
2614
2709
|
},
|
|
2710
|
+
parent: {
|
|
2711
|
+
type: "string",
|
|
2712
|
+
description: "New parent issue key (e.g., PROJECT-100). Set to empty string to remove the parent."
|
|
2713
|
+
},
|
|
2615
2714
|
returnIssue: {
|
|
2616
2715
|
type: "boolean",
|
|
2617
2716
|
description: "If false, returns a success message without fetching the updated issue",
|
|
@@ -2638,6 +2737,7 @@ async function handleUpdateIssue(input) {
|
|
|
2638
2737
|
if (validated.assignee !== void 0) updateParams.assignee = validated.assignee;
|
|
2639
2738
|
if (validated.labels !== void 0) updateParams.labels = validated.labels;
|
|
2640
2739
|
if (validated.components !== void 0) updateParams.components = validated.components;
|
|
2740
|
+
if (validated.parent !== void 0) updateParams.parent = validated.parent;
|
|
2641
2741
|
if (validated.format !== void 0) updateParams.format = validated.format;
|
|
2642
2742
|
await updateIssue(validated.issueKey, updateParams);
|
|
2643
2743
|
if (validated.returnIssue === false) {
|
|
@@ -3130,6 +3230,115 @@ async function handleDeleteAttachment(input) {
|
|
|
3130
3230
|
}
|
|
3131
3231
|
}
|
|
3132
3232
|
|
|
3233
|
+
// src/tools/get-transitions.ts
|
|
3234
|
+
var log23 = createLogger("tool:get-transitions");
|
|
3235
|
+
var getTransitionsTool = {
|
|
3236
|
+
name: TOOL_NAMES.GET_TRANSITIONS,
|
|
3237
|
+
description: "Retrieves all available workflow transitions for a Jira issue. Use this to discover which status changes are possible for an issue before calling jira_transition_issue.",
|
|
3238
|
+
inputSchema: {
|
|
3239
|
+
type: "object",
|
|
3240
|
+
properties: {
|
|
3241
|
+
issueKey: {
|
|
3242
|
+
type: "string",
|
|
3243
|
+
description: "Issue key to get available transitions for (e.g., PROJECT-123)"
|
|
3244
|
+
}
|
|
3245
|
+
},
|
|
3246
|
+
required: ["issueKey"]
|
|
3247
|
+
}
|
|
3248
|
+
};
|
|
3249
|
+
async function handleGetTransitions(input) {
|
|
3250
|
+
try {
|
|
3251
|
+
const validated = validateInput(GetTransitionsInputSchema, input);
|
|
3252
|
+
log23.info(`Getting transitions for issue ${validated.issueKey}...`);
|
|
3253
|
+
const transitions = await getTransitions(validated.issueKey);
|
|
3254
|
+
log23.info(`Found ${transitions.length} transition(s) for ${validated.issueKey}`);
|
|
3255
|
+
return formatTransitionsResponse(transitions, validated.issueKey);
|
|
3256
|
+
} catch (error) {
|
|
3257
|
+
log23.error("Error in handleGetTransitions:", error);
|
|
3258
|
+
return handleError(error);
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
// src/tools/transition-issue.ts
|
|
3263
|
+
var log24 = createLogger("tool:transition-issue");
|
|
3264
|
+
var transitionIssueTool = {
|
|
3265
|
+
name: TOOL_NAMES.TRANSITION_ISSUE,
|
|
3266
|
+
description: 'Transitions a Jira issue to a new workflow status (e.g., "To Do" -> "In Progress" -> "Done"). Use jira_get_transitions first to discover available transitions. Supports adding a comment and setting resolution during the transition.',
|
|
3267
|
+
inputSchema: {
|
|
3268
|
+
type: "object",
|
|
3269
|
+
properties: {
|
|
3270
|
+
issueKey: {
|
|
3271
|
+
type: "string",
|
|
3272
|
+
description: "Issue key to transition (e.g., PROJECT-123)"
|
|
3273
|
+
},
|
|
3274
|
+
transitionId: {
|
|
3275
|
+
type: "string",
|
|
3276
|
+
description: "ID of the transition to perform. Use jira_get_transitions to find available transition IDs."
|
|
3277
|
+
},
|
|
3278
|
+
transitionName: {
|
|
3279
|
+
type: "string",
|
|
3280
|
+
description: 'Name of the transition to perform (e.g., "In Progress", "Done"). Case-insensitive. Alternative to transitionId.'
|
|
3281
|
+
},
|
|
3282
|
+
comment: {
|
|
3283
|
+
type: "string",
|
|
3284
|
+
description: "Optional comment to add when transitioning the issue"
|
|
3285
|
+
},
|
|
3286
|
+
resolution: {
|
|
3287
|
+
type: "string",
|
|
3288
|
+
description: 'Resolution name when transitioning to a resolved/done status (e.g., "Done", "Fixed")'
|
|
3289
|
+
},
|
|
3290
|
+
format: {
|
|
3291
|
+
type: "string",
|
|
3292
|
+
enum: ["markdown", "adf", "plain"],
|
|
3293
|
+
description: 'Comment format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"',
|
|
3294
|
+
default: "markdown"
|
|
3295
|
+
}
|
|
3296
|
+
},
|
|
3297
|
+
required: ["issueKey"]
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
3300
|
+
async function handleTransitionIssue(input) {
|
|
3301
|
+
try {
|
|
3302
|
+
const validated = validateInput(TransitionIssueInputSchema, input);
|
|
3303
|
+
let transitionId = validated.transitionId;
|
|
3304
|
+
if (!transitionId && validated.transitionName) {
|
|
3305
|
+
log24.info(
|
|
3306
|
+
`Looking up transition "${validated.transitionName}" for issue ${validated.issueKey}...`
|
|
3307
|
+
);
|
|
3308
|
+
const transitions = await getTransitions(validated.issueKey);
|
|
3309
|
+
const match = transitions.find(
|
|
3310
|
+
(t) => t.name.toLowerCase() === validated.transitionName.toLowerCase()
|
|
3311
|
+
);
|
|
3312
|
+
if (!match) {
|
|
3313
|
+
const available = transitions.map((t) => `"${t.name}" (ID: ${t.id})`).join(", ");
|
|
3314
|
+
return {
|
|
3315
|
+
content: [
|
|
3316
|
+
{
|
|
3317
|
+
type: "text",
|
|
3318
|
+
text: `Transition "${validated.transitionName}" not found for issue ${validated.issueKey}.
|
|
3319
|
+
|
|
3320
|
+
Available transitions: ${available || "none"}`
|
|
3321
|
+
}
|
|
3322
|
+
]
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
transitionId = match.id;
|
|
3326
|
+
}
|
|
3327
|
+
log24.info(`Transitioning issue ${validated.issueKey} with transition ${transitionId}...`);
|
|
3328
|
+
const transitionOptions = {};
|
|
3329
|
+
if (validated.comment) transitionOptions.comment = validated.comment;
|
|
3330
|
+
if (validated.resolution) transitionOptions.resolution = validated.resolution;
|
|
3331
|
+
if (validated.format) transitionOptions.format = validated.format;
|
|
3332
|
+
await transitionIssue(validated.issueKey, transitionId, transitionOptions);
|
|
3333
|
+
const updatedIssue = await getIssue(validated.issueKey);
|
|
3334
|
+
log24.info(`Transitioned issue ${validated.issueKey} to ${updatedIssue.fields.status.name}`);
|
|
3335
|
+
return formatIssueResponse(updatedIssue);
|
|
3336
|
+
} catch (error) {
|
|
3337
|
+
log24.error("Error in handleTransitionIssue:", error);
|
|
3338
|
+
return handleError(error);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
|
|
3133
3342
|
// src/index.ts
|
|
3134
3343
|
if (process.env.NODE_ENV !== "production") {
|
|
3135
3344
|
try {
|
|
@@ -3138,7 +3347,7 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
3138
3347
|
} catch {
|
|
3139
3348
|
}
|
|
3140
3349
|
}
|
|
3141
|
-
var
|
|
3350
|
+
var log25 = createLogger("server");
|
|
3142
3351
|
var toolHandlers = /* @__PURE__ */ new Map([
|
|
3143
3352
|
[TOOL_NAMES.GET_VISIBLE_PROJECTS, handleGetVisibleProjects],
|
|
3144
3353
|
[TOOL_NAMES.GET_ISSUE, handleGetIssue],
|
|
@@ -3159,7 +3368,9 @@ var toolHandlers = /* @__PURE__ */ new Map([
|
|
|
3159
3368
|
[TOOL_NAMES.GET_COMMENTS, handleGetComments],
|
|
3160
3369
|
[TOOL_NAMES.ADD_ATTACHMENT, handleAddAttachment],
|
|
3161
3370
|
[TOOL_NAMES.GET_ATTACHMENTS, handleGetAttachments],
|
|
3162
|
-
[TOOL_NAMES.DELETE_ATTACHMENT, handleDeleteAttachment]
|
|
3371
|
+
[TOOL_NAMES.DELETE_ATTACHMENT, handleDeleteAttachment],
|
|
3372
|
+
[TOOL_NAMES.GET_TRANSITIONS, handleGetTransitions],
|
|
3373
|
+
[TOOL_NAMES.TRANSITION_ISSUE, handleTransitionIssue]
|
|
3163
3374
|
]);
|
|
3164
3375
|
var allTools = [
|
|
3165
3376
|
getVisibleProjectsTool,
|
|
@@ -3181,7 +3392,9 @@ var allTools = [
|
|
|
3181
3392
|
getCommentsTool,
|
|
3182
3393
|
addAttachmentTool,
|
|
3183
3394
|
getAttachmentsTool,
|
|
3184
|
-
deleteAttachmentTool
|
|
3395
|
+
deleteAttachmentTool,
|
|
3396
|
+
getTransitionsTool,
|
|
3397
|
+
transitionIssueTool
|
|
3185
3398
|
];
|
|
3186
3399
|
async function main() {
|
|
3187
3400
|
const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
|
|
@@ -3252,7 +3465,7 @@ async function main() {
|
|
|
3252
3465
|
console.error("\u{1F680} Jira MCP server running on stdio");
|
|
3253
3466
|
}
|
|
3254
3467
|
main().catch((error) => {
|
|
3255
|
-
|
|
3468
|
+
log25.error("Fatal error:", error);
|
|
3256
3469
|
process.exit(1);
|
|
3257
3470
|
});
|
|
3258
3471
|
//# sourceMappingURL=index.js.map
|