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 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.1",
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} observe env with Radar: https://dotenvx.com/radar",
122
- "\u{1F4E1} auto-backup env with Radar: https://dotenvx.com/radar",
123
- "\u{1F4E1} version env with Radar: https://dotenvx.com/radar",
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.3.0",
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 validatePagination(startAt, maxResults) {
719
- if (startAt < 0) {
720
- throw new Error("startAt must be non-negative");
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
- startAt: z.number().min(0).default(0).describe("Index of first result to return"),
742
- maxResults: z.number().min(1).max(100).default(50).describe("Maximum number of results to return"),
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
- startAt: z.number().min(0).default(0).describe("Index of first result to return"),
772
- maxResults: z.number().min(1).max(100).default(50).describe("Maximum number of results to return"),
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, startAt = 0, maxResults = 50 } = options;
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 data = { body };
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.assignee?.displayName || "Unassigned";
1306
- return `\u2022 **${issue.key}** - ${issue.fields.summary}
1307
- Status: ${issue.fields.status.name} | Assignee: ${assignee} | Priority: ${issue.fields.priority?.name || "None"}`;
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
- const paginationInfo = showRangeInline ? `
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
- errorMessage = Object.entries(jiraError.errors).map(([field, message]) => `${field}: ${message}`).join(", ");
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 key = (validated.issueKey || "").trim();
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
- startAt: {
1657
- type: "number",
1658
- description: "Index of first result to return (for pagination)",
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.startAt !== void 0) searchParams.startAt = validated.startAt;
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
- startAt: {
1713
- type: "number",
1714
- description: "Index of first result to return (for pagination)",
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.startAt !== void 0) getParams.startAt = validated.startAt;
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 log16 = createLogger("server");
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
- log16.error("Fatal error:", error);
2574
+ log18.error("Fatal error:", error);
2349
2575
  process.exit(1);
2350
2576
  });
2351
2577
  //# sourceMappingURL=index.js.map