mcp-jira-stdio 1.4.0 → 1.5.2

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
@@ -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.4.0",
442
+ version: "1.5.2",
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,8 +815,16 @@ 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
827
+ var log2 = createLogger("api-helpers");
818
828
  function ensureAdfDescription(desc) {
819
829
  if (!desc) return desc;
820
830
  if (typeof desc === "object") return desc;
@@ -929,20 +939,32 @@ function ensureAdfDescription(desc) {
929
939
  return { type: "doc", version: 1, content };
930
940
  }
931
941
  async function getVisibleProjects(options = {}) {
932
- const params = {};
933
- if (options.expand) {
934
- params.expand = options.expand.join(",");
935
- }
936
- if (options.recent) {
937
- params.recent = options.recent;
942
+ const allProjects = [];
943
+ let startAt = 0;
944
+ let isLast = false;
945
+ while (!isLast) {
946
+ const params = {
947
+ startAt,
948
+ maxResults: 50
949
+ // Jira default, fetch 50 projects per page
950
+ };
951
+ if (options.expand) {
952
+ params.expand = options.expand.join(",");
953
+ }
954
+ if (options.recent) {
955
+ params.recent = options.recent;
956
+ }
957
+ const config = {
958
+ method: "GET",
959
+ url: "/project/search",
960
+ params
961
+ };
962
+ const response = await makeJiraRequest(config);
963
+ allProjects.push(...response.values);
964
+ isLast = response.isLast;
965
+ startAt += response.maxResults;
938
966
  }
939
- const config = {
940
- method: "GET",
941
- url: "/project/search",
942
- params
943
- };
944
- const response = await makeJiraRequest(config);
945
- return response.values;
967
+ return allProjects;
946
968
  }
947
969
  async function getIssue(issueKey, options = {}) {
948
970
  const params = {};
@@ -1125,7 +1147,10 @@ async function getStatuses(options = {}) {
1125
1147
  return await makeJiraRequest(config);
1126
1148
  }
1127
1149
  async function addComment(issueKey, body, visibility) {
1128
- const data = { body };
1150
+ const adfBody = ensureAdfDescription(body);
1151
+ const data = {
1152
+ body: adfBody
1153
+ };
1129
1154
  if (visibility) {
1130
1155
  data.visibility = visibility;
1131
1156
  }
@@ -1185,6 +1210,81 @@ async function createSubtask(parentIssueKey, subtaskData) {
1185
1210
  const response = await makeJiraRequest(config);
1186
1211
  return await getIssue(response.key);
1187
1212
  }
1213
+ async function getCreateMeta(options = {}) {
1214
+ if (options.projectKeys && options.projectKeys.length === 1) {
1215
+ const projectKey = options.projectKeys[0];
1216
+ const allIssueTypes = await getIssueTypes(projectKey);
1217
+ let issueTypesToFetch = allIssueTypes;
1218
+ if (options.issueTypeNames && options.issueTypeNames.length > 0) {
1219
+ issueTypesToFetch = allIssueTypes.filter(
1220
+ (type) => options.issueTypeNames?.includes(type.name)
1221
+ );
1222
+ }
1223
+ const issueTypesWithFields = [];
1224
+ for (const issueType of issueTypesToFetch) {
1225
+ try {
1226
+ const config2 = {
1227
+ method: "GET",
1228
+ url: `/issue/createmeta/${projectKey}/issuetypes/${issueType.id}`
1229
+ };
1230
+ const issueTypeMetadata = await makeJiraRequest(config2);
1231
+ const fieldsMap = {};
1232
+ if (issueTypeMetadata.values && Array.isArray(issueTypeMetadata.values)) {
1233
+ for (const field of issueTypeMetadata.values) {
1234
+ const fieldKey = field.key || field.fieldId || field.name;
1235
+ if (fieldKey) {
1236
+ fieldsMap[fieldKey] = field;
1237
+ }
1238
+ }
1239
+ }
1240
+ issueTypesWithFields.push({
1241
+ id: issueType.id,
1242
+ name: issueType.name,
1243
+ description: issueType.description,
1244
+ iconUrl: issueType.iconUrl,
1245
+ subtask: issueType.subtask,
1246
+ fields: fieldsMap
1247
+ });
1248
+ } catch (error) {
1249
+ log2.error(`Failed to get metadata for issue type ${issueType.name}:`, error);
1250
+ }
1251
+ }
1252
+ const projectDetails = await getProjectDetails(projectKey);
1253
+ return {
1254
+ projects: [
1255
+ {
1256
+ id: projectDetails.id,
1257
+ key: projectDetails.key,
1258
+ name: projectDetails.name,
1259
+ issuetypes: issueTypesWithFields
1260
+ }
1261
+ ]
1262
+ };
1263
+ }
1264
+ const params = {};
1265
+ if (options.projectKeys && options.projectKeys.length > 0) {
1266
+ params.projectKeys = options.projectKeys.join(",");
1267
+ }
1268
+ if (options.issueTypeNames && options.issueTypeNames.length > 0) {
1269
+ params.issuetypeNames = options.issueTypeNames.join(",");
1270
+ }
1271
+ if (options.expand) {
1272
+ params.expand = options.expand;
1273
+ }
1274
+ const config = {
1275
+ method: "GET",
1276
+ url: "/issue/createmeta",
1277
+ params
1278
+ };
1279
+ return await makeJiraRequest(config);
1280
+ }
1281
+ async function getFields() {
1282
+ const config = {
1283
+ method: "GET",
1284
+ url: "/field"
1285
+ };
1286
+ return await makeJiraRequest(config);
1287
+ }
1188
1288
 
1189
1289
  // src/utils/redaction.ts
1190
1290
  function isRedactionEnabled() {
@@ -1307,9 +1407,12 @@ function formatSearchResultsResponse(result) {
1307
1407
  };
1308
1408
  }
1309
1409
  const issuesList = result.issues.map((issue) => {
1310
- const assignee = issue.fields.assignee?.displayName || "Unassigned";
1311
- return `\u2022 **${issue.key}** - ${issue.fields.summary}
1312
- Status: ${issue.fields.status.name} | Assignee: ${assignee} | Priority: ${issue.fields.priority?.name || "None"}`;
1410
+ const assignee = issue.fields?.assignee?.displayName || "Unassigned";
1411
+ const summary = issue.fields?.summary || "No summary";
1412
+ const status = issue.fields?.status?.name || "Unknown";
1413
+ const priority = issue.fields?.priority?.name || "None";
1414
+ return `\u2022 **${issue.key}** - ${summary}
1415
+ Status: ${status} | Assignee: ${assignee} | Priority: ${priority}`;
1313
1416
  }).join("\n\n");
1314
1417
  const rangeStart = result.startAt + 1;
1315
1418
  const rangeEnd = Math.min(result.startAt + result.maxResults, result.total);
@@ -1339,23 +1442,34 @@ ${issuesList}${paginationInfo}`
1339
1442
  }
1340
1443
  function formatErrorResponse(error) {
1341
1444
  let errorMessage = "An unexpected error occurred.";
1445
+ let hasCustomFieldError = false;
1342
1446
  if (typeof error === "string") {
1343
1447
  errorMessage = error;
1448
+ hasCustomFieldError = error.includes("customfield_");
1344
1449
  } else if (error instanceof Error) {
1345
1450
  errorMessage = error.message;
1451
+ hasCustomFieldError = error.message.includes("customfield_");
1346
1452
  } else if (error?.response?.data) {
1347
1453
  const jiraError = error.response.data;
1348
1454
  if (jiraError.errorMessages && jiraError.errorMessages.length > 0) {
1349
1455
  errorMessage = jiraError.errorMessages.join(", ");
1350
1456
  } else if (jiraError.errors) {
1351
- errorMessage = Object.entries(jiraError.errors).map(([field, message]) => `${field}: ${message}`).join(", ");
1457
+ const errors = Object.entries(jiraError.errors);
1458
+ errorMessage = errors.map(([field, message]) => `${field}: ${message}`).join(", ");
1459
+ hasCustomFieldError = errors.some(([field]) => field.startsWith("customfield_"));
1352
1460
  }
1353
1461
  }
1462
+ let hint = "";
1463
+ if (hasCustomFieldError) {
1464
+ hint = `
1465
+
1466
+ \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.`;
1467
+ }
1354
1468
  return {
1355
1469
  content: [
1356
1470
  {
1357
1471
  type: "text",
1358
- text: `\u274C Error: ${errorMessage}`
1472
+ text: `\u274C Error: ${errorMessage}${hint}`
1359
1473
  }
1360
1474
  ]
1361
1475
  };
@@ -1522,9 +1636,99 @@ Last Updated: ${new Date(project.insight.lastIssueUpdateTime).toISOString()}` :
1522
1636
  ]
1523
1637
  };
1524
1638
  }
1639
+ function formatCreateMetaResponse(meta) {
1640
+ if (!meta.projects || meta.projects.length === 0) {
1641
+ return {
1642
+ content: [
1643
+ {
1644
+ type: "text",
1645
+ text: "No create metadata found for the specified project."
1646
+ }
1647
+ ]
1648
+ };
1649
+ }
1650
+ const project = meta.projects[0];
1651
+ if (!project) {
1652
+ return {
1653
+ content: [
1654
+ {
1655
+ type: "text",
1656
+ text: "No create metadata found for the specified project."
1657
+ }
1658
+ ]
1659
+ };
1660
+ }
1661
+ const issueTypesList = project.issuetypes.map((issueType) => {
1662
+ const requiredFields = [];
1663
+ const optionalFields = [];
1664
+ Object.entries(issueType.fields).forEach(([fieldKey, field]) => {
1665
+ const allowedValuesText = field.allowedValues ? `
1666
+ Allowed values: ${field.allowedValues.map((v) => v.name || v.value || v.id).join(", ")}` : "";
1667
+ const defaultValueText = field.hasDefaultValue ? `
1668
+ Default: ${JSON.stringify(field.defaultValue)}` : "";
1669
+ const fieldInfo = ` \u2022 **${field.name}** (${fieldKey})
1670
+ Type: ${field.schema.type}${allowedValuesText}${defaultValueText}`;
1671
+ if (field.required) {
1672
+ requiredFields.push(fieldInfo);
1673
+ } else {
1674
+ optionalFields.push(fieldInfo);
1675
+ }
1676
+ });
1677
+ const requiredSection = requiredFields.length > 0 ? `**Required Fields:**
1678
+ ${requiredFields.join("\n\n")}` : "No required fields";
1679
+ const optionalSection = optionalFields.length > 0 ? `
1680
+
1681
+ **Optional Fields:**
1682
+ ${optionalFields.join("\n\n")}` : "";
1683
+ return `### ${issueType.name} (${issueType.subtask ? "Subtask" : "Issue"})
1684
+ ${issueType.description}
1685
+
1686
+ ${requiredSection}${optionalSection}`;
1687
+ }).join("\n\n---\n\n");
1688
+ return {
1689
+ content: [
1690
+ {
1691
+ type: "text",
1692
+ text: `**Create Metadata for Project: ${project.name} (${project.key})**
1693
+
1694
+ ${issueTypesList}`
1695
+ }
1696
+ ]
1697
+ };
1698
+ }
1699
+ function formatCustomFieldsResponse(fields, projectKey) {
1700
+ const customFields = fields.filter((field) => field.custom);
1701
+ if (customFields.length === 0) {
1702
+ return {
1703
+ content: [
1704
+ {
1705
+ type: "text",
1706
+ text: projectKey ? `No custom fields found for project ${projectKey}.` : "No custom fields found."
1707
+ }
1708
+ ]
1709
+ };
1710
+ }
1711
+ const fieldsList = customFields.map((field) => {
1712
+ const schemaInfo = field.schema.custom ? `Custom Type: ${field.schema.custom}` : `Type: ${field.schema.type}`;
1713
+ return `\u2022 **${field.name}** (${field.key})
1714
+ ${schemaInfo}
1715
+ ID: ${field.id}`;
1716
+ }).join("\n\n");
1717
+ const header = projectKey ? `Found ${customFields.length} custom field(s) for project ${projectKey}:` : `Found ${customFields.length} custom field(s):`;
1718
+ return {
1719
+ content: [
1720
+ {
1721
+ type: "text",
1722
+ text: `${header}
1723
+
1724
+ ${fieldsList}`
1725
+ }
1726
+ ]
1727
+ };
1728
+ }
1525
1729
 
1526
1730
  // src/utils/error-handler.ts
1527
- var log2 = createLogger("error-handler");
1731
+ var log3 = createLogger("error-handler");
1528
1732
  var JiraApiError = class extends Error {
1529
1733
  constructor(message, statusCode, jiraErrors, originalError) {
1530
1734
  super(message);
@@ -1535,7 +1739,7 @@ var JiraApiError = class extends Error {
1535
1739
  }
1536
1740
  };
1537
1741
  function handleError(error) {
1538
- log2.error("Tool error:", error);
1742
+ log3.error("Tool error:", error);
1539
1743
  if (typeof error === "string") {
1540
1744
  return formatErrorResponse(error);
1541
1745
  }
@@ -1572,7 +1776,7 @@ function handleError(error) {
1572
1776
  }
1573
1777
 
1574
1778
  // src/tools/get-visible-projects.ts
1575
- var log3 = createLogger("tool:get-visible-projects");
1779
+ var log4 = createLogger("tool:get-visible-projects");
1576
1780
  var getVisibleProjectsTool = {
1577
1781
  name: TOOL_NAMES.GET_VISIBLE_PROJECTS,
1578
1782
  description: "Retrieves all projects accessible to the authenticated user. Returns project keys, names, descriptions, and basic metadata.",
@@ -1598,21 +1802,21 @@ var getVisibleProjectsTool = {
1598
1802
  async function handleGetVisibleProjects(input) {
1599
1803
  try {
1600
1804
  const validated = validateInput(GetVisibleProjectsInputSchema, input);
1601
- log3.info("Getting visible projects...");
1805
+ log4.info("Getting visible projects...");
1602
1806
  const getParams = {};
1603
1807
  if (validated.expand !== void 0) getParams.expand = validated.expand;
1604
1808
  if (validated.recent !== void 0) getParams.recent = validated.recent;
1605
1809
  const projects = await getVisibleProjects(getParams);
1606
- log3.info(`Found ${projects.length} project(s)`);
1810
+ log4.info(`Found ${projects.length} project(s)`);
1607
1811
  return formatProjectsResponse(projects);
1608
1812
  } catch (error) {
1609
- log3.error("Error in handleGetVisibleProjects:", error);
1813
+ log4.error("Error in handleGetVisibleProjects:", error);
1610
1814
  return handleError(error);
1611
1815
  }
1612
1816
  }
1613
1817
 
1614
1818
  // src/tools/get-issue.ts
1615
- var log4 = createLogger("tool:get-issue");
1819
+ var log5 = createLogger("tool:get-issue");
1616
1820
  var getIssueTool = {
1617
1821
  name: TOOL_NAMES.GET_ISSUE,
1618
1822
  description: 'Retrieve details for a specific Jira issue by key or URL. Use this when the user mentions an issue like "PAYWALL-943" or pastes a Jira link (e.g., https://your.atlassian.net/browse/PAYWALL-943). Returns status, assignee, priority, project, type, labels, components, timestamps, and description.',
@@ -1643,21 +1847,21 @@ async function handleGetIssue(input) {
1643
1847
  const validated = validateInput(GetIssueInputSchema, input);
1644
1848
  const trimmedInput = (validated.issueKey || "").trim();
1645
1849
  const key = extractIssueKey(trimmedInput) || trimmedInput;
1646
- log4.info(`Getting issue ${key}...`);
1850
+ log5.info(`Getting issue ${key}...`);
1647
1851
  const getParams = {};
1648
1852
  if (validated.expand !== void 0) getParams.expand = validated.expand;
1649
1853
  if (validated.fields !== void 0) getParams.fields = validated.fields;
1650
1854
  const issue = await getIssue(key, getParams);
1651
- log4.info(`Retrieved issue ${issue.key}`);
1855
+ log5.info(`Retrieved issue ${issue.key}`);
1652
1856
  return formatIssueResponse(issue);
1653
1857
  } catch (error) {
1654
- log4.error("Error in handleGetIssue:", error);
1858
+ log5.error("Error in handleGetIssue:", error);
1655
1859
  return handleError(error);
1656
1860
  }
1657
1861
  }
1658
1862
 
1659
1863
  // src/tools/search-issues.ts
1660
- var log5 = createLogger("tool:search-issues");
1864
+ var log6 = createLogger("tool:search-issues");
1661
1865
  var searchIssuesTool = {
1662
1866
  name: TOOL_NAMES.SEARCH_ISSUES,
1663
1867
  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.',
@@ -1697,7 +1901,7 @@ var searchIssuesTool = {
1697
1901
  async function handleSearchIssues(input) {
1698
1902
  try {
1699
1903
  const validated = validateInput(SearchIssuesInputSchema, input);
1700
- log5.info(`Searching issues with JQL: "${validated.jql}"...`);
1904
+ log6.info(`Searching issues with JQL: "${validated.jql}"...`);
1701
1905
  const searchParams = {
1702
1906
  jql: validated.jql
1703
1907
  };
@@ -1706,16 +1910,16 @@ async function handleSearchIssues(input) {
1706
1910
  if (validated.fields !== void 0) searchParams.fields = validated.fields;
1707
1911
  if (validated.expand !== void 0) searchParams.expand = validated.expand;
1708
1912
  const result = await searchIssues(searchParams);
1709
- log5.info(`Found ${result.total} issue(s), showing ${result.issues.length}`);
1913
+ log6.info(`Found ${result.total} issue(s), showing ${result.issues.length}`);
1710
1914
  return formatSearchResultsResponse(result);
1711
1915
  } catch (error) {
1712
- log5.error("Error in handleSearchIssues:", error);
1916
+ log6.error("Error in handleSearchIssues:", error);
1713
1917
  return handleError(error);
1714
1918
  }
1715
1919
  }
1716
1920
 
1717
1921
  // src/tools/get-my-issues.ts
1718
- var log6 = createLogger("tool:get-my-issues");
1922
+ var log7 = createLogger("tool:get-my-issues");
1719
1923
  var getMyIssuesTool = {
1720
1924
  name: TOOL_NAMES.GET_MY_ISSUES,
1721
1925
  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.",
@@ -1751,23 +1955,23 @@ var getMyIssuesTool = {
1751
1955
  async function handleGetMyIssues(input) {
1752
1956
  try {
1753
1957
  const validated = validateInput(GetMyIssuesInputSchema, input);
1754
- log6.info("Getting issues assigned to current user...");
1958
+ log7.info("Getting issues assigned to current user...");
1755
1959
  const getParams = {};
1756
1960
  if (validated.nextPageToken !== void 0) getParams.nextPageToken = validated.nextPageToken;
1757
1961
  if (validated.maxResults !== void 0) getParams.maxResults = validated.maxResults;
1758
1962
  if (validated.fields !== void 0) getParams.fields = validated.fields;
1759
1963
  if (validated.expand !== void 0) getParams.expand = validated.expand;
1760
1964
  const result = await getMyIssues(getParams);
1761
- log6.info(`Found ${result.total} issue(s) assigned to you, showing ${result.issues.length}`);
1965
+ log7.info(`Found ${result.total} issue(s) assigned to you, showing ${result.issues.length}`);
1762
1966
  return formatSearchResultsResponse(result);
1763
1967
  } catch (error) {
1764
- log6.error("Error in handleGetMyIssues:", error);
1968
+ log7.error("Error in handleGetMyIssues:", error);
1765
1969
  return handleError(error);
1766
1970
  }
1767
1971
  }
1768
1972
 
1769
1973
  // src/tools/get-issue-types.ts
1770
- var log7 = createLogger("tool:get-issue-types");
1974
+ var log8 = createLogger("tool:get-issue-types");
1771
1975
  var getIssueTypesTool = {
1772
1976
  name: TOOL_NAMES.GET_ISSUE_TYPES,
1773
1977
  description: "Retrieves available issue types. Can get global issue types or project-specific issue types including regular issues and subtasks (Bug, Story, Task, Epic, etc.).",
@@ -1786,21 +1990,21 @@ async function handleGetIssueTypes(input) {
1786
1990
  try {
1787
1991
  const validated = validateInput(GetIssueTypesInputSchema, input);
1788
1992
  if (validated.projectKey) {
1789
- log7.info(`Getting issue types for project ${validated.projectKey}...`);
1993
+ log8.info(`Getting issue types for project ${validated.projectKey}...`);
1790
1994
  } else {
1791
- log7.info("Getting global issue types...");
1995
+ log8.info("Getting global issue types...");
1792
1996
  }
1793
1997
  const issueTypes = await getIssueTypes(validated.projectKey);
1794
- log7.info(`Found ${issueTypes.length} issue type(s)`);
1998
+ log8.info(`Found ${issueTypes.length} issue type(s)`);
1795
1999
  return formatIssueTypesResponse(issueTypes);
1796
2000
  } catch (error) {
1797
- log7.error("Error in handleGetIssueTypes:", error);
2001
+ log8.error("Error in handleGetIssueTypes:", error);
1798
2002
  return handleError(error);
1799
2003
  }
1800
2004
  }
1801
2005
 
1802
2006
  // src/tools/get-users.ts
1803
- var log8 = createLogger("tool:get-users");
2007
+ var log9 = createLogger("tool:get-users");
1804
2008
  var getUsersTool = {
1805
2009
  name: TOOL_NAMES.GET_USERS,
1806
2010
  description: "Search for users by name, email, username, or account ID. Returns display name, email, account status, and account type. Supports pagination.",
@@ -1840,13 +2044,13 @@ async function handleGetUsers(input) {
1840
2044
  try {
1841
2045
  const validated = validateInput(GetUsersInputSchema, input);
1842
2046
  if (validated.query) {
1843
- log8.info(`Searching users with query: "${validated.query}"...`);
2047
+ log9.info(`Searching users with query: "${validated.query}"...`);
1844
2048
  } else if (validated.username) {
1845
- log8.info(`Searching for username: "${validated.username}"...`);
2049
+ log9.info(`Searching for username: "${validated.username}"...`);
1846
2050
  } else if (validated.accountId) {
1847
- log8.info(`Searching for account ID: "${validated.accountId}"...`);
2051
+ log9.info(`Searching for account ID: "${validated.accountId}"...`);
1848
2052
  } else {
1849
- log8.info("Getting all users...");
2053
+ log9.info("Getting all users...");
1850
2054
  }
1851
2055
  const getParams = {};
1852
2056
  if (validated.query !== void 0) getParams.query = validated.query;
@@ -1855,16 +2059,16 @@ async function handleGetUsers(input) {
1855
2059
  if (validated.startAt !== void 0) getParams.startAt = validated.startAt;
1856
2060
  if (validated.maxResults !== void 0) getParams.maxResults = validated.maxResults;
1857
2061
  const users = await getUsers(getParams);
1858
- log8.info(`Found ${users.length} user(s)`);
2062
+ log9.info(`Found ${users.length} user(s)`);
1859
2063
  return formatUsersResponse(users);
1860
2064
  } catch (error) {
1861
- log8.error("Error in handleGetUsers:", error);
2065
+ log9.error("Error in handleGetUsers:", error);
1862
2066
  return handleError(error);
1863
2067
  }
1864
2068
  }
1865
2069
 
1866
2070
  // src/tools/get-priorities.ts
1867
- var log9 = createLogger("tool:get-priorities");
2071
+ var log10 = createLogger("tool:get-priorities");
1868
2072
  var getPrioritiesTool = {
1869
2073
  name: TOOL_NAMES.GET_PRIORITIES,
1870
2074
  description: "Retrieves available priorities (e.g., Highest, High, Medium, Low, Lowest). Returns IDs, names, and descriptions.",
@@ -1877,18 +2081,18 @@ var getPrioritiesTool = {
1877
2081
  async function handleGetPriorities(input) {
1878
2082
  try {
1879
2083
  validateInput(GetPrioritiesInputSchema, input);
1880
- log9.info("Getting all priority levels...");
2084
+ log10.info("Getting all priority levels...");
1881
2085
  const priorities = await getPriorities();
1882
- log9.info(`Found ${priorities.length} priority level(s)`);
2086
+ log10.info(`Found ${priorities.length} priority level(s)`);
1883
2087
  return formatPrioritiesResponse(priorities);
1884
2088
  } catch (error) {
1885
- log9.error("Error in handleGetPriorities:", error);
2089
+ log10.error("Error in handleGetPriorities:", error);
1886
2090
  return handleError(error);
1887
2091
  }
1888
2092
  }
1889
2093
 
1890
2094
  // src/tools/get-statuses.ts
1891
- var log10 = createLogger("tool:get-statuses");
2095
+ var log11 = createLogger("tool:get-statuses");
1892
2096
  var getStatusesTool = {
1893
2097
  name: TOOL_NAMES.GET_STATUSES,
1894
2098
  description: "Retrieves available statuses (global or project-specific, e.g., To Do, In Progress, Done). Returns status categories and workflow information.",
@@ -1911,28 +2115,28 @@ async function handleGetStatuses(input) {
1911
2115
  try {
1912
2116
  const validated = validateInput(GetStatusesInputSchema, input);
1913
2117
  if (validated.projectKey && validated.issueTypeId) {
1914
- log10.info(
2118
+ log11.info(
1915
2119
  `Getting statuses for project ${validated.projectKey} and issue type ${validated.issueTypeId}...`
1916
2120
  );
1917
2121
  } else if (validated.projectKey) {
1918
- log10.info(`Getting statuses for project ${validated.projectKey}...`);
2122
+ log11.info(`Getting statuses for project ${validated.projectKey}...`);
1919
2123
  } else {
1920
- log10.info("Getting global statuses...");
2124
+ log11.info("Getting global statuses...");
1921
2125
  }
1922
2126
  const getParams = {};
1923
2127
  if (validated.projectKey !== void 0) getParams.projectKey = validated.projectKey;
1924
2128
  if (validated.issueTypeId !== void 0) getParams.issueTypeId = validated.issueTypeId;
1925
2129
  const statuses = await getStatuses(getParams);
1926
- log10.info(`Found ${statuses.length} status(es)`);
2130
+ log11.info(`Found ${statuses.length} status(es)`);
1927
2131
  return formatStatusesResponse(statuses);
1928
2132
  } catch (error) {
1929
- log10.error("Error in handleGetStatuses:", error);
2133
+ log11.error("Error in handleGetStatuses:", error);
1930
2134
  return handleError(error);
1931
2135
  }
1932
2136
  }
1933
2137
 
1934
2138
  // src/tools/create-issue.ts
1935
- var log11 = createLogger("tool:create-issue");
2139
+ var log12 = createLogger("tool:create-issue");
1936
2140
  var createIssueTool = {
1937
2141
  name: TOOL_NAMES.CREATE_ISSUE,
1938
2142
  description: 'Creates a new Jira issue in the specified project. Supports setting issue type, priority, assignee, labels, components, and custom fields. Description accepts plain text and is auto-formatted to ADF: lines ending with ":" become headings, numbered lines create ordered lists, and URLs are linkified. For required custom fields, supply them via customFields (e.g., { "customfield_12345": { id: "..." } }). Returns the created issue with all details.',
@@ -1993,7 +2197,7 @@ var createIssueTool = {
1993
2197
  async function handleCreateIssue(input) {
1994
2198
  try {
1995
2199
  const validated = validateInput(CreateIssueInputSchema, input);
1996
- log11.info(`Creating issue in project ${validated.projectKey}...`);
2200
+ log12.info(`Creating issue in project ${validated.projectKey}...`);
1997
2201
  const createParams = {
1998
2202
  projectKey: validated.projectKey,
1999
2203
  summary: validated.summary,
@@ -2013,20 +2217,20 @@ async function handleCreateIssue(input) {
2013
2217
  }
2014
2218
  if (validated.returnIssue === false) {
2015
2219
  const key = typeof issueOrKey === "string" ? issueOrKey : issueOrKey.key;
2016
- log11.info(`Created issue ${key}`);
2220
+ log12.info(`Created issue ${key}`);
2017
2221
  return formatSuccessResponse(`Issue created: ${key}`);
2018
2222
  }
2019
2223
  const issue = issueOrKey;
2020
- log11.info(`Created issue ${issue.key}`);
2224
+ log12.info(`Created issue ${issue.key}`);
2021
2225
  return formatIssueResponse(issue);
2022
2226
  } catch (error) {
2023
- log11.error("Error in handleCreateIssue:", error);
2227
+ log12.error("Error in handleCreateIssue:", error);
2024
2228
  return handleError(error);
2025
2229
  }
2026
2230
  }
2027
2231
 
2028
2232
  // src/tools/update-issue.ts
2029
- var log12 = createLogger("tool:update-issue");
2233
+ var log13 = createLogger("tool:update-issue");
2030
2234
  var updateIssueTool = {
2031
2235
  name: TOOL_NAMES.UPDATE_ISSUE,
2032
2236
  description: 'Updates an existing Jira issue by its key. Supports updating summary, description, priority, assignee, labels, and components. Description accepts plain text and is auto-formatted to ADF: headings (lines ending with ":"), numbered/bullet lists, and links. Only specified fields will be updated.',
@@ -2075,7 +2279,7 @@ var updateIssueTool = {
2075
2279
  async function handleUpdateIssue(input) {
2076
2280
  try {
2077
2281
  const validated = validateInput(UpdateIssueInputSchema, input);
2078
- log12.info(`Updating issue ${validated.issueKey}...`);
2282
+ log13.info(`Updating issue ${validated.issueKey}...`);
2079
2283
  const updateParams = {};
2080
2284
  if (validated.summary !== void 0) updateParams.summary = validated.summary;
2081
2285
  if (validated.description !== void 0) updateParams.description = validated.description;
@@ -2085,20 +2289,20 @@ async function handleUpdateIssue(input) {
2085
2289
  if (validated.components !== void 0) updateParams.components = validated.components;
2086
2290
  await updateIssue(validated.issueKey, updateParams);
2087
2291
  if (validated.returnIssue === false) {
2088
- log12.info(`Updated issue ${validated.issueKey}`);
2292
+ log13.info(`Updated issue ${validated.issueKey}`);
2089
2293
  return formatSuccessResponse(`Issue ${validated.issueKey} updated successfully`);
2090
2294
  }
2091
2295
  const updatedIssue = await getIssue(validated.issueKey);
2092
- log12.info(`Updated issue ${updatedIssue.key}`);
2296
+ log13.info(`Updated issue ${updatedIssue.key}`);
2093
2297
  return formatIssueResponse(updatedIssue);
2094
2298
  } catch (error) {
2095
- log12.error("Error in handleUpdateIssue:", error);
2299
+ log13.error("Error in handleUpdateIssue:", error);
2096
2300
  return handleError(error);
2097
2301
  }
2098
2302
  }
2099
2303
 
2100
2304
  // src/tools/add-comment.ts
2101
- var log13 = createLogger("tool:add-comment");
2305
+ var log14 = createLogger("tool:add-comment");
2102
2306
  var addCommentTool = {
2103
2307
  name: TOOL_NAMES.ADD_COMMENT,
2104
2308
  description: "Adds a comment to an issue. Supports visibility restrictions for groups or roles. Returns the created comment with author details and timestamp.",
@@ -2137,18 +2341,18 @@ var addCommentTool = {
2137
2341
  async function handleAddComment(input) {
2138
2342
  try {
2139
2343
  const validated = validateInput(AddCommentInputSchema, input);
2140
- log13.info(`Adding comment to issue ${validated.issueKey}...`);
2344
+ log14.info(`Adding comment to issue ${validated.issueKey}...`);
2141
2345
  const comment = await addComment(validated.issueKey, validated.body, validated.visibility);
2142
- log13.info(`Added comment to ${validated.issueKey}`);
2346
+ log14.info(`Added comment to ${validated.issueKey}`);
2143
2347
  return formatCommentResponse(comment);
2144
2348
  } catch (error) {
2145
- log13.error("Error in handleAddComment:", error);
2349
+ log14.error("Error in handleAddComment:", error);
2146
2350
  return handleError(error);
2147
2351
  }
2148
2352
  }
2149
2353
 
2150
2354
  // src/tools/get-project-info.ts
2151
- var log14 = createLogger("tool:get-project-info");
2355
+ var log15 = createLogger("tool:get-project-info");
2152
2356
  var getProjectInfoTool = {
2153
2357
  name: TOOL_NAMES.GET_PROJECT_INFO,
2154
2358
  description: "Retrieves detailed information about a project (components, versions, issue types, roles, insights). More comprehensive than the basic project list.",
@@ -2172,18 +2376,18 @@ var getProjectInfoTool = {
2172
2376
  async function handleGetProjectInfo(input) {
2173
2377
  try {
2174
2378
  const validated = validateInput(GetProjectInfoInputSchema, input);
2175
- log14.info(`Getting detailed project information for ${validated.projectKey}...`);
2379
+ log15.info(`Getting detailed project information for ${validated.projectKey}...`);
2176
2380
  const project = await getProjectDetails(validated.projectKey, validated.expand);
2177
- log14.info(`Retrieved project details for ${project.key}`);
2381
+ log15.info(`Retrieved project details for ${project.key}`);
2178
2382
  return formatProjectDetailsResponse(project);
2179
2383
  } catch (error) {
2180
- log14.error("Error in handleGetProjectInfo:", error);
2384
+ log15.error("Error in handleGetProjectInfo:", error);
2181
2385
  return handleError(error);
2182
2386
  }
2183
2387
  }
2184
2388
 
2185
2389
  // src/tools/create-subtask.ts
2186
- var log15 = createLogger("tool:create-subtask");
2390
+ var log16 = createLogger("tool:create-subtask");
2187
2391
  var createSubtaskTool = {
2188
2392
  name: TOOL_NAMES.CREATE_SUBTASK,
2189
2393
  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.",
@@ -2230,7 +2434,7 @@ var createSubtaskTool = {
2230
2434
  async function handleCreateSubtask(input) {
2231
2435
  try {
2232
2436
  const validated = validateInput(CreateSubtaskInputSchema, input);
2233
- log15.info(`Creating subtask under parent issue ${validated.parentIssueKey}...`);
2437
+ log16.info(`Creating subtask under parent issue ${validated.parentIssueKey}...`);
2234
2438
  const subtaskParams = {
2235
2439
  summary: validated.summary
2236
2440
  };
@@ -2240,10 +2444,80 @@ async function handleCreateSubtask(input) {
2240
2444
  if (validated.labels !== void 0) subtaskParams.labels = validated.labels;
2241
2445
  if (validated.components !== void 0) subtaskParams.components = validated.components;
2242
2446
  const subtask = await createSubtask(validated.parentIssueKey, subtaskParams);
2243
- log15.info(`Created subtask ${subtask.key}`);
2447
+ log16.info(`Created subtask ${subtask.key}`);
2244
2448
  return formatIssueResponse(subtask);
2245
2449
  } catch (error) {
2246
- log15.error("Error in handleCreateSubtask:", error);
2450
+ log16.error("Error in handleCreateSubtask:", error);
2451
+ return handleError(error);
2452
+ }
2453
+ }
2454
+
2455
+ // src/tools/get-create-meta.ts
2456
+ var log17 = createLogger("tool:get-create-meta");
2457
+ var getCreateMetaTool = {
2458
+ name: TOOL_NAMES.GET_CREATE_META,
2459
+ 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.",
2460
+ inputSchema: {
2461
+ type: "object",
2462
+ properties: {
2463
+ projectKey: {
2464
+ type: "string",
2465
+ description: "Project key to get create metadata for (e.g., PROJECT)"
2466
+ },
2467
+ issueTypeName: {
2468
+ type: "string",
2469
+ description: "Specific issue type name to get metadata for (optional)"
2470
+ }
2471
+ },
2472
+ required: ["projectKey"]
2473
+ }
2474
+ };
2475
+ async function handleGetCreateMeta(input) {
2476
+ try {
2477
+ const validated = validateInput(GetCreateMetaInputSchema, input);
2478
+ log17.info(`Getting create metadata for project ${validated.projectKey}...`);
2479
+ const options = {
2480
+ projectKeys: [validated.projectKey],
2481
+ expand: "projects.issuetypes.fields"
2482
+ };
2483
+ if (validated.issueTypeName) {
2484
+ options.issueTypeNames = [validated.issueTypeName];
2485
+ }
2486
+ const meta = await getCreateMeta(options);
2487
+ log17.info(`Retrieved create metadata for ${validated.projectKey}`);
2488
+ return formatCreateMetaResponse(meta);
2489
+ } catch (error) {
2490
+ log17.error("Error in handleGetCreateMeta:", error);
2491
+ return handleError(error);
2492
+ }
2493
+ }
2494
+
2495
+ // src/tools/get-custom-fields.ts
2496
+ var log18 = createLogger("tool:get-custom-fields");
2497
+ var getCustomFieldsTool = {
2498
+ name: TOOL_NAMES.GET_CUSTOM_FIELDS,
2499
+ 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.",
2500
+ inputSchema: {
2501
+ type: "object",
2502
+ properties: {
2503
+ projectKey: {
2504
+ type: "string",
2505
+ description: "Project key to filter custom fields (optional)"
2506
+ }
2507
+ },
2508
+ required: []
2509
+ }
2510
+ };
2511
+ async function handleGetCustomFields(input) {
2512
+ try {
2513
+ const validated = validateInput(GetCustomFieldsInputSchema, input);
2514
+ const projectMsg = validated.projectKey ? ` for project ${validated.projectKey}` : "";
2515
+ log18.info(`Getting custom fields${projectMsg}...`);
2516
+ const fields = await getFields();
2517
+ log18.info(`Retrieved ${fields.filter((f) => f.custom).length} custom fields`);
2518
+ return formatCustomFieldsResponse(fields, validated.projectKey);
2519
+ } catch (error) {
2520
+ log18.error("Error in handleGetCustomFields:", error);
2247
2521
  return handleError(error);
2248
2522
  }
2249
2523
  }
@@ -2256,7 +2530,7 @@ if (process.env.NODE_ENV !== "production") {
2256
2530
  } catch {
2257
2531
  }
2258
2532
  }
2259
- var log16 = createLogger("server");
2533
+ var log19 = createLogger("server");
2260
2534
  var toolHandlers = /* @__PURE__ */ new Map([
2261
2535
  [TOOL_NAMES.GET_VISIBLE_PROJECTS, handleGetVisibleProjects],
2262
2536
  [TOOL_NAMES.GET_ISSUE, handleGetIssue],
@@ -2270,7 +2544,9 @@ var toolHandlers = /* @__PURE__ */ new Map([
2270
2544
  [TOOL_NAMES.UPDATE_ISSUE, handleUpdateIssue],
2271
2545
  [TOOL_NAMES.ADD_COMMENT, handleAddComment],
2272
2546
  [TOOL_NAMES.GET_PROJECT_INFO, handleGetProjectInfo],
2273
- [TOOL_NAMES.CREATE_SUBTASK, handleCreateSubtask]
2547
+ [TOOL_NAMES.CREATE_SUBTASK, handleCreateSubtask],
2548
+ [TOOL_NAMES.GET_CREATE_META, handleGetCreateMeta],
2549
+ [TOOL_NAMES.GET_CUSTOM_FIELDS, handleGetCustomFields]
2274
2550
  ]);
2275
2551
  var allTools = [
2276
2552
  getVisibleProjectsTool,
@@ -2285,7 +2561,9 @@ var allTools = [
2285
2561
  updateIssueTool,
2286
2562
  addCommentTool,
2287
2563
  getProjectInfoTool,
2288
- createSubtaskTool
2564
+ createSubtaskTool,
2565
+ getCreateMetaTool,
2566
+ getCustomFieldsTool
2289
2567
  ];
2290
2568
  async function main() {
2291
2569
  const isDryRun = (process.env.DRY_RUN || "").toLowerCase() === "1" || (process.env.DRY_RUN || "").toLowerCase() === "true";
@@ -2356,7 +2634,7 @@ async function main() {
2356
2634
  console.error("\u{1F680} Jira MCP server running on stdio");
2357
2635
  }
2358
2636
  main().catch((error) => {
2359
- log16.error("Fatal error:", error);
2637
+ log19.error("Fatal error:", error);
2360
2638
  process.exit(1);
2361
2639
  });
2362
2640
  //# sourceMappingURL=index.js.map