mcp-jira-stdio 1.5.5 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/index.js +184 -35
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -21,10 +21,13 @@ A Model Context Protocol (MCP) server for Jira API integration. Enables reading,
|
|
|
21
21
|
The fastest way to add this MCP server to Claude Code:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
claude mcp add jira npx mcp-jira-stdio@latest
|
|
24
|
+
claude mcp add jira npx mcp-jira-stdio@latest \
|
|
25
|
+
--env JIRA_BASE_URL=https://yourcompany.atlassian.net \
|
|
26
|
+
--env JIRA_EMAIL=your-email@example.com \
|
|
27
|
+
--env JIRA_API_TOKEN=your-api-token
|
|
25
28
|
```
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
Replace the values with your actual Jira credentials:
|
|
28
31
|
- **JIRA_BASE_URL**: Your Jira instance URL (e.g., `https://yourcompany.atlassian.net`)
|
|
29
32
|
- **JIRA_EMAIL**: Your Jira account email
|
|
30
33
|
- **JIRA_API_TOKEN**: Your Jira API token ([generate here](https://id.atlassian.com/manage-profile/security/api-tokens))
|
|
@@ -119,7 +122,10 @@ task jira:projects
|
|
|
119
122
|
Use the quick install command (recommended):
|
|
120
123
|
|
|
121
124
|
```bash
|
|
122
|
-
claude mcp add jira npx mcp-jira-stdio@latest
|
|
125
|
+
claude mcp add jira npx mcp-jira-stdio@latest \
|
|
126
|
+
--env JIRA_BASE_URL=https://yourcompany.atlassian.net \
|
|
127
|
+
--env JIRA_EMAIL=your-email@example.com \
|
|
128
|
+
--env JIRA_API_TOKEN=your-api-token
|
|
123
129
|
```
|
|
124
130
|
|
|
125
131
|
#### For Claude Desktop
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
4
4
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import axios from 'axios';
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
+
import mdToAdf from 'md-to-adf';
|
|
7
8
|
|
|
8
9
|
var __create = Object.create;
|
|
9
10
|
var __defProp = Object.defineProperty;
|
|
@@ -439,7 +440,7 @@ var require_package2 = __commonJS({
|
|
|
439
440
|
"package.json"(exports, module) {
|
|
440
441
|
module.exports = {
|
|
441
442
|
name: "mcp-jira-stdio",
|
|
442
|
-
version: "1.
|
|
443
|
+
version: "1.6.1",
|
|
443
444
|
description: "Model Context Protocol (MCP) server for Jira API integration",
|
|
444
445
|
author: "graslt",
|
|
445
446
|
license: "MIT",
|
|
@@ -490,6 +491,7 @@ var require_package2 = __commonJS({
|
|
|
490
491
|
dependencies: {
|
|
491
492
|
"@modelcontextprotocol/sdk": "^1.17.1",
|
|
492
493
|
axios: "^1.7.0",
|
|
494
|
+
"md-to-adf": "^0.6.4",
|
|
493
495
|
zod: "^3.22.4"
|
|
494
496
|
},
|
|
495
497
|
devDependencies: {
|
|
@@ -557,7 +559,8 @@ var TOOL_NAMES = {
|
|
|
557
559
|
GET_PROJECT_INFO: "jira_get_project_info",
|
|
558
560
|
CREATE_SUBTASK: "jira_create_subtask",
|
|
559
561
|
GET_CREATE_META: "jira_get_create_meta",
|
|
560
|
-
GET_CUSTOM_FIELDS: "jira_get_custom_fields"
|
|
562
|
+
GET_CUSTOM_FIELDS: "jira_get_custom_fields",
|
|
563
|
+
CREATE_ISSUE_LINK: "jira_create_issue_link"
|
|
561
564
|
};
|
|
562
565
|
|
|
563
566
|
// src/utils/logger.ts
|
|
@@ -759,7 +762,10 @@ var CreateIssueInputSchema = z.object({
|
|
|
759
762
|
customFields: z.record(z.any()).optional().describe(
|
|
760
763
|
'Additional Jira field mappings, e.g. { "customfield_12345": value }. Use for required custom fields.'
|
|
761
764
|
),
|
|
762
|
-
returnIssue: z.boolean().optional().describe("When false, skip fetching full issue after creation")
|
|
765
|
+
returnIssue: z.boolean().optional().describe("When false, skip fetching full issue after creation"),
|
|
766
|
+
format: z.enum(["markdown", "adf", "plain"]).optional().default("markdown").describe(
|
|
767
|
+
'Description format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"'
|
|
768
|
+
)
|
|
763
769
|
});
|
|
764
770
|
var UpdateIssueInputSchema = z.object({
|
|
765
771
|
issueKey: z.string().describe("Issue key to update").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
|
|
@@ -769,7 +775,10 @@ var UpdateIssueInputSchema = z.object({
|
|
|
769
775
|
assignee: z.string().optional().describe("New assignee account ID"),
|
|
770
776
|
labels: z.array(z.string()).optional().describe("New labels (replaces existing)"),
|
|
771
777
|
components: z.array(z.string()).optional().describe("New components (replaces existing)"),
|
|
772
|
-
returnIssue: z.boolean().optional().describe("When false, skip fetching full issue after update")
|
|
778
|
+
returnIssue: z.boolean().optional().describe("When false, skip fetching full issue after update"),
|
|
779
|
+
format: z.enum(["markdown", "adf", "plain"]).optional().default("markdown").describe(
|
|
780
|
+
'Description format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"'
|
|
781
|
+
)
|
|
773
782
|
});
|
|
774
783
|
var GetMyIssuesInputSchema = z.object({
|
|
775
784
|
nextPageToken: z.string().optional().describe(
|
|
@@ -800,7 +809,10 @@ var AddCommentInputSchema = z.object({
|
|
|
800
809
|
visibility: z.object({
|
|
801
810
|
type: z.enum(["group", "role"]).describe("Visibility type"),
|
|
802
811
|
value: z.string().describe("Group name or role name")
|
|
803
|
-
}).optional().describe("Comment visibility restrictions")
|
|
812
|
+
}).optional().describe("Comment visibility restrictions"),
|
|
813
|
+
format: z.enum(["markdown", "adf", "plain"]).optional().default("markdown").describe(
|
|
814
|
+
'Comment format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"'
|
|
815
|
+
)
|
|
804
816
|
});
|
|
805
817
|
var GetProjectInfoInputSchema = z.object({
|
|
806
818
|
projectKey: z.string().describe("Project key to get detailed information for").refine((v) => isValidProjectKey(v), "Invalid project key format"),
|
|
@@ -813,7 +825,10 @@ var CreateSubtaskInputSchema = z.object({
|
|
|
813
825
|
priority: z.string().optional().describe("Subtask priority"),
|
|
814
826
|
assignee: z.string().optional().describe("Assignee account ID"),
|
|
815
827
|
labels: z.array(z.string()).optional().describe("Subtask labels"),
|
|
816
|
-
components: z.array(z.string()).optional().describe("Component names")
|
|
828
|
+
components: z.array(z.string()).optional().describe("Component names"),
|
|
829
|
+
format: z.enum(["markdown", "adf", "plain"]).optional().default("markdown").describe(
|
|
830
|
+
'Description format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"'
|
|
831
|
+
)
|
|
817
832
|
});
|
|
818
833
|
var GetCreateMetaInputSchema = z.object({
|
|
819
834
|
projectKey: z.string().describe("Project key to get create metadata for").refine((v) => isValidProjectKey(v), "Invalid project key format"),
|
|
@@ -822,12 +837,28 @@ var GetCreateMetaInputSchema = z.object({
|
|
|
822
837
|
var GetCustomFieldsInputSchema = z.object({
|
|
823
838
|
projectKey: z.string().optional().describe("Project key to filter custom fields (optional)").refine((v) => v ? isValidProjectKey(v) : true, "Invalid project key format")
|
|
824
839
|
});
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
840
|
+
var CreateIssueLinkInputSchema = z.object({
|
|
841
|
+
fromIssue: z.string().describe("Source issue key").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
|
|
842
|
+
toIssue: z.string().describe("Target issue key").refine((v) => isValidIssueKey(v), "Invalid issue key format"),
|
|
843
|
+
linkType: z.string().describe(
|
|
844
|
+
'Link type: "blocks", "is blocked by", "relates", "duplicates", "clones", or custom link type name'
|
|
845
|
+
)
|
|
846
|
+
});
|
|
847
|
+
function ensureAdfDescription(desc, format = "markdown") {
|
|
828
848
|
if (!desc) return desc;
|
|
829
849
|
if (typeof desc === "object") return desc;
|
|
830
850
|
if (typeof desc !== "string") return desc;
|
|
851
|
+
if (format === "markdown") {
|
|
852
|
+
try {
|
|
853
|
+
return mdToAdf(desc);
|
|
854
|
+
} catch (error) {
|
|
855
|
+
console.warn("Markdown to ADF conversion failed, falling back to plain text:", error);
|
|
856
|
+
format = "plain";
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (format === "adf") {
|
|
860
|
+
return desc;
|
|
861
|
+
}
|
|
831
862
|
const urlRegex = /https?:\/\/[^\s)]+/g;
|
|
832
863
|
const makeTextNodes = (text) => {
|
|
833
864
|
const nodes = [];
|
|
@@ -993,8 +1024,8 @@ async function searchIssues(options) {
|
|
|
993
1024
|
if (options.fields) {
|
|
994
1025
|
data.fields = options.fields;
|
|
995
1026
|
}
|
|
996
|
-
if (options.expand) {
|
|
997
|
-
data.expand = options.expand;
|
|
1027
|
+
if (options.expand && options.expand.length > 0) {
|
|
1028
|
+
data.expand = options.expand.join(",");
|
|
998
1029
|
}
|
|
999
1030
|
const config = {
|
|
1000
1031
|
method: "POST",
|
|
@@ -1010,7 +1041,10 @@ async function createIssue(issueData, options = { returnIssue: true }) {
|
|
|
1010
1041
|
issuetype: { name: issueData.issueType }
|
|
1011
1042
|
};
|
|
1012
1043
|
if (issueData.description !== void 0) {
|
|
1013
|
-
fields.description = ensureAdfDescription(
|
|
1044
|
+
fields.description = ensureAdfDescription(
|
|
1045
|
+
issueData.description,
|
|
1046
|
+
issueData.format || "markdown"
|
|
1047
|
+
);
|
|
1014
1048
|
}
|
|
1015
1049
|
if (issueData.priority) {
|
|
1016
1050
|
fields.priority = { name: issueData.priority };
|
|
@@ -1048,7 +1082,7 @@ async function updateIssue(issueKey, updates) {
|
|
|
1048
1082
|
fields.summary = updates.summary;
|
|
1049
1083
|
}
|
|
1050
1084
|
if (updates.description !== void 0) {
|
|
1051
|
-
fields.description = ensureAdfDescription(updates.description);
|
|
1085
|
+
fields.description = ensureAdfDescription(updates.description, updates.format || "markdown");
|
|
1052
1086
|
}
|
|
1053
1087
|
if (updates.priority !== void 0) {
|
|
1054
1088
|
fields.priority = { name: updates.priority };
|
|
@@ -1145,8 +1179,8 @@ async function getStatuses(options = {}) {
|
|
|
1145
1179
|
}
|
|
1146
1180
|
return await makeJiraRequest(config);
|
|
1147
1181
|
}
|
|
1148
|
-
async function addComment(issueKey, body, visibility) {
|
|
1149
|
-
const adfBody = ensureAdfDescription(body);
|
|
1182
|
+
async function addComment(issueKey, body, visibility, format) {
|
|
1183
|
+
const adfBody = ensureAdfDescription(body, format || "markdown");
|
|
1150
1184
|
const data = {
|
|
1151
1185
|
body: adfBody
|
|
1152
1186
|
};
|
|
@@ -1187,7 +1221,10 @@ async function createSubtask(parentIssueKey, subtaskData) {
|
|
|
1187
1221
|
issuetype: { id: subtaskType.id }
|
|
1188
1222
|
};
|
|
1189
1223
|
if (subtaskData.description !== void 0) {
|
|
1190
|
-
fields.description = ensureAdfDescription(
|
|
1224
|
+
fields.description = ensureAdfDescription(
|
|
1225
|
+
subtaskData.description,
|
|
1226
|
+
subtaskData.format || "markdown"
|
|
1227
|
+
);
|
|
1191
1228
|
}
|
|
1192
1229
|
if (subtaskData.priority) {
|
|
1193
1230
|
fields.priority = { name: subtaskData.priority };
|
|
@@ -1234,6 +1271,40 @@ async function getFields() {
|
|
|
1234
1271
|
};
|
|
1235
1272
|
return await makeJiraRequest(config);
|
|
1236
1273
|
}
|
|
1274
|
+
async function createIssueLink(fromIssue, toIssue, linkType) {
|
|
1275
|
+
const linkTypeMap = {
|
|
1276
|
+
blocks: "Blocks",
|
|
1277
|
+
"is blocked by": "Blocks",
|
|
1278
|
+
relates: "Relates",
|
|
1279
|
+
"relates to": "Relates",
|
|
1280
|
+
duplicates: "Duplicate",
|
|
1281
|
+
duplicate: "Duplicate",
|
|
1282
|
+
"is duplicated by": "Duplicate",
|
|
1283
|
+
clones: "Cloners",
|
|
1284
|
+
"is cloned by": "Cloners"
|
|
1285
|
+
};
|
|
1286
|
+
const normalizedLinkType = linkType.toLowerCase();
|
|
1287
|
+
const jiraLinkType = linkTypeMap[normalizedLinkType] || linkType;
|
|
1288
|
+
const isInward = normalizedLinkType === "is blocked by" || normalizedLinkType === "is duplicated by" || normalizedLinkType === "is cloned by";
|
|
1289
|
+
const data = {
|
|
1290
|
+
type: {
|
|
1291
|
+
name: jiraLinkType
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
if (isInward) {
|
|
1295
|
+
data.inwardIssue = { key: fromIssue };
|
|
1296
|
+
data.outwardIssue = { key: toIssue };
|
|
1297
|
+
} else {
|
|
1298
|
+
data.outwardIssue = { key: fromIssue };
|
|
1299
|
+
data.inwardIssue = { key: toIssue };
|
|
1300
|
+
}
|
|
1301
|
+
const config = {
|
|
1302
|
+
method: "POST",
|
|
1303
|
+
url: "/issueLink",
|
|
1304
|
+
data
|
|
1305
|
+
};
|
|
1306
|
+
await makeJiraRequest(config);
|
|
1307
|
+
}
|
|
1237
1308
|
|
|
1238
1309
|
// src/utils/redaction.ts
|
|
1239
1310
|
function isRedactionEnabled() {
|
|
@@ -1363,14 +1434,15 @@ function formatSearchResultsResponse(result) {
|
|
|
1363
1434
|
return `\u2022 **${issue.key}** - ${summary}
|
|
1364
1435
|
Status: ${status} | Assignee: ${assignee} | Priority: ${priority}`;
|
|
1365
1436
|
}).join("\n\n");
|
|
1437
|
+
const total = result.total ?? result.issues.length;
|
|
1366
1438
|
const rangeStart = result.startAt + 1;
|
|
1367
|
-
const rangeEnd = Math.min(result.startAt + result.maxResults,
|
|
1368
|
-
const showRangeInline =
|
|
1439
|
+
const rangeEnd = Math.min(result.startAt + result.maxResults, total);
|
|
1440
|
+
const showRangeInline = total > result.maxResults || result.startAt > 0;
|
|
1369
1441
|
let paginationInfo = "";
|
|
1370
1442
|
if (showRangeInline) {
|
|
1371
1443
|
paginationInfo = `
|
|
1372
1444
|
|
|
1373
|
-
*Showing ${rangeStart}-${rangeEnd} of ${
|
|
1445
|
+
*Showing ${rangeStart}-${rangeEnd} of ${total} results*`;
|
|
1374
1446
|
}
|
|
1375
1447
|
if (result.nextPageToken && !result.isLast) {
|
|
1376
1448
|
paginationInfo += `
|
|
@@ -1382,7 +1454,7 @@ function formatSearchResultsResponse(result) {
|
|
|
1382
1454
|
content: [
|
|
1383
1455
|
{
|
|
1384
1456
|
type: "text",
|
|
1385
|
-
text: `Found ${
|
|
1457
|
+
text: `Found ${total} issue(s)${showRangeInline ? ` (showing ${rangeStart}-${rangeEnd})` : ""}:
|
|
1386
1458
|
|
|
1387
1459
|
${issuesList}${paginationInfo}`
|
|
1388
1460
|
}
|
|
@@ -1859,7 +1931,7 @@ async function handleSearchIssues(input) {
|
|
|
1859
1931
|
if (validated.fields !== void 0) searchParams.fields = validated.fields;
|
|
1860
1932
|
if (validated.expand !== void 0) searchParams.expand = validated.expand;
|
|
1861
1933
|
const result = await searchIssues(searchParams);
|
|
1862
|
-
log5.info(`Found ${result.total} issue(s), showing ${result.issues.length}`);
|
|
1934
|
+
log5.info(`Found ${result.total ?? 0} issue(s), showing ${result.issues.length}`);
|
|
1863
1935
|
return formatSearchResultsResponse(result);
|
|
1864
1936
|
} catch (error) {
|
|
1865
1937
|
log5.error("Error in handleSearchIssues:", error);
|
|
@@ -1911,7 +1983,9 @@ async function handleGetMyIssues(input) {
|
|
|
1911
1983
|
if (validated.fields !== void 0) getParams.fields = validated.fields;
|
|
1912
1984
|
if (validated.expand !== void 0) getParams.expand = validated.expand;
|
|
1913
1985
|
const result = await getMyIssues(getParams);
|
|
1914
|
-
log6.info(
|
|
1986
|
+
log6.info(
|
|
1987
|
+
`Found ${result.total ?? 0} issue(s) assigned to you, showing ${result.issues.length}`
|
|
1988
|
+
);
|
|
1915
1989
|
return formatSearchResultsResponse(result);
|
|
1916
1990
|
} catch (error) {
|
|
1917
1991
|
log6.error("Error in handleGetMyIssues:", error);
|
|
@@ -2088,7 +2162,7 @@ async function handleGetStatuses(input) {
|
|
|
2088
2162
|
var log11 = createLogger("tool:create-issue");
|
|
2089
2163
|
var createIssueTool = {
|
|
2090
2164
|
name: TOOL_NAMES.CREATE_ISSUE,
|
|
2091
|
-
description: 'Creates a new Jira issue in the specified project. Supports setting issue type, priority, assignee, labels, components, and custom fields. Description
|
|
2165
|
+
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.',
|
|
2092
2166
|
inputSchema: {
|
|
2093
2167
|
type: "object",
|
|
2094
2168
|
properties: {
|
|
@@ -2103,7 +2177,7 @@ var createIssueTool = {
|
|
|
2103
2177
|
},
|
|
2104
2178
|
description: {
|
|
2105
2179
|
anyOf: [{ type: "string" }, { type: "object" }],
|
|
2106
|
-
description:
|
|
2180
|
+
description: 'Detailed issue description (optional). Format depends on the "format" parameter.'
|
|
2107
2181
|
},
|
|
2108
2182
|
issueType: {
|
|
2109
2183
|
type: "string",
|
|
@@ -2138,6 +2212,12 @@ var createIssueTool = {
|
|
|
2138
2212
|
type: "boolean",
|
|
2139
2213
|
description: "If false, returns only the issue key without fetching full details",
|
|
2140
2214
|
default: true
|
|
2215
|
+
},
|
|
2216
|
+
format: {
|
|
2217
|
+
type: "string",
|
|
2218
|
+
enum: ["markdown", "adf", "plain"],
|
|
2219
|
+
description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
|
|
2220
|
+
default: "markdown"
|
|
2141
2221
|
}
|
|
2142
2222
|
},
|
|
2143
2223
|
required: ["projectKey", "summary", "issueType"]
|
|
@@ -2158,6 +2238,7 @@ async function handleCreateIssue(input) {
|
|
|
2158
2238
|
if (validated.labels !== void 0) createParams.labels = validated.labels;
|
|
2159
2239
|
if (validated.components !== void 0) createParams.components = validated.components;
|
|
2160
2240
|
if (validated.customFields !== void 0) createParams.customFields = validated.customFields;
|
|
2241
|
+
if (validated.format !== void 0) createParams.format = validated.format;
|
|
2161
2242
|
let issueOrKey;
|
|
2162
2243
|
if (validated.returnIssue === false) {
|
|
2163
2244
|
issueOrKey = await createIssue(createParams, { returnIssue: false });
|
|
@@ -2182,7 +2263,7 @@ async function handleCreateIssue(input) {
|
|
|
2182
2263
|
var log12 = createLogger("tool:update-issue");
|
|
2183
2264
|
var updateIssueTool = {
|
|
2184
2265
|
name: TOOL_NAMES.UPDATE_ISSUE,
|
|
2185
|
-
description: 'Updates an existing Jira issue by its key. Supports updating summary, description, priority, assignee, labels, and components. Description
|
|
2266
|
+
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.',
|
|
2186
2267
|
inputSchema: {
|
|
2187
2268
|
type: "object",
|
|
2188
2269
|
properties: {
|
|
@@ -2196,7 +2277,7 @@ var updateIssueTool = {
|
|
|
2196
2277
|
},
|
|
2197
2278
|
description: {
|
|
2198
2279
|
anyOf: [{ type: "string" }, { type: "object" }],
|
|
2199
|
-
description:
|
|
2280
|
+
description: 'New issue description (optional). Format depends on the "format" parameter.'
|
|
2200
2281
|
},
|
|
2201
2282
|
priority: {
|
|
2202
2283
|
type: "string",
|
|
@@ -2220,6 +2301,12 @@ var updateIssueTool = {
|
|
|
2220
2301
|
type: "boolean",
|
|
2221
2302
|
description: "If false, returns a success message without fetching the updated issue",
|
|
2222
2303
|
default: true
|
|
2304
|
+
},
|
|
2305
|
+
format: {
|
|
2306
|
+
type: "string",
|
|
2307
|
+
enum: ["markdown", "adf", "plain"],
|
|
2308
|
+
description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
|
|
2309
|
+
default: "markdown"
|
|
2223
2310
|
}
|
|
2224
2311
|
},
|
|
2225
2312
|
required: ["issueKey"]
|
|
@@ -2236,6 +2323,7 @@ async function handleUpdateIssue(input) {
|
|
|
2236
2323
|
if (validated.assignee !== void 0) updateParams.assignee = validated.assignee;
|
|
2237
2324
|
if (validated.labels !== void 0) updateParams.labels = validated.labels;
|
|
2238
2325
|
if (validated.components !== void 0) updateParams.components = validated.components;
|
|
2326
|
+
if (validated.format !== void 0) updateParams.format = validated.format;
|
|
2239
2327
|
await updateIssue(validated.issueKey, updateParams);
|
|
2240
2328
|
if (validated.returnIssue === false) {
|
|
2241
2329
|
log12.info(`Updated issue ${validated.issueKey}`);
|
|
@@ -2254,7 +2342,7 @@ async function handleUpdateIssue(input) {
|
|
|
2254
2342
|
var log13 = createLogger("tool:add-comment");
|
|
2255
2343
|
var addCommentTool = {
|
|
2256
2344
|
name: TOOL_NAMES.ADD_COMMENT,
|
|
2257
|
-
description:
|
|
2345
|
+
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.',
|
|
2258
2346
|
inputSchema: {
|
|
2259
2347
|
type: "object",
|
|
2260
2348
|
properties: {
|
|
@@ -2264,7 +2352,7 @@ var addCommentTool = {
|
|
|
2264
2352
|
},
|
|
2265
2353
|
body: {
|
|
2266
2354
|
type: "string",
|
|
2267
|
-
description:
|
|
2355
|
+
description: 'Comment body text. Format depends on the "format" parameter.',
|
|
2268
2356
|
minLength: 1
|
|
2269
2357
|
},
|
|
2270
2358
|
visibility: {
|
|
@@ -2282,6 +2370,12 @@ var addCommentTool = {
|
|
|
2282
2370
|
}
|
|
2283
2371
|
},
|
|
2284
2372
|
required: ["type", "value"]
|
|
2373
|
+
},
|
|
2374
|
+
format: {
|
|
2375
|
+
type: "string",
|
|
2376
|
+
enum: ["markdown", "adf", "plain"],
|
|
2377
|
+
description: 'Comment format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
|
|
2378
|
+
default: "markdown"
|
|
2285
2379
|
}
|
|
2286
2380
|
},
|
|
2287
2381
|
required: ["issueKey", "body"]
|
|
@@ -2291,7 +2385,12 @@ async function handleAddComment(input) {
|
|
|
2291
2385
|
try {
|
|
2292
2386
|
const validated = validateInput(AddCommentInputSchema, input);
|
|
2293
2387
|
log13.info(`Adding comment to issue ${validated.issueKey}...`);
|
|
2294
|
-
const comment = await addComment(
|
|
2388
|
+
const comment = await addComment(
|
|
2389
|
+
validated.issueKey,
|
|
2390
|
+
validated.body,
|
|
2391
|
+
validated.visibility,
|
|
2392
|
+
validated.format
|
|
2393
|
+
);
|
|
2295
2394
|
log13.info(`Added comment to ${validated.issueKey}`);
|
|
2296
2395
|
return formatCommentResponse(comment);
|
|
2297
2396
|
} catch (error) {
|
|
@@ -2339,7 +2438,7 @@ async function handleGetProjectInfo(input) {
|
|
|
2339
2438
|
var log15 = createLogger("tool:create-subtask");
|
|
2340
2439
|
var createSubtaskTool = {
|
|
2341
2440
|
name: TOOL_NAMES.CREATE_SUBTASK,
|
|
2342
|
-
description:
|
|
2441
|
+
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).',
|
|
2343
2442
|
inputSchema: {
|
|
2344
2443
|
type: "object",
|
|
2345
2444
|
properties: {
|
|
@@ -2354,7 +2453,7 @@ var createSubtaskTool = {
|
|
|
2354
2453
|
},
|
|
2355
2454
|
description: {
|
|
2356
2455
|
type: "string",
|
|
2357
|
-
description:
|
|
2456
|
+
description: 'Detailed subtask description (optional). Format depends on the "format" parameter.'
|
|
2358
2457
|
},
|
|
2359
2458
|
priority: {
|
|
2360
2459
|
type: "string",
|
|
@@ -2375,6 +2474,12 @@ var createSubtaskTool = {
|
|
|
2375
2474
|
items: { type: "string" },
|
|
2376
2475
|
description: "Component names (optional)",
|
|
2377
2476
|
default: []
|
|
2477
|
+
},
|
|
2478
|
+
format: {
|
|
2479
|
+
type: "string",
|
|
2480
|
+
enum: ["markdown", "adf", "plain"],
|
|
2481
|
+
description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)',
|
|
2482
|
+
default: "markdown"
|
|
2378
2483
|
}
|
|
2379
2484
|
},
|
|
2380
2485
|
required: ["parentIssueKey", "summary"]
|
|
@@ -2392,6 +2497,7 @@ async function handleCreateSubtask(input) {
|
|
|
2392
2497
|
if (validated.assignee !== void 0) subtaskParams.assignee = validated.assignee;
|
|
2393
2498
|
if (validated.labels !== void 0) subtaskParams.labels = validated.labels;
|
|
2394
2499
|
if (validated.components !== void 0) subtaskParams.components = validated.components;
|
|
2500
|
+
if (validated.format !== void 0) subtaskParams.format = validated.format;
|
|
2395
2501
|
const subtask = await createSubtask(validated.parentIssueKey, subtaskParams);
|
|
2396
2502
|
log15.info(`Created subtask ${subtask.key}`);
|
|
2397
2503
|
return formatIssueResponse(subtask);
|
|
@@ -2471,6 +2577,47 @@ async function handleGetCustomFields(input) {
|
|
|
2471
2577
|
}
|
|
2472
2578
|
}
|
|
2473
2579
|
|
|
2580
|
+
// src/tools/create-issue-link.ts
|
|
2581
|
+
var log18 = createLogger("tool:create-issue-link");
|
|
2582
|
+
var createIssueLinkTool = {
|
|
2583
|
+
name: TOOL_NAMES.CREATE_ISSUE_LINK,
|
|
2584
|
+
description: 'Creates a link between two Jira issues. Supports common link types like "blocks", "relates", "duplicates", and "clones". Use this to establish relationships between issues.',
|
|
2585
|
+
inputSchema: {
|
|
2586
|
+
type: "object",
|
|
2587
|
+
properties: {
|
|
2588
|
+
fromIssue: {
|
|
2589
|
+
type: "string",
|
|
2590
|
+
description: "Source issue key (e.g., PROJECT-123)"
|
|
2591
|
+
},
|
|
2592
|
+
toIssue: {
|
|
2593
|
+
type: "string",
|
|
2594
|
+
description: "Target issue key (e.g., PROJECT-456)"
|
|
2595
|
+
},
|
|
2596
|
+
linkType: {
|
|
2597
|
+
type: "string",
|
|
2598
|
+
description: 'Link type: "blocks" (fromIssue blocks toIssue), "is blocked by", "relates", "duplicates", "clones", or custom link type name'
|
|
2599
|
+
}
|
|
2600
|
+
},
|
|
2601
|
+
required: ["fromIssue", "toIssue", "linkType"]
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
async function handleCreateIssueLink(input) {
|
|
2605
|
+
try {
|
|
2606
|
+
const validated = validateInput(CreateIssueLinkInputSchema, input);
|
|
2607
|
+
log18.info(
|
|
2608
|
+
`Creating ${validated.linkType} link from ${validated.fromIssue} to ${validated.toIssue}...`
|
|
2609
|
+
);
|
|
2610
|
+
await createIssueLink(validated.fromIssue, validated.toIssue, validated.linkType);
|
|
2611
|
+
log18.info("Issue link created successfully");
|
|
2612
|
+
return formatSuccessResponse(
|
|
2613
|
+
`Link created: ${validated.fromIssue} ${validated.linkType} ${validated.toIssue}`
|
|
2614
|
+
);
|
|
2615
|
+
} catch (error) {
|
|
2616
|
+
log18.error("Error in handleCreateIssueLink:", error);
|
|
2617
|
+
return handleError(error);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2474
2621
|
// src/index.ts
|
|
2475
2622
|
if (process.env.NODE_ENV !== "production") {
|
|
2476
2623
|
try {
|
|
@@ -2479,7 +2626,7 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
2479
2626
|
} catch {
|
|
2480
2627
|
}
|
|
2481
2628
|
}
|
|
2482
|
-
var
|
|
2629
|
+
var log19 = createLogger("server");
|
|
2483
2630
|
var toolHandlers = /* @__PURE__ */ new Map([
|
|
2484
2631
|
[TOOL_NAMES.GET_VISIBLE_PROJECTS, handleGetVisibleProjects],
|
|
2485
2632
|
[TOOL_NAMES.GET_ISSUE, handleGetIssue],
|
|
@@ -2495,7 +2642,8 @@ var toolHandlers = /* @__PURE__ */ new Map([
|
|
|
2495
2642
|
[TOOL_NAMES.GET_PROJECT_INFO, handleGetProjectInfo],
|
|
2496
2643
|
[TOOL_NAMES.CREATE_SUBTASK, handleCreateSubtask],
|
|
2497
2644
|
[TOOL_NAMES.GET_CREATE_META, handleGetCreateMeta],
|
|
2498
|
-
[TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields]
|
|
2645
|
+
[TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields],
|
|
2646
|
+
[TOOL_NAMES.CREATE_ISSUE_LINK, handleCreateIssueLink]
|
|
2499
2647
|
]);
|
|
2500
2648
|
var allTools = [
|
|
2501
2649
|
getVisibleProjectsTool,
|
|
@@ -2512,7 +2660,8 @@ var allTools = [
|
|
|
2512
2660
|
getProjectInfoTool,
|
|
2513
2661
|
createSubtaskTool,
|
|
2514
2662
|
getCreateMetaTool,
|
|
2515
|
-
getCustomFieldsTool
|
|
2663
|
+
getCustomFieldsTool,
|
|
2664
|
+
createIssueLinkTool
|
|
2516
2665
|
];
|
|
2517
2666
|
async function main() {
|
|
2518
2667
|
const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
|
|
@@ -2583,7 +2732,7 @@ async function main() {
|
|
|
2583
2732
|
console.error("\u{1F680} Jira MCP server running on stdio");
|
|
2584
2733
|
}
|
|
2585
2734
|
main().catch((error) => {
|
|
2586
|
-
|
|
2735
|
+
log19.error("Fatal error:", error);
|
|
2587
2736
|
process.exit(1);
|
|
2588
2737
|
});
|
|
2589
2738
|
//# sourceMappingURL=index.js.map
|