mcp-jira-stdio 1.4.0 → 1.5.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/dist/index.js +227 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -439,7 +439,7 @@ var require_package2 = __commonJS({
|
|
|
439
439
|
"package.json"(exports, module) {
|
|
440
440
|
module.exports = {
|
|
441
441
|
name: "mcp-jira-stdio",
|
|
442
|
-
version: "1.
|
|
442
|
+
version: "1.5.1",
|
|
443
443
|
description: "Model Context Protocol (MCP) server for Jira API integration",
|
|
444
444
|
author: "graslt",
|
|
445
445
|
license: "MIT",
|
|
@@ -555,7 +555,9 @@ var TOOL_NAMES = {
|
|
|
555
555
|
UPDATE_ISSUE: "jira_update_issue",
|
|
556
556
|
ADD_COMMENT: "jira_add_comment",
|
|
557
557
|
GET_PROJECT_INFO: "jira_get_project_info",
|
|
558
|
-
CREATE_SUBTASK: "jira_create_subtask"
|
|
558
|
+
CREATE_SUBTASK: "jira_create_subtask",
|
|
559
|
+
GET_CREATE_META: "jira_get_create_meta",
|
|
560
|
+
GET_CUSTOM_FIELDS: "jira_get_custom_fields"
|
|
559
561
|
};
|
|
560
562
|
|
|
561
563
|
// src/utils/logger.ts
|
|
@@ -813,6 +815,13 @@ var CreateSubtaskInputSchema = z.object({
|
|
|
813
815
|
labels: z.array(z.string()).optional().describe("Subtask labels"),
|
|
814
816
|
components: z.array(z.string()).optional().describe("Component names")
|
|
815
817
|
});
|
|
818
|
+
var GetCreateMetaInputSchema = z.object({
|
|
819
|
+
projectKey: z.string().describe("Project key to get create metadata for").refine((v) => isValidProjectKey(v), "Invalid project key format"),
|
|
820
|
+
issueTypeName: z.string().optional().describe("Specific issue type name to get metadata for (optional)")
|
|
821
|
+
});
|
|
822
|
+
var GetCustomFieldsInputSchema = z.object({
|
|
823
|
+
projectKey: z.string().optional().describe("Project key to filter custom fields (optional)").refine((v) => v ? isValidProjectKey(v) : true, "Invalid project key format")
|
|
824
|
+
});
|
|
816
825
|
|
|
817
826
|
// src/utils/api-helpers.ts
|
|
818
827
|
function ensureAdfDescription(desc) {
|
|
@@ -1125,7 +1134,10 @@ async function getStatuses(options = {}) {
|
|
|
1125
1134
|
return await makeJiraRequest(config);
|
|
1126
1135
|
}
|
|
1127
1136
|
async function addComment(issueKey, body, visibility) {
|
|
1128
|
-
const
|
|
1137
|
+
const adfBody = ensureAdfDescription(body);
|
|
1138
|
+
const data = {
|
|
1139
|
+
body: adfBody
|
|
1140
|
+
};
|
|
1129
1141
|
if (visibility) {
|
|
1130
1142
|
data.visibility = visibility;
|
|
1131
1143
|
}
|
|
@@ -1185,6 +1197,31 @@ async function createSubtask(parentIssueKey, subtaskData) {
|
|
|
1185
1197
|
const response = await makeJiraRequest(config);
|
|
1186
1198
|
return await getIssue(response.key);
|
|
1187
1199
|
}
|
|
1200
|
+
async function getCreateMeta(options = {}) {
|
|
1201
|
+
const params = {};
|
|
1202
|
+
if (options.projectKeys && options.projectKeys.length > 0) {
|
|
1203
|
+
params.projectKeys = options.projectKeys.join(",");
|
|
1204
|
+
}
|
|
1205
|
+
if (options.issueTypeNames && options.issueTypeNames.length > 0) {
|
|
1206
|
+
params.issuetypeNames = options.issueTypeNames.join(",");
|
|
1207
|
+
}
|
|
1208
|
+
if (options.expand) {
|
|
1209
|
+
params.expand = options.expand;
|
|
1210
|
+
}
|
|
1211
|
+
const config = {
|
|
1212
|
+
method: "GET",
|
|
1213
|
+
url: "/issue/createmeta",
|
|
1214
|
+
params
|
|
1215
|
+
};
|
|
1216
|
+
return await makeJiraRequest(config);
|
|
1217
|
+
}
|
|
1218
|
+
async function getFields() {
|
|
1219
|
+
const config = {
|
|
1220
|
+
method: "GET",
|
|
1221
|
+
url: "/field"
|
|
1222
|
+
};
|
|
1223
|
+
return await makeJiraRequest(config);
|
|
1224
|
+
}
|
|
1188
1225
|
|
|
1189
1226
|
// src/utils/redaction.ts
|
|
1190
1227
|
function isRedactionEnabled() {
|
|
@@ -1307,9 +1344,12 @@ function formatSearchResultsResponse(result) {
|
|
|
1307
1344
|
};
|
|
1308
1345
|
}
|
|
1309
1346
|
const issuesList = result.issues.map((issue) => {
|
|
1310
|
-
const assignee = issue.fields
|
|
1311
|
-
|
|
1312
|
-
|
|
1347
|
+
const assignee = issue.fields?.assignee?.displayName || "Unassigned";
|
|
1348
|
+
const summary = issue.fields?.summary || "No summary";
|
|
1349
|
+
const status = issue.fields?.status?.name || "Unknown";
|
|
1350
|
+
const priority = issue.fields?.priority?.name || "None";
|
|
1351
|
+
return `\u2022 **${issue.key}** - ${summary}
|
|
1352
|
+
Status: ${status} | Assignee: ${assignee} | Priority: ${priority}`;
|
|
1313
1353
|
}).join("\n\n");
|
|
1314
1354
|
const rangeStart = result.startAt + 1;
|
|
1315
1355
|
const rangeEnd = Math.min(result.startAt + result.maxResults, result.total);
|
|
@@ -1339,23 +1379,34 @@ ${issuesList}${paginationInfo}`
|
|
|
1339
1379
|
}
|
|
1340
1380
|
function formatErrorResponse(error) {
|
|
1341
1381
|
let errorMessage = "An unexpected error occurred.";
|
|
1382
|
+
let hasCustomFieldError = false;
|
|
1342
1383
|
if (typeof error === "string") {
|
|
1343
1384
|
errorMessage = error;
|
|
1385
|
+
hasCustomFieldError = error.includes("customfield_");
|
|
1344
1386
|
} else if (error instanceof Error) {
|
|
1345
1387
|
errorMessage = error.message;
|
|
1388
|
+
hasCustomFieldError = error.message.includes("customfield_");
|
|
1346
1389
|
} else if (error?.response?.data) {
|
|
1347
1390
|
const jiraError = error.response.data;
|
|
1348
1391
|
if (jiraError.errorMessages && jiraError.errorMessages.length > 0) {
|
|
1349
1392
|
errorMessage = jiraError.errorMessages.join(", ");
|
|
1350
1393
|
} else if (jiraError.errors) {
|
|
1351
|
-
|
|
1394
|
+
const errors = Object.entries(jiraError.errors);
|
|
1395
|
+
errorMessage = errors.map(([field, message]) => `${field}: ${message}`).join(", ");
|
|
1396
|
+
hasCustomFieldError = errors.some(([field]) => field.startsWith("customfield_"));
|
|
1352
1397
|
}
|
|
1353
1398
|
}
|
|
1399
|
+
let hint = "";
|
|
1400
|
+
if (hasCustomFieldError) {
|
|
1401
|
+
hint = `
|
|
1402
|
+
|
|
1403
|
+
\u{1F4A1} **Hint:** This error involves custom fields. Use 'jira_get_create_meta' tool to discover required fields and their allowed values before creating an issue.`;
|
|
1404
|
+
}
|
|
1354
1405
|
return {
|
|
1355
1406
|
content: [
|
|
1356
1407
|
{
|
|
1357
1408
|
type: "text",
|
|
1358
|
-
text: `\u274C Error: ${errorMessage}`
|
|
1409
|
+
text: `\u274C Error: ${errorMessage}${hint}`
|
|
1359
1410
|
}
|
|
1360
1411
|
]
|
|
1361
1412
|
};
|
|
@@ -1522,6 +1573,96 @@ Last Updated: ${new Date(project.insight.lastIssueUpdateTime).toISOString()}` :
|
|
|
1522
1573
|
]
|
|
1523
1574
|
};
|
|
1524
1575
|
}
|
|
1576
|
+
function formatCreateMetaResponse(meta) {
|
|
1577
|
+
if (!meta.projects || meta.projects.length === 0) {
|
|
1578
|
+
return {
|
|
1579
|
+
content: [
|
|
1580
|
+
{
|
|
1581
|
+
type: "text",
|
|
1582
|
+
text: "No create metadata found for the specified project."
|
|
1583
|
+
}
|
|
1584
|
+
]
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
const project = meta.projects[0];
|
|
1588
|
+
if (!project) {
|
|
1589
|
+
return {
|
|
1590
|
+
content: [
|
|
1591
|
+
{
|
|
1592
|
+
type: "text",
|
|
1593
|
+
text: "No create metadata found for the specified project."
|
|
1594
|
+
}
|
|
1595
|
+
]
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
const issueTypesList = project.issuetypes.map((issueType) => {
|
|
1599
|
+
const requiredFields = [];
|
|
1600
|
+
const optionalFields = [];
|
|
1601
|
+
Object.entries(issueType.fields).forEach(([fieldKey, field]) => {
|
|
1602
|
+
const allowedValuesText = field.allowedValues ? `
|
|
1603
|
+
Allowed values: ${field.allowedValues.map((v) => v.name || v.value || v.id).join(", ")}` : "";
|
|
1604
|
+
const defaultValueText = field.hasDefaultValue ? `
|
|
1605
|
+
Default: ${JSON.stringify(field.defaultValue)}` : "";
|
|
1606
|
+
const fieldInfo = ` \u2022 **${field.name}** (${fieldKey})
|
|
1607
|
+
Type: ${field.schema.type}${allowedValuesText}${defaultValueText}`;
|
|
1608
|
+
if (field.required) {
|
|
1609
|
+
requiredFields.push(fieldInfo);
|
|
1610
|
+
} else {
|
|
1611
|
+
optionalFields.push(fieldInfo);
|
|
1612
|
+
}
|
|
1613
|
+
});
|
|
1614
|
+
const requiredSection = requiredFields.length > 0 ? `**Required Fields:**
|
|
1615
|
+
${requiredFields.join("\n\n")}` : "No required fields";
|
|
1616
|
+
const optionalSection = optionalFields.length > 0 ? `
|
|
1617
|
+
|
|
1618
|
+
**Optional Fields:**
|
|
1619
|
+
${optionalFields.join("\n\n")}` : "";
|
|
1620
|
+
return `### ${issueType.name} (${issueType.subtask ? "Subtask" : "Issue"})
|
|
1621
|
+
${issueType.description}
|
|
1622
|
+
|
|
1623
|
+
${requiredSection}${optionalSection}`;
|
|
1624
|
+
}).join("\n\n---\n\n");
|
|
1625
|
+
return {
|
|
1626
|
+
content: [
|
|
1627
|
+
{
|
|
1628
|
+
type: "text",
|
|
1629
|
+
text: `**Create Metadata for Project: ${project.name} (${project.key})**
|
|
1630
|
+
|
|
1631
|
+
${issueTypesList}`
|
|
1632
|
+
}
|
|
1633
|
+
]
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
function formatCustomFieldsResponse(fields, projectKey) {
|
|
1637
|
+
const customFields = fields.filter((field) => field.custom);
|
|
1638
|
+
if (customFields.length === 0) {
|
|
1639
|
+
return {
|
|
1640
|
+
content: [
|
|
1641
|
+
{
|
|
1642
|
+
type: "text",
|
|
1643
|
+
text: projectKey ? `No custom fields found for project ${projectKey}.` : "No custom fields found."
|
|
1644
|
+
}
|
|
1645
|
+
]
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
const fieldsList = customFields.map((field) => {
|
|
1649
|
+
const schemaInfo = field.schema.custom ? `Custom Type: ${field.schema.custom}` : `Type: ${field.schema.type}`;
|
|
1650
|
+
return `\u2022 **${field.name}** (${field.key})
|
|
1651
|
+
${schemaInfo}
|
|
1652
|
+
ID: ${field.id}`;
|
|
1653
|
+
}).join("\n\n");
|
|
1654
|
+
const header = projectKey ? `Found ${customFields.length} custom field(s) for project ${projectKey}:` : `Found ${customFields.length} custom field(s):`;
|
|
1655
|
+
return {
|
|
1656
|
+
content: [
|
|
1657
|
+
{
|
|
1658
|
+
type: "text",
|
|
1659
|
+
text: `${header}
|
|
1660
|
+
|
|
1661
|
+
${fieldsList}`
|
|
1662
|
+
}
|
|
1663
|
+
]
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1525
1666
|
|
|
1526
1667
|
// src/utils/error-handler.ts
|
|
1527
1668
|
var log2 = createLogger("error-handler");
|
|
@@ -2248,6 +2389,76 @@ async function handleCreateSubtask(input) {
|
|
|
2248
2389
|
}
|
|
2249
2390
|
}
|
|
2250
2391
|
|
|
2392
|
+
// src/tools/get-create-meta.ts
|
|
2393
|
+
var log16 = createLogger("tool:get-create-meta");
|
|
2394
|
+
var getCreateMetaTool = {
|
|
2395
|
+
name: TOOL_NAMES.GET_CREATE_META,
|
|
2396
|
+
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.",
|
|
2397
|
+
inputSchema: {
|
|
2398
|
+
type: "object",
|
|
2399
|
+
properties: {
|
|
2400
|
+
projectKey: {
|
|
2401
|
+
type: "string",
|
|
2402
|
+
description: "Project key to get create metadata for (e.g., PROJECT)"
|
|
2403
|
+
},
|
|
2404
|
+
issueTypeName: {
|
|
2405
|
+
type: "string",
|
|
2406
|
+
description: "Specific issue type name to get metadata for (optional)"
|
|
2407
|
+
}
|
|
2408
|
+
},
|
|
2409
|
+
required: ["projectKey"]
|
|
2410
|
+
}
|
|
2411
|
+
};
|
|
2412
|
+
async function handleGetCreateMeta(input) {
|
|
2413
|
+
try {
|
|
2414
|
+
const validated = validateInput(GetCreateMetaInputSchema, input);
|
|
2415
|
+
log16.info(`Getting create metadata for project ${validated.projectKey}...`);
|
|
2416
|
+
const options = {
|
|
2417
|
+
projectKeys: [validated.projectKey],
|
|
2418
|
+
expand: "projects.issuetypes.fields"
|
|
2419
|
+
};
|
|
2420
|
+
if (validated.issueTypeName) {
|
|
2421
|
+
options.issueTypeNames = [validated.issueTypeName];
|
|
2422
|
+
}
|
|
2423
|
+
const meta = await getCreateMeta(options);
|
|
2424
|
+
log16.info(`Retrieved create metadata for ${validated.projectKey}`);
|
|
2425
|
+
return formatCreateMetaResponse(meta);
|
|
2426
|
+
} catch (error) {
|
|
2427
|
+
log16.error("Error in handleGetCreateMeta:", error);
|
|
2428
|
+
return handleError(error);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
// src/tools/get-custom-fields.ts
|
|
2433
|
+
var log17 = createLogger("tool:get-custom-fields");
|
|
2434
|
+
var getCustomFieldsTool = {
|
|
2435
|
+
name: TOOL_NAMES.GET_CUSTOM_FIELDS,
|
|
2436
|
+
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.",
|
|
2437
|
+
inputSchema: {
|
|
2438
|
+
type: "object",
|
|
2439
|
+
properties: {
|
|
2440
|
+
projectKey: {
|
|
2441
|
+
type: "string",
|
|
2442
|
+
description: "Project key to filter custom fields (optional)"
|
|
2443
|
+
}
|
|
2444
|
+
},
|
|
2445
|
+
required: []
|
|
2446
|
+
}
|
|
2447
|
+
};
|
|
2448
|
+
async function handleGetCustomFields(input) {
|
|
2449
|
+
try {
|
|
2450
|
+
const validated = validateInput(GetCustomFieldsInputSchema, input);
|
|
2451
|
+
const projectMsg = validated.projectKey ? ` for project ${validated.projectKey}` : "";
|
|
2452
|
+
log17.info(`Getting custom fields${projectMsg}...`);
|
|
2453
|
+
const fields = await getFields();
|
|
2454
|
+
log17.info(`Retrieved ${fields.filter((f) => f.custom).length} custom fields`);
|
|
2455
|
+
return formatCustomFieldsResponse(fields, validated.projectKey);
|
|
2456
|
+
} catch (error) {
|
|
2457
|
+
log17.error("Error in handleGetCustomFields:", error);
|
|
2458
|
+
return handleError(error);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2251
2462
|
// src/index.ts
|
|
2252
2463
|
if (process.env.NODE_ENV !== "production") {
|
|
2253
2464
|
try {
|
|
@@ -2256,7 +2467,7 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
2256
2467
|
} catch {
|
|
2257
2468
|
}
|
|
2258
2469
|
}
|
|
2259
|
-
var
|
|
2470
|
+
var log18 = createLogger("server");
|
|
2260
2471
|
var toolHandlers = /* @__PURE__ */ new Map([
|
|
2261
2472
|
[TOOL_NAMES.GET_VISIBLE_PROJECTS, handleGetVisibleProjects],
|
|
2262
2473
|
[TOOL_NAMES.GET_ISSUE, handleGetIssue],
|
|
@@ -2270,7 +2481,9 @@ var toolHandlers = /* @__PURE__ */ new Map([
|
|
|
2270
2481
|
[TOOL_NAMES.UPDATE_ISSUE, handleUpdateIssue],
|
|
2271
2482
|
[TOOL_NAMES.ADD_COMMENT, handleAddComment],
|
|
2272
2483
|
[TOOL_NAMES.GET_PROJECT_INFO, handleGetProjectInfo],
|
|
2273
|
-
[TOOL_NAMES.CREATE_SUBTASK, handleCreateSubtask]
|
|
2484
|
+
[TOOL_NAMES.CREATE_SUBTASK, handleCreateSubtask],
|
|
2485
|
+
[TOOL_NAMES.GET_CREATE_META, handleGetCreateMeta],
|
|
2486
|
+
[TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields]
|
|
2274
2487
|
]);
|
|
2275
2488
|
var allTools = [
|
|
2276
2489
|
getVisibleProjectsTool,
|
|
@@ -2285,7 +2498,9 @@ var allTools = [
|
|
|
2285
2498
|
updateIssueTool,
|
|
2286
2499
|
addCommentTool,
|
|
2287
2500
|
getProjectInfoTool,
|
|
2288
|
-
createSubtaskTool
|
|
2501
|
+
createSubtaskTool,
|
|
2502
|
+
getCreateMetaTool,
|
|
2503
|
+
getCustomFieldsTool
|
|
2289
2504
|
];
|
|
2290
2505
|
async function main() {
|
|
2291
2506
|
const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
|
|
@@ -2356,7 +2571,7 @@ async function main() {
|
|
|
2356
2571
|
console.error("\u{1F680} Jira MCP server running on stdio");
|
|
2357
2572
|
}
|
|
2358
2573
|
main().catch((error) => {
|
|
2359
|
-
|
|
2574
|
+
log18.error("Fatal error:", error);
|
|
2360
2575
|
process.exit(1);
|
|
2361
2576
|
});
|
|
2362
2577
|
//# sourceMappingURL=index.js.map
|