mcp-jira-stdio 1.6.0 → 1.6.1
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 +102 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -440,7 +440,7 @@ var require_package2 = __commonJS({
|
|
|
440
440
|
"package.json"(exports, module) {
|
|
441
441
|
module.exports = {
|
|
442
442
|
name: "mcp-jira-stdio",
|
|
443
|
-
version: "1.6.
|
|
443
|
+
version: "1.6.1",
|
|
444
444
|
description: "Model Context Protocol (MCP) server for Jira API integration",
|
|
445
445
|
author: "graslt",
|
|
446
446
|
license: "MIT",
|
|
@@ -559,7 +559,8 @@ var TOOL_NAMES = {
|
|
|
559
559
|
GET_PROJECT_INFO: "jira_get_project_info",
|
|
560
560
|
CREATE_SUBTASK: "jira_create_subtask",
|
|
561
561
|
GET_CREATE_META: "jira_get_create_meta",
|
|
562
|
-
GET_CUSTOM_FIELDS: "jira_get_custom_fields"
|
|
562
|
+
GET_CUSTOM_FIELDS: "jira_get_custom_fields",
|
|
563
|
+
CREATE_ISSUE_LINK: "jira_create_issue_link"
|
|
563
564
|
};
|
|
564
565
|
|
|
565
566
|
// src/utils/logger.ts
|
|
@@ -836,6 +837,13 @@ var GetCreateMetaInputSchema = z.object({
|
|
|
836
837
|
var GetCustomFieldsInputSchema = z.object({
|
|
837
838
|
projectKey: z.string().optional().describe("Project key to filter custom fields (optional)").refine((v) => v ? isValidProjectKey(v) : true, "Invalid project key format")
|
|
838
839
|
});
|
|
840
|
+
var CreateIssueLinkInputSchema = z.object({
|
|
841
|
+
fromIssue: z.string().describe("Source issue key").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
|
|
842
|
+
toIssue: z.string().describe("Target issue key").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
|
|
843
|
+
linkType: z.string().describe(
|
|
844
|
+
'Link type: "blocks", "is blocked by", "relates", "duplicates", "clones", or custom link type name'
|
|
845
|
+
)
|
|
846
|
+
});
|
|
839
847
|
function ensureAdfDescription(desc, format = "markdown") {
|
|
840
848
|
if (!desc) return desc;
|
|
841
849
|
if (typeof desc === "object") return desc;
|
|
@@ -1016,8 +1024,8 @@ async function searchIssues(options) {
|
|
|
1016
1024
|
if (options.fields) {
|
|
1017
1025
|
data.fields = options.fields;
|
|
1018
1026
|
}
|
|
1019
|
-
if (options.expand) {
|
|
1020
|
-
data.expand = options.expand;
|
|
1027
|
+
if (options.expand && options.expand.length > 0) {
|
|
1028
|
+
data.expand = options.expand.join(",");
|
|
1021
1029
|
}
|
|
1022
1030
|
const config = {
|
|
1023
1031
|
method: "POST",
|
|
@@ -1263,6 +1271,40 @@ async function getFields() {
|
|
|
1263
1271
|
};
|
|
1264
1272
|
return await makeJiraRequest(config);
|
|
1265
1273
|
}
|
|
1274
|
+
async function createIssueLink(fromIssue, toIssue, linkType) {
|
|
1275
|
+
const linkTypeMap = {
|
|
1276
|
+
blocks: "Blocks",
|
|
1277
|
+
"is blocked by": "Blocks",
|
|
1278
|
+
relates: "Relates",
|
|
1279
|
+
"relates to": "Relates",
|
|
1280
|
+
duplicates: "Duplicate",
|
|
1281
|
+
duplicate: "Duplicate",
|
|
1282
|
+
"is duplicated by": "Duplicate",
|
|
1283
|
+
clones: "Cloners",
|
|
1284
|
+
"is cloned by": "Cloners"
|
|
1285
|
+
};
|
|
1286
|
+
const normalizedLinkType = linkType.toLowerCase();
|
|
1287
|
+
const jiraLinkType = linkTypeMap[normalizedLinkType] || linkType;
|
|
1288
|
+
const isInward = normalizedLinkType === "is blocked by" || normalizedLinkType === "is duplicated by" || normalizedLinkType === "is cloned by";
|
|
1289
|
+
const data = {
|
|
1290
|
+
type: {
|
|
1291
|
+
name: jiraLinkType
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
if (isInward) {
|
|
1295
|
+
data.inwardIssue = { key: fromIssue };
|
|
1296
|
+
data.outwardIssue = { key: toIssue };
|
|
1297
|
+
} else {
|
|
1298
|
+
data.outwardIssue = { key: fromIssue };
|
|
1299
|
+
data.inwardIssue = { key: toIssue };
|
|
1300
|
+
}
|
|
1301
|
+
const config = {
|
|
1302
|
+
method: "POST",
|
|
1303
|
+
url: "/issueLink",
|
|
1304
|
+
data
|
|
1305
|
+
};
|
|
1306
|
+
await makeJiraRequest(config);
|
|
1307
|
+
}
|
|
1266
1308
|
|
|
1267
1309
|
// src/utils/redaction.ts
|
|
1268
1310
|
function isRedactionEnabled() {
|
|
@@ -1392,14 +1434,15 @@ function formatSearchResultsResponse(result) {
|
|
|
1392
1434
|
return `\u2022 **${issue.key}** - ${summary}
|
|
1393
1435
|
Status: ${status} | Assignee: ${assignee} | Priority: ${priority}`;
|
|
1394
1436
|
}).join("\n\n");
|
|
1437
|
+
const total = result.total ?? result.issues.length;
|
|
1395
1438
|
const rangeStart = result.startAt + 1;
|
|
1396
|
-
const rangeEnd = Math.min(result.startAt + result.maxResults,
|
|
1397
|
-
const showRangeInline =
|
|
1439
|
+
const rangeEnd = Math.min(result.startAt + result.maxResults, total);
|
|
1440
|
+
const showRangeInline = total > result.maxResults || result.startAt > 0;
|
|
1398
1441
|
let paginationInfo = "";
|
|
1399
1442
|
if (showRangeInline) {
|
|
1400
1443
|
paginationInfo = `
|
|
1401
1444
|
|
|
1402
|
-
*Showing ${rangeStart}-${rangeEnd} of ${
|
|
1445
|
+
*Showing ${rangeStart}-${rangeEnd} of ${total} results*`;
|
|
1403
1446
|
}
|
|
1404
1447
|
if (result.nextPageToken && !result.isLast) {
|
|
1405
1448
|
paginationInfo += `
|
|
@@ -1411,7 +1454,7 @@ function formatSearchResultsResponse(result) {
|
|
|
1411
1454
|
content: [
|
|
1412
1455
|
{
|
|
1413
1456
|
type: "text",
|
|
1414
|
-
text: `Found ${
|
|
1457
|
+
text: `Found ${total} issue(s)${showRangeInline ? ` (showing ${rangeStart}-${rangeEnd})` : ""}:
|
|
1415
1458
|
|
|
1416
1459
|
${issuesList}${paginationInfo}`
|
|
1417
1460
|
}
|
|
@@ -1888,7 +1931,7 @@ async function handleSearchIssues(input) {
|
|
|
1888
1931
|
if (validated.fields !== void 0) searchParams.fields = validated.fields;
|
|
1889
1932
|
if (validated.expand !== void 0) searchParams.expand = validated.expand;
|
|
1890
1933
|
const result = await searchIssues(searchParams);
|
|
1891
|
-
log5.info(`Found ${result.total} issue(s), showing ${result.issues.length}`);
|
|
1934
|
+
log5.info(`Found ${result.total ?? 0} issue(s), showing ${result.issues.length}`);
|
|
1892
1935
|
return formatSearchResultsResponse(result);
|
|
1893
1936
|
} catch (error) {
|
|
1894
1937
|
log5.error("Error in handleSearchIssues:", error);
|
|
@@ -1940,7 +1983,9 @@ async function handleGetMyIssues(input) {
|
|
|
1940
1983
|
if (validated.fields !== void 0) getParams.fields = validated.fields;
|
|
1941
1984
|
if (validated.expand !== void 0) getParams.expand = validated.expand;
|
|
1942
1985
|
const result = await getMyIssues(getParams);
|
|
1943
|
-
log6.info(
|
|
1986
|
+
log6.info(
|
|
1987
|
+
`Found ${result.total ?? 0} issue(s) assigned to you, showing ${result.issues.length}`
|
|
1988
|
+
);
|
|
1944
1989
|
return formatSearchResultsResponse(result);
|
|
1945
1990
|
} catch (error) {
|
|
1946
1991
|
log6.error("Error in handleGetMyIssues:", error);
|
|
@@ -2532,6 +2577,47 @@ async function handleGetCustomFields(input) {
|
|
|
2532
2577
|
}
|
|
2533
2578
|
}
|
|
2534
2579
|
|
|
2580
|
+
// src/tools/create-issue-link.ts
|
|
2581
|
+
var log18 = createLogger("tool:create-issue-link");
|
|
2582
|
+
var createIssueLinkTool = {
|
|
2583
|
+
name: TOOL_NAMES.CREATE_ISSUE_LINK,
|
|
2584
|
+
description: 'Creates a link between two Jira issues. Supports common link types like "blocks", "relates", "duplicates", and "clones". Use this to establish relationships between issues.',
|
|
2585
|
+
inputSchema: {
|
|
2586
|
+
type: "object",
|
|
2587
|
+
properties: {
|
|
2588
|
+
fromIssue: {
|
|
2589
|
+
type: "string",
|
|
2590
|
+
description: "Source issue key (e.g., PROJECT-123)"
|
|
2591
|
+
},
|
|
2592
|
+
toIssue: {
|
|
2593
|
+
type: "string",
|
|
2594
|
+
description: "Target issue key (e.g., PROJECT-456)"
|
|
2595
|
+
},
|
|
2596
|
+
linkType: {
|
|
2597
|
+
type: "string",
|
|
2598
|
+
description: 'Link type: "blocks" (fromIssue blocks toIssue), "is blocked by", "relates", "duplicates", "clones", or custom link type name'
|
|
2599
|
+
}
|
|
2600
|
+
},
|
|
2601
|
+
required: ["fromIssue", "toIssue", "linkType"]
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
async function handleCreateIssueLink(input) {
|
|
2605
|
+
try {
|
|
2606
|
+
const validated = validateInput(CreateIssueLinkInputSchema, input);
|
|
2607
|
+
log18.info(
|
|
2608
|
+
`Creating ${validated.linkType} link from ${validated.fromIssue} to ${validated.toIssue}...`
|
|
2609
|
+
);
|
|
2610
|
+
await createIssueLink(validated.fromIssue, validated.toIssue, validated.linkType);
|
|
2611
|
+
log18.info("Issue link created successfully");
|
|
2612
|
+
return formatSuccessResponse(
|
|
2613
|
+
`Link created: ${validated.fromIssue} ${validated.linkType} ${validated.toIssue}`
|
|
2614
|
+
);
|
|
2615
|
+
} catch (error) {
|
|
2616
|
+
log18.error("Error in handleCreateIssueLink:", error);
|
|
2617
|
+
return handleError(error);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2535
2621
|
// src/index.ts
|
|
2536
2622
|
if (process.env.NODE_ENV !== "production") {
|
|
2537
2623
|
try {
|
|
@@ -2540,7 +2626,7 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
2540
2626
|
} catch {
|
|
2541
2627
|
}
|
|
2542
2628
|
}
|
|
2543
|
-
var
|
|
2629
|
+
var log19 = createLogger("server");
|
|
2544
2630
|
var toolHandlers = /* @__PURE__ */ new Map([
|
|
2545
2631
|
[TOOL_NAMES.GET_VISIBLE_PROJECTS, handleGetVisibleProjects],
|
|
2546
2632
|
[TOOL_NAMES.GET_ISSUE, handleGetIssue],
|
|
@@ -2556,7 +2642,8 @@ var toolHandlers = /* @__PURE__ */ new Map([
|
|
|
2556
2642
|
[TOOL_NAMES.GET_PROJECT_INFO, handleGetProjectInfo],
|
|
2557
2643
|
[TOOL_NAMES.CREATE_SUBTASK, handleCreateSubtask],
|
|
2558
2644
|
[TOOL_NAMES.GET_CREATE_META, handleGetCreateMeta],
|
|
2559
|
-
[TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields]
|
|
2645
|
+
[TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields],
|
|
2646
|
+
[TOOL_NAMES.CREATE_ISSUE_LINK, handleCreateIssueLink]
|
|
2560
2647
|
]);
|
|
2561
2648
|
var allTools = [
|
|
2562
2649
|
getVisibleProjectsTool,
|
|
@@ -2573,7 +2660,8 @@ var allTools = [
|
|
|
2573
2660
|
getProjectInfoTool,
|
|
2574
2661
|
createSubtaskTool,
|
|
2575
2662
|
getCreateMetaTool,
|
|
2576
|
-
getCustomFieldsTool
|
|
2663
|
+
getCustomFieldsTool,
|
|
2664
|
+
createIssueLinkTool
|
|
2577
2665
|
];
|
|
2578
2666
|
async function main() {
|
|
2579
2667
|
const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
|
|
@@ -2644,7 +2732,7 @@ async function main() {
|
|
|
2644
2732
|
console.error("\u{1F680} Jira MCP server running on stdio");
|
|
2645
2733
|
}
|
|
2646
2734
|
main().catch((error) => {
|
|
2647
|
-
|
|
2735
|
+
log19.error("Fatal error:", error);
|
|
2648
2736
|
process.exit(1);
|
|
2649
2737
|
});
|
|
2650
2738
|
//# sourceMappingURL=index.js.map
|