mcp-jira-stdio 1.8.1 → 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 +639 -621
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
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.
|
|
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,14 +489,14 @@ var require_package2 = __commonJS({
|
|
|
490
489
|
node: ">=20.0.0"
|
|
491
490
|
},
|
|
492
491
|
dependencies: {
|
|
493
|
-
"@modelcontextprotocol/sdk": "^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",
|
|
497
496
|
zod: "^3.22.4"
|
|
498
497
|
},
|
|
499
498
|
devDependencies: {
|
|
500
|
-
"@types/node": "^
|
|
499
|
+
"@types/node": "^25.3.3",
|
|
501
500
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
502
501
|
"@typescript-eslint/parser": "^8.0.0",
|
|
503
502
|
"@vitest/coverage-v8": "^4.0.10",
|
|
@@ -577,7 +576,10 @@ var TOOL_NAMES = {
|
|
|
577
576
|
GET_COMMENTS: "jira_get_comments",
|
|
578
577
|
ADD_ATTACHMENT: "jira_add_attachment",
|
|
579
578
|
GET_ATTACHMENTS: "jira_get_attachments",
|
|
580
|
-
DELETE_ATTACHMENT: "jira_delete_attachment"
|
|
579
|
+
DELETE_ATTACHMENT: "jira_delete_attachment",
|
|
580
|
+
GET_TRANSITIONS: "jira_get_transitions",
|
|
581
|
+
TRANSITION_ISSUE: "jira_transition_issue",
|
|
582
|
+
GET_ISSUE_GRAPH: "jira_get_issue_graph"
|
|
581
583
|
};
|
|
582
584
|
|
|
583
585
|
// src/utils/logger.ts
|
|
@@ -883,6 +885,9 @@ var UpdateIssueInputSchema = z.object({
|
|
|
883
885
|
assignee: z.string().optional().describe("New assignee account ID"),
|
|
884
886
|
labels: z.array(z.string()).optional().describe("New labels (replaces existing)"),
|
|
885
887
|
components: z.array(z.string()).optional().describe("New components (replaces existing)"),
|
|
888
|
+
parent: z.string().optional().describe(
|
|
889
|
+
"New parent issue key (e.g., PROJECT-100). Set to empty string to remove the parent."
|
|
890
|
+
),
|
|
886
891
|
returnIssue: z.boolean().optional().describe("When false, skip fetching full issue after update"),
|
|
887
892
|
format: z.enum(["markdown", "adf", "plain"]).optional().default("markdown").describe(
|
|
888
893
|
'Description format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"'
|
|
@@ -985,6 +990,37 @@ var GetAttachmentsInputSchema = z.object({
|
|
|
985
990
|
var DeleteAttachmentInputSchema = z.object({
|
|
986
991
|
attachmentId: z.string().min(1).describe("ID of the attachment to delete")
|
|
987
992
|
});
|
|
993
|
+
var GetTransitionsInputSchema = z.object({
|
|
994
|
+
issueKey: z.string().describe("Issue key to get available transitions for (e.g., PROJECT-123)").refine((v) => isValidIssueKey(v), "Invalid issue key format")
|
|
995
|
+
});
|
|
996
|
+
var TransitionIssueInputSchema = z.object({
|
|
997
|
+
issueKey: z.string().describe("Issue key to transition (e.g., PROJECT-123)").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
|
|
998
|
+
transitionId: z.string().optional().describe(
|
|
999
|
+
"ID of the transition to perform. Use jira_get_transitions to find available transition IDs."
|
|
1000
|
+
),
|
|
1001
|
+
transitionName: z.string().optional().describe(
|
|
1002
|
+
'Name of the transition to perform (e.g., "In Progress", "Done"). Case-insensitive. Alternative to transitionId.'
|
|
1003
|
+
),
|
|
1004
|
+
comment: z.string().optional().describe("Optional comment to add when transitioning the issue"),
|
|
1005
|
+
resolution: z.string().optional().describe(
|
|
1006
|
+
'Resolution name when transitioning to a resolved/done status (e.g., "Done", "Fixed")'
|
|
1007
|
+
),
|
|
1008
|
+
format: z.enum(["markdown", "adf", "plain"]).optional().default("markdown").describe(
|
|
1009
|
+
'Comment format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"'
|
|
1010
|
+
)
|
|
1011
|
+
}).refine((data) => data.transitionId || data.transitionName, {
|
|
1012
|
+
message: "Either transitionId or transitionName must be provided"
|
|
1013
|
+
});
|
|
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
|
+
});
|
|
988
1024
|
function ensureAdfDescription(desc, format = "markdown") {
|
|
989
1025
|
if (!desc) return desc;
|
|
990
1026
|
if (typeof desc === "object") return desc;
|
|
@@ -1237,6 +1273,9 @@ async function updateIssue(issueKey, updates) {
|
|
|
1237
1273
|
if (updates.components !== void 0) {
|
|
1238
1274
|
fields.components = updates.components.map((name) => ({ name }));
|
|
1239
1275
|
}
|
|
1276
|
+
if (updates.parent !== void 0) {
|
|
1277
|
+
fields.parent = updates.parent ? { key: updates.parent } : null;
|
|
1278
|
+
}
|
|
1240
1279
|
const config = {
|
|
1241
1280
|
method: "PUT",
|
|
1242
1281
|
url: `/issue/${issueKey}`,
|
|
@@ -1521,6 +1560,42 @@ async function deleteAttachment(attachmentId) {
|
|
|
1521
1560
|
};
|
|
1522
1561
|
await makeJiraRequest(config);
|
|
1523
1562
|
}
|
|
1563
|
+
async function getTransitions(issueKey) {
|
|
1564
|
+
const config = {
|
|
1565
|
+
method: "GET",
|
|
1566
|
+
url: `/issue/${issueKey}/transitions`
|
|
1567
|
+
};
|
|
1568
|
+
const response = await makeJiraRequest(config);
|
|
1569
|
+
return response.transitions;
|
|
1570
|
+
}
|
|
1571
|
+
async function transitionIssue(issueKey, transitionId, options = {}) {
|
|
1572
|
+
const data = {
|
|
1573
|
+
transition: { id: transitionId }
|
|
1574
|
+
};
|
|
1575
|
+
if (options.resolution) {
|
|
1576
|
+
data.fields = {
|
|
1577
|
+
resolution: { name: options.resolution }
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
if (options.comment) {
|
|
1581
|
+
const adfBody = ensureAdfDescription(options.comment, options.format || "markdown");
|
|
1582
|
+
data.update = {
|
|
1583
|
+
comment: [
|
|
1584
|
+
{
|
|
1585
|
+
add: {
|
|
1586
|
+
body: adfBody
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
]
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
const config = {
|
|
1593
|
+
method: "POST",
|
|
1594
|
+
url: `/issue/${issueKey}/transitions`,
|
|
1595
|
+
data
|
|
1596
|
+
};
|
|
1597
|
+
await makeJiraRequest(config);
|
|
1598
|
+
}
|
|
1524
1599
|
|
|
1525
1600
|
// src/utils/redaction.ts
|
|
1526
1601
|
function isRedactionEnabled() {
|
|
@@ -1608,6 +1683,8 @@ function formatIssueResponse(issue) {
|
|
|
1608
1683
|
}
|
|
1609
1684
|
const createdText = fields?.created ? new Date(fields.created).toISOString() : "N/A";
|
|
1610
1685
|
const updatedText = fields?.updated ? new Date(fields.updated).toISOString() : "N/A";
|
|
1686
|
+
const parentText = fields?.parent ? `
|
|
1687
|
+
**Parent:** ${fields.parent.key}${fields.parent.fields?.summary ? ` - ${fields.parent.fields.summary}` : ""}${fields.parent.fields?.status ? ` (${fields.parent.fields.status.name})` : ""}` : "";
|
|
1611
1688
|
return {
|
|
1612
1689
|
content: [
|
|
1613
1690
|
{
|
|
@@ -1621,7 +1698,7 @@ function formatIssueResponse(issue) {
|
|
|
1621
1698
|
**Project:** ${fields?.project?.name || "Unknown"} (${fields?.project?.key || "N/A"})
|
|
1622
1699
|
**Issue Type:** ${fields?.issuetype?.name || "Unknown"}
|
|
1623
1700
|
**Labels:** ${labelsText}
|
|
1624
|
-
**Components:** ${componentsText}
|
|
1701
|
+
**Components:** ${componentsText}${parentText}
|
|
1625
1702
|
**Created:** ${createdText}
|
|
1626
1703
|
**Updated:** ${updatedText}
|
|
1627
1704
|
|
|
@@ -2062,6 +2139,34 @@ ${attachmentsList}
|
|
|
2062
2139
|
]
|
|
2063
2140
|
};
|
|
2064
2141
|
}
|
|
2142
|
+
function formatTransitionsResponse(transitions, issueKey) {
|
|
2143
|
+
if (transitions.length === 0) {
|
|
2144
|
+
return {
|
|
2145
|
+
content: [
|
|
2146
|
+
{
|
|
2147
|
+
type: "text",
|
|
2148
|
+
text: `No transitions available for issue ${issueKey}. The issue may be in a final state or you may lack permission.`
|
|
2149
|
+
}
|
|
2150
|
+
]
|
|
2151
|
+
};
|
|
2152
|
+
}
|
|
2153
|
+
const transitionsList = transitions.map(
|
|
2154
|
+
(t) => `\u2022 **${t.name}** (ID: ${t.id})
|
|
2155
|
+
Target status: ${t.to.name} (Category: ${t.to.statusCategory?.name || "Unknown"})`
|
|
2156
|
+
).join("\n\n");
|
|
2157
|
+
return {
|
|
2158
|
+
content: [
|
|
2159
|
+
{
|
|
2160
|
+
type: "text",
|
|
2161
|
+
text: `**Available transitions for ${issueKey}** (${transitions.length}):
|
|
2162
|
+
|
|
2163
|
+
${transitionsList}
|
|
2164
|
+
|
|
2165
|
+
\u{1F4A1} **Tip:** Use \`jira_transition_issue\` with the transition ID or name to move this issue.`
|
|
2166
|
+
}
|
|
2167
|
+
]
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2065
2170
|
|
|
2066
2171
|
// src/utils/error-handler.ts
|
|
2067
2172
|
var log2 = createLogger("error-handler");
|
|
@@ -2114,27 +2219,7 @@ function handleError(error) {
|
|
|
2114
2219
|
// src/tools/get-visible-projects.ts
|
|
2115
2220
|
var log3 = createLogger("tool:get-visible-projects");
|
|
2116
2221
|
var getVisibleProjectsTool = {
|
|
2117
|
-
|
|
2118
|
-
description: "Retrieves all projects accessible to the authenticated user. Returns project keys, names, descriptions, and basic metadata.",
|
|
2119
|
-
inputSchema: {
|
|
2120
|
-
type: "object",
|
|
2121
|
-
properties: {
|
|
2122
|
-
expand: {
|
|
2123
|
-
type: "array",
|
|
2124
|
-
items: { type: "string" },
|
|
2125
|
-
description: 'Additional project details to include (e.g., ["description", "lead", "issueTypes"])',
|
|
2126
|
-
default: []
|
|
2127
|
-
},
|
|
2128
|
-
recent: {
|
|
2129
|
-
type: "number",
|
|
2130
|
-
description: "Limit results to recently accessed projects (max number)",
|
|
2131
|
-
minimum: 1,
|
|
2132
|
-
maximum: 100
|
|
2133
|
-
}
|
|
2134
|
-
},
|
|
2135
|
-
required: []
|
|
2136
|
-
}
|
|
2137
|
-
};
|
|
2222
|
+
description: "Retrieves all projects accessible to the authenticated user. Returns project keys, names, descriptions, and basic metadata."};
|
|
2138
2223
|
async function handleGetVisibleProjects(input) {
|
|
2139
2224
|
try {
|
|
2140
2225
|
const validated = validateInput(GetVisibleProjectsInputSchema, input);
|
|
@@ -2154,30 +2239,7 @@ async function handleGetVisibleProjects(input) {
|
|
|
2154
2239
|
// src/tools/get-issue.ts
|
|
2155
2240
|
var log4 = createLogger("tool:get-issue");
|
|
2156
2241
|
var getIssueTool = {
|
|
2157
|
-
|
|
2158
|
-
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.',
|
|
2159
|
-
inputSchema: {
|
|
2160
|
-
type: "object",
|
|
2161
|
-
properties: {
|
|
2162
|
-
issueKey: {
|
|
2163
|
-
type: "string",
|
|
2164
|
-
description: "Issue key or full Jira URL (e.g., PROJECT-123 or https://your.atlassian.net/browse/PROJECT-123)"
|
|
2165
|
-
},
|
|
2166
|
-
expand: {
|
|
2167
|
-
type: "array",
|
|
2168
|
-
items: { type: "string" },
|
|
2169
|
-
description: 'Additional issue details to include (e.g., ["comments", "attachments", "changelog"])',
|
|
2170
|
-
default: []
|
|
2171
|
-
},
|
|
2172
|
-
fields: {
|
|
2173
|
-
type: "array",
|
|
2174
|
-
items: { type: "string" },
|
|
2175
|
-
description: 'Specific fields to retrieve (e.g., ["summary", "status", "assignee"])'
|
|
2176
|
-
}
|
|
2177
|
-
},
|
|
2178
|
-
required: ["issueKey"]
|
|
2179
|
-
}
|
|
2180
|
-
};
|
|
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.'};
|
|
2181
2243
|
async function handleGetIssue(input) {
|
|
2182
2244
|
try {
|
|
2183
2245
|
const validated = validateInput(GetIssueInputSchema, input);
|
|
@@ -2199,41 +2261,7 @@ async function handleGetIssue(input) {
|
|
|
2199
2261
|
// src/tools/search-issues.ts
|
|
2200
2262
|
var log5 = createLogger("tool:search-issues");
|
|
2201
2263
|
var searchIssuesTool = {
|
|
2202
|
-
|
|
2203
|
-
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.',
|
|
2204
|
-
inputSchema: {
|
|
2205
|
-
type: "object",
|
|
2206
|
-
properties: {
|
|
2207
|
-
jql: {
|
|
2208
|
-
type: "string",
|
|
2209
|
-
description: 'JQL query string (e.g., "project = PROJECT AND status = Open")'
|
|
2210
|
-
},
|
|
2211
|
-
nextPageToken: {
|
|
2212
|
-
type: "string",
|
|
2213
|
-
description: "Token for pagination. Omit for first page, use value from previous response for next page."
|
|
2214
|
-
},
|
|
2215
|
-
maxResults: {
|
|
2216
|
-
type: "number",
|
|
2217
|
-
description: "Maximum number of results to return per page",
|
|
2218
|
-
minimum: 1,
|
|
2219
|
-
maximum: 100,
|
|
2220
|
-
default: 50
|
|
2221
|
-
},
|
|
2222
|
-
fields: {
|
|
2223
|
-
type: "array",
|
|
2224
|
-
items: { type: "string" },
|
|
2225
|
-
description: "Specific fields to retrieve for each issue"
|
|
2226
|
-
},
|
|
2227
|
-
expand: {
|
|
2228
|
-
type: "array",
|
|
2229
|
-
items: { type: "string" },
|
|
2230
|
-
description: "Additional details to include for each issue",
|
|
2231
|
-
default: []
|
|
2232
|
-
}
|
|
2233
|
-
},
|
|
2234
|
-
required: ["jql"]
|
|
2235
|
-
}
|
|
2236
|
-
};
|
|
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.'};
|
|
2237
2265
|
async function handleSearchIssues(input) {
|
|
2238
2266
|
try {
|
|
2239
2267
|
const validated = validateInput(SearchIssuesInputSchema, input);
|
|
@@ -2257,37 +2285,7 @@ async function handleSearchIssues(input) {
|
|
|
2257
2285
|
// src/tools/get-my-issues.ts
|
|
2258
2286
|
var log6 = createLogger("tool:get-my-issues");
|
|
2259
2287
|
var getMyIssuesTool = {
|
|
2260
|
-
|
|
2261
|
-
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.",
|
|
2262
|
-
inputSchema: {
|
|
2263
|
-
type: "object",
|
|
2264
|
-
properties: {
|
|
2265
|
-
nextPageToken: {
|
|
2266
|
-
type: "string",
|
|
2267
|
-
description: "Token for pagination. Omit for first page, use value from previous response for next page."
|
|
2268
|
-
},
|
|
2269
|
-
maxResults: {
|
|
2270
|
-
type: "number",
|
|
2271
|
-
description: "Maximum number of results to return per page",
|
|
2272
|
-
minimum: 1,
|
|
2273
|
-
maximum: 100,
|
|
2274
|
-
default: 50
|
|
2275
|
-
},
|
|
2276
|
-
fields: {
|
|
2277
|
-
type: "array",
|
|
2278
|
-
items: { type: "string" },
|
|
2279
|
-
description: "Specific fields to retrieve for each issue"
|
|
2280
|
-
},
|
|
2281
|
-
expand: {
|
|
2282
|
-
type: "array",
|
|
2283
|
-
items: { type: "string" },
|
|
2284
|
-
description: "Additional details to include for each issue",
|
|
2285
|
-
default: []
|
|
2286
|
-
}
|
|
2287
|
-
},
|
|
2288
|
-
required: []
|
|
2289
|
-
}
|
|
2290
|
-
};
|
|
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."};
|
|
2291
2289
|
async function handleGetMyIssues(input) {
|
|
2292
2290
|
try {
|
|
2293
2291
|
const validated = validateInput(GetMyIssuesInputSchema, input);
|
|
@@ -2311,19 +2309,7 @@ async function handleGetMyIssues(input) {
|
|
|
2311
2309
|
// src/tools/get-issue-types.ts
|
|
2312
2310
|
var log7 = createLogger("tool:get-issue-types");
|
|
2313
2311
|
var getIssueTypesTool = {
|
|
2314
|
-
|
|
2315
|
-
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.).",
|
|
2316
|
-
inputSchema: {
|
|
2317
|
-
type: "object",
|
|
2318
|
-
properties: {
|
|
2319
|
-
projectKey: {
|
|
2320
|
-
type: "string",
|
|
2321
|
-
description: "Project key to get issue types for specific project (optional - if not provided, returns global issue types)"
|
|
2322
|
-
}
|
|
2323
|
-
},
|
|
2324
|
-
required: []
|
|
2325
|
-
}
|
|
2326
|
-
};
|
|
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.)."};
|
|
2327
2313
|
async function handleGetIssueTypes(input) {
|
|
2328
2314
|
try {
|
|
2329
2315
|
const validated = validateInput(GetIssueTypesInputSchema, input);
|
|
@@ -2344,40 +2330,7 @@ async function handleGetIssueTypes(input) {
|
|
|
2344
2330
|
// src/tools/get-users.ts
|
|
2345
2331
|
var log8 = createLogger("tool:get-users");
|
|
2346
2332
|
var getUsersTool = {
|
|
2347
|
-
|
|
2348
|
-
description: "Search for users by name, email, username, or account ID. Returns display name, email, account status, and account type. Supports pagination.",
|
|
2349
|
-
inputSchema: {
|
|
2350
|
-
type: "object",
|
|
2351
|
-
properties: {
|
|
2352
|
-
query: {
|
|
2353
|
-
type: "string",
|
|
2354
|
-
description: "Search query for user name or email (partial matches supported)"
|
|
2355
|
-
},
|
|
2356
|
-
username: {
|
|
2357
|
-
type: "string",
|
|
2358
|
-
description: "Specific username to search for"
|
|
2359
|
-
},
|
|
2360
|
-
accountId: {
|
|
2361
|
-
type: "string",
|
|
2362
|
-
description: "Specific account ID to search for"
|
|
2363
|
-
},
|
|
2364
|
-
startAt: {
|
|
2365
|
-
type: "number",
|
|
2366
|
-
description: "Index of first result to return (for pagination)",
|
|
2367
|
-
minimum: 0,
|
|
2368
|
-
default: 0
|
|
2369
|
-
},
|
|
2370
|
-
maxResults: {
|
|
2371
|
-
type: "number",
|
|
2372
|
-
description: "Maximum number of results to return",
|
|
2373
|
-
minimum: 1,
|
|
2374
|
-
maximum: 50,
|
|
2375
|
-
default: 50
|
|
2376
|
-
}
|
|
2377
|
-
},
|
|
2378
|
-
required: []
|
|
2379
|
-
}
|
|
2380
|
-
};
|
|
2333
|
+
description: "Search for users by name, email, username, or account ID. Returns display name, email, account status, and account type. Supports pagination."};
|
|
2381
2334
|
async function handleGetUsers(input) {
|
|
2382
2335
|
try {
|
|
2383
2336
|
const validated = validateInput(GetUsersInputSchema, input);
|
|
@@ -2408,14 +2361,7 @@ async function handleGetUsers(input) {
|
|
|
2408
2361
|
// src/tools/get-priorities.ts
|
|
2409
2362
|
var log9 = createLogger("tool:get-priorities");
|
|
2410
2363
|
var getPrioritiesTool = {
|
|
2411
|
-
|
|
2412
|
-
description: "Retrieves available priorities (e.g., Highest, High, Medium, Low, Lowest). Returns IDs, names, and descriptions.",
|
|
2413
|
-
inputSchema: {
|
|
2414
|
-
type: "object",
|
|
2415
|
-
properties: {},
|
|
2416
|
-
required: []
|
|
2417
|
-
}
|
|
2418
|
-
};
|
|
2364
|
+
description: "Retrieves available priorities (e.g., Highest, High, Medium, Low, Lowest). Returns IDs, names, and descriptions."};
|
|
2419
2365
|
async function handleGetPriorities(input) {
|
|
2420
2366
|
try {
|
|
2421
2367
|
validateInput(GetPrioritiesInputSchema, input);
|
|
@@ -2432,23 +2378,7 @@ async function handleGetPriorities(input) {
|
|
|
2432
2378
|
// src/tools/get-statuses.ts
|
|
2433
2379
|
var log10 = createLogger("tool:get-statuses");
|
|
2434
2380
|
var getStatusesTool = {
|
|
2435
|
-
|
|
2436
|
-
description: "Retrieves available statuses (global or project-specific, e.g., To Do, In Progress, Done). Returns status categories and workflow information.",
|
|
2437
|
-
inputSchema: {
|
|
2438
|
-
type: "object",
|
|
2439
|
-
properties: {
|
|
2440
|
-
projectKey: {
|
|
2441
|
-
type: "string",
|
|
2442
|
-
description: "Project key to get statuses for specific project (optional)"
|
|
2443
|
-
},
|
|
2444
|
-
issueTypeId: {
|
|
2445
|
-
type: "string",
|
|
2446
|
-
description: "Issue type ID to get statuses for specific issue type (requires projectKey)"
|
|
2447
|
-
}
|
|
2448
|
-
},
|
|
2449
|
-
required: []
|
|
2450
|
-
}
|
|
2451
|
-
};
|
|
2381
|
+
description: "Retrieves available statuses (global or project-specific, e.g., To Do, In Progress, Done). Returns status categories and workflow information."};
|
|
2452
2382
|
async function handleGetStatuses(input) {
|
|
2453
2383
|
try {
|
|
2454
2384
|
const validated = validateInput(GetStatusesInputSchema, input);
|
|
@@ -2476,68 +2406,7 @@ async function handleGetStatuses(input) {
|
|
|
2476
2406
|
// src/tools/create-issue.ts
|
|
2477
2407
|
var log11 = createLogger("tool:create-issue");
|
|
2478
2408
|
var createIssueTool = {
|
|
2479
|
-
|
|
2480
|
-
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.',
|
|
2481
|
-
inputSchema: {
|
|
2482
|
-
type: "object",
|
|
2483
|
-
properties: {
|
|
2484
|
-
projectKey: {
|
|
2485
|
-
type: "string",
|
|
2486
|
-
description: "Project key where the issue will be created"
|
|
2487
|
-
},
|
|
2488
|
-
summary: {
|
|
2489
|
-
type: "string",
|
|
2490
|
-
description: "Issue summary/title",
|
|
2491
|
-
minLength: 1
|
|
2492
|
-
},
|
|
2493
|
-
description: {
|
|
2494
|
-
anyOf: [{ type: "string" }, { type: "object" }],
|
|
2495
|
-
description: 'Detailed issue description (optional). Format depends on the "format" parameter.'
|
|
2496
|
-
},
|
|
2497
|
-
issueType: {
|
|
2498
|
-
type: "string",
|
|
2499
|
-
description: "Issue type name (e.g., Bug, Story, Task, Epic)"
|
|
2500
|
-
},
|
|
2501
|
-
priority: {
|
|
2502
|
-
type: "string",
|
|
2503
|
-
description: "Issue priority name (e.g., High, Medium, Low) - optional"
|
|
2504
|
-
},
|
|
2505
|
-
assignee: {
|
|
2506
|
-
type: "string",
|
|
2507
|
-
description: "Assignee account ID (optional)"
|
|
2508
|
-
},
|
|
2509
|
-
labels: {
|
|
2510
|
-
type: "array",
|
|
2511
|
-
items: { type: "string" },
|
|
2512
|
-
description: "Issue labels (optional)",
|
|
2513
|
-
default: []
|
|
2514
|
-
},
|
|
2515
|
-
components: {
|
|
2516
|
-
type: "array",
|
|
2517
|
-
items: { type: "string" },
|
|
2518
|
-
description: "Component names (optional)",
|
|
2519
|
-
default: []
|
|
2520
|
-
},
|
|
2521
|
-
customFields: {
|
|
2522
|
-
type: "object",
|
|
2523
|
-
additionalProperties: true,
|
|
2524
|
-
description: 'Additional Jira fields, e.g. { "customfield_10071": value }. Use this to set required custom fields.'
|
|
2525
|
-
},
|
|
2526
|
-
returnIssue: {
|
|
2527
|
-
type: "boolean",
|
|
2528
|
-
description: "If false, returns only the issue key without fetching full details",
|
|
2529
|
-
default: true
|
|
2530
|
-
},
|
|
2531
|
-
format: {
|
|
2532
|
-
type: "string",
|
|
2533
|
-
enum: ["markdown", "adf", "plain"],
|
|
2534
|
-
description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
|
|
2535
|
-
default: "markdown"
|
|
2536
|
-
}
|
|
2537
|
-
},
|
|
2538
|
-
required: ["projectKey", "summary", "issueType"]
|
|
2539
|
-
}
|
|
2540
|
-
};
|
|
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.'};
|
|
2541
2410
|
async function handleCreateIssue(input) {
|
|
2542
2411
|
try {
|
|
2543
2412
|
const validated = validateInput(CreateIssueInputSchema, input);
|
|
@@ -2577,56 +2446,7 @@ async function handleCreateIssue(input) {
|
|
|
2577
2446
|
// src/tools/update-issue.ts
|
|
2578
2447
|
var log12 = createLogger("tool:update-issue");
|
|
2579
2448
|
var updateIssueTool = {
|
|
2580
|
-
|
|
2581
|
-
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.',
|
|
2582
|
-
inputSchema: {
|
|
2583
|
-
type: "object",
|
|
2584
|
-
properties: {
|
|
2585
|
-
issueKey: {
|
|
2586
|
-
type: "string",
|
|
2587
|
-
description: "Issue key to update (e.g., PROJECT-123)"
|
|
2588
|
-
},
|
|
2589
|
-
summary: {
|
|
2590
|
-
type: "string",
|
|
2591
|
-
description: "New issue summary/title (optional)"
|
|
2592
|
-
},
|
|
2593
|
-
description: {
|
|
2594
|
-
anyOf: [{ type: "string" }, { type: "object" }],
|
|
2595
|
-
description: 'New issue description (optional). Format depends on the "format" parameter.'
|
|
2596
|
-
},
|
|
2597
|
-
priority: {
|
|
2598
|
-
type: "string",
|
|
2599
|
-
description: "New priority name (e.g., High, Medium, Low) - optional"
|
|
2600
|
-
},
|
|
2601
|
-
assignee: {
|
|
2602
|
-
type: "string",
|
|
2603
|
-
description: "New assignee account ID (optional, use null string to unassign)"
|
|
2604
|
-
},
|
|
2605
|
-
labels: {
|
|
2606
|
-
type: "array",
|
|
2607
|
-
items: { type: "string" },
|
|
2608
|
-
description: "New labels array (replaces existing labels) - optional"
|
|
2609
|
-
},
|
|
2610
|
-
components: {
|
|
2611
|
-
type: "array",
|
|
2612
|
-
items: { type: "string" },
|
|
2613
|
-
description: "New components array (replaces existing components) - optional"
|
|
2614
|
-
},
|
|
2615
|
-
returnIssue: {
|
|
2616
|
-
type: "boolean",
|
|
2617
|
-
description: "If false, returns a success message without fetching the updated issue",
|
|
2618
|
-
default: true
|
|
2619
|
-
},
|
|
2620
|
-
format: {
|
|
2621
|
-
type: "string",
|
|
2622
|
-
enum: ["markdown", "adf", "plain"],
|
|
2623
|
-
description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
|
|
2624
|
-
default: "markdown"
|
|
2625
|
-
}
|
|
2626
|
-
},
|
|
2627
|
-
required: ["issueKey"]
|
|
2628
|
-
}
|
|
2629
|
-
};
|
|
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.'};
|
|
2630
2450
|
async function handleUpdateIssue(input) {
|
|
2631
2451
|
try {
|
|
2632
2452
|
const validated = validateInput(UpdateIssueInputSchema, input);
|
|
@@ -2638,6 +2458,7 @@ async function handleUpdateIssue(input) {
|
|
|
2638
2458
|
if (validated.assignee !== void 0) updateParams.assignee = validated.assignee;
|
|
2639
2459
|
if (validated.labels !== void 0) updateParams.labels = validated.labels;
|
|
2640
2460
|
if (validated.components !== void 0) updateParams.components = validated.components;
|
|
2461
|
+
if (validated.parent !== void 0) updateParams.parent = validated.parent;
|
|
2641
2462
|
if (validated.format !== void 0) updateParams.format = validated.format;
|
|
2642
2463
|
await updateIssue(validated.issueKey, updateParams);
|
|
2643
2464
|
if (validated.returnIssue === false) {
|
|
@@ -2656,46 +2477,7 @@ async function handleUpdateIssue(input) {
|
|
|
2656
2477
|
// src/tools/add-comment.ts
|
|
2657
2478
|
var log13 = createLogger("tool:add-comment");
|
|
2658
2479
|
var addCommentTool = {
|
|
2659
|
-
|
|
2660
|
-
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.',
|
|
2661
|
-
inputSchema: {
|
|
2662
|
-
type: "object",
|
|
2663
|
-
properties: {
|
|
2664
|
-
issueKey: {
|
|
2665
|
-
type: "string",
|
|
2666
|
-
description: "Issue key to add comment to (e.g., PROJECT-123)"
|
|
2667
|
-
},
|
|
2668
|
-
body: {
|
|
2669
|
-
type: "string",
|
|
2670
|
-
description: 'Comment body text. Format depends on the "format" parameter.',
|
|
2671
|
-
minLength: 1
|
|
2672
|
-
},
|
|
2673
|
-
visibility: {
|
|
2674
|
-
type: "object",
|
|
2675
|
-
description: "Comment visibility restrictions (optional)",
|
|
2676
|
-
properties: {
|
|
2677
|
-
type: {
|
|
2678
|
-
type: "string",
|
|
2679
|
-
enum: ["group", "role"],
|
|
2680
|
-
description: 'Visibility type - either "group" or "role"'
|
|
2681
|
-
},
|
|
2682
|
-
value: {
|
|
2683
|
-
type: "string",
|
|
2684
|
-
description: "Group name or role name for visibility restriction"
|
|
2685
|
-
}
|
|
2686
|
-
},
|
|
2687
|
-
required: ["type", "value"]
|
|
2688
|
-
},
|
|
2689
|
-
format: {
|
|
2690
|
-
type: "string",
|
|
2691
|
-
enum: ["markdown", "adf", "plain"],
|
|
2692
|
-
description: 'Comment format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
|
|
2693
|
-
default: "markdown"
|
|
2694
|
-
}
|
|
2695
|
-
},
|
|
2696
|
-
required: ["issueKey", "body"]
|
|
2697
|
-
}
|
|
2698
|
-
};
|
|
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.'};
|
|
2699
2481
|
async function handleAddComment(input) {
|
|
2700
2482
|
try {
|
|
2701
2483
|
const validated = validateInput(AddCommentInputSchema, input);
|
|
@@ -2717,25 +2499,7 @@ async function handleAddComment(input) {
|
|
|
2717
2499
|
// src/tools/get-project-info.ts
|
|
2718
2500
|
var log14 = createLogger("tool:get-project-info");
|
|
2719
2501
|
var getProjectInfoTool = {
|
|
2720
|
-
|
|
2721
|
-
description: "Retrieves detailed information about a project (components, versions, issue types, roles, insights). More comprehensive than the basic project list.",
|
|
2722
|
-
inputSchema: {
|
|
2723
|
-
type: "object",
|
|
2724
|
-
properties: {
|
|
2725
|
-
projectKey: {
|
|
2726
|
-
type: "string",
|
|
2727
|
-
description: "Project key to get detailed information for"
|
|
2728
|
-
},
|
|
2729
|
-
expand: {
|
|
2730
|
-
type: "array",
|
|
2731
|
-
items: { type: "string" },
|
|
2732
|
-
description: 'Additional project details to include (e.g., ["description", "lead", "issueTypes", "versions"])',
|
|
2733
|
-
default: []
|
|
2734
|
-
}
|
|
2735
|
-
},
|
|
2736
|
-
required: ["projectKey"]
|
|
2737
|
-
}
|
|
2738
|
-
};
|
|
2502
|
+
description: "Retrieves detailed information about a project (components, versions, issue types, roles, insights). More comprehensive than the basic project list."};
|
|
2739
2503
|
async function handleGetProjectInfo(input) {
|
|
2740
2504
|
try {
|
|
2741
2505
|
const validated = validateInput(GetProjectInfoInputSchema, input);
|
|
@@ -2752,54 +2516,7 @@ async function handleGetProjectInfo(input) {
|
|
|
2752
2516
|
// src/tools/create-subtask.ts
|
|
2753
2517
|
var log15 = createLogger("tool:create-subtask");
|
|
2754
2518
|
var createSubtaskTool = {
|
|
2755
|
-
|
|
2756
|
-
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).',
|
|
2757
|
-
inputSchema: {
|
|
2758
|
-
type: "object",
|
|
2759
|
-
properties: {
|
|
2760
|
-
parentIssueKey: {
|
|
2761
|
-
type: "string",
|
|
2762
|
-
description: "Parent issue key (e.g., PROJECT-123)"
|
|
2763
|
-
},
|
|
2764
|
-
summary: {
|
|
2765
|
-
type: "string",
|
|
2766
|
-
description: "Subtask summary/title",
|
|
2767
|
-
minLength: 1
|
|
2768
|
-
},
|
|
2769
|
-
description: {
|
|
2770
|
-
type: "string",
|
|
2771
|
-
description: 'Detailed subtask description (optional). Format depends on the "format" parameter.'
|
|
2772
|
-
},
|
|
2773
|
-
priority: {
|
|
2774
|
-
type: "string",
|
|
2775
|
-
description: "Subtask priority name (e.g., High, Medium, Low) - optional"
|
|
2776
|
-
},
|
|
2777
|
-
assignee: {
|
|
2778
|
-
type: "string",
|
|
2779
|
-
description: "Assignee account ID (optional)"
|
|
2780
|
-
},
|
|
2781
|
-
labels: {
|
|
2782
|
-
type: "array",
|
|
2783
|
-
items: { type: "string" },
|
|
2784
|
-
description: "Subtask labels (optional)",
|
|
2785
|
-
default: []
|
|
2786
|
-
},
|
|
2787
|
-
components: {
|
|
2788
|
-
type: "array",
|
|
2789
|
-
items: { type: "string" },
|
|
2790
|
-
description: "Component names (optional)",
|
|
2791
|
-
default: []
|
|
2792
|
-
},
|
|
2793
|
-
format: {
|
|
2794
|
-
type: "string",
|
|
2795
|
-
enum: ["markdown", "adf", "plain"],
|
|
2796
|
-
description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
|
|
2797
|
-
default: "markdown"
|
|
2798
|
-
}
|
|
2799
|
-
},
|
|
2800
|
-
required: ["parentIssueKey", "summary"]
|
|
2801
|
-
}
|
|
2802
|
-
};
|
|
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).'};
|
|
2803
2520
|
async function handleCreateSubtask(input) {
|
|
2804
2521
|
try {
|
|
2805
2522
|
const validated = validateInput(CreateSubtaskInputSchema, input);
|
|
@@ -2825,23 +2542,7 @@ async function handleCreateSubtask(input) {
|
|
|
2825
2542
|
// src/tools/get-create-meta.ts
|
|
2826
2543
|
var log16 = createLogger("tool:get-create-meta");
|
|
2827
2544
|
var getCreateMetaTool = {
|
|
2828
|
-
|
|
2829
|
-
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.",
|
|
2830
|
-
inputSchema: {
|
|
2831
|
-
type: "object",
|
|
2832
|
-
properties: {
|
|
2833
|
-
projectKey: {
|
|
2834
|
-
type: "string",
|
|
2835
|
-
description: "Project key to get create metadata for (e.g., PROJECT)"
|
|
2836
|
-
},
|
|
2837
|
-
issueTypeName: {
|
|
2838
|
-
type: "string",
|
|
2839
|
-
description: "Specific issue type name to get metadata for (optional)"
|
|
2840
|
-
}
|
|
2841
|
-
},
|
|
2842
|
-
required: ["projectKey"]
|
|
2843
|
-
}
|
|
2844
|
-
};
|
|
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."};
|
|
2845
2546
|
async function handleGetCreateMeta(input) {
|
|
2846
2547
|
try {
|
|
2847
2548
|
const validated = validateInput(GetCreateMetaInputSchema, input);
|
|
@@ -2865,19 +2566,7 @@ async function handleGetCreateMeta(input) {
|
|
|
2865
2566
|
// src/tools/get-custom-fields.ts
|
|
2866
2567
|
var log17 = createLogger("tool:get-custom-fields");
|
|
2867
2568
|
var getCustomFieldsTool = {
|
|
2868
|
-
|
|
2869
|
-
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.",
|
|
2870
|
-
inputSchema: {
|
|
2871
|
-
type: "object",
|
|
2872
|
-
properties: {
|
|
2873
|
-
projectKey: {
|
|
2874
|
-
type: "string",
|
|
2875
|
-
description: "Project key to filter custom fields (optional)"
|
|
2876
|
-
}
|
|
2877
|
-
},
|
|
2878
|
-
required: []
|
|
2879
|
-
}
|
|
2880
|
-
};
|
|
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."};
|
|
2881
2570
|
async function handleGetCustomFields(input) {
|
|
2882
2571
|
try {
|
|
2883
2572
|
const validated = validateInput(GetCustomFieldsInputSchema, input);
|
|
@@ -2895,27 +2584,7 @@ async function handleGetCustomFields(input) {
|
|
|
2895
2584
|
// src/tools/create-issue-link.ts
|
|
2896
2585
|
var log18 = createLogger("tool:create-issue-link");
|
|
2897
2586
|
var createIssueLinkTool = {
|
|
2898
|
-
|
|
2899
|
-
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.',
|
|
2900
|
-
inputSchema: {
|
|
2901
|
-
type: "object",
|
|
2902
|
-
properties: {
|
|
2903
|
-
fromIssue: {
|
|
2904
|
-
type: "string",
|
|
2905
|
-
description: "Source issue key (e.g., PROJECT-123)"
|
|
2906
|
-
},
|
|
2907
|
-
toIssue: {
|
|
2908
|
-
type: "string",
|
|
2909
|
-
description: "Target issue key (e.g., PROJECT-456)"
|
|
2910
|
-
},
|
|
2911
|
-
linkType: {
|
|
2912
|
-
type: "string",
|
|
2913
|
-
description: 'Link type: "blocks" (fromIssue blocks toIssue), "is blocked by", "relates", "duplicates", "clones", or custom link type name'
|
|
2914
|
-
}
|
|
2915
|
-
},
|
|
2916
|
-
required: ["fromIssue", "toIssue", "linkType"]
|
|
2917
|
-
}
|
|
2918
|
-
};
|
|
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.'};
|
|
2919
2588
|
async function handleCreateIssueLink(input) {
|
|
2920
2589
|
try {
|
|
2921
2590
|
const validated = validateInput(CreateIssueLinkInputSchema, input);
|
|
@@ -2936,35 +2605,7 @@ async function handleCreateIssueLink(input) {
|
|
|
2936
2605
|
// src/tools/get-comments.ts
|
|
2937
2606
|
var log19 = createLogger("tool:get-comments");
|
|
2938
2607
|
var getCommentsTool = {
|
|
2939
|
-
|
|
2940
|
-
description: "Retrieves all comments for a Jira issue. Returns comment author, content, timestamps, and visibility settings. Supports pagination for issues with many comments.",
|
|
2941
|
-
inputSchema: {
|
|
2942
|
-
type: "object",
|
|
2943
|
-
properties: {
|
|
2944
|
-
issueKey: {
|
|
2945
|
-
type: "string",
|
|
2946
|
-
description: "Issue key to get comments for (e.g., PROJECT-123)"
|
|
2947
|
-
},
|
|
2948
|
-
maxResults: {
|
|
2949
|
-
type: "number",
|
|
2950
|
-
description: "Maximum number of comments to return (default: 50, max: 100)",
|
|
2951
|
-
minimum: 1,
|
|
2952
|
-
maximum: 100
|
|
2953
|
-
},
|
|
2954
|
-
orderBy: {
|
|
2955
|
-
type: "string",
|
|
2956
|
-
enum: ["created", "-created", "+created"],
|
|
2957
|
-
description: 'Sort order for comments: "created" or "+created" (oldest first), "-created" (newest first)'
|
|
2958
|
-
},
|
|
2959
|
-
startAt: {
|
|
2960
|
-
type: "number",
|
|
2961
|
-
description: "Index of first comment to return (for pagination, default: 0)",
|
|
2962
|
-
minimum: 0
|
|
2963
|
-
}
|
|
2964
|
-
},
|
|
2965
|
-
required: ["issueKey"]
|
|
2966
|
-
}
|
|
2967
|
-
};
|
|
2608
|
+
description: "Retrieves all comments for a Jira issue. Returns comment author, content, timestamps, and visibility settings. Supports pagination for issues with many comments."};
|
|
2968
2609
|
async function handleGetComments(input) {
|
|
2969
2610
|
try {
|
|
2970
2611
|
const validated = validateInput(GetCommentsInputSchema, input);
|
|
@@ -2991,7 +2632,6 @@ async function handleGetComments(input) {
|
|
|
2991
2632
|
// src/tools/add-attachment.ts
|
|
2992
2633
|
var log20 = createLogger("tool:add-attachment");
|
|
2993
2634
|
var addAttachmentTool = {
|
|
2994
|
-
name: TOOL_NAMES.ADD_ATTACHMENT,
|
|
2995
2635
|
description: `Uploads an attachment (image, document, etc.) to a Jira issue.
|
|
2996
2636
|
|
|
2997
2637
|
**EFFICIENT METHOD (recommended for large files):**
|
|
@@ -3002,37 +2642,7 @@ var addAttachmentTool = {
|
|
|
3002
2642
|
- content: Base64 encoded content - WARNING: HIGH token cost (~330,000 tokens for 1MB file)
|
|
3003
2643
|
Only suitable for small files (< 500 KB)
|
|
3004
2644
|
|
|
3005
|
-
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
|
|
3006
|
-
inputSchema: {
|
|
3007
|
-
type: "object",
|
|
3008
|
-
properties: {
|
|
3009
|
-
issueKey: {
|
|
3010
|
-
type: "string",
|
|
3011
|
-
description: "Issue key to add attachment to (e.g., PROJECT-123)"
|
|
3012
|
-
},
|
|
3013
|
-
filename: {
|
|
3014
|
-
type: "string",
|
|
3015
|
-
description: "Name of the file to attach (e.g., screenshot.png, document.pdf)",
|
|
3016
|
-
minLength: 1
|
|
3017
|
-
},
|
|
3018
|
-
fileUrl: {
|
|
3019
|
-
type: "string",
|
|
3020
|
-
description: "URL to download file from (efficient for remote files, ~60 tokens). Upload large files to Dropbox/S3/imgur first, then use URL.",
|
|
3021
|
-
format: "uri"
|
|
3022
|
-
},
|
|
3023
|
-
content: {
|
|
3024
|
-
type: "string",
|
|
3025
|
-
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."
|
|
3026
|
-
},
|
|
3027
|
-
isBase64: {
|
|
3028
|
-
type: "boolean",
|
|
3029
|
-
description: "Whether content is base64-encoded (default: true). Set to false for plain text files.",
|
|
3030
|
-
default: true
|
|
3031
|
-
}
|
|
3032
|
-
},
|
|
3033
|
-
required: ["issueKey", "filename"]
|
|
3034
|
-
}
|
|
3035
|
-
};
|
|
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!`};
|
|
3036
2646
|
async function handleAddAttachment(input) {
|
|
3037
2647
|
try {
|
|
3038
2648
|
const validated = validateInput(AddAttachmentInputSchema, input);
|
|
@@ -3067,19 +2677,7 @@ async function handleAddAttachment(input) {
|
|
|
3067
2677
|
// src/tools/get-attachments.ts
|
|
3068
2678
|
var log21 = createLogger("tool:get-attachments");
|
|
3069
2679
|
var getAttachmentsTool = {
|
|
3070
|
-
|
|
3071
|
-
description: "Lists all attachments on a Jira issue. Returns attachment metadata including filename, size, MIME type, author, and download URL.",
|
|
3072
|
-
inputSchema: {
|
|
3073
|
-
type: "object",
|
|
3074
|
-
properties: {
|
|
3075
|
-
issueKey: {
|
|
3076
|
-
type: "string",
|
|
3077
|
-
description: "Issue key to get attachments for (e.g., PROJECT-123)"
|
|
3078
|
-
}
|
|
3079
|
-
},
|
|
3080
|
-
required: ["issueKey"]
|
|
3081
|
-
}
|
|
3082
|
-
};
|
|
2680
|
+
description: "Lists all attachments on a Jira issue. Returns attachment metadata including filename, size, MIME type, author, and download URL."};
|
|
3083
2681
|
async function handleGetAttachments(input) {
|
|
3084
2682
|
try {
|
|
3085
2683
|
const validated = validateInput(GetAttachmentsInputSchema, input);
|
|
@@ -3096,20 +2694,7 @@ async function handleGetAttachments(input) {
|
|
|
3096
2694
|
// src/tools/delete-attachment.ts
|
|
3097
2695
|
var log22 = createLogger("tool:delete-attachment");
|
|
3098
2696
|
var deleteAttachmentTool = {
|
|
3099
|
-
|
|
3100
|
-
description: "Deletes an attachment from Jira by its attachment ID. Use jira_get_attachments to find attachment IDs.",
|
|
3101
|
-
inputSchema: {
|
|
3102
|
-
type: "object",
|
|
3103
|
-
properties: {
|
|
3104
|
-
attachmentId: {
|
|
3105
|
-
type: "string",
|
|
3106
|
-
description: "ID of the attachment to delete (can be found using jira_get_attachments)",
|
|
3107
|
-
minLength: 1
|
|
3108
|
-
}
|
|
3109
|
-
},
|
|
3110
|
-
required: ["attachmentId"]
|
|
3111
|
-
}
|
|
3112
|
-
};
|
|
2697
|
+
description: "Deletes an attachment from Jira by its attachment ID. Use jira_get_attachments to find attachment IDs."};
|
|
3113
2698
|
async function handleDeleteAttachment(input) {
|
|
3114
2699
|
try {
|
|
3115
2700
|
const validated = validateInput(DeleteAttachmentInputSchema, input);
|
|
@@ -3130,6 +2715,322 @@ async function handleDeleteAttachment(input) {
|
|
|
3130
2715
|
}
|
|
3131
2716
|
}
|
|
3132
2717
|
|
|
2718
|
+
// src/tools/get-transitions.ts
|
|
2719
|
+
var log23 = createLogger("tool:get-transitions");
|
|
2720
|
+
var getTransitionsTool = {
|
|
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."};
|
|
2722
|
+
async function handleGetTransitions(input) {
|
|
2723
|
+
try {
|
|
2724
|
+
const validated = validateInput(GetTransitionsInputSchema, input);
|
|
2725
|
+
log23.info(`Getting transitions for issue ${validated.issueKey}...`);
|
|
2726
|
+
const transitions = await getTransitions(validated.issueKey);
|
|
2727
|
+
log23.info(`Found ${transitions.length} transition(s) for ${validated.issueKey}`);
|
|
2728
|
+
return formatTransitionsResponse(transitions, validated.issueKey);
|
|
2729
|
+
} catch (error) {
|
|
2730
|
+
log23.error("Error in handleGetTransitions:", error);
|
|
2731
|
+
return handleError(error);
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
// src/tools/transition-issue.ts
|
|
2736
|
+
var log24 = createLogger("tool:transition-issue");
|
|
2737
|
+
var transitionIssueTool = {
|
|
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.'};
|
|
2739
|
+
async function handleTransitionIssue(input) {
|
|
2740
|
+
try {
|
|
2741
|
+
const validated = validateInput(TransitionIssueInputSchema, input);
|
|
2742
|
+
let transitionId = validated.transitionId;
|
|
2743
|
+
if (!transitionId && validated.transitionName) {
|
|
2744
|
+
log24.info(
|
|
2745
|
+
`Looking up transition "${validated.transitionName}" for issue ${validated.issueKey}...`
|
|
2746
|
+
);
|
|
2747
|
+
const transitions = await getTransitions(validated.issueKey);
|
|
2748
|
+
const match = transitions.find(
|
|
2749
|
+
(t) => t.name.toLowerCase() === validated.transitionName.toLowerCase()
|
|
2750
|
+
);
|
|
2751
|
+
if (!match) {
|
|
2752
|
+
const available = transitions.map((t) => `"${t.name}" (ID: ${t.id})`).join(", ");
|
|
2753
|
+
return {
|
|
2754
|
+
content: [
|
|
2755
|
+
{
|
|
2756
|
+
type: "text",
|
|
2757
|
+
text: `Transition "${validated.transitionName}" not found for issue ${validated.issueKey}.
|
|
2758
|
+
|
|
2759
|
+
Available transitions: ${available || "none"}`
|
|
2760
|
+
}
|
|
2761
|
+
]
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
transitionId = match.id;
|
|
2765
|
+
}
|
|
2766
|
+
log24.info(`Transitioning issue ${validated.issueKey} with transition ${transitionId}...`);
|
|
2767
|
+
const transitionOptions = {};
|
|
2768
|
+
if (validated.comment) transitionOptions.comment = validated.comment;
|
|
2769
|
+
if (validated.resolution) transitionOptions.resolution = validated.resolution;
|
|
2770
|
+
if (validated.format) transitionOptions.format = validated.format;
|
|
2771
|
+
await transitionIssue(validated.issueKey, transitionId, transitionOptions);
|
|
2772
|
+
const updatedIssue = await getIssue(validated.issueKey);
|
|
2773
|
+
log24.info(`Transitioned issue ${validated.issueKey} to ${updatedIssue.fields.status.name}`);
|
|
2774
|
+
return formatIssueResponse(updatedIssue);
|
|
2775
|
+
} catch (error) {
|
|
2776
|
+
log24.error("Error in handleTransitionIssue:", error);
|
|
2777
|
+
return handleError(error);
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
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
|
+
|
|
3133
3034
|
// src/index.ts
|
|
3134
3035
|
if (process.env.NODE_ENV !== "production") {
|
|
3135
3036
|
try {
|
|
@@ -3138,50 +3039,172 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
3138
3039
|
} catch {
|
|
3139
3040
|
}
|
|
3140
3041
|
}
|
|
3141
|
-
var
|
|
3142
|
-
var
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
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
|
+
}
|
|
3185
3208
|
];
|
|
3186
3209
|
async function main() {
|
|
3187
3210
|
const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
|
|
@@ -3209,50 +3232,45 @@ async function main() {
|
|
|
3209
3232
|
version = pkg.default?.version || pkg.version || version;
|
|
3210
3233
|
} catch {
|
|
3211
3234
|
}
|
|
3212
|
-
const server = new
|
|
3235
|
+
const server = new McpServer(
|
|
3213
3236
|
{
|
|
3214
3237
|
name: "jira-mcp-server",
|
|
3215
3238
|
version
|
|
3216
3239
|
},
|
|
3217
3240
|
{
|
|
3218
3241
|
capabilities: {
|
|
3219
|
-
resources: {},
|
|
3220
3242
|
tools: {}
|
|
3221
3243
|
}
|
|
3222
3244
|
}
|
|
3223
3245
|
);
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
});
|
|
3247
|
-
server.setRequestHandler(ReadResourceRequestSchema, async () => {
|
|
3248
|
-
throw new Error("Resource reading not implemented");
|
|
3249
|
-
});
|
|
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)`);
|
|
3250
3268
|
const transport = new StdioServerTransport();
|
|
3251
3269
|
await server.connect(transport);
|
|
3252
3270
|
console.error("\u{1F680} Jira MCP server running on stdio");
|
|
3253
3271
|
}
|
|
3254
3272
|
main().catch((error) => {
|
|
3255
|
-
|
|
3273
|
+
log26.error("Fatal error:", error);
|
|
3256
3274
|
process.exit(1);
|
|
3257
3275
|
});
|
|
3258
3276
|
//# sourceMappingURL=index.js.map
|