mcp-jira-stdio 1.9.0 → 1.10.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 CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
5
4
  import axios from 'axios';
6
5
  import { z } from 'zod';
7
6
  import FormData from 'form-data';
@@ -441,7 +440,7 @@ var require_package2 = __commonJS({
441
440
  "package.json"(exports$1, module) {
442
441
  module.exports = {
443
442
  name: "mcp-jira-stdio",
444
- version: "1.9.0",
443
+ version: "1.10.0",
445
444
  description: "Model Context Protocol (MCP) server for Jira API integration",
446
445
  author: "graslt",
447
446
  license: "MIT",
@@ -490,7 +489,7 @@ var require_package2 = __commonJS({
490
489
  node: ">=20.0.0"
491
490
  },
492
491
  dependencies: {
493
- "@modelcontextprotocol/sdk": "^1.17.1",
492
+ "@modelcontextprotocol/sdk": "^1.29.0",
494
493
  axios: "^1.7.0",
495
494
  "form-data": "^4.0.5",
496
495
  "md-to-adf": "^0.5.0",
@@ -579,7 +578,8 @@ var TOOL_NAMES = {
579
578
  GET_ATTACHMENTS: "jira_get_attachments",
580
579
  DELETE_ATTACHMENT: "jira_delete_attachment",
581
580
  GET_TRANSITIONS: "jira_get_transitions",
582
- TRANSITION_ISSUE: "jira_transition_issue"
581
+ TRANSITION_ISSUE: "jira_transition_issue",
582
+ GET_ISSUE_GRAPH: "jira_get_issue_graph"
583
583
  };
584
584
 
585
585
  // src/utils/logger.ts
@@ -1011,6 +1011,16 @@ var TransitionIssueInputSchema = z.object({
1011
1011
  }).refine((data) => data.transitionId || data.transitionName, {
1012
1012
  message: "Either transitionId or transitionName must be provided"
1013
1013
  });
1014
+ var GetIssueGraphInputSchema = z.object({
1015
+ issueKey: z.string().min(1).describe("Seed issue key to start graph traversal from (e.g., PROJECT-123)").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
1016
+ maxDepth: z.number().min(1).max(5).default(2).describe("Maximum BFS traversal depth from seed issue (default: 2, max: 5)"),
1017
+ maxNodes: z.number().min(1).max(200).default(50).describe("Maximum number of nodes to include in the graph (default: 50, max: 200)"),
1018
+ includeHierarchy: z.boolean().default(true).describe("Include parent/child/subtask edges (default: true)"),
1019
+ linkTypes: z.array(z.string()).optional().describe(
1020
+ 'Filter to specific link types (e.g., ["Blocks", "Relates"]). If omitted, includes all link types.'
1021
+ ),
1022
+ direction: z.enum(["all", "inward", "outward"]).default("all").describe('Which link directions to follow: "all", "inward", or "outward" (default: "all")')
1023
+ });
1014
1024
  function ensureAdfDescription(desc, format = "markdown") {
1015
1025
  if (!desc) return desc;
1016
1026
  if (typeof desc === "object") return desc;
@@ -2209,27 +2219,7 @@ function handleError(error) {
2209
2219
  // src/tools/get-visible-projects.ts
2210
2220
  var log3 = createLogger("tool:get-visible-projects");
2211
2221
  var getVisibleProjectsTool = {
2212
- name: TOOL_NAMES.GET_VISIBLE_PROJECTS,
2213
- description: "Retrieves all projects accessible to the authenticated user. Returns project keys, names, descriptions, and basic metadata.",
2214
- inputSchema: {
2215
- type: "object",
2216
- properties: {
2217
- expand: {
2218
- type: "array",
2219
- items: { type: "string" },
2220
- description: 'Additional project details to include (e.g., ["description", "lead", "issueTypes"])',
2221
- default: []
2222
- },
2223
- recent: {
2224
- type: "number",
2225
- description: "Limit results to recently accessed projects (max number)",
2226
- minimum: 1,
2227
- maximum: 100
2228
- }
2229
- },
2230
- required: []
2231
- }
2232
- };
2222
+ description: "Retrieves all projects accessible to the authenticated user. Returns project keys, names, descriptions, and basic metadata."};
2233
2223
  async function handleGetVisibleProjects(input) {
2234
2224
  try {
2235
2225
  const validated = validateInput(GetVisibleProjectsInputSchema, input);
@@ -2249,30 +2239,7 @@ async function handleGetVisibleProjects(input) {
2249
2239
  // src/tools/get-issue.ts
2250
2240
  var log4 = createLogger("tool:get-issue");
2251
2241
  var getIssueTool = {
2252
- name: TOOL_NAMES.GET_ISSUE,
2253
- description: 'Retrieve details for a specific Jira issue by key or URL. Use this when the user mentions an issue like "PAYWALL-943" or pastes a Jira link (e.g., https://your.atlassian.net/browse/PAYWALL-943). Returns status, assignee, priority, project, type, labels, components, timestamps, and description.',
2254
- inputSchema: {
2255
- type: "object",
2256
- properties: {
2257
- issueKey: {
2258
- type: "string",
2259
- description: "Issue key or full Jira URL (e.g., PROJECT-123 or https://your.atlassian.net/browse/PROJECT-123)"
2260
- },
2261
- expand: {
2262
- type: "array",
2263
- items: { type: "string" },
2264
- description: 'Additional issue details to include (e.g., ["comments", "attachments", "changelog"])',
2265
- default: []
2266
- },
2267
- fields: {
2268
- type: "array",
2269
- items: { type: "string" },
2270
- description: 'Specific fields to retrieve (e.g., ["summary", "status", "assignee"])'
2271
- }
2272
- },
2273
- required: ["issueKey"]
2274
- }
2275
- };
2242
+ description: 'Retrieve details for a specific Jira issue by key or URL. Use this when the user mentions an issue like "PAYWALL-943" or pastes a Jira link (e.g., https://your.atlassian.net/browse/PAYWALL-943). Returns status, assignee, priority, project, type, labels, components, timestamps, and description.'};
2276
2243
  async function handleGetIssue(input) {
2277
2244
  try {
2278
2245
  const validated = validateInput(GetIssueInputSchema, input);
@@ -2294,41 +2261,7 @@ async function handleGetIssue(input) {
2294
2261
  // src/tools/search-issues.ts
2295
2262
  var log5 = createLogger("tool:search-issues");
2296
2263
  var searchIssuesTool = {
2297
- name: TOOL_NAMES.SEARCH_ISSUES,
2298
- description: 'Search for Jira issues using JQL. Supports complex queries with pagination and field selection. Examples: "project = PROJECT AND status = Open", "assignee = currentUser()". For pagination, use nextPageToken from previous response.',
2299
- inputSchema: {
2300
- type: "object",
2301
- properties: {
2302
- jql: {
2303
- type: "string",
2304
- description: 'JQL query string (e.g., "project = PROJECT AND status = Open")'
2305
- },
2306
- nextPageToken: {
2307
- type: "string",
2308
- description: "Token for pagination. Omit for first page, use value from previous response for next page."
2309
- },
2310
- maxResults: {
2311
- type: "number",
2312
- description: "Maximum number of results to return per page",
2313
- minimum: 1,
2314
- maximum: 100,
2315
- default: 50
2316
- },
2317
- fields: {
2318
- type: "array",
2319
- items: { type: "string" },
2320
- description: "Specific fields to retrieve for each issue"
2321
- },
2322
- expand: {
2323
- type: "array",
2324
- items: { type: "string" },
2325
- description: "Additional details to include for each issue",
2326
- default: []
2327
- }
2328
- },
2329
- required: ["jql"]
2330
- }
2331
- };
2264
+ description: 'Search for Jira issues using JQL. Supports complex queries with pagination and field selection. Examples: "project = PROJECT AND status = Open", "assignee = currentUser()". For pagination, use nextPageToken from previous response.'};
2332
2265
  async function handleSearchIssues(input) {
2333
2266
  try {
2334
2267
  const validated = validateInput(SearchIssuesInputSchema, input);
@@ -2352,37 +2285,7 @@ async function handleSearchIssues(input) {
2352
2285
  // src/tools/get-my-issues.ts
2353
2286
  var log6 = createLogger("tool:get-my-issues");
2354
2287
  var getMyIssuesTool = {
2355
- name: TOOL_NAMES.GET_MY_ISSUES,
2356
- description: "Retrieves issues assigned to current user, sorted by most recently updated first. Supports pagination and field selection. For pagination, use nextPageToken from previous response.",
2357
- inputSchema: {
2358
- type: "object",
2359
- properties: {
2360
- nextPageToken: {
2361
- type: "string",
2362
- description: "Token for pagination. Omit for first page, use value from previous response for next page."
2363
- },
2364
- maxResults: {
2365
- type: "number",
2366
- description: "Maximum number of results to return per page",
2367
- minimum: 1,
2368
- maximum: 100,
2369
- default: 50
2370
- },
2371
- fields: {
2372
- type: "array",
2373
- items: { type: "string" },
2374
- description: "Specific fields to retrieve for each issue"
2375
- },
2376
- expand: {
2377
- type: "array",
2378
- items: { type: "string" },
2379
- description: "Additional details to include for each issue",
2380
- default: []
2381
- }
2382
- },
2383
- required: []
2384
- }
2385
- };
2288
+ description: "Retrieves issues assigned to current user, sorted by most recently updated first. Supports pagination and field selection. For pagination, use nextPageToken from previous response."};
2386
2289
  async function handleGetMyIssues(input) {
2387
2290
  try {
2388
2291
  const validated = validateInput(GetMyIssuesInputSchema, input);
@@ -2406,19 +2309,7 @@ async function handleGetMyIssues(input) {
2406
2309
  // src/tools/get-issue-types.ts
2407
2310
  var log7 = createLogger("tool:get-issue-types");
2408
2311
  var getIssueTypesTool = {
2409
- name: TOOL_NAMES.GET_ISSUE_TYPES,
2410
- description: "Retrieves available issue types. Can get global issue types or project-specific issue types including regular issues and subtasks (Bug, Story, Task, Epic, etc.).",
2411
- inputSchema: {
2412
- type: "object",
2413
- properties: {
2414
- projectKey: {
2415
- type: "string",
2416
- description: "Project key to get issue types for specific project (optional - if not provided, returns global issue types)"
2417
- }
2418
- },
2419
- required: []
2420
- }
2421
- };
2312
+ description: "Retrieves available issue types. Can get global issue types or project-specific issue types including regular issues and subtasks (Bug, Story, Task, Epic, etc.)."};
2422
2313
  async function handleGetIssueTypes(input) {
2423
2314
  try {
2424
2315
  const validated = validateInput(GetIssueTypesInputSchema, input);
@@ -2439,40 +2330,7 @@ async function handleGetIssueTypes(input) {
2439
2330
  // src/tools/get-users.ts
2440
2331
  var log8 = createLogger("tool:get-users");
2441
2332
  var getUsersTool = {
2442
- name: TOOL_NAMES.GET_USERS,
2443
- description: "Search for users by name, email, username, or account ID. Returns display name, email, account status, and account type. Supports pagination.",
2444
- inputSchema: {
2445
- type: "object",
2446
- properties: {
2447
- query: {
2448
- type: "string",
2449
- description: "Search query for user name or email (partial matches supported)"
2450
- },
2451
- username: {
2452
- type: "string",
2453
- description: "Specific username to search for"
2454
- },
2455
- accountId: {
2456
- type: "string",
2457
- description: "Specific account ID to search for"
2458
- },
2459
- startAt: {
2460
- type: "number",
2461
- description: "Index of first result to return (for pagination)",
2462
- minimum: 0,
2463
- default: 0
2464
- },
2465
- maxResults: {
2466
- type: "number",
2467
- description: "Maximum number of results to return",
2468
- minimum: 1,
2469
- maximum: 50,
2470
- default: 50
2471
- }
2472
- },
2473
- required: []
2474
- }
2475
- };
2333
+ description: "Search for users by name, email, username, or account ID. Returns display name, email, account status, and account type. Supports pagination."};
2476
2334
  async function handleGetUsers(input) {
2477
2335
  try {
2478
2336
  const validated = validateInput(GetUsersInputSchema, input);
@@ -2503,14 +2361,7 @@ async function handleGetUsers(input) {
2503
2361
  // src/tools/get-priorities.ts
2504
2362
  var log9 = createLogger("tool:get-priorities");
2505
2363
  var getPrioritiesTool = {
2506
- name: TOOL_NAMES.GET_PRIORITIES,
2507
- description: "Retrieves available priorities (e.g., Highest, High, Medium, Low, Lowest). Returns IDs, names, and descriptions.",
2508
- inputSchema: {
2509
- type: "object",
2510
- properties: {},
2511
- required: []
2512
- }
2513
- };
2364
+ description: "Retrieves available priorities (e.g., Highest, High, Medium, Low, Lowest). Returns IDs, names, and descriptions."};
2514
2365
  async function handleGetPriorities(input) {
2515
2366
  try {
2516
2367
  validateInput(GetPrioritiesInputSchema, input);
@@ -2527,23 +2378,7 @@ async function handleGetPriorities(input) {
2527
2378
  // src/tools/get-statuses.ts
2528
2379
  var log10 = createLogger("tool:get-statuses");
2529
2380
  var getStatusesTool = {
2530
- name: TOOL_NAMES.GET_STATUSES,
2531
- description: "Retrieves available statuses (global or project-specific, e.g., To Do, In Progress, Done). Returns status categories and workflow information.",
2532
- inputSchema: {
2533
- type: "object",
2534
- properties: {
2535
- projectKey: {
2536
- type: "string",
2537
- description: "Project key to get statuses for specific project (optional)"
2538
- },
2539
- issueTypeId: {
2540
- type: "string",
2541
- description: "Issue type ID to get statuses for specific issue type (requires projectKey)"
2542
- }
2543
- },
2544
- required: []
2545
- }
2546
- };
2381
+ description: "Retrieves available statuses (global or project-specific, e.g., To Do, In Progress, Done). Returns status categories and workflow information."};
2547
2382
  async function handleGetStatuses(input) {
2548
2383
  try {
2549
2384
  const validated = validateInput(GetStatusesInputSchema, input);
@@ -2571,68 +2406,7 @@ async function handleGetStatuses(input) {
2571
2406
  // src/tools/create-issue.ts
2572
2407
  var log11 = createLogger("tool:create-issue");
2573
2408
  var createIssueTool = {
2574
- name: TOOL_NAMES.CREATE_ISSUE,
2575
- description: 'Creates a new Jira issue in the specified project. Supports setting issue type, priority, assignee, labels, components, and custom fields. Description format is controlled by the "format" parameter (default: markdown). For required custom fields, supply them via customFields (e.g., { "customfield_12345": { id: "..." } }). Returns the created issue with all details.',
2576
- inputSchema: {
2577
- type: "object",
2578
- properties: {
2579
- projectKey: {
2580
- type: "string",
2581
- description: "Project key where the issue will be created"
2582
- },
2583
- summary: {
2584
- type: "string",
2585
- description: "Issue summary/title",
2586
- minLength: 1
2587
- },
2588
- description: {
2589
- anyOf: [{ type: "string" }, { type: "object" }],
2590
- description: 'Detailed issue description (optional). Format depends on the "format" parameter.'
2591
- },
2592
- issueType: {
2593
- type: "string",
2594
- description: "Issue type name (e.g., Bug, Story, Task, Epic)"
2595
- },
2596
- priority: {
2597
- type: "string",
2598
- description: "Issue priority name (e.g., High, Medium, Low) - optional"
2599
- },
2600
- assignee: {
2601
- type: "string",
2602
- description: "Assignee account ID (optional)"
2603
- },
2604
- labels: {
2605
- type: "array",
2606
- items: { type: "string" },
2607
- description: "Issue labels (optional)",
2608
- default: []
2609
- },
2610
- components: {
2611
- type: "array",
2612
- items: { type: "string" },
2613
- description: "Component names (optional)",
2614
- default: []
2615
- },
2616
- customFields: {
2617
- type: "object",
2618
- additionalProperties: true,
2619
- description: 'Additional Jira fields, e.g. { "customfield_10071": value }. Use this to set required custom fields.'
2620
- },
2621
- returnIssue: {
2622
- type: "boolean",
2623
- description: "If false, returns only the issue key without fetching full details",
2624
- default: true
2625
- },
2626
- format: {
2627
- type: "string",
2628
- enum: ["markdown", "adf", "plain"],
2629
- description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
2630
- default: "markdown"
2631
- }
2632
- },
2633
- required: ["projectKey", "summary", "issueType"]
2634
- }
2635
- };
2409
+ description: 'Creates a new Jira issue in the specified project. Supports setting issue type, priority, assignee, labels, components, and custom fields. Description format is controlled by the "format" parameter (default: markdown). For required custom fields, supply them via customFields (e.g., { "customfield_12345": { id: "..." } }). Returns the created issue with all details.'};
2636
2410
  async function handleCreateIssue(input) {
2637
2411
  try {
2638
2412
  const validated = validateInput(CreateIssueInputSchema, input);
@@ -2672,60 +2446,7 @@ async function handleCreateIssue(input) {
2672
2446
  // src/tools/update-issue.ts
2673
2447
  var log12 = createLogger("tool:update-issue");
2674
2448
  var updateIssueTool = {
2675
- name: TOOL_NAMES.UPDATE_ISSUE,
2676
- description: 'Updates an existing Jira issue by its key. Supports updating summary, description, priority, assignee, labels, and components. Description format is controlled by the "format" parameter (default: markdown). Only specified fields will be updated.',
2677
- inputSchema: {
2678
- type: "object",
2679
- properties: {
2680
- issueKey: {
2681
- type: "string",
2682
- description: "Issue key to update (e.g., PROJECT-123)"
2683
- },
2684
- summary: {
2685
- type: "string",
2686
- description: "New issue summary/title (optional)"
2687
- },
2688
- description: {
2689
- anyOf: [{ type: "string" }, { type: "object" }],
2690
- description: 'New issue description (optional). Format depends on the "format" parameter.'
2691
- },
2692
- priority: {
2693
- type: "string",
2694
- description: "New priority name (e.g., High, Medium, Low) - optional"
2695
- },
2696
- assignee: {
2697
- type: "string",
2698
- description: "New assignee account ID (optional, use null string to unassign)"
2699
- },
2700
- labels: {
2701
- type: "array",
2702
- items: { type: "string" },
2703
- description: "New labels array (replaces existing labels) - optional"
2704
- },
2705
- components: {
2706
- type: "array",
2707
- items: { type: "string" },
2708
- description: "New components array (replaces existing components) - optional"
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
- },
2714
- returnIssue: {
2715
- type: "boolean",
2716
- description: "If false, returns a success message without fetching the updated issue",
2717
- default: true
2718
- },
2719
- format: {
2720
- type: "string",
2721
- enum: ["markdown", "adf", "plain"],
2722
- description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
2723
- default: "markdown"
2724
- }
2725
- },
2726
- required: ["issueKey"]
2727
- }
2728
- };
2449
+ description: 'Updates an existing Jira issue by its key. Supports updating summary, description, priority, assignee, labels, and components. Description format is controlled by the "format" parameter (default: markdown). Only specified fields will be updated.'};
2729
2450
  async function handleUpdateIssue(input) {
2730
2451
  try {
2731
2452
  const validated = validateInput(UpdateIssueInputSchema, input);
@@ -2756,46 +2477,7 @@ async function handleUpdateIssue(input) {
2756
2477
  // src/tools/add-comment.ts
2757
2478
  var log13 = createLogger("tool:add-comment");
2758
2479
  var addCommentTool = {
2759
- name: TOOL_NAMES.ADD_COMMENT,
2760
- description: 'Adds a comment to an issue. Supports visibility restrictions for groups or roles. Comment format is controlled by the "format" parameter (default: markdown). Returns the created comment with author details and timestamp.',
2761
- inputSchema: {
2762
- type: "object",
2763
- properties: {
2764
- issueKey: {
2765
- type: "string",
2766
- description: "Issue key to add comment to (e.g., PROJECT-123)"
2767
- },
2768
- body: {
2769
- type: "string",
2770
- description: 'Comment body text. Format depends on the "format" parameter.',
2771
- minLength: 1
2772
- },
2773
- visibility: {
2774
- type: "object",
2775
- description: "Comment visibility restrictions (optional)",
2776
- properties: {
2777
- type: {
2778
- type: "string",
2779
- enum: ["group", "role"],
2780
- description: 'Visibility type - either "group" or "role"'
2781
- },
2782
- value: {
2783
- type: "string",
2784
- description: "Group name or role name for visibility restriction"
2785
- }
2786
- },
2787
- required: ["type", "value"]
2788
- },
2789
- format: {
2790
- type: "string",
2791
- enum: ["markdown", "adf", "plain"],
2792
- description: 'Comment format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
2793
- default: "markdown"
2794
- }
2795
- },
2796
- required: ["issueKey", "body"]
2797
- }
2798
- };
2480
+ description: 'Adds a comment to an issue. Supports visibility restrictions for groups or roles. Comment format is controlled by the "format" parameter (default: markdown). Returns the created comment with author details and timestamp.'};
2799
2481
  async function handleAddComment(input) {
2800
2482
  try {
2801
2483
  const validated = validateInput(AddCommentInputSchema, input);
@@ -2817,25 +2499,7 @@ async function handleAddComment(input) {
2817
2499
  // src/tools/get-project-info.ts
2818
2500
  var log14 = createLogger("tool:get-project-info");
2819
2501
  var getProjectInfoTool = {
2820
- name: TOOL_NAMES.GET_PROJECT_INFO,
2821
- description: "Retrieves detailed information about a project (components, versions, issue types, roles, insights). More comprehensive than the basic project list.",
2822
- inputSchema: {
2823
- type: "object",
2824
- properties: {
2825
- projectKey: {
2826
- type: "string",
2827
- description: "Project key to get detailed information for"
2828
- },
2829
- expand: {
2830
- type: "array",
2831
- items: { type: "string" },
2832
- description: 'Additional project details to include (e.g., ["description", "lead", "issueTypes", "versions"])',
2833
- default: []
2834
- }
2835
- },
2836
- required: ["projectKey"]
2837
- }
2838
- };
2502
+ description: "Retrieves detailed information about a project (components, versions, issue types, roles, insights). More comprehensive than the basic project list."};
2839
2503
  async function handleGetProjectInfo(input) {
2840
2504
  try {
2841
2505
  const validated = validateInput(GetProjectInfoInputSchema, input);
@@ -2852,54 +2516,7 @@ async function handleGetProjectInfo(input) {
2852
2516
  // src/tools/create-subtask.ts
2853
2517
  var log15 = createLogger("tool:create-subtask");
2854
2518
  var createSubtaskTool = {
2855
- name: TOOL_NAMES.CREATE_SUBTASK,
2856
- description: 'Creates a subtask under an existing parent issue. Automatically determines the correct project and subtask issue type. Supports setting priority, assignee, labels, and components. Description format is controlled by the "format" parameter (default: markdown).',
2857
- inputSchema: {
2858
- type: "object",
2859
- properties: {
2860
- parentIssueKey: {
2861
- type: "string",
2862
- description: "Parent issue key (e.g., PROJECT-123)"
2863
- },
2864
- summary: {
2865
- type: "string",
2866
- description: "Subtask summary/title",
2867
- minLength: 1
2868
- },
2869
- description: {
2870
- type: "string",
2871
- description: 'Detailed subtask description (optional). Format depends on the "format" parameter.'
2872
- },
2873
- priority: {
2874
- type: "string",
2875
- description: "Subtask priority name (e.g., High, Medium, Low) - optional"
2876
- },
2877
- assignee: {
2878
- type: "string",
2879
- description: "Assignee account ID (optional)"
2880
- },
2881
- labels: {
2882
- type: "array",
2883
- items: { type: "string" },
2884
- description: "Subtask labels (optional)",
2885
- default: []
2886
- },
2887
- components: {
2888
- type: "array",
2889
- items: { type: "string" },
2890
- description: "Component names (optional)",
2891
- default: []
2892
- },
2893
- format: {
2894
- type: "string",
2895
- enum: ["markdown", "adf", "plain"],
2896
- description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
2897
- default: "markdown"
2898
- }
2899
- },
2900
- required: ["parentIssueKey", "summary"]
2901
- }
2902
- };
2519
+ description: 'Creates a subtask under an existing parent issue. Automatically determines the correct project and subtask issue type. Supports setting priority, assignee, labels, and components. Description format is controlled by the "format" parameter (default: markdown).'};
2903
2520
  async function handleCreateSubtask(input) {
2904
2521
  try {
2905
2522
  const validated = validateInput(CreateSubtaskInputSchema, input);
@@ -2925,23 +2542,7 @@ async function handleCreateSubtask(input) {
2925
2542
  // src/tools/get-create-meta.ts
2926
2543
  var log16 = createLogger("tool:get-create-meta");
2927
2544
  var getCreateMetaTool = {
2928
- name: TOOL_NAMES.GET_CREATE_META,
2929
- description: "Retrieves create metadata for a project, showing all available fields (including custom fields) for creating issues. Shows required vs optional fields, field types, and allowed values. Use this before creating issues to discover what fields are needed.",
2930
- inputSchema: {
2931
- type: "object",
2932
- properties: {
2933
- projectKey: {
2934
- type: "string",
2935
- description: "Project key to get create metadata for (e.g., PROJECT)"
2936
- },
2937
- issueTypeName: {
2938
- type: "string",
2939
- description: "Specific issue type name to get metadata for (optional)"
2940
- }
2941
- },
2942
- required: ["projectKey"]
2943
- }
2944
- };
2545
+ description: "Retrieves create metadata for a project, showing all available fields (including custom fields) for creating issues. Shows required vs optional fields, field types, and allowed values. Use this before creating issues to discover what fields are needed."};
2945
2546
  async function handleGetCreateMeta(input) {
2946
2547
  try {
2947
2548
  const validated = validateInput(GetCreateMetaInputSchema, input);
@@ -2965,19 +2566,7 @@ async function handleGetCreateMeta(input) {
2965
2566
  // src/tools/get-custom-fields.ts
2966
2567
  var log17 = createLogger("tool:get-custom-fields");
2967
2568
  var getCustomFieldsTool = {
2968
- name: TOOL_NAMES.GET_CUSTOM_FIELDS,
2969
- description: "Retrieves all custom fields available in Jira. Shows custom field names, IDs (e.g., customfield_10071), and types. Useful for discovering what custom fields exist and their identifiers.",
2970
- inputSchema: {
2971
- type: "object",
2972
- properties: {
2973
- projectKey: {
2974
- type: "string",
2975
- description: "Project key to filter custom fields (optional)"
2976
- }
2977
- },
2978
- required: []
2979
- }
2980
- };
2569
+ description: "Retrieves all custom fields available in Jira. Shows custom field names, IDs (e.g., customfield_10071), and types. Useful for discovering what custom fields exist and their identifiers."};
2981
2570
  async function handleGetCustomFields(input) {
2982
2571
  try {
2983
2572
  const validated = validateInput(GetCustomFieldsInputSchema, input);
@@ -2995,27 +2584,7 @@ async function handleGetCustomFields(input) {
2995
2584
  // src/tools/create-issue-link.ts
2996
2585
  var log18 = createLogger("tool:create-issue-link");
2997
2586
  var createIssueLinkTool = {
2998
- name: TOOL_NAMES.CREATE_ISSUE_LINK,
2999
- 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.',
3000
- inputSchema: {
3001
- type: "object",
3002
- properties: {
3003
- fromIssue: {
3004
- type: "string",
3005
- description: "Source issue key (e.g., PROJECT-123)"
3006
- },
3007
- toIssue: {
3008
- type: "string",
3009
- description: "Target issue key (e.g., PROJECT-456)"
3010
- },
3011
- linkType: {
3012
- type: "string",
3013
- description: 'Link type: "blocks" (fromIssue blocks toIssue), "is blocked by", "relates", "duplicates", "clones", or custom link type name'
3014
- }
3015
- },
3016
- required: ["fromIssue", "toIssue", "linkType"]
3017
- }
3018
- };
2587
+ 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.'};
3019
2588
  async function handleCreateIssueLink(input) {
3020
2589
  try {
3021
2590
  const validated = validateInput(CreateIssueLinkInputSchema, input);
@@ -3036,35 +2605,7 @@ async function handleCreateIssueLink(input) {
3036
2605
  // src/tools/get-comments.ts
3037
2606
  var log19 = createLogger("tool:get-comments");
3038
2607
  var getCommentsTool = {
3039
- name: TOOL_NAMES.GET_COMMENTS,
3040
- description: "Retrieves all comments for a Jira issue. Returns comment author, content, timestamps, and visibility settings. Supports pagination for issues with many comments.",
3041
- inputSchema: {
3042
- type: "object",
3043
- properties: {
3044
- issueKey: {
3045
- type: "string",
3046
- description: "Issue key to get comments for (e.g., PROJECT-123)"
3047
- },
3048
- maxResults: {
3049
- type: "number",
3050
- description: "Maximum number of comments to return (default: 50, max: 100)",
3051
- minimum: 1,
3052
- maximum: 100
3053
- },
3054
- orderBy: {
3055
- type: "string",
3056
- enum: ["created", "-created", "+created"],
3057
- description: 'Sort order for comments: "created" or "+created" (oldest first), "-created" (newest first)'
3058
- },
3059
- startAt: {
3060
- type: "number",
3061
- description: "Index of first comment to return (for pagination, default: 0)",
3062
- minimum: 0
3063
- }
3064
- },
3065
- required: ["issueKey"]
3066
- }
3067
- };
2608
+ description: "Retrieves all comments for a Jira issue. Returns comment author, content, timestamps, and visibility settings. Supports pagination for issues with many comments."};
3068
2609
  async function handleGetComments(input) {
3069
2610
  try {
3070
2611
  const validated = validateInput(GetCommentsInputSchema, input);
@@ -3091,7 +2632,6 @@ async function handleGetComments(input) {
3091
2632
  // src/tools/add-attachment.ts
3092
2633
  var log20 = createLogger("tool:add-attachment");
3093
2634
  var addAttachmentTool = {
3094
- name: TOOL_NAMES.ADD_ATTACHMENT,
3095
2635
  description: `Uploads an attachment (image, document, etc.) to a Jira issue.
3096
2636
 
3097
2637
  **EFFICIENT METHOD (recommended for large files):**
@@ -3102,37 +2642,7 @@ var addAttachmentTool = {
3102
2642
  - content: Base64 encoded content - WARNING: HIGH token cost (~330,000 tokens for 1MB file)
3103
2643
  Only suitable for small files (< 500 KB)
3104
2644
 
3105
- Returns attachment metadata including ID and download URL. To reference the image in a comment or description, use wiki markup: !filename.png! or !filename.png|thumbnail!`,
3106
- inputSchema: {
3107
- type: "object",
3108
- properties: {
3109
- issueKey: {
3110
- type: "string",
3111
- description: "Issue key to add attachment to (e.g., PROJECT-123)"
3112
- },
3113
- filename: {
3114
- type: "string",
3115
- description: "Name of the file to attach (e.g., screenshot.png, document.pdf)",
3116
- minLength: 1
3117
- },
3118
- fileUrl: {
3119
- type: "string",
3120
- description: "URL to download file from (efficient for remote files, ~60 tokens). Upload large files to Dropbox/S3/imgur first, then use URL.",
3121
- format: "uri"
3122
- },
3123
- content: {
3124
- type: "string",
3125
- description: "Base64-encoded or plain text content. WARNING: HIGH token cost (~330k tokens for 1MB file). Use fileUrl for large files or upload to cloud storage first."
3126
- },
3127
- isBase64: {
3128
- type: "boolean",
3129
- description: "Whether content is base64-encoded (default: true). Set to false for plain text files.",
3130
- default: true
3131
- }
3132
- },
3133
- required: ["issueKey", "filename"]
3134
- }
3135
- };
2645
+ Returns attachment metadata including ID and download URL. To reference the image in a comment or description, use wiki markup: !filename.png! or !filename.png|thumbnail!`};
3136
2646
  async function handleAddAttachment(input) {
3137
2647
  try {
3138
2648
  const validated = validateInput(AddAttachmentInputSchema, input);
@@ -3167,19 +2677,7 @@ async function handleAddAttachment(input) {
3167
2677
  // src/tools/get-attachments.ts
3168
2678
  var log21 = createLogger("tool:get-attachments");
3169
2679
  var getAttachmentsTool = {
3170
- name: TOOL_NAMES.GET_ATTACHMENTS,
3171
- description: "Lists all attachments on a Jira issue. Returns attachment metadata including filename, size, MIME type, author, and download URL.",
3172
- inputSchema: {
3173
- type: "object",
3174
- properties: {
3175
- issueKey: {
3176
- type: "string",
3177
- description: "Issue key to get attachments for (e.g., PROJECT-123)"
3178
- }
3179
- },
3180
- required: ["issueKey"]
3181
- }
3182
- };
2680
+ description: "Lists all attachments on a Jira issue. Returns attachment metadata including filename, size, MIME type, author, and download URL."};
3183
2681
  async function handleGetAttachments(input) {
3184
2682
  try {
3185
2683
  const validated = validateInput(GetAttachmentsInputSchema, input);
@@ -3196,20 +2694,7 @@ async function handleGetAttachments(input) {
3196
2694
  // src/tools/delete-attachment.ts
3197
2695
  var log22 = createLogger("tool:delete-attachment");
3198
2696
  var deleteAttachmentTool = {
3199
- name: TOOL_NAMES.DELETE_ATTACHMENT,
3200
- description: "Deletes an attachment from Jira by its attachment ID. Use jira_get_attachments to find attachment IDs.",
3201
- inputSchema: {
3202
- type: "object",
3203
- properties: {
3204
- attachmentId: {
3205
- type: "string",
3206
- description: "ID of the attachment to delete (can be found using jira_get_attachments)",
3207
- minLength: 1
3208
- }
3209
- },
3210
- required: ["attachmentId"]
3211
- }
3212
- };
2697
+ description: "Deletes an attachment from Jira by its attachment ID. Use jira_get_attachments to find attachment IDs."};
3213
2698
  async function handleDeleteAttachment(input) {
3214
2699
  try {
3215
2700
  const validated = validateInput(DeleteAttachmentInputSchema, input);
@@ -3233,19 +2718,7 @@ async function handleDeleteAttachment(input) {
3233
2718
  // src/tools/get-transitions.ts
3234
2719
  var log23 = createLogger("tool:get-transitions");
3235
2720
  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
- };
2721
+ 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."};
3249
2722
  async function handleGetTransitions(input) {
3250
2723
  try {
3251
2724
  const validated = validateInput(GetTransitionsInputSchema, input);
@@ -3262,41 +2735,7 @@ async function handleGetTransitions(input) {
3262
2735
  // src/tools/transition-issue.ts
3263
2736
  var log24 = createLogger("tool:transition-issue");
3264
2737
  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
- };
2738
+ 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.'};
3300
2739
  async function handleTransitionIssue(input) {
3301
2740
  try {
3302
2741
  const validated = validateInput(TransitionIssueInputSchema, input);
@@ -3339,6 +2778,259 @@ Available transitions: ${available || "none"}`
3339
2778
  }
3340
2779
  }
3341
2780
 
2781
+ // src/tools/get-issue-graph.ts
2782
+ var log25 = createLogger("tool:get-issue-graph");
2783
+ var GRAPH_FIELDS = [
2784
+ "summary",
2785
+ "status",
2786
+ "issuetype",
2787
+ "priority",
2788
+ "parent",
2789
+ "subtasks",
2790
+ "issuelinks"
2791
+ ];
2792
+ var getIssueGraphTool = {
2793
+ description: "Build a dependency/relationship graph starting from a seed issue. Returns a map of connected issues (parent/child hierarchy, blocks, relates to, duplicates, etc.) with nodes and edges, plus a Mermaid diagram for visualization. Use this to understand how issues are connected across epics, stories, and subtasks."};
2794
+ function extractIssueData(issue) {
2795
+ const fields = issue.fields;
2796
+ return {
2797
+ key: issue.key,
2798
+ summary: fields?.summary || "No summary",
2799
+ status: fields?.status?.name || "Unknown",
2800
+ statusCategory: fields?.status?.statusCategory?.name || "Unknown",
2801
+ issueType: fields?.issuetype?.name || "Unknown",
2802
+ priority: fields?.priority?.name || "None",
2803
+ issuelinks: fields?.issuelinks || [],
2804
+ parent: fields?.parent ? {
2805
+ key: fields.parent.key,
2806
+ summary: fields.parent.fields?.summary,
2807
+ status: fields.parent.fields?.status?.name,
2808
+ issueType: fields.parent.fields?.issuetype?.name
2809
+ } : void 0,
2810
+ subtasks: fields?.subtasks || []
2811
+ };
2812
+ }
2813
+ function buildGraph(issueDataMap, rootKey, options) {
2814
+ const nodes = [];
2815
+ const edges = [];
2816
+ const edgeSet = /* @__PURE__ */ new Set();
2817
+ const addEdge = (edge) => {
2818
+ const edgeId = `${edge.from}->${edge.to}:${edge.type}:${edge.category}`;
2819
+ if (!edgeSet.has(edgeId)) {
2820
+ edgeSet.add(edgeId);
2821
+ edges.push(edge);
2822
+ }
2823
+ };
2824
+ for (const [, data] of issueDataMap) {
2825
+ nodes.push({
2826
+ key: data.key,
2827
+ summary: data.summary,
2828
+ status: data.status,
2829
+ statusCategory: data.statusCategory,
2830
+ issueType: data.issueType,
2831
+ priority: data.priority
2832
+ });
2833
+ if (options.includeHierarchy) {
2834
+ if (data.parent && issueDataMap.has(data.parent.key)) {
2835
+ addEdge({
2836
+ from: data.parent.key,
2837
+ to: data.key,
2838
+ type: "parent of",
2839
+ label: "parent of",
2840
+ category: "hierarchy"
2841
+ });
2842
+ }
2843
+ for (const sub of data.subtasks) {
2844
+ if (issueDataMap.has(sub.key)) {
2845
+ addEdge({
2846
+ from: data.key,
2847
+ to: sub.key,
2848
+ type: "parent of",
2849
+ label: "parent of",
2850
+ category: "hierarchy"
2851
+ });
2852
+ }
2853
+ }
2854
+ }
2855
+ for (const link of data.issuelinks) {
2856
+ const typeName = link.type?.name || "Unknown";
2857
+ if (options.linkTypes && options.linkTypes.length > 0) {
2858
+ if (!options.linkTypes.some((lt) => lt.toLowerCase() === typeName.toLowerCase())) {
2859
+ continue;
2860
+ }
2861
+ }
2862
+ if (link.outwardIssue && (options.direction === "all" || options.direction === "outward")) {
2863
+ if (issueDataMap.has(link.outwardIssue.key)) {
2864
+ addEdge({
2865
+ from: data.key,
2866
+ to: link.outwardIssue.key,
2867
+ type: typeName,
2868
+ label: link.type?.outward || typeName,
2869
+ category: "link"
2870
+ });
2871
+ }
2872
+ }
2873
+ if (link.inwardIssue && (options.direction === "all" || options.direction === "inward")) {
2874
+ if (issueDataMap.has(link.inwardIssue.key)) {
2875
+ addEdge({
2876
+ from: link.inwardIssue.key,
2877
+ to: data.key,
2878
+ type: typeName,
2879
+ label: link.type?.outward || typeName,
2880
+ category: "link"
2881
+ });
2882
+ }
2883
+ }
2884
+ }
2885
+ }
2886
+ return {
2887
+ rootIssue: rootKey,
2888
+ nodes,
2889
+ edges,
2890
+ metadata: {
2891
+ totalNodes: nodes.length,
2892
+ totalEdges: edges.length,
2893
+ maxDepthReached: false,
2894
+ // set by caller
2895
+ traversalDepth: 0
2896
+ // set by caller
2897
+ }
2898
+ };
2899
+ }
2900
+ function formatGraphResponse(graph) {
2901
+ const { nodes, edges, metadata, rootIssue } = graph;
2902
+ const nodesTable = nodes.map((n) => `| ${n.key} | ${n.summary} | ${n.status} | ${n.issueType} | ${n.priority} |`).join("\n");
2903
+ const edgesTable = edges.map((e) => `| ${e.from} | ${e.label} | ${e.to} | ${e.category} |`).join("\n");
2904
+ const mermaidNodes = nodes.map((n) => {
2905
+ const shape = n.statusCategory === "Done" ? `${n.key}[/"${n.issueType}: ${escapeMermaid(n.summary)}"/]` : n.statusCategory === "In Progress" ? `${n.key}[["${n.issueType}: ${escapeMermaid(n.summary)}"]]` : `${n.key}["${n.issueType}: ${escapeMermaid(n.summary)}"]`;
2906
+ return ` ${shape}`;
2907
+ }).join("\n");
2908
+ const mermaidEdges = edges.map((e) => {
2909
+ if (e.category === "hierarchy") {
2910
+ return ` ${e.from} --> ${e.to}`;
2911
+ }
2912
+ return ` ${e.from} -->|${escapeMermaid(e.label)}| ${e.to}`;
2913
+ }).join("\n");
2914
+ const doneNodes = nodes.filter((n) => n.statusCategory === "Done").map((n) => n.key);
2915
+ const inProgressNodes = nodes.filter((n) => n.statusCategory === "In Progress").map((n) => n.key);
2916
+ const todoNodes = nodes.filter((n) => n.statusCategory !== "Done" && n.statusCategory !== "In Progress").map((n) => n.key);
2917
+ let mermaidStyles = "";
2918
+ if (doneNodes.length > 0) mermaidStyles += `
2919
+ classDef done fill:#d4edda,stroke:#28a745`;
2920
+ if (inProgressNodes.length > 0)
2921
+ mermaidStyles += `
2922
+ classDef inprogress fill:#fff3cd,stroke:#ffc107`;
2923
+ if (todoNodes.length > 0) mermaidStyles += `
2924
+ classDef todo fill:#f8d7da,stroke:#dc3545`;
2925
+ if (doneNodes.length > 0) mermaidStyles += `
2926
+ class ${doneNodes.join(",")} done`;
2927
+ if (inProgressNodes.length > 0)
2928
+ mermaidStyles += `
2929
+ class ${inProgressNodes.join(",")} inprogress`;
2930
+ if (todoNodes.length > 0) mermaidStyles += `
2931
+ class ${todoNodes.join(",")} todo`;
2932
+ const depthNote = metadata.maxDepthReached ? " (max depth reached, graph may be incomplete)" : "";
2933
+ const text = `## Issue Graph: ${rootIssue} (depth: ${metadata.traversalDepth}, ${metadata.totalNodes} nodes, ${metadata.totalEdges} edges)${depthNote}
2934
+
2935
+ ### Nodes
2936
+ | Key | Summary | Status | Type | Priority |
2937
+ |-----|---------|--------|------|----------|
2938
+ ${nodesTable}
2939
+
2940
+ ### Edges
2941
+ | From | Relationship | To | Category |
2942
+ |------|-------------|-----|----------|
2943
+ ${edgesTable}
2944
+
2945
+ ### Mermaid Diagram
2946
+ \`\`\`mermaid
2947
+ graph TD
2948
+ ${mermaidNodes}
2949
+ ${mermaidEdges}${mermaidStyles}
2950
+ \`\`\``;
2951
+ return { content: [{ type: "text", text }] };
2952
+ }
2953
+ function escapeMermaid(text) {
2954
+ return text.replace(/"/g, "'").replace(/[[\]{}()#]/g, "").substring(0, 60);
2955
+ }
2956
+ async function handleGetIssueGraph(input) {
2957
+ try {
2958
+ const validated = validateInput(GetIssueGraphInputSchema, input);
2959
+ const {
2960
+ issueKey,
2961
+ maxDepth = 2,
2962
+ maxNodes = 50,
2963
+ includeHierarchy = true,
2964
+ linkTypes,
2965
+ direction = "all"
2966
+ } = validated;
2967
+ log25.info(`Building issue graph from ${issueKey} (depth: ${maxDepth}, maxNodes: ${maxNodes})`);
2968
+ const issueDataMap = /* @__PURE__ */ new Map();
2969
+ const queue = [{ key: issueKey, depth: 0 }];
2970
+ const visited = /* @__PURE__ */ new Set();
2971
+ let maxDepthReached = false;
2972
+ let actualDepth = 0;
2973
+ while (queue.length > 0 && issueDataMap.size < maxNodes) {
2974
+ const current = queue.shift();
2975
+ if (visited.has(current.key)) continue;
2976
+ visited.add(current.key);
2977
+ if (current.depth > maxDepth) {
2978
+ maxDepthReached = true;
2979
+ continue;
2980
+ }
2981
+ actualDepth = Math.max(actualDepth, current.depth);
2982
+ let issue;
2983
+ try {
2984
+ issue = await getIssue(current.key, { fields: GRAPH_FIELDS });
2985
+ } catch (err) {
2986
+ log25.warn(`Skipping issue ${current.key}: ${err.message}`);
2987
+ continue;
2988
+ }
2989
+ const data = extractIssueData(issue);
2990
+ issueDataMap.set(current.key, data);
2991
+ if (current.depth >= maxDepth) continue;
2992
+ const nextDepth = current.depth + 1;
2993
+ if (includeHierarchy && data.parent && !visited.has(data.parent.key)) {
2994
+ queue.push({ key: data.parent.key, depth: nextDepth });
2995
+ }
2996
+ if (includeHierarchy) {
2997
+ for (const sub of data.subtasks) {
2998
+ if (!visited.has(sub.key)) {
2999
+ queue.push({ key: sub.key, depth: nextDepth });
3000
+ }
3001
+ }
3002
+ }
3003
+ for (const link of data.issuelinks) {
3004
+ if (linkTypes && linkTypes.length > 0) {
3005
+ const typeName = link.type?.name || "";
3006
+ if (!linkTypes.some((lt) => lt.toLowerCase() === typeName.toLowerCase())) continue;
3007
+ }
3008
+ if (link.outwardIssue && !visited.has(link.outwardIssue.key) && (direction === "all" || direction === "outward")) {
3009
+ queue.push({ key: link.outwardIssue.key, depth: nextDepth });
3010
+ }
3011
+ if (link.inwardIssue && !visited.has(link.inwardIssue.key) && (direction === "all" || direction === "inward")) {
3012
+ queue.push({ key: link.inwardIssue.key, depth: nextDepth });
3013
+ }
3014
+ }
3015
+ }
3016
+ if (queue.length > 0) {
3017
+ maxDepthReached = true;
3018
+ }
3019
+ log25.info(`Graph built: ${issueDataMap.size} nodes discovered`);
3020
+ const graph = buildGraph(issueDataMap, issueKey, {
3021
+ includeHierarchy,
3022
+ linkTypes,
3023
+ direction
3024
+ });
3025
+ graph.metadata.maxDepthReached = maxDepthReached;
3026
+ graph.metadata.traversalDepth = actualDepth;
3027
+ return formatGraphResponse(graph);
3028
+ } catch (error) {
3029
+ log25.error("Error in handleGetIssueGraph:", error);
3030
+ return handleError(error);
3031
+ }
3032
+ }
3033
+
3342
3034
  // src/index.ts
3343
3035
  if (process.env.NODE_ENV !== "production") {
3344
3036
  try {
@@ -3347,54 +3039,172 @@ if (process.env.NODE_ENV !== "production") {
3347
3039
  } catch {
3348
3040
  }
3349
3041
  }
3350
- var log25 = createLogger("server");
3351
- var toolHandlers = /* @__PURE__ */ new Map([
3352
- [TOOL_NAMES.GET_VISIBLE_PROJECTS, handleGetVisibleProjects],
3353
- [TOOL_NAMES.GET_ISSUE, handleGetIssue],
3354
- [TOOL_NAMES.SEARCH_ISSUES, handleSearchIssues],
3355
- [TOOL_NAMES.GET_MY_ISSUES, handleGetMyIssues],
3356
- [TOOL_NAMES.GET_ISSUE_TYPES, handleGetIssueTypes],
3357
- [TOOL_NAMES.GET_USERS, handleGetUsers],
3358
- [TOOL_NAMES.GET_PRIORITIES, handleGetPriorities],
3359
- [TOOL_NAMES.GET_STATUSES, handleGetStatuses],
3360
- [TOOL_NAMES.CREATE_ISSUE, handleCreateIssue],
3361
- [TOOL_NAMES.UPDATE_ISSUE, handleUpdateIssue],
3362
- [TOOL_NAMES.ADD_COMMENT, handleAddComment],
3363
- [TOOL_NAMES.GET_PROJECT_INFO, handleGetProjectInfo],
3364
- [TOOL_NAMES.CREATE_SUBTASK, handleCreateSubtask],
3365
- [TOOL_NAMES.GET_CREATE_META, handleGetCreateMeta],
3366
- [TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields],
3367
- [TOOL_NAMES.CREATE_ISSUE_LINK, handleCreateIssueLink],
3368
- [TOOL_NAMES.GET_COMMENTS, handleGetComments],
3369
- [TOOL_NAMES.ADD_ATTACHMENT, handleAddAttachment],
3370
- [TOOL_NAMES.GET_ATTACHMENTS, handleGetAttachments],
3371
- [TOOL_NAMES.DELETE_ATTACHMENT, handleDeleteAttachment],
3372
- [TOOL_NAMES.GET_TRANSITIONS, handleGetTransitions],
3373
- [TOOL_NAMES.TRANSITION_ISSUE, handleTransitionIssue]
3374
- ]);
3375
- var allTools = [
3376
- getVisibleProjectsTool,
3377
- getIssueTool,
3378
- searchIssuesTool,
3379
- getMyIssuesTool,
3380
- getIssueTypesTool,
3381
- getUsersTool,
3382
- getPrioritiesTool,
3383
- getStatusesTool,
3384
- createIssueTool,
3385
- updateIssueTool,
3386
- addCommentTool,
3387
- getProjectInfoTool,
3388
- createSubtaskTool,
3389
- getCreateMetaTool,
3390
- getCustomFieldsTool,
3391
- createIssueLinkTool,
3392
- getCommentsTool,
3393
- addAttachmentTool,
3394
- getAttachmentsTool,
3395
- deleteAttachmentTool,
3396
- getTransitionsTool,
3397
- transitionIssueTool
3042
+ var log26 = createLogger("server");
3043
+ var toolRegistry = [
3044
+ // === Read-only tools ===
3045
+ {
3046
+ name: TOOL_NAMES.GET_VISIBLE_PROJECTS,
3047
+ description: getVisibleProjectsTool.description,
3048
+ inputSchema: GetVisibleProjectsInputSchema,
3049
+ handler: handleGetVisibleProjects,
3050
+ annotations: { readOnlyHint: true }
3051
+ },
3052
+ {
3053
+ name: TOOL_NAMES.GET_ISSUE,
3054
+ description: getIssueTool.description,
3055
+ inputSchema: GetIssueInputSchema,
3056
+ handler: handleGetIssue,
3057
+ annotations: { readOnlyHint: true }
3058
+ },
3059
+ {
3060
+ name: TOOL_NAMES.SEARCH_ISSUES,
3061
+ description: searchIssuesTool.description,
3062
+ inputSchema: SearchIssuesInputSchema,
3063
+ handler: handleSearchIssues,
3064
+ annotations: { readOnlyHint: true }
3065
+ },
3066
+ {
3067
+ name: TOOL_NAMES.GET_MY_ISSUES,
3068
+ description: getMyIssuesTool.description,
3069
+ inputSchema: GetMyIssuesInputSchema,
3070
+ handler: handleGetMyIssues,
3071
+ annotations: { readOnlyHint: true }
3072
+ },
3073
+ {
3074
+ name: TOOL_NAMES.GET_ISSUE_TYPES,
3075
+ description: getIssueTypesTool.description,
3076
+ inputSchema: GetIssueTypesInputSchema,
3077
+ handler: handleGetIssueTypes,
3078
+ annotations: { readOnlyHint: true }
3079
+ },
3080
+ {
3081
+ name: TOOL_NAMES.GET_USERS,
3082
+ description: getUsersTool.description,
3083
+ inputSchema: GetUsersInputSchema,
3084
+ handler: handleGetUsers,
3085
+ annotations: { readOnlyHint: true }
3086
+ },
3087
+ {
3088
+ name: TOOL_NAMES.GET_PRIORITIES,
3089
+ description: getPrioritiesTool.description,
3090
+ inputSchema: GetPrioritiesInputSchema,
3091
+ handler: handleGetPriorities,
3092
+ annotations: { readOnlyHint: true }
3093
+ },
3094
+ {
3095
+ name: TOOL_NAMES.GET_STATUSES,
3096
+ description: getStatusesTool.description,
3097
+ inputSchema: GetStatusesInputSchema,
3098
+ handler: handleGetStatuses,
3099
+ annotations: { readOnlyHint: true }
3100
+ },
3101
+ {
3102
+ name: TOOL_NAMES.GET_PROJECT_INFO,
3103
+ description: getProjectInfoTool.description,
3104
+ inputSchema: GetProjectInfoInputSchema,
3105
+ handler: handleGetProjectInfo,
3106
+ annotations: { readOnlyHint: true }
3107
+ },
3108
+ {
3109
+ name: TOOL_NAMES.GET_CREATE_META,
3110
+ description: getCreateMetaTool.description,
3111
+ inputSchema: GetCreateMetaInputSchema,
3112
+ handler: handleGetCreateMeta,
3113
+ annotations: { readOnlyHint: true }
3114
+ },
3115
+ {
3116
+ name: TOOL_NAMES.GET_CUSTOM_FIELDS,
3117
+ description: getCustomFieldsTool.description,
3118
+ inputSchema: GetCustomFieldsInputSchema,
3119
+ handler: handleGetCustomFields,
3120
+ annotations: { readOnlyHint: true }
3121
+ },
3122
+ {
3123
+ name: TOOL_NAMES.GET_COMMENTS,
3124
+ description: getCommentsTool.description,
3125
+ inputSchema: GetCommentsInputSchema,
3126
+ handler: handleGetComments,
3127
+ annotations: { readOnlyHint: true }
3128
+ },
3129
+ {
3130
+ name: TOOL_NAMES.GET_ATTACHMENTS,
3131
+ description: getAttachmentsTool.description,
3132
+ inputSchema: GetAttachmentsInputSchema,
3133
+ handler: handleGetAttachments,
3134
+ annotations: { readOnlyHint: true }
3135
+ },
3136
+ {
3137
+ name: TOOL_NAMES.GET_TRANSITIONS,
3138
+ description: getTransitionsTool.description,
3139
+ inputSchema: GetTransitionsInputSchema,
3140
+ handler: handleGetTransitions,
3141
+ annotations: { readOnlyHint: true }
3142
+ },
3143
+ // === Mutating tools ===
3144
+ {
3145
+ name: TOOL_NAMES.CREATE_ISSUE,
3146
+ description: createIssueTool.description,
3147
+ inputSchema: CreateIssueInputSchema,
3148
+ handler: handleCreateIssue,
3149
+ annotations: { readOnlyHint: false }
3150
+ },
3151
+ {
3152
+ name: TOOL_NAMES.UPDATE_ISSUE,
3153
+ description: updateIssueTool.description,
3154
+ inputSchema: UpdateIssueInputSchema,
3155
+ handler: handleUpdateIssue,
3156
+ annotations: { readOnlyHint: false }
3157
+ },
3158
+ {
3159
+ name: TOOL_NAMES.ADD_COMMENT,
3160
+ description: addCommentTool.description,
3161
+ inputSchema: AddCommentInputSchema,
3162
+ handler: handleAddComment,
3163
+ annotations: { readOnlyHint: false }
3164
+ },
3165
+ {
3166
+ name: TOOL_NAMES.CREATE_SUBTASK,
3167
+ description: createSubtaskTool.description,
3168
+ inputSchema: CreateSubtaskInputSchema,
3169
+ handler: handleCreateSubtask,
3170
+ annotations: { readOnlyHint: false }
3171
+ },
3172
+ {
3173
+ name: TOOL_NAMES.CREATE_ISSUE_LINK,
3174
+ description: createIssueLinkTool.description,
3175
+ inputSchema: CreateIssueLinkInputSchema,
3176
+ handler: handleCreateIssueLink,
3177
+ annotations: { readOnlyHint: false }
3178
+ },
3179
+ {
3180
+ name: TOOL_NAMES.ADD_ATTACHMENT,
3181
+ description: addAttachmentTool.description,
3182
+ inputSchema: AddAttachmentInputSchema,
3183
+ handler: handleAddAttachment,
3184
+ annotations: { readOnlyHint: false }
3185
+ },
3186
+ {
3187
+ name: TOOL_NAMES.TRANSITION_ISSUE,
3188
+ description: transitionIssueTool.description,
3189
+ inputSchema: TransitionIssueInputSchema,
3190
+ handler: handleTransitionIssue,
3191
+ annotations: { readOnlyHint: false }
3192
+ },
3193
+ {
3194
+ name: TOOL_NAMES.GET_ISSUE_GRAPH,
3195
+ description: getIssueGraphTool.description,
3196
+ inputSchema: GetIssueGraphInputSchema,
3197
+ handler: handleGetIssueGraph,
3198
+ annotations: { readOnlyHint: true }
3199
+ },
3200
+ // === Destructive tools ===
3201
+ {
3202
+ name: TOOL_NAMES.DELETE_ATTACHMENT,
3203
+ description: deleteAttachmentTool.description,
3204
+ inputSchema: DeleteAttachmentInputSchema,
3205
+ handler: handleDeleteAttachment,
3206
+ annotations: { readOnlyHint: false, destructiveHint: true }
3207
+ }
3398
3208
  ];
3399
3209
  async function main() {
3400
3210
  const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
@@ -3422,50 +3232,45 @@ async function main() {
3422
3232
  version = pkg.default?.version || pkg.version || version;
3423
3233
  } catch {
3424
3234
  }
3425
- const server = new Server(
3235
+ const server = new McpServer(
3426
3236
  {
3427
3237
  name: "jira-mcp-server",
3428
3238
  version
3429
3239
  },
3430
3240
  {
3431
3241
  capabilities: {
3432
- resources: {},
3433
3242
  tools: {}
3434
3243
  }
3435
3244
  }
3436
3245
  );
3437
- server.setRequestHandler(ListToolsRequestSchema, async () => {
3438
- console.error(`\u{1F4CB} Listing ${allTools.length} available tool(s)`);
3439
- return { tools: allTools };
3440
- });
3441
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
3442
- const { name, arguments: args } = request.params;
3443
- console.error(`\u{1F6E0}\uFE0F Executing tool: ${name}`);
3444
- const handler = toolHandlers.get(name);
3445
- if (!handler) {
3446
- throw new Error(`Unknown tool: ${name}`);
3447
- }
3448
- try {
3449
- const result = await handler(args);
3450
- console.error(`\u2705 Tool ${name} executed successfully`);
3451
- return result;
3452
- } catch (error) {
3453
- console.error(`\u274C Error executing tool ${name}:`, error.message);
3454
- throw error;
3455
- }
3456
- });
3457
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
3458
- return { resources: [] };
3459
- });
3460
- server.setRequestHandler(ReadResourceRequestSchema, async () => {
3461
- throw new Error("Resource reading not implemented");
3462
- });
3246
+ for (const entry of toolRegistry) {
3247
+ server.registerTool(
3248
+ entry.name,
3249
+ {
3250
+ description: entry.description,
3251
+ inputSchema: entry.inputSchema,
3252
+ annotations: entry.annotations
3253
+ },
3254
+ async (args) => {
3255
+ console.error(`\u{1F6E0}\uFE0F Executing tool: ${entry.name}`);
3256
+ try {
3257
+ const result = await entry.handler(args);
3258
+ console.error(`\u2705 Tool ${entry.name} executed successfully`);
3259
+ return result;
3260
+ } catch (error) {
3261
+ console.error(`\u274C Error executing tool ${entry.name}:`, error.message);
3262
+ throw error;
3263
+ }
3264
+ }
3265
+ );
3266
+ }
3267
+ console.error(`\u{1F4CB} Registered ${toolRegistry.length} tool(s)`);
3463
3268
  const transport = new StdioServerTransport();
3464
3269
  await server.connect(transport);
3465
3270
  console.error("\u{1F680} Jira MCP server running on stdio");
3466
3271
  }
3467
3272
  main().catch((error) => {
3468
- log25.error("Fatal error:", error);
3273
+ log26.error("Fatal error:", error);
3469
3274
  process.exit(1);
3470
3275
  });
3471
3276
  //# sourceMappingURL=index.js.map