mcp-jira-stdio 1.6.1 → 1.7.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/README.md CHANGED
@@ -338,6 +338,18 @@ jira_get_visible_projects({
338
338
  - Look for errors in Claude Desktop logs
339
339
  - Use `task inspector` to debug
340
340
 
341
+ **Timeout when running multiple instances with npx**
342
+
343
+ If you're running multiple Claude Code sessions simultaneously and experience timeouts, this is caused by `npx` cache/registry locking — not the MCP server itself. Each instance tries to verify the package, causing conflicts. To fix this, install the package globally instead:
344
+
345
+ ```bash
346
+ npm install -g mcp-jira-stdio
347
+ claude mcp add jira mcp-jira-stdio \
348
+ --env JIRA_BASE_URL=... \
349
+ --env JIRA_EMAIL=... \
350
+ --env JIRA_API_TOKEN=...
351
+ ```
352
+
341
353
  ### Debug Commands
342
354
 
343
355
  ```bash
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.6.1",
443
+ version: "1.7.1",
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: ">=18.0.0"
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.6.4",
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": "^3.2.4",
502
- "@vitest/ui": "^3.2.4",
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.0.0",
508
+ tsup: "^8.5.1",
509
509
  tsx: "^4.16.0",
510
510
  typescript: "^5.5.0",
511
- vitest: "^3.2.4"
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
  }
@@ -560,7 +564,8 @@ var TOOL_NAMES = {
560
564
  CREATE_SUBTASK: "jira_create_subtask",
561
565
  GET_CREATE_META: "jira_get_create_meta",
562
566
  GET_CUSTOM_FIELDS: "jira_get_custom_fields",
563
- CREATE_ISSUE_LINK: "jira_create_issue_link"
567
+ CREATE_ISSUE_LINK: "jira_create_issue_link",
568
+ GET_COMMENTS: "jira_get_comments"
564
569
  };
565
570
 
566
571
  // src/utils/logger.ts
@@ -844,6 +849,12 @@ var CreateIssueLinkInputSchema = z.object({
844
849
  'Link type: "blocks", "is blocked by", "relates", "duplicates", "clones", or custom link type name'
845
850
  )
846
851
  });
852
+ var GetCommentsInputSchema = z.object({
853
+ issueKey: z.string().describe("Issue key to get comments for (e.g., PROJECT-123)").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
854
+ maxResults: z.number().min(1).max(100).optional().describe("Maximum number of comments to return (default: 50)"),
855
+ orderBy: z.enum(["created", "-created", "+created"]).optional().describe('Sort order for comments: "created" (oldest first), "-created" (newest first)'),
856
+ startAt: z.number().min(0).optional().describe("Index of first comment to return (for pagination)")
857
+ });
847
858
  function ensureAdfDescription(desc, format = "markdown") {
848
859
  if (!desc) return desc;
849
860
  if (typeof desc === "object") return desc;
@@ -1194,6 +1205,24 @@ async function addComment(issueKey, body, visibility, format) {
1194
1205
  };
1195
1206
  return await makeJiraRequest(config);
1196
1207
  }
1208
+ async function getComments(issueKey, options = {}) {
1209
+ const params = {};
1210
+ if (options.maxResults !== void 0) {
1211
+ params.maxResults = options.maxResults;
1212
+ }
1213
+ if (options.orderBy !== void 0) {
1214
+ params.orderBy = options.orderBy;
1215
+ }
1216
+ if (options.startAt !== void 0) {
1217
+ params.startAt = options.startAt;
1218
+ }
1219
+ const config = {
1220
+ method: "GET",
1221
+ url: `/issue/${issueKey}/comment`,
1222
+ params
1223
+ };
1224
+ return await makeJiraRequest(config);
1225
+ }
1197
1226
  async function getProjectDetails(projectKey, expand) {
1198
1227
  const params = {};
1199
1228
  if (expand) {
@@ -1627,6 +1656,44 @@ ${comment.body}`
1627
1656
  ]
1628
1657
  };
1629
1658
  }
1659
+ function formatCommentsResponse(response, issueKey) {
1660
+ if (!response.comments || response.comments.length === 0) {
1661
+ return {
1662
+ content: [
1663
+ {
1664
+ type: "text",
1665
+ text: `No comments found for issue ${issueKey}.`
1666
+ }
1667
+ ]
1668
+ };
1669
+ }
1670
+ const commentsList = response.comments.map((comment) => {
1671
+ const bodyText = typeof comment.body === "string" ? comment.body : adfToPlainText(comment.body);
1672
+ const visibilityText = comment.visibility ? ` [${comment.visibility.type}: ${comment.visibility.value}]` : "";
1673
+ const authorName = comment.author?.displayName || "Unknown";
1674
+ const createdDate = comment.created ? new Date(comment.created).toISOString() : "N/A";
1675
+ const updatedDate = comment.updated ? new Date(comment.updated).toISOString() : "N/A";
1676
+ const wasEdited = comment.created !== comment.updated;
1677
+ return `**${authorName}** - ${createdDate}${wasEdited ? ` (edited: ${updatedDate})` : ""}${visibilityText}
1678
+ ${bodyText}`;
1679
+ }).join("\n\n---\n\n");
1680
+ const total = response.total;
1681
+ const showingStart = response.startAt + 1;
1682
+ const showingEnd = Math.min(response.startAt + response.comments.length, total);
1683
+ const paginationInfo = total > response.comments.length ? `
1684
+
1685
+ *Showing ${showingStart}-${showingEnd} of ${total} comments*` : "";
1686
+ return {
1687
+ content: [
1688
+ {
1689
+ type: "text",
1690
+ text: `**Comments for ${issueKey}** (${response.comments.length} comment${response.comments.length !== 1 ? "s" : ""}):
1691
+
1692
+ ${commentsList}${paginationInfo}`
1693
+ }
1694
+ ]
1695
+ };
1696
+ }
1630
1697
  function formatProjectDetailsResponse(project) {
1631
1698
  const leadText = project.lead ? `${project.lead.displayName} (${project.lead.accountId})` : "No lead assigned";
1632
1699
  const componentsText = project.components.length > 0 ? project.components.map((c) => c.name).join(", ") : "None";
@@ -2618,6 +2685,61 @@ async function handleCreateIssueLink(input) {
2618
2685
  }
2619
2686
  }
