mcp-jira-stdio 1.3.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 +279 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -42,7 +42,7 @@ var require_package = __commonJS({
|
|
|
42
42
|
"node_modules/dotenv/package.json"(exports, module) {
|
|
43
43
|
module.exports = {
|
|
44
44
|
name: "dotenv",
|
|
45
|
-
version: "17.2.
|
|
45
|
+
version: "17.2.3",
|
|
46
46
|
description: "Loads environment variables from .env file",
|
|
47
47
|
main: "lib/main.js",
|
|
48
48
|
types: "lib/main.d.ts",
|
|
@@ -64,8 +64,8 @@ var require_package = __commonJS({
|
|
|
64
64
|
"dts-check": "tsc --project tests/types/tsconfig.json",
|
|
65
65
|
lint: "standard",
|
|
66
66
|
pretest: "npm run lint && npm run dts-check",
|
|
67
|
-
test: "tap run --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
68
|
-
"test:coverage": "tap run --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
|
|
67
|
+
test: "tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
68
|
+
"test:coverage": "tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
|
|
69
69
|
prerelease: "npm test",
|
|
70
70
|
release: "standard-version"
|
|
71
71
|
},
|
|
@@ -118,9 +118,12 @@ var require_main = __commonJS({
|
|
|
118
118
|
"\u{1F510} encrypt with Dotenvx: https://dotenvx.com",
|
|
119
119
|
"\u{1F510} prevent committing .env to code: https://dotenvx.com/precommit",
|
|
120
120
|
"\u{1F510} prevent building .env in docker: https://dotenvx.com/prebuild",
|
|
121
|
-
"\u{1F4E1}
|
|
122
|
-
"\u{
|
|
123
|
-
"\u{
|
|
121
|
+
"\u{1F4E1} add observability to secrets: https://dotenvx.com/ops",
|
|
122
|
+
"\u{1F465} sync secrets across teammates & machines: https://dotenvx.com/ops",
|
|
123
|
+
"\u{1F5C2}\uFE0F backup and recover secrets: https://dotenvx.com/ops",
|
|
124
|
+
"\u2705 audit secrets and track compliance: https://dotenvx.com/ops",
|
|
125
|
+
"\u{1F504} add secrets lifecycle management: https://dotenvx.com/ops",
|
|
126
|
+
"\u{1F511} add access controls to secrets: https://dotenvx.com/ops",
|
|
124
127
|
"\u{1F6E0}\uFE0F run anywhere with `dotenvx run -- yourcommand`",
|
|
125
128
|
"\u2699\uFE0F specify custom .env file path with { path: '/custom/path/.env' }",
|
|
126
129
|
"\u2699\uFE0F enable debug logging with { debug: true }",
|
|
@@ -436,7 +439,7 @@ var require_package2 = __commonJS({
|
|
|
436
439
|
"package.json"(exports, module) {
|
|
437
440
|
module.exports = {
|
|
438
441
|
name: "mcp-jira-stdio",
|
|
439
|
-
version: "1.
|
|
442
|
+
version: "1.5.1",
|
|
440
443
|
description: "Model Context Protocol (MCP) server for Jira API integration",
|
|
441
444
|
author: "graslt",
|
|
442
445
|
license: "MIT",
|
|
@@ -552,7 +555,9 @@ var TOOL_NAMES = {
|
|
|
552
555
|
UPDATE_ISSUE: "jira_update_issue",
|
|
553
556
|
ADD_COMMENT: "jira_add_comment",
|
|
554
557
|
GET_PROJECT_INFO: "jira_get_project_info",
|
|
555
|
-
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"
|
|
556
561
|
};
|
|
557
562
|
|
|
558
563
|
// src/utils/logger.ts
|
|
@@ -715,13 +720,10 @@ function isValidProjectKey(key) {
|
|
|
715
720
|
function sanitizeJQL(jql) {
|
|
716
721
|
return jql.replace(/[<>"'`]/g, "").replace(/\s+/g, " ").trim();
|
|
717
722
|
}
|
|
718
|
-
function
|
|
719
|
-
if (
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (maxResults < 1 || maxResults > 100) {
|
|
723
|
-
throw new Error("maxResults must be between 1 and 100");
|
|
724
|
-
}
|
|
723
|
+
function extractIssueKey(input) {
|
|
724
|
+
if (typeof input !== "string") return null;
|
|
725
|
+
const match = input.match(/[A-Z][A-Z0-9]*-\d+/);
|
|
726
|
+
return match ? match[0] : null;
|
|
725
727
|
}
|
|
726
728
|
|
|
727
729
|
// src/types/tools.ts
|
|
@@ -738,8 +740,10 @@ var GetIssueInputSchema = z.object({
|
|
|
738
740
|
});
|
|
739
741
|
var SearchIssuesInputSchema = z.object({
|
|
740
742
|
jql: z.string().min(1).describe("JQL query string"),
|
|
741
|
-
|
|
742
|
-
|
|
743
|
+
nextPageToken: z.string().optional().describe(
|
|
744
|
+
"Token for pagination. Omit for first page, use value from previous response for next page."
|
|
745
|
+
),
|
|
746
|
+
maxResults: z.number().min(1).max(100).default(50).describe("Maximum number of results to return per page"),
|
|
743
747
|
fields: z.array(z.string()).optional().describe("Specific fields to retrieve"),
|
|
744
748
|
expand: z.array(z.string()).optional().describe("Additional details to include")
|
|
745
749
|
});
|
|
@@ -768,8 +772,10 @@ var UpdateIssueInputSchema = z.object({
|
|
|
768
772
|
returnIssue: z.boolean().optional().describe("When false, skip fetching full issue after update")
|
|
769
773
|
});
|
|
770
774
|
var GetMyIssuesInputSchema = z.object({
|
|
771
|
-
|
|
772
|
-
|
|
775
|
+
nextPageToken: z.string().optional().describe(
|
|
776
|
+
"Token for pagination. Omit for first page, use value from previous response for next page."
|
|
777
|
+
),
|
|
778
|
+
maxResults: z.number().min(1).max(100).default(50).describe("Maximum number of results to return per page"),
|
|
773
779
|
fields: z.array(z.string()).optional().describe("Specific fields to retrieve"),
|
|
774
780
|
expand: z.array(z.string()).optional().describe("Additional details to include")
|
|
775
781
|
});
|
|
@@ -809,6 +815,13 @@ var CreateSubtaskInputSchema = z.object({
|
|
|
809
815
|
labels: z.array(z.string()).optional().describe("Subtask labels"),
|
|
810
816
|
components: z.array(z.string()).optional().describe("Component names")
|
|
811
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
|
+
});
|
|
812
825
|
|
|
813
826
|
// src/utils/api-helpers.ts
|
|
814
827
|
function ensureAdfDescription(desc) {
|
|
@@ -956,14 +969,15 @@ async function getIssue(issueKey, options = {}) {
|
|
|
956
969
|
return await makeJiraRequest(config);
|
|
957
970
|
}
|
|
958
971
|
async function searchIssues(options) {
|
|
959
|
-
const { jql,
|
|
960
|
-
validatePagination(startAt, maxResults);
|
|
972
|
+
const { jql, nextPageToken, maxResults = 50 } = options;
|
|
961
973
|
const sanitizedJql = sanitizeJQL(jql);
|
|
962
974
|
const data = {
|
|
963
975
|
jql: sanitizedJql,
|
|
964
|
-
startAt,
|
|
965
976
|
maxResults
|
|
966
977
|
};
|
|
978
|
+
if (nextPageToken) {
|
|
979
|
+
data.nextPageToken = nextPageToken;
|
|
980
|
+
}
|
|
967
981
|
if (options.fields) {
|
|
968
982
|
data.fields = options.fields;
|
|
969
983
|
}
|
|
@@ -972,7 +986,7 @@ async function searchIssues(options) {
|
|
|
972
986
|
}
|
|
973
987
|
const config = {
|
|
974
988
|
method: "POST",
|
|
975
|
-
url: "/search",
|
|
989
|
+
url: "/search/jql",
|
|
976
990
|
data
|
|
977
991
|
};
|
|
978
992
|
return await makeJiraRequest(config);
|
|
@@ -1047,9 +1061,9 @@ async function getMyIssues(options = {}) {
|
|
|
1047
1061
|
const jql = `assignee = currentUser() ORDER BY updated DESC`;
|
|
1048
1062
|
const searchParams = {
|
|
1049
1063
|
jql,
|
|
1050
|
-
startAt: options.startAt || 0,
|
|
1051
1064
|
maxResults: options.maxResults || 50
|
|
1052
1065
|
};
|
|
1066
|
+
if (options.nextPageToken !== void 0) searchParams.nextPageToken = options.nextPageToken;
|
|
1053
1067
|
if (options.fields !== void 0) searchParams.fields = options.fields;
|
|
1054
1068
|
if (options.expand !== void 0) searchParams.expand = options.expand;
|
|
1055
1069
|
return await searchIssues(searchParams);
|
|
@@ -1120,7 +1134,10 @@ async function getStatuses(options = {}) {
|
|
|
1120
1134
|
return await makeJiraRequest(config);
|
|
1121
1135
|
}
|
|
1122
1136
|
async function addComment(issueKey, body, visibility) {
|
|
1123
|
-
const
|
|
1137
|
+
const adfBody = ensureAdfDescription(body);
|
|
1138
|
+
const data = {
|
|
1139
|
+
body: adfBody
|
|
1140
|
+
};
|
|
1124
1141
|
if (visibility) {
|
|
1125
1142
|
data.visibility = visibility;
|
|
1126
1143
|
}
|
|
@@ -1180,6 +1197,31 @@ async function createSubtask(parentIssueKey, subtaskData) {
|
|
|
1180
1197
|
const response = await makeJiraRequest(config);
|
|
1181
1198
|
return await getIssue(response.key);
|
|
1182
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
|
+
}
|
|
1183
1225
|
|
|
1184
1226
|
// src/utils/redaction.ts
|
|
1185
1227
|
function isRedactionEnabled() {
|
|
@@ -1302,16 +1344,28 @@ function formatSearchResultsResponse(result) {
|
|
|
1302
1344
|
};
|
|
1303
1345
|
}
|
|
1304
1346
|
const issuesList = result.issues.map((issue) => {
|
|
1305
|
-
const assignee = issue.fields
|
|
1306
|
-
|
|
1307
|
-
|
|
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}`;
|
|
1308
1353
|
}).join("\n\n");
|
|
1309
1354
|
const rangeStart = result.startAt + 1;
|
|
1310
1355
|
const rangeEnd = Math.min(result.startAt + result.maxResults, result.total);
|
|
1311
1356
|
const showRangeInline = result.total > result.maxResults || result.startAt > 0;
|
|
1312
|
-
|
|
1357
|
+
let paginationInfo = "";
|
|
1358
|
+
if (showRangeInline) {
|
|
1359
|
+
paginationInfo = `
|
|
1313
1360
|
|
|
1314
|
-
*Showing ${rangeStart}-${rangeEnd} of ${result.total} results
|
|
1361
|
+
*Showing ${rangeStart}-${rangeEnd} of ${result.total} results*`;
|
|
1362
|
+
}
|
|
1363
|
+
if (result.nextPageToken && !result.isLast) {
|
|
1364
|
+
paginationInfo += `
|
|
1365
|
+
*Next page token: \`${result.nextPageToken}\`*`;
|
|
1366
|
+
} else if (result.isLast) {
|
|
1367
|
+
paginationInfo += "\n*This is the last page of results*";
|
|
1368
|
+
}
|
|
1315
1369
|
return {
|
|
1316
1370
|
content: [
|
|
1317
1371
|
{
|
|
@@ -1325,23 +1379,34 @@ ${issuesList}${paginationInfo}`
|
|
|
1325
1379
|
}
|
|
1326
1380
|
function formatErrorResponse(error) {
|
|
1327
1381
|
let errorMessage = "An unexpected error occurred.";
|
|
1382
|
+
let hasCustomFieldError = false;
|
|
1328
1383
|
if (typeof error === "string") {
|
|
1329
1384
|
errorMessage = error;
|
|
1385
|
+
hasCustomFieldError = error.includes("customfield_");
|
|
1330
1386
|
} else if (error instanceof Error) {
|
|
1331
1387
|
errorMessage = error.message;
|
|
1388
|
+
hasCustomFieldError = error.message.includes("customfield_");
|
|
1332
1389
|
} else if (error?.response?.data) {
|
|
1333
1390
|
const jiraError = error.response.data;
|
|
1334
1391
|
if (jiraError.errorMessages && jiraError.errorMessages.length > 0) {
|
|
1335
1392
|
errorMessage = jiraError.errorMessages.join(", ");
|
|
1336
1393
|
} else if (jiraError.errors) {
|
|
1337
|
-
|
|
1394
|
+
const errors = Object.entries(jiraError.errors);
|
|
1395
|
+
errorMessage = errors.map(([field, message]) => `${field}: ${message}`).join(", ");
|
|
1396
|
+
hasCustomFieldError = errors.some(([field]) => field.startsWith("customfield_"));
|
|
1338
1397
|
}
|
|
1339
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
|
+
}
|
|
1340
1405
|
return {
|
|
1341
1406
|
content: [
|
|
1342
1407
|
{
|
|
1343
1408
|
type: "text",
|
|
1344
|
-
text: `\u274C Error: ${errorMessage}`
|
|
1409
|
+
text: `\u274C Error: ${errorMessage}${hint}`
|
|
1345
1410
|
}
|
|
1346
1411
|
]
|
|
1347
1412
|
};
|
|
@@ -1508,6 +1573,96 @@ Last Updated: ${new Date(project.insight.lastIssueUpdateTime).toISOString()}` :
|
|
|
1508
1573
|
]
|
|
1509
1574
|
};
|
|
1510
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
|
+
}
|
|
1511
1666
|
|
|
1512
1667
|
// src/utils/error-handler.ts
|
|
1513
1668
|
var log2 = createLogger("error-handler");
|
|
@@ -1627,7 +1782,8 @@ var getIssueTool = {
|
|
|
1627
1782
|
async function handleGetIssue(input) {
|
|
1628
1783
|
try {
|
|
1629
1784
|
const validated = validateInput(GetIssueInputSchema, input);
|
|
1630
|
-
const
|
|
1785
|
+
const trimmedInput = (validated.issueKey || "").trim();
|
|
1786
|
+
const key = extractIssueKey(trimmedInput) || trimmedInput;
|
|
1631
1787
|
log4.info(`Getting issue ${key}...`);
|
|
1632
1788
|
const getParams = {};
|
|
1633
1789
|
if (validated.expand !== void 0) getParams.expand = validated.expand;
|
|
@@ -1645,7 +1801,7 @@ async function handleGetIssue(input) {
|
|
|
1645
1801
|
var log5 = createLogger("tool:search-issues");
|
|
1646
1802
|
var searchIssuesTool = {
|
|
1647
1803
|
name: TOOL_NAMES.SEARCH_ISSUES,
|
|
1648
|
-
description: 'Search for Jira issues using JQL. Supports complex queries with pagination and field selection. Examples: "project = PROJECT AND status = Open", "assignee = currentUser()".',
|
|
1804
|
+
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.',
|
|
1649
1805
|
inputSchema: {
|
|
1650
1806
|
type: "object",
|
|
1651
1807
|
properties: {
|
|
@@ -1653,15 +1809,13 @@ var searchIssuesTool = {
|
|
|
1653
1809
|
type: "string",
|
|
1654
1810
|
description: 'JQL query string (e.g., "project = PROJECT AND status = Open")'
|
|
1655
1811
|
},
|
|
1656
|
-
|
|
1657
|
-
type: "
|
|
1658
|
-
description: "
|
|
1659
|
-
minimum: 0,
|
|
1660
|
-
default: 0
|
|
1812
|
+
nextPageToken: {
|
|
1813
|
+
type: "string",
|
|
1814
|
+
description: "Token for pagination. Omit for first page, use value from previous response for next page."
|
|
1661
1815
|
},
|
|
1662
1816
|
maxResults: {
|
|
1663
1817
|
type: "number",
|
|
1664
|
-
description: "Maximum number of results to return",
|
|
1818
|
+
description: "Maximum number of results to return per page",
|
|
1665
1819
|
minimum: 1,
|
|
1666
1820
|
maximum: 100,
|
|
1667
1821
|
default: 50
|
|
@@ -1688,7 +1842,7 @@ async function handleSearchIssues(input) {
|
|
|
1688
1842
|
const searchParams = {
|
|
1689
1843
|
jql: validated.jql
|
|
1690
1844
|
};
|
|
1691
|
-
if (validated.
|
|
1845
|
+
if (validated.nextPageToken !== void 0) searchParams.nextPageToken = validated.nextPageToken;
|
|
1692
1846
|
if (validated.maxResults !== void 0) searchParams.maxResults = validated.maxResults;
|
|
1693
1847
|
if (validated.fields !== void 0) searchParams.fields = validated.fields;
|
|
1694
1848
|
if (validated.expand !== void 0) searchParams.expand = validated.expand;
|
|
@@ -1705,19 +1859,17 @@ async function handleSearchIssues(input) {
|
|
|
1705
1859
|
var log6 = createLogger("tool:get-my-issues");
|
|
1706
1860
|
var getMyIssuesTool = {
|
|
1707
1861
|
name: TOOL_NAMES.GET_MY_ISSUES,
|
|
1708
|
-
description: "Retrieves issues assigned to current user, sorted by most recently updated first. Supports pagination and field selection.",
|
|
1862
|
+
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.",
|
|
1709
1863
|
inputSchema: {
|
|
1710
1864
|
type: "object",
|
|
1711
1865
|
properties: {
|
|
1712
|
-
|
|
1713
|
-
type: "
|
|
1714
|
-
description: "
|
|
1715
|
-
minimum: 0,
|
|
1716
|
-
default: 0
|
|
1866
|
+
nextPageToken: {
|
|
1867
|
+
type: "string",
|
|
1868
|
+
description: "Token for pagination. Omit for first page, use value from previous response for next page."
|
|
1717
1869
|
},
|
|
1718
1870
|
maxResults: {
|
|
1719
1871
|
type: "number",
|
|
1720
|
-
description: "Maximum number of results to return",
|
|
1872
|
+
description: "Maximum number of results to return per page",
|
|
1721
1873
|
minimum: 1,
|
|
1722
1874
|
maximum: 100,
|
|
1723
1875
|
default: 50
|
|
@@ -1742,7 +1894,7 @@ async function handleGetMyIssues(input) {
|
|
|
1742
1894
|
const validated = validateInput(GetMyIssuesInputSchema, input);
|
|
1743
1895
|
log6.info("Getting issues assigned to current user...");
|
|
1744
1896
|
const getParams = {};
|
|
1745
|
-
if (validated.
|
|
1897
|
+
if (validated.nextPageToken !== void 0) getParams.nextPageToken = validated.nextPageToken;
|
|
1746
1898
|
if (validated.maxResults !== void 0) getParams.maxResults = validated.maxResults;
|
|
1747
1899
|
if (validated.fields !== void 0) getParams.fields = validated.fields;
|
|
1748
1900
|
if (validated.expand !== void 0) getParams.expand = validated.expand;
|
|
@@ -2237,6 +2389,76 @@ async function handleCreateSubtask(input) {
|
|
|
2237
2389
|
}
|
|
2238
2390
|
}
|
|
2239
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
|
+
|
|
2240
2462
|
// src/index.ts
|
|
2241
2463
|
if (process.env.NODE_ENV !== "production") {
|
|
2242
2464
|
try {
|
|
@@ -2245,7 +2467,7 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
2245
2467
|
} catch {
|
|
2246
2468
|
}
|
|
2247
2469
|
}
|
|
2248
|
-
var
|
|
2470
|
+
var log18 = createLogger("server");
|
|
2249
2471
|
var toolHandlers = /* @__PURE__ */ new Map([
|
|
2250
2472
|
[TOOL_NAMES.GET_VISIBLE_PROJECTS, handleGetVisibleProjects],
|
|
2251
2473
|
[TOOL_NAMES.GET_ISSUE, handleGetIssue],
|
|
@@ -2259,7 +2481,9 @@ var toolHandlers = /* @__PURE__ */ new Map([
|
|
|
2259
2481
|
[TOOL_NAMES.UPDATE_ISSUE, handleUpdateIssue],
|
|
2260
2482
|
[TOOL_NAMES.ADD_COMMENT, handleAddComment],
|
|
2261
2483
|
[TOOL_NAMES.GET_PROJECT_INFO, handleGetProjectInfo],
|
|
2262
|
-
[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]
|
|
2263
2487
|
]);
|
|
2264
2488
|
var allTools = [
|
|
2265
2489
|
getVisibleProjectsTool,
|
|
@@ -2274,7 +2498,9 @@ var allTools = [
|
|
|
2274
2498
|
updateIssueTool,
|
|
2275
2499
|
addCommentTool,
|
|
2276
2500
|
getProjectInfoTool,
|
|
2277
|
-
createSubtaskTool
|
|
2501
|
+
createSubtaskTool,
|
|
2502
|
+
getCreateMetaTool,
|
|
2503
|
+
getCustomFieldsTool
|
|
2278
2504
|
];
|
|
2279
2505
|
async function main() {
|
|
2280
2506
|
const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
|
|
@@ -2345,7 +2571,7 @@ async function main() {
|
|
|
2345
2571
|
console.error("\u{1F680} Jira MCP server running on stdio");
|
|
2346
2572
|
}
|
|
2347
2573
|
main().catch((error) => {
|
|
2348
|
-
|
|
2574
|
+
log18.error("Fatal error:", error);
|
|
2349
2575
|
process.exit(1);
|
|
2350
2576
|
});
|
|
2351
2577
|
//# sourceMappingURL=index.js.map
|