mcp-jira-stdio 1.6.0 → 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.
- package/dist/index.js +115 -23
- package/dist/index.js.map +1 -1
- package/package.json +11 -7
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
40
40
|
|
|
41
41
|
// node_modules/dotenv/package.json
|
|
42
42
|
var require_package = __commonJS({
|
|
43
|
-
"node_modules/dotenv/package.json"(exports, module) {
|
|
43
|
+
"node_modules/dotenv/package.json"(exports$1, module) {
|
|
44
44
|
module.exports = {
|
|
45
45
|
name: "dotenv",
|
|
46
46
|
version: "17.2.3",
|
|
@@ -108,7 +108,7 @@ var require_package = __commonJS({
|
|
|
108
108
|
|
|
109
109
|
// node_modules/dotenv/lib/main.js
|
|
110
110
|
var require_main = __commonJS({
|
|
111
|
-
"node_modules/dotenv/lib/main.js"(exports, module) {
|
|
111
|
+
"node_modules/dotenv/lib/main.js"(exports$1, module) {
|
|
112
112
|
var fs = __require("fs");
|
|
113
113
|
var path = __require("path");
|
|
114
114
|
var os = __require("os");
|
|
@@ -437,10 +437,10 @@ var require_main = __commonJS({
|
|
|
437
437
|
|
|
438
438
|
// package.json
|
|
439
439
|
var require_package2 = __commonJS({
|
|
440
|
-
"package.json"(exports, module) {
|
|
440
|
+
"package.json"(exports$1, module) {
|
|
441
441
|
module.exports = {
|
|
442
442
|
name: "mcp-jira-stdio",
|
|
443
|
-
version: "1.
|
|
443
|
+
version: "1.7.0",
|
|
444
444
|
description: "Model Context Protocol (MCP) server for Jira API integration",
|
|
445
445
|
author: "graslt",
|
|
446
446
|
license: "MIT",
|
|
@@ -486,29 +486,29 @@ var require_package2 = __commonJS({
|
|
|
486
486
|
"ai"
|
|
487
487
|
],
|
|
488
488
|
engines: {
|
|
489
|
-
node: ">=
|
|
489
|
+
node: ">=20.0.0"
|
|
490
490
|
},
|
|
491
491
|
dependencies: {
|
|
492
492
|
"@modelcontextprotocol/sdk": "^1.17.1",
|
|
493
493
|
axios: "^1.7.0",
|
|
494
|
-
"md-to-adf": "^0.
|
|
494
|
+
"md-to-adf": "^0.5.0",
|
|
495
495
|
zod: "^3.22.4"
|
|
496
496
|
},
|
|
497
497
|
devDependencies: {
|
|
498
498
|
"@types/node": "^22.5.0",
|
|
499
499
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
500
500
|
"@typescript-eslint/parser": "^8.0.0",
|
|
501
|
-
"@vitest/coverage-v8": "^
|
|
502
|
-
"@vitest/ui": "^
|
|
501
|
+
"@vitest/coverage-v8": "^4.0.10",
|
|
502
|
+
"@vitest/ui": "^4.0.10",
|
|
503
503
|
dotenv: "^17.2.1",
|
|
504
504
|
eslint: "^9.9.0",
|
|
505
505
|
"eslint-config-prettier": "^9.1.0",
|
|
506
506
|
"eslint-plugin-prettier": "^5.2.0",
|
|
507
507
|
prettier: "^3.5.0",
|
|
508
|
-
tsup: "^8.
|
|
508
|
+
tsup: "^8.5.1",
|
|
509
509
|
tsx: "^4.16.0",
|
|
510
510
|
typescript: "^5.5.0",
|
|
511
|
-
vitest: "^
|
|
511
|
+
vitest: "^4.0.10"
|
|
512
512
|
},
|
|
513
513
|
files: [
|
|
514
514
|
"dist",
|
|
@@ -526,6 +526,10 @@ var require_package2 = __commonJS({
|
|
|
526
526
|
homepage: "https://github.com/freema/mcp-jira-stdio#readme",
|
|
527
527
|
publishConfig: {
|
|
528
528
|
access: "public"
|
|
529
|
+
},
|
|
530
|
+
overrides: {
|
|
531
|
+
glob: ">=11.0.4",
|
|
532
|
+
marked: ">=4.0.10"
|
|
529
533
|
}
|
|
530
534
|
};
|
|
531
535
|
}
|
|
@@ -559,7 +563,8 @@ var TOOL_NAMES = {
|
|
|
559
563
|
GET_PROJECT_INFO: "jira_get_project_info",
|
|
560
564
|
CREATE_SUBTASK: "jira_create_subtask",
|
|
561
565
|
GET_CREATE_META: "jira_get_create_meta",
|
|
562
|
-
GET_CUSTOM_FIELDS: "jira_get_custom_fields"
|
|
566
|
+
GET_CUSTOM_FIELDS: "jira_get_custom_fields",
|
|
567
|
+
CREATE_ISSUE_LINK: "jira_create_issue_link"
|
|
563
568
|
};
|
|
564
569
|
|
|
565
570
|
// src/utils/logger.ts
|
|
@@ -836,6 +841,13 @@ var GetCreateMetaInputSchema = z.object({
|
|
|
836
841
|
var GetCustomFieldsInputSchema = z.object({
|
|
837
842
|
projectKey: z.string().optional().describe("Project key to filter custom fields (optional)").refine((v) => v ? isValidProjectKey(v) : true, "Invalid project key format")
|
|
838
843
|
});
|
|
844
|
+
var CreateIssueLinkInputSchema = z.object({
|
|
845
|
+
fromIssue: z.string().describe("Source issue key").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
|
|
846
|
+
toIssue: z.string().describe("Target issue key").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
|
|
847
|
+
linkType: z.string().describe(
|
|
848
|
+
'Link type: "blocks", "is blocked by", "relates", "duplicates", "clones", or custom link type name'
|
|
849
|
+
)
|
|
850
|
+
});
|
|
839
851
|
function ensureAdfDescription(desc, format = "markdown") {
|
|
840
852
|
if (!desc) return desc;
|
|
841
853
|
if (typeof desc === "object") return desc;
|
|
@@ -1016,8 +1028,8 @@ async function searchIssues(options) {
|
|
|
1016
1028
|
if (options.fields) {
|
|
1017
1029
|
data.fields = options.fields;
|
|
1018
1030
|
}
|
|
1019
|
-
if (options.expand) {
|
|
1020
|
-
data.expand = options.expand;
|
|
1031
|
+
if (options.expand && options.expand.length > 0) {
|
|
1032
|
+
data.expand = options.expand.join(",");
|
|
1021
1033
|
}
|
|
1022
1034
|
const config = {
|
|
1023
1035
|
method: "POST",
|
|
@@ -1263,6 +1275,40 @@ async function getFields() {
|
|
|
1263
1275
|
};
|
|
1264
1276
|
return await makeJiraRequest(config);
|
|
1265
1277
|
}
|
|
1278
|
+
async function createIssueLink(fromIssue, toIssue, linkType) {
|
|
1279
|
+
const linkTypeMap = {
|
|
1280
|
+
blocks: "Blocks",
|
|
1281
|
+
"is blocked by": "Blocks",
|
|
1282
|
+
relates: "Relates",
|
|
1283
|
+
"relates to": "Relates",
|
|
1284
|
+
duplicates: "Duplicate",
|
|
1285
|
+
duplicate: "Duplicate",
|
|
1286
|
+
"is duplicated by": "Duplicate",
|
|
1287
|
+
clones: "Cloners",
|
|
1288
|
+
"is cloned by": "Cloners"
|
|
1289
|
+
};
|
|
1290
|
+
const normalizedLinkType = linkType.toLowerCase();
|
|
1291
|
+
const jiraLinkType = linkTypeMap[normalizedLinkType] || linkType;
|
|
1292
|
+
const isInward = normalizedLinkType === "is blocked by" || normalizedLinkType === "is duplicated by" || normalizedLinkType === "is cloned by";
|
|
1293
|
+
const data = {
|
|
1294
|
+
type: {
|
|
1295
|
+
name: jiraLinkType
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
if (isInward) {
|
|
1299
|
+
data.inwardIssue = { key: fromIssue };
|
|
1300
|
+
data.outwardIssue = { key: toIssue };
|
|
1301
|
+
} else {
|
|
1302
|
+
data.outwardIssue = { key: fromIssue };
|
|
1303
|
+
data.inwardIssue = { key: toIssue };
|
|
1304
|
+
}
|
|
1305
|
+
const config = {
|
|
1306
|
+
method: "POST",
|
|
1307
|
+
url: "/issueLink",
|
|
1308
|
+
data
|
|
1309
|
+
};
|
|
1310
|
+
await makeJiraRequest(config);
|
|
1311
|
+
}
|
|
1266
1312
|
|
|
1267
1313
|
// src/utils/redaction.ts
|
|
1268
1314
|
function isRedactionEnabled() {
|
|
@@ -1392,14 +1438,15 @@ function formatSearchResultsResponse(result) {
|
|
|
1392
1438
|
return `\u2022 **${issue.key}** - ${summary}
|
|
1393
1439
|
Status: ${status} | Assignee: ${assignee} | Priority: ${priority}`;
|
|
1394
1440
|
}).join("\n\n");
|
|
1441
|
+
const total = result.total ?? result.issues.length;
|
|
1395
1442
|
const rangeStart = result.startAt + 1;
|
|
1396
|
-
const rangeEnd = Math.min(result.startAt + result.maxResults,
|
|
1397
|
-
const showRangeInline =
|
|
1443
|
+
const rangeEnd = Math.min(result.startAt + result.maxResults, total);
|
|
1444
|
+
const showRangeInline = total > result.maxResults || result.startAt > 0;
|
|
1398
1445
|
let paginationInfo = "";
|
|
1399
1446
|
if (showRangeInline) {
|
|
1400
1447
|
paginationInfo = `
|
|
1401
1448
|
|
|
1402
|
-
*Showing ${rangeStart}-${rangeEnd} of ${
|
|
1449
|
+
*Showing ${rangeStart}-${rangeEnd} of ${total} results*`;
|
|
1403
1450
|
}
|
|
1404
1451
|
if (result.nextPageToken && !result.isLast) {
|
|
1405
1452
|
paginationInfo += `
|
|
@@ -1411,7 +1458,7 @@ function formatSearchResultsResponse(result) {
|
|
|
1411
1458
|
content: [
|
|
1412
1459
|
{
|
|
1413
1460
|
type: "text",
|
|
1414
|
-
text: `Found ${
|
|
1461
|
+
text: `Found ${total} issue(s)${showRangeInline ? ` (showing ${rangeStart}-${rangeEnd})` : ""}:
|
|
1415
1462
|
|
|
1416
1463
|
${issuesList}${paginationInfo}`
|
|
1417
1464
|
}
|
|
@@ -1888,7 +1935,7 @@ async function handleSearchIssues(input) {
|
|
|
1888
1935
|
if (validated.fields !== void 0) searchParams.fields = validated.fields;
|
|
1889
1936
|
if (validated.expand !== void 0) searchParams.expand = validated.expand;
|
|
1890
1937
|
const result = await searchIssues(searchParams);
|
|
1891
|
-
log5.info(`Found ${result.total} issue(s), showing ${result.issues.length}`);
|
|
1938
|
+
log5.info(`Found ${result.total ?? 0} issue(s), showing ${result.issues.length}`);
|
|
1892
1939
|
return formatSearchResultsResponse(result);
|
|
1893
1940
|
} catch (error) {
|
|
1894
1941
|
log5.error("Error in handleSearchIssues:", error);
|
|
@@ -1940,7 +1987,9 @@ async function handleGetMyIssues(input) {
|
|
|
1940
1987
|
if (validated.fields !== void 0) getParams.fields = validated.fields;
|
|
1941
1988
|
if (validated.expand !== void 0) getParams.expand = validated.expand;
|
|
1942
1989
|
const result = await getMyIssues(getParams);
|
|
1943
|
-
log6.info(
|
|
1990
|
+
log6.info(
|
|
1991
|
+
`Found ${result.total ?? 0} issue(s) assigned to you, showing ${result.issues.length}`
|
|
1992
|
+
);
|
|
1944
1993
|
return formatSearchResultsResponse(result);
|
|
1945
1994
|
} catch (error) {
|
|
1946
1995
|
log6.error("Error in handleGetMyIssues:", error);
|
|
@@ -2532,6 +2581,47 @@ async function handleGetCustomFields(input) {
|
|
|
2532
2581
|
}
|
|
2533
2582
|
}
|
|
2534
2583
|
|
|
2584
|
+
// src/tools/create-issue-link.ts
|
|
2585
|
+
var log18 = createLogger("tool:create-issue-link");
|
|
2586
|
+
var createIssueLinkTool = {
|
|
2587
|
+
name: TOOL_NAMES.CREATE_ISSUE_LINK,
|
|
2588
|
+
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.',
|
|
2589
|
+
inputSchema: {
|
|
2590
|
+
type: "object",
|
|
2591
|
+
properties: {
|
|
2592
|
+
fromIssue: {
|
|
2593
|
+
type: "string",
|
|
2594
|
+
description: "Source issue key (e.g., PROJECT-123)"
|
|
2595
|
+
},
|
|
2596
|
+
toIssue: {
|
|
2597
|
+
type: "string",
|
|
2598
|
+
description: "Target issue key (e.g., PROJECT-456)"
|
|
2599
|
+
},
|
|
2600
|
+
linkType: {
|
|
2601
|
+
type: "string",
|
|
2602
|
+
description: 'Link type: "blocks" (fromIssue blocks toIssue), "is blocked by", "relates", "duplicates", "clones", or custom link type name'
|
|
2603
|
+
}
|
|
2604
|
+
},
|
|
2605
|
+
required: ["fromIssue", "toIssue", "linkType"]
|
|
2606
|
+
}
|
|
2607
|
+
};
|
|
2608
|
+
async function handleCreateIssueLink(input) {
|
|
2609
|
+
try {
|
|
2610
|
+
const validated = validateInput(CreateIssueLinkInputSchema, input);
|
|
2611
|
+
log18.info(
|
|
2612
|
+
`Creating ${validated.linkType} link from ${validated.fromIssue} to ${validated.toIssue}...`
|
|
2613
|
+
);
|
|
2614
|
+
await createIssueLink(validated.fromIssue, validated.toIssue, validated.linkType);
|
|
2615
|
+
log18.info("Issue link created successfully");
|
|
2616
|
+
return formatSuccessResponse(
|
|
2617
|
+
`Link created: ${validated.fromIssue} ${validated.linkType} ${validated.toIssue}`
|
|
2618
|
+
);
|
|
2619
|
+
} catch (error) {
|
|
2620
|
+
log18.error("Error in handleCreateIssueLink:", error);
|
|
2621
|
+
return handleError(error);
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2535
2625
|
// src/index.ts
|
|
2536
2626
|
if (process.env.NODE_ENV !== "production") {
|
|
2537
2627
|
try {
|
|
@@ -2540,7 +2630,7 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
2540
2630
|
} catch {
|
|
2541
2631
|
}
|
|
2542
2632
|
}
|
|
2543
|
-
var
|
|
2633
|
+
var log19 = createLogger("server");
|
|
2544
2634
|
var toolHandlers = /* @__PURE__ */ new Map([
|
|
2545
2635
|
[TOOL_NAMES.GET_VISIBLE_PROJECTS, handleGetVisibleProjects],
|
|
2546
2636
|
[TOOL_NAMES.GET_ISSUE, handleGetIssue],
|
|
@@ -2556,7 +2646,8 @@ var toolHandlers = /* @__PURE__ */ new Map([
|
|
|
2556
2646
|
[TOOL_NAMES.GET_PROJECT_INFO, handleGetProjectInfo],
|
|
2557
2647
|
[TOOL_NAMES.CREATE_SUBTASK, handleCreateSubtask],
|
|
2558
2648
|
[TOOL_NAMES.GET_CREATE_META, handleGetCreateMeta],
|
|
2559
|
-
[TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields]
|
|
2649
|
+
[TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields],
|
|
2650
|
+
[TOOL_NAMES.CREATE_ISSUE_LINK, handleCreateIssueLink]
|
|
2560
2651
|
]);
|
|
2561
2652
|
var allTools = [
|
|
2562
2653
|
getVisibleProjectsTool,
|
|
@@ -2573,7 +2664,8 @@ var allTools = [
|
|
|
2573
2664
|
getProjectInfoTool,
|
|
2574
2665
|
createSubtaskTool,
|
|
2575
2666
|
getCreateMetaTool,
|
|
2576
|
-
getCustomFieldsTool
|
|
2667
|
+
getCustomFieldsTool,
|
|
2668
|
+
createIssueLinkTool
|
|
2577
2669
|
];
|
|
2578
2670
|
async function main() {
|
|
2579
2671
|
const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
|
|
@@ -2644,7 +2736,7 @@ async function main() {
|
|
|
2644
2736
|
console.error("\u{1F680} Jira MCP server running on stdio");
|
|
2645
2737
|
}
|
|
2646
2738
|
main().catch((error) => {
|
|
2647
|
-
|
|
2739
|
+
log19.error("Fatal error:", error);
|
|
2648
2740
|
process.exit(1);
|
|
2649
2741
|
});
|
|
2650
2742
|
//# sourceMappingURL=index.js.map
|