2620
2687
 
2688
+ // src/tools/get-comments.ts
2689
+ var log19 = createLogger("tool:get-comments");
2690
+ var getCommentsTool = {
2691
+ name: TOOL_NAMES.GET_COMMENTS,
2692
+ description: "Retrieves all comments for a Jira issue. Returns comment author, content, timestamps, and visibility settings. Supports pagination for issues with many comments.",
2693
+ inputSchema: {
2694
+ type: "object",
2695
+ properties: {
2696
+ issueKey: {
2697
+ type: "string",
2698
+ description: "Issue key to get comments for (e.g., PROJECT-123)"
2699
+ },
2700
+ maxResults: {
2701
+ type: "number",
2702
+ description: "Maximum number of comments to return (default: 50, max: 100)",
2703
+ minimum: 1,
2704
+ maximum: 100
2705
+ },
2706
+ orderBy: {
2707
+ type: "string",
2708
+ enum: ["created", "-created", "+created"],
2709
+ description: 'Sort order for comments: "created" or "+created" (oldest first), "-created" (newest first)'
2710
+ },
2711
+ startAt: {
2712
+ type: "number",
2713
+ description: "Index of first comment to return (for pagination, default: 0)",
2714
+ minimum: 0
2715
+ }
2716
+ },
2717
+ required: ["issueKey"]
2718
+ }
2719
+ };
2720
+ async function handleGetComments(input) {
2721
+ try {
2722
+ const validated = validateInput(GetCommentsInputSchema, input);
2723
+ log19.info(`Getting comments for issue ${validated.issueKey}...`);
2724
+ const options = {};
2725
+ if (validated.maxResults !== void 0) {
2726
+ options.maxResults = validated.maxResults;
2727
+ }
2728
+ if (validated.orderBy !== void 0) {
2729
+ options.orderBy = validated.orderBy;
2730
+ }
2731
+ if (validated.startAt !== void 0) {
2732
+ options.startAt = validated.startAt;
2733
+ }
2734
+ const comments = await getComments(validated.issueKey, options);
2735
+ log19.info(`Retrieved ${comments.comments.length} comment(s) for ${validated.issueKey}`);
2736
+ return formatCommentsResponse(comments, validated.issueKey);
2737
+ } catch (error) {
2738
+ log19.error("Error in handleGetComments:", error);
2739
+ return handleError(error);
2740
+ }
2741
+ }
2742
+
2621
2743
  // src/index.ts
2622
2744
  if (process.env.NODE_ENV !== "production") {
2623
2745
  try {
@@ -2626,7 +2748,7 @@ if (process.env.NODE_ENV !== "production") {
2626
2748
  } catch {
2627
2749
  }
2628
2750
  }
2629
- var log19 = createLogger("server");
2751
+ var log20 = createLogger("server");
2630
2752
  var toolHandlers = /* @__PURE__ */ new Map([
2631
2753
  [TOOL_NAMES.GET_VISIBLE_PROJECTS, handleGetVisibleProjects],
2632
2754
  [TOOL_NAMES.GET_ISSUE, handleGetIssue],
@@ -2643,7 +2765,8 @@ var toolHandlers = /* @__PURE__ */ new Map([
2643
2765
  [TOOL_NAMES.CREATE_SUBTASK, handleCreateSubtask],
2644
2766
  [TOOL_NAMES.GET_CREATE_META, handleGetCreateMeta],
2645
2767
  [TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields],
2646
- [TOOL_NAMES.CREATE_ISSUE_LINK, handleCreateIssueLink]
2768
+ [TOOL_NAMES.CREATE_ISSUE_LINK, handleCreateIssueLink],
2769
+ [TOOL_NAMES.GET_COMMENTS, handleGetComments]
2647
2770
  ]);
2648
2771
  var allTools = [
2649
2772
  getVisibleProjectsTool,
@@ -2661,7 +2784,8 @@ var allTools = [
2661
2784
  createSubtaskTool,
2662
2785
  getCreateMetaTool,
2663
2786
  getCustomFieldsTool,
2664
- createIssueLinkTool
2787
+ createIssueLinkTool,
2788
+ getCommentsTool
2665
2789
  ];
2666
2790
  async function main() {
2667
2791
  const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
@@ -2732,7 +2856,7 @@ async function main() {
2732
2856
  console.error("\u{1F680} Jira MCP server running on stdio");
2733
2857
  }
2734
2858
  main().catch((error) => {
2735
- log19.error("Fatal error:", error);
2859
+ log20.error("Fatal error:", error);
2736
2860
  process.exit(1);
2737
2861
  });
2738
2862
  //# sourceMappingURL=index.js.map