@mcpio/jira 2.2.0 → 2.2.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 +358 -443
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -3,8 +3,25 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import axios from 'axios';
|
|
6
|
-
import
|
|
7
|
-
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
import { resolve } from 'path';
|
|
8
|
+
try {
|
|
9
|
+
const envPath = resolve(process.cwd(), '.env');
|
|
10
|
+
const envContent = readFileSync(envPath, 'utf-8');
|
|
11
|
+
for (const line of envContent.split('\n')) {
|
|
12
|
+
const trimmed = line.trim();
|
|
13
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
14
|
+
continue;
|
|
15
|
+
const eqIndex = trimmed.indexOf('=');
|
|
16
|
+
if (eqIndex === -1)
|
|
17
|
+
continue;
|
|
18
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
19
|
+
const value = trimmed.slice(eqIndex + 1).trim().replace(/^(['"])(.*)\1$/, '$2');
|
|
20
|
+
if (!process.env[key])
|
|
21
|
+
process.env[key] = value;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
8
25
|
function getRequiredEnv(name, fallback = null) {
|
|
9
26
|
const value = process.env[name];
|
|
10
27
|
if (value !== undefined && value !== '') {
|
|
@@ -28,8 +45,8 @@ function validateProjectKey(key) {
|
|
|
28
45
|
if (!key || typeof key !== 'string') {
|
|
29
46
|
throw new Error('Invalid project key: must be a string');
|
|
30
47
|
}
|
|
31
|
-
if (!/^[A-Z][A-Z0-9_]{
|
|
32
|
-
throw new Error(`Invalid project key format: ${key}. Expected
|
|
48
|
+
if (!/^[A-Z][A-Z0-9_]{0,9}$/.test(key)) {
|
|
49
|
+
throw new Error(`Invalid project key format: ${key}. Expected 1-10 uppercase alphanumeric characters`);
|
|
33
50
|
}
|
|
34
51
|
return key;
|
|
35
52
|
}
|
|
@@ -52,16 +69,11 @@ function sanitizeString(str, maxLength = 1000, fieldName = 'input') {
|
|
|
52
69
|
return str.trim();
|
|
53
70
|
}
|
|
54
71
|
function validateSafeParam(str, fieldName, maxLength = 100) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
if (str.length > maxLength) {
|
|
59
|
-
throw new Error(`${fieldName} exceeds maximum length of ${maxLength} characters`);
|
|
60
|
-
}
|
|
61
|
-
if (/[\/\\]/.test(str)) {
|
|
72
|
+
const value = sanitizeString(str, maxLength, fieldName);
|
|
73
|
+
if (/[\/\\]/.test(value)) {
|
|
62
74
|
throw new Error(`Invalid ${fieldName}: contains unsafe characters`);
|
|
63
75
|
}
|
|
64
|
-
return
|
|
76
|
+
return value;
|
|
65
77
|
}
|
|
66
78
|
function validateMaxResults(maxResults) {
|
|
67
79
|
if (typeof maxResults !== 'number' || !Number.isInteger(maxResults) || maxResults < 1) {
|
|
@@ -188,34 +200,19 @@ function parseInlineContent(text) {
|
|
|
188
200
|
if (lastIndex < text.length) {
|
|
189
201
|
parts.push({ type: 'text', text: text.substring(lastIndex) });
|
|
190
202
|
}
|
|
191
|
-
|
|
192
|
-
return parts;
|
|
193
|
-
return text ? [{ type: 'text', text }] : [];
|
|
194
|
-
}
|
|
195
|
-
function addBulletItem(nodes, content) {
|
|
196
|
-
const listItem = {
|
|
197
|
-
type: 'listItem',
|
|
198
|
-
content: [{ type: 'paragraph', content }]
|
|
199
|
-
};
|
|
200
|
-
const lastNode = nodes[nodes.length - 1];
|
|
201
|
-
if (lastNode && lastNode.type === 'bulletList') {
|
|
202
|
-
lastNode.content.push(listItem);
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
nodes.push({ type: 'bulletList', content: [listItem] });
|
|
206
|
-
}
|
|
203
|
+
return parts;
|
|
207
204
|
}
|
|
208
|
-
function
|
|
205
|
+
function addListItem(nodes, content, listType) {
|
|
209
206
|
const listItem = {
|
|
210
207
|
type: 'listItem',
|
|
211
208
|
content: [{ type: 'paragraph', content }]
|
|
212
209
|
};
|
|
213
210
|
const lastNode = nodes[nodes.length - 1];
|
|
214
|
-
if (lastNode && lastNode.type ===
|
|
211
|
+
if (lastNode && lastNode.type === listType) {
|
|
215
212
|
lastNode.content.push(listItem);
|
|
216
213
|
}
|
|
217
214
|
else {
|
|
218
|
-
nodes.push({ type:
|
|
215
|
+
nodes.push({ type: listType, content: [listItem] });
|
|
219
216
|
}
|
|
220
217
|
}
|
|
221
218
|
function createADFDocument(content) {
|
|
@@ -249,10 +246,10 @@ function createADFDocument(content) {
|
|
|
249
246
|
});
|
|
250
247
|
}
|
|
251
248
|
else if (line.startsWith('* ') || line.startsWith('- ')) {
|
|
252
|
-
|
|
249
|
+
addListItem(nodes, parseInlineContent(line.substring(2)), 'bulletList');
|
|
253
250
|
}
|
|
254
251
|
else if (/^\d+\.\s+/.test(line)) {
|
|
255
|
-
|
|
252
|
+
addListItem(nodes, parseInlineContent(line.replace(/^\d+\.\s+/, '')), 'orderedList');
|
|
256
253
|
}
|
|
257
254
|
else if (line.startsWith('> ')) {
|
|
258
255
|
const text = line.substring(2);
|
|
@@ -678,419 +675,337 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
678
675
|
],
|
|
679
676
|
};
|
|
680
677
|
});
|
|
678
|
+
async function handleCreateIssue(a) {
|
|
679
|
+
const { summary, description, issueType = 'Task', priority = 'Medium', labels = [], storyPoints } = a;
|
|
680
|
+
const projectKey = a.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
681
|
+
validateSafeParam(issueType, 'issueType');
|
|
682
|
+
validateSafeParam(priority, 'priority');
|
|
683
|
+
const validatedLabels = validateLabels(labels);
|
|
684
|
+
const issueData = {
|
|
685
|
+
fields: {
|
|
686
|
+
project: { key: projectKey },
|
|
687
|
+
summary: sanitizeString(summary, 500, 'summary'),
|
|
688
|
+
description: createADFDocument(description),
|
|
689
|
+
issuetype: { name: issueType },
|
|
690
|
+
priority: { name: priority },
|
|
691
|
+
labels: validatedLabels,
|
|
692
|
+
},
|
|
693
|
+
};
|
|
694
|
+
if (storyPoints !== undefined && storyPoints !== null) {
|
|
695
|
+
issueData.fields[STORY_POINTS_FIELD] = validateStoryPoints(storyPoints);
|
|
696
|
+
}
|
|
697
|
+
const response = await jiraApi.post('/issue', issueData);
|
|
698
|
+
return createSuccessResponse({
|
|
699
|
+
success: true,
|
|
700
|
+
key: response.data.key,
|
|
701
|
+
id: response.data.id,
|
|
702
|
+
url: createIssueUrl(response.data.key),
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
async function handleGetIssue(a) {
|
|
706
|
+
validateIssueKey(a.issueKey);
|
|
707
|
+
const response = await jiraApi.get(`/issue/${a.issueKey}`);
|
|
708
|
+
const f = response.data.fields;
|
|
709
|
+
return createSuccessResponse({
|
|
710
|
+
key: response.data.key,
|
|
711
|
+
summary: f.summary,
|
|
712
|
+
description: adfToText(f.description),
|
|
713
|
+
status: f.status?.name,
|
|
714
|
+
assignee: f.assignee ? { displayName: f.assignee.displayName, accountId: f.assignee.accountId } : null,
|
|
715
|
+
reporter: f.reporter?.displayName,
|
|
716
|
+
priority: f.priority?.name,
|
|
717
|
+
issueType: f.issuetype?.name,
|
|
718
|
+
labels: f.labels || [],
|
|
719
|
+
storyPoints: f[STORY_POINTS_FIELD],
|
|
720
|
+
parent: f.parent?.key,
|
|
721
|
+
created: f.created,
|
|
722
|
+
updated: f.updated,
|
|
723
|
+
url: createIssueUrl(response.data.key),
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
async function handleSearchIssues(a) {
|
|
727
|
+
const { jql, maxResults = 50 } = a;
|
|
728
|
+
validateJQL(jql);
|
|
729
|
+
const validatedMaxResults = validateMaxResults(maxResults);
|
|
730
|
+
const response = await jiraApi.get('/search/jql', {
|
|
731
|
+
params: {
|
|
732
|
+
jql,
|
|
733
|
+
maxResults: validatedMaxResults,
|
|
734
|
+
fields: 'summary,status,assignee,priority,created,updated,issuetype,parent,labels',
|
|
735
|
+
},
|
|
736
|
+
});
|
|
737
|
+
return createSuccessResponse({
|
|
738
|
+
total: response.data.total,
|
|
739
|
+
issues: response.data.issues.map((issue) => ({
|
|
740
|
+
key: issue.key,
|
|
741
|
+
summary: issue.fields.summary,
|
|
742
|
+
status: issue.fields.status?.name,
|
|
743
|
+
assignee: issue.fields.assignee ? { displayName: issue.fields.assignee.displayName, accountId: issue.fields.assignee.accountId } : null,
|
|
744
|
+
priority: issue.fields.priority?.name,
|
|
745
|
+
issueType: issue.fields.issuetype?.name,
|
|
746
|
+
labels: issue.fields.labels || [],
|
|
747
|
+
parent: issue.fields.parent?.key,
|
|
748
|
+
url: createIssueUrl(issue.key),
|
|
749
|
+
})),
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
async function handleUpdateIssue(a) {
|
|
753
|
+
const { issueKey, summary, description, status } = a;
|
|
754
|
+
validateIssueKey(issueKey);
|
|
755
|
+
const updateData = { fields: {} };
|
|
756
|
+
let hasFieldUpdates = false;
|
|
757
|
+
if (summary) {
|
|
758
|
+
updateData.fields.summary = sanitizeString(summary, 500, 'summary');
|
|
759
|
+
hasFieldUpdates = true;
|
|
760
|
+
}
|
|
761
|
+
if (description) {
|
|
762
|
+
updateData.fields.description = createADFDocument(description);
|
|
763
|
+
hasFieldUpdates = true;
|
|
764
|
+
}
|
|
765
|
+
if (hasFieldUpdates) {
|
|
766
|
+
await jiraApi.put(`/issue/${issueKey}`, updateData);
|
|
767
|
+
}
|
|
768
|
+
const warnings = [];
|
|
769
|
+
if (status) {
|
|
770
|
+
const transitions = await jiraApi.get(`/issue/${issueKey}/transitions`);
|
|
771
|
+
const transition = transitions.data.transitions.find((t) => t.name === status);
|
|
772
|
+
if (transition) {
|
|
773
|
+
await jiraApi.post(`/issue/${issueKey}/transitions`, {
|
|
774
|
+
transition: { id: transition.id },
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
const available = transitions.data.transitions.map((t) => t.name).join(', ');
|
|
779
|
+
warnings.push(`Transition "${status}" not found. Available transitions: ${available}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (!hasFieldUpdates && !status) {
|
|
783
|
+
return createSuccessResponse({ success: false, message: `No updates provided for ${issueKey}` });
|
|
784
|
+
}
|
|
785
|
+
const result = {
|
|
786
|
+
success: warnings.length === 0,
|
|
787
|
+
message: `Issue ${issueKey} updated${warnings.length > 0 ? ' with warnings' : ' successfully'}`,
|
|
788
|
+
url: createIssueUrl(issueKey),
|
|
789
|
+
};
|
|
790
|
+
if (warnings.length > 0) {
|
|
791
|
+
result.warnings = warnings;
|
|
792
|
+
}
|
|
793
|
+
return createSuccessResponse(result);
|
|
794
|
+
}
|
|
795
|
+
async function handleAddComment(a) {
|
|
796
|
+
validateIssueKey(a.issueKey);
|
|
797
|
+
await jiraApi.post(`/issue/${a.issueKey}/comment`, { body: createADFDocument(a.comment) });
|
|
798
|
+
return createSuccessResponse({ success: true, message: `Comment added to ${a.issueKey}` });
|
|
799
|
+
}
|
|
800
|
+
async function handleLinkIssues(a) {
|
|
801
|
+
const { inwardIssue, outwardIssue, linkType = 'Relates' } = a;
|
|
802
|
+
validateIssueKey(inwardIssue);
|
|
803
|
+
validateIssueKey(outwardIssue);
|
|
804
|
+
validateSafeParam(linkType, 'linkType');
|
|
805
|
+
try {
|
|
806
|
+
await jiraApi.post('/issueLink', {
|
|
807
|
+
type: { name: linkType },
|
|
808
|
+
inwardIssue: { key: inwardIssue },
|
|
809
|
+
outwardIssue: { key: outwardIssue },
|
|
810
|
+
});
|
|
811
|
+
return createSuccessResponse({ success: true, message: `Linked ${inwardIssue} to ${outwardIssue} with type "${linkType}"` });
|
|
812
|
+
}
|
|
813
|
+
catch (linkError) {
|
|
814
|
+
const axiosErr = linkError;
|
|
815
|
+
if (axiosErr.response?.status === 400 && axiosErr.response?.data?.errorMessages?.includes('link already exists')) {
|
|
816
|
+
return createSuccessResponse({ success: true, message: `Link between ${inwardIssue} and ${outwardIssue} already exists`, alreadyLinked: true });
|
|
817
|
+
}
|
|
818
|
+
throw linkError;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
async function handleGetProjectInfo(a) {
|
|
822
|
+
const projectKey = a.projectKey ?? JIRA_PROJECT_KEY;
|
|
823
|
+
validateProjectKey(projectKey);
|
|
824
|
+
const response = await jiraApi.get(`/project/${projectKey}`);
|
|
825
|
+
return createSuccessResponse({
|
|
826
|
+
key: response.data.key,
|
|
827
|
+
name: response.data.name,
|
|
828
|
+
description: response.data.description,
|
|
829
|
+
lead: response.data.lead?.displayName,
|
|
830
|
+
url: response.data.url,
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
async function handleDeleteIssue(a) {
|
|
834
|
+
validateIssueKey(a.issueKey);
|
|
835
|
+
await jiraApi.delete(`/issue/${a.issueKey}`);
|
|
836
|
+
return createSuccessResponse({ success: true, message: `Issue ${a.issueKey} deleted successfully` });
|
|
837
|
+
}
|
|
838
|
+
async function handleCreateSubtask(a) {
|
|
839
|
+
const { parentKey, summary, description, priority = 'Medium' } = a;
|
|
840
|
+
validateIssueKey(parentKey);
|
|
841
|
+
validateSafeParam(priority, 'priority');
|
|
842
|
+
const projectKey = a.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
843
|
+
const response = await jiraApi.post('/issue', {
|
|
844
|
+
fields: {
|
|
845
|
+
project: { key: projectKey },
|
|
846
|
+
summary: sanitizeString(summary, 500, 'summary'),
|
|
847
|
+
description: createADFDocument(description),
|
|
848
|
+
issuetype: { name: 'Subtask' },
|
|
849
|
+
priority: { name: priority },
|
|
850
|
+
parent: { key: parentKey },
|
|
851
|
+
},
|
|
852
|
+
});
|
|
853
|
+
return createSuccessResponse({ success: true, key: response.data.key, id: response.data.id, parent: parentKey, url: createIssueUrl(response.data.key) });
|
|
854
|
+
}
|
|
855
|
+
async function handleAssignIssue(a) {
|
|
856
|
+
validateIssueKey(a.issueKey);
|
|
857
|
+
await jiraApi.put(`/issue/${a.issueKey}/assignee`, { accountId: a.accountId !== undefined ? a.accountId : null });
|
|
858
|
+
return createSuccessResponse({
|
|
859
|
+
success: true,
|
|
860
|
+
message: a.accountId ? `Issue ${a.issueKey} assigned to ${a.accountId}` : `Issue ${a.issueKey} unassigned`,
|
|
861
|
+
url: createIssueUrl(a.issueKey),
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
async function handleListTransitions(a) {
|
|
865
|
+
validateIssueKey(a.issueKey);
|
|
866
|
+
const response = await jiraApi.get(`/issue/${a.issueKey}/transitions`);
|
|
867
|
+
return createSuccessResponse({
|
|
868
|
+
issueKey: a.issueKey,
|
|
869
|
+
transitions: response.data.transitions.map((t) => ({
|
|
870
|
+
id: t.id,
|
|
871
|
+
name: t.name,
|
|
872
|
+
to: { id: t.to.id, name: t.to.name, category: t.to.statusCategory?.name },
|
|
873
|
+
})),
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
async function handleAddWorklog(a) {
|
|
877
|
+
const { issueKey, timeSpent, comment, started } = a;
|
|
878
|
+
validateIssueKey(issueKey);
|
|
879
|
+
sanitizeString(timeSpent, 50, 'timeSpent');
|
|
880
|
+
const worklogData = { timeSpent };
|
|
881
|
+
if (comment)
|
|
882
|
+
worklogData.comment = createADFDocument(comment);
|
|
883
|
+
if (started)
|
|
884
|
+
worklogData.started = started;
|
|
885
|
+
const response = await jiraApi.post(`/issue/${issueKey}/worklog`, worklogData);
|
|
886
|
+
return createSuccessResponse({ success: true, id: response.data.id, issueKey, timeSpent: response.data.timeSpent, author: response.data.author?.displayName });
|
|
887
|
+
}
|
|
888
|
+
async function handleGetComments(a) {
|
|
889
|
+
const { issueKey, maxResults = 50, orderBy = '-created' } = a;
|
|
890
|
+
validateIssueKey(issueKey);
|
|
891
|
+
const validatedMaxResults = validateMaxResults(maxResults);
|
|
892
|
+
const response = await jiraApi.get(`/issue/${issueKey}/comment`, { params: { maxResults: validatedMaxResults, orderBy } });
|
|
893
|
+
return createSuccessResponse({
|
|
894
|
+
issueKey,
|
|
895
|
+
total: response.data.total,
|
|
896
|
+
comments: response.data.comments.map((c) => ({
|
|
897
|
+
id: c.id,
|
|
898
|
+
author: c.author?.displayName,
|
|
899
|
+
body: adfToText(c.body),
|
|
900
|
+
created: c.created,
|
|
901
|
+
updated: c.updated,
|
|
902
|
+
})),
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
async function handleGetWorklogs(a) {
|
|
906
|
+
validateIssueKey(a.issueKey);
|
|
907
|
+
const response = await jiraApi.get(`/issue/${a.issueKey}/worklog`);
|
|
908
|
+
return createSuccessResponse({
|
|
909
|
+
issueKey: a.issueKey,
|
|
910
|
+
total: response.data.total,
|
|
911
|
+
worklogs: response.data.worklogs.map((w) => ({
|
|
912
|
+
id: w.id,
|
|
913
|
+
author: w.author?.displayName,
|
|
914
|
+
timeSpent: w.timeSpent,
|
|
915
|
+
timeSpentSeconds: w.timeSpentSeconds,
|
|
916
|
+
started: w.started,
|
|
917
|
+
comment: adfToText(w.comment),
|
|
918
|
+
})),
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
async function handleListProjects(a) {
|
|
922
|
+
const { maxResults = 50, query } = a;
|
|
923
|
+
const validatedMaxResults = validateMaxResults(maxResults);
|
|
924
|
+
const params = { maxResults: validatedMaxResults };
|
|
925
|
+
if (query)
|
|
926
|
+
params.query = sanitizeString(query, 200, 'query');
|
|
927
|
+
const response = await jiraApi.get('/project/search', { params });
|
|
928
|
+
return createSuccessResponse({
|
|
929
|
+
total: response.data.total,
|
|
930
|
+
projects: response.data.values.map((p) => ({ key: p.key, name: p.name, projectTypeKey: p.projectTypeKey, style: p.style, lead: p.lead?.displayName })),
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
async function handleGetProjectComponents(a) {
|
|
934
|
+
const projectKey = a?.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
935
|
+
const response = await jiraApi.get(`/project/${projectKey}/components`);
|
|
936
|
+
return createSuccessResponse({
|
|
937
|
+
projectKey,
|
|
938
|
+
components: response.data.map((c) => ({ id: c.id, name: c.name, description: c.description, lead: c.lead?.displayName, assigneeType: c.assigneeType })),
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
async function handleGetProjectVersions(a) {
|
|
942
|
+
const projectKey = a?.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
943
|
+
const response = await jiraApi.get(`/project/${projectKey}/versions`);
|
|
944
|
+
return createSuccessResponse({
|
|
945
|
+
projectKey,
|
|
946
|
+
versions: response.data.map((v) => ({ id: v.id, name: v.name, description: v.description, released: v.released, archived: v.archived, releaseDate: v.releaseDate, startDate: v.startDate })),
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
async function handleGetFields(_a) {
|
|
950
|
+
const response = await jiraApi.get('/field');
|
|
951
|
+
return createSuccessResponse({ fields: response.data.map((f) => ({ id: f.id, name: f.name, custom: f.custom, schema: f.schema })) });
|
|
952
|
+
}
|
|
953
|
+
async function handleGetIssueTypes(a) {
|
|
954
|
+
const projectKey = a?.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
955
|
+
const response = await jiraApi.get(`/issue/createmeta/${projectKey}/issuetypes`);
|
|
956
|
+
return createSuccessResponse({
|
|
957
|
+
projectKey,
|
|
958
|
+
issueTypes: response.data.issueTypes.map((t) => ({ id: t.id, name: t.name, subtask: t.subtask, description: t.description })),
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
async function handleGetPriorities(_a) {
|
|
962
|
+
const response = await jiraApi.get('/priority/search');
|
|
963
|
+
return createSuccessResponse({ priorities: response.data.values.map((p) => ({ id: p.id, name: p.name, description: p.description, iconUrl: p.iconUrl })) });
|
|
964
|
+
}
|
|
965
|
+
async function handleGetLinkTypes(_a) {
|
|
966
|
+
const response = await jiraApi.get('/issueLinkType');
|
|
967
|
+
return createSuccessResponse({ linkTypes: response.data.issueLinkTypes.map((lt) => ({ id: lt.id, name: lt.name, inward: lt.inward, outward: lt.outward })) });
|
|
968
|
+
}
|
|
969
|
+
async function handleSearchUsers(a) {
|
|
970
|
+
const { query, maxResults = 10 } = a;
|
|
971
|
+
sanitizeString(query, 200, 'query');
|
|
972
|
+
const validatedMaxResults = validateMaxResults(maxResults);
|
|
973
|
+
const response = await jiraApi.get('/user/search', { params: { query, maxResults: validatedMaxResults } });
|
|
974
|
+
return createSuccessResponse({
|
|
975
|
+
users: response.data.map((u) => ({ accountId: u.accountId, displayName: u.displayName, emailAddress: u.emailAddress, active: u.active, accountType: u.accountType })),
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
const toolHandlers = {
|
|
979
|
+
jira_create_issue: handleCreateIssue,
|
|
980
|
+
jira_get_issue: handleGetIssue,
|
|
981
|
+
jira_search_issues: handleSearchIssues,
|
|
982
|
+
jira_update_issue: handleUpdateIssue,
|
|
983
|
+
jira_add_comment: handleAddComment,
|
|
984
|
+
jira_link_issues: handleLinkIssues,
|
|
985
|
+
jira_get_project_info: handleGetProjectInfo,
|
|
986
|
+
jira_delete_issue: handleDeleteIssue,
|
|
987
|
+
jira_create_subtask: handleCreateSubtask,
|
|
988
|
+
jira_assign_issue: handleAssignIssue,
|
|
989
|
+
jira_list_transitions: handleListTransitions,
|
|
990
|
+
jira_add_worklog: handleAddWorklog,
|
|
991
|
+
jira_get_comments: handleGetComments,
|
|
992
|
+
jira_get_worklogs: handleGetWorklogs,
|
|
993
|
+
jira_list_projects: handleListProjects,
|
|
994
|
+
jira_get_project_components: handleGetProjectComponents,
|
|
995
|
+
jira_get_project_versions: handleGetProjectVersions,
|
|
996
|
+
jira_get_fields: handleGetFields,
|
|
997
|
+
jira_get_issue_types: handleGetIssueTypes,
|
|
998
|
+
jira_get_priorities: handleGetPriorities,
|
|
999
|
+
jira_get_link_types: handleGetLinkTypes,
|
|
1000
|
+
jira_search_users: handleSearchUsers,
|
|
1001
|
+
};
|
|
681
1002
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
682
1003
|
const { name, arguments: args } = request.params;
|
|
683
|
-
const
|
|
1004
|
+
const handler = toolHandlers[name];
|
|
1005
|
+
if (!handler)
|
|
1006
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
684
1007
|
try {
|
|
685
|
-
|
|
686
|
-
case 'jira_create_issue': {
|
|
687
|
-
const { summary, description, issueType = 'Task', priority = 'Medium', labels = [], storyPoints } = a;
|
|
688
|
-
const projectKey = a.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
689
|
-
validateSafeParam(issueType, 'issueType');
|
|
690
|
-
validateSafeParam(priority, 'priority');
|
|
691
|
-
const validatedLabels = validateLabels(labels);
|
|
692
|
-
const issueData = {
|
|
693
|
-
fields: {
|
|
694
|
-
project: { key: projectKey },
|
|
695
|
-
summary: sanitizeString(summary, 500, 'summary'),
|
|
696
|
-
description: createADFDocument(description),
|
|
697
|
-
issuetype: { name: issueType },
|
|
698
|
-
priority: { name: priority },
|
|
699
|
-
labels: validatedLabels,
|
|
700
|
-
},
|
|
701
|
-
};
|
|
702
|
-
if (storyPoints !== undefined && storyPoints !== null) {
|
|
703
|
-
issueData.fields[STORY_POINTS_FIELD] = validateStoryPoints(storyPoints);
|
|
704
|
-
}
|
|
705
|
-
const response = await jiraApi.post('/issue', issueData);
|
|
706
|
-
return createSuccessResponse({
|
|
707
|
-
success: true,
|
|
708
|
-
key: response.data.key,
|
|
709
|
-
id: response.data.id,
|
|
710
|
-
url: createIssueUrl(response.data.key),
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
case 'jira_get_issue': {
|
|
714
|
-
const { issueKey } = a;
|
|
715
|
-
validateIssueKey(issueKey);
|
|
716
|
-
const response = await jiraApi.get(`/issue/${issueKey}`);
|
|
717
|
-
const f = response.data.fields;
|
|
718
|
-
return createSuccessResponse({
|
|
719
|
-
key: response.data.key,
|
|
720
|
-
summary: f.summary,
|
|
721
|
-
description: adfToText(f.description),
|
|
722
|
-
status: f.status?.name,
|
|
723
|
-
assignee: f.assignee ? { displayName: f.assignee.displayName, accountId: f.assignee.accountId } : null,
|
|
724
|
-
reporter: f.reporter?.displayName,
|
|
725
|
-
priority: f.priority?.name,
|
|
726
|
-
issueType: f.issuetype?.name,
|
|
727
|
-
labels: f.labels || [],
|
|
728
|
-
storyPoints: f[STORY_POINTS_FIELD],
|
|
729
|
-
parent: f.parent?.key,
|
|
730
|
-
created: f.created,
|
|
731
|
-
updated: f.updated,
|
|
732
|
-
url: createIssueUrl(response.data.key),
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
case 'jira_search_issues': {
|
|
736
|
-
const { jql, maxResults = 50 } = a;
|
|
737
|
-
validateJQL(jql);
|
|
738
|
-
const validatedMaxResults = validateMaxResults(maxResults);
|
|
739
|
-
const response = await jiraApi.get('/search/jql', {
|
|
740
|
-
params: {
|
|
741
|
-
jql,
|
|
742
|
-
maxResults: validatedMaxResults,
|
|
743
|
-
fields: 'summary,status,assignee,priority,created,updated,issuetype,parent,labels',
|
|
744
|
-
},
|
|
745
|
-
});
|
|
746
|
-
return createSuccessResponse({
|
|
747
|
-
total: response.data.total,
|
|
748
|
-
issues: response.data.issues.map((issue) => ({
|
|
749
|
-
key: issue.key,
|
|
750
|
-
summary: issue.fields.summary,
|
|
751
|
-
status: issue.fields.status?.name,
|
|
752
|
-
assignee: issue.fields.assignee ? { displayName: issue.fields.assignee.displayName, accountId: issue.fields.assignee.accountId } : null,
|
|
753
|
-
priority: issue.fields.priority?.name,
|
|
754
|
-
issueType: issue.fields.issuetype?.name,
|
|
755
|
-
labels: issue.fields.labels || [],
|
|
756
|
-
parent: issue.fields.parent?.key,
|
|
757
|
-
url: createIssueUrl(issue.key),
|
|
758
|
-
})),
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
case 'jira_update_issue': {
|
|
762
|
-
const { issueKey, summary, description, status } = a;
|
|
763
|
-
validateIssueKey(issueKey);
|
|
764
|
-
const updateData = { fields: {} };
|
|
765
|
-
let hasFieldUpdates = false;
|
|
766
|
-
if (summary) {
|
|
767
|
-
updateData.fields.summary = sanitizeString(summary, 500, 'summary');
|
|
768
|
-
hasFieldUpdates = true;
|
|
769
|
-
}
|
|
770
|
-
if (description) {
|
|
771
|
-
updateData.fields.description = createADFDocument(description);
|
|
772
|
-
hasFieldUpdates = true;
|
|
773
|
-
}
|
|
774
|
-
if (hasFieldUpdates) {
|
|
775
|
-
await jiraApi.put(`/issue/${issueKey}`, updateData);
|
|
776
|
-
}
|
|
777
|
-
const warnings = [];
|
|
778
|
-
if (status) {
|
|
779
|
-
const transitions = await jiraApi.get(`/issue/${issueKey}/transitions`);
|
|
780
|
-
const transition = transitions.data.transitions.find((t) => t.name === status);
|
|
781
|
-
if (transition) {
|
|
782
|
-
await jiraApi.post(`/issue/${issueKey}/transitions`, {
|
|
783
|
-
transition: { id: transition.id },
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
else {
|
|
787
|
-
const available = transitions.data.transitions.map((t) => t.name).join(', ');
|
|
788
|
-
warnings.push(`Transition "${status}" not found. Available transitions: ${available}`);
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
if (!hasFieldUpdates && !status) {
|
|
792
|
-
return createSuccessResponse({
|
|
793
|
-
success: false,
|
|
794
|
-
message: `No updates provided for ${issueKey}`,
|
|
795
|
-
});
|
|
796
|
-
}
|
|
797
|
-
const result = {
|
|
798
|
-
success: warnings.length === 0,
|
|
799
|
-
message: `Issue ${issueKey} updated${warnings.length > 0 ? ' with warnings' : ' successfully'}`,
|
|
800
|
-
url: createIssueUrl(issueKey),
|
|
801
|
-
};
|
|
802
|
-
if (warnings.length > 0) {
|
|
803
|
-
result.warnings = warnings;
|
|
804
|
-
}
|
|
805
|
-
return createSuccessResponse(result);
|
|
806
|
-
}
|
|
807
|
-
case 'jira_add_comment': {
|
|
808
|
-
const { issueKey, comment } = a;
|
|
809
|
-
validateIssueKey(issueKey);
|
|
810
|
-
await jiraApi.post(`/issue/${issueKey}/comment`, {
|
|
811
|
-
body: createADFDocument(comment),
|
|
812
|
-
});
|
|
813
|
-
return createSuccessResponse({
|
|
814
|
-
success: true,
|
|
815
|
-
message: `Comment added to ${issueKey}`,
|
|
816
|
-
});
|
|
817
|
-
}
|
|
818
|
-
case 'jira_link_issues': {
|
|
819
|
-
const { inwardIssue, outwardIssue, linkType = 'Relates' } = a;
|
|
820
|
-
validateIssueKey(inwardIssue);
|
|
821
|
-
validateIssueKey(outwardIssue);
|
|
822
|
-
validateSafeParam(linkType, 'linkType');
|
|
823
|
-
try {
|
|
824
|
-
await jiraApi.post('/issueLink', {
|
|
825
|
-
type: { name: linkType },
|
|
826
|
-
inwardIssue: { key: inwardIssue },
|
|
827
|
-
outwardIssue: { key: outwardIssue },
|
|
828
|
-
});
|
|
829
|
-
return createSuccessResponse({
|
|
830
|
-
success: true,
|
|
831
|
-
message: `Linked ${inwardIssue} to ${outwardIssue} with type "${linkType}"`,
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
catch (linkError) {
|
|
835
|
-
const axiosErr = linkError;
|
|
836
|
-
if (axiosErr.response?.status === 400 &&
|
|
837
|
-
axiosErr.response?.data?.errorMessages?.includes('link already exists')) {
|
|
838
|
-
return createSuccessResponse({
|
|
839
|
-
success: true,
|
|
840
|
-
message: `Link between ${inwardIssue} and ${outwardIssue} already exists`,
|
|
841
|
-
alreadyLinked: true,
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
|
-
throw linkError;
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
case 'jira_get_project_info': {
|
|
848
|
-
const { projectKey = JIRA_PROJECT_KEY } = a;
|
|
849
|
-
validateProjectKey(projectKey);
|
|
850
|
-
const response = await jiraApi.get(`/project/${projectKey}`);
|
|
851
|
-
return createSuccessResponse({
|
|
852
|
-
key: response.data.key,
|
|
853
|
-
name: response.data.name,
|
|
854
|
-
description: response.data.description,
|
|
855
|
-
lead: response.data.lead?.displayName,
|
|
856
|
-
url: response.data.url,
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
case 'jira_delete_issue': {
|
|
860
|
-
const { issueKey } = a;
|
|
861
|
-
validateIssueKey(issueKey);
|
|
862
|
-
await jiraApi.delete(`/issue/${issueKey}`);
|
|
863
|
-
return createSuccessResponse({
|
|
864
|
-
success: true,
|
|
865
|
-
message: `Issue ${issueKey} deleted successfully`,
|
|
866
|
-
});
|
|
867
|
-
}
|
|
868
|
-
case 'jira_create_subtask': {
|
|
869
|
-
const { parentKey, summary, description, priority = 'Medium' } = a;
|
|
870
|
-
validateIssueKey(parentKey);
|
|
871
|
-
validateSafeParam(priority, 'priority');
|
|
872
|
-
const projectKey = a.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
873
|
-
const issueData = {
|
|
874
|
-
fields: {
|
|
875
|
-
project: { key: projectKey },
|
|
876
|
-
summary: sanitizeString(summary, 500, 'summary'),
|
|
877
|
-
description: createADFDocument(description),
|
|
878
|
-
issuetype: { name: 'Subtask' },
|
|
879
|
-
priority: { name: priority },
|
|
880
|
-
parent: { key: parentKey },
|
|
881
|
-
},
|
|
882
|
-
};
|
|
883
|
-
const response = await jiraApi.post('/issue', issueData);
|
|
884
|
-
return createSuccessResponse({
|
|
885
|
-
success: true,
|
|
886
|
-
key: response.data.key,
|
|
887
|
-
id: response.data.id,
|
|
888
|
-
parent: parentKey,
|
|
889
|
-
url: createIssueUrl(response.data.key),
|
|
890
|
-
});
|
|
891
|
-
}
|
|
892
|
-
case 'jira_assign_issue': {
|
|
893
|
-
const { issueKey, accountId } = a;
|
|
894
|
-
validateIssueKey(issueKey);
|
|
895
|
-
await jiraApi.put(`/issue/${issueKey}/assignee`, {
|
|
896
|
-
accountId: accountId !== undefined ? accountId : null,
|
|
897
|
-
});
|
|
898
|
-
return createSuccessResponse({
|
|
899
|
-
success: true,
|
|
900
|
-
message: accountId
|
|
901
|
-
? `Issue ${issueKey} assigned to ${accountId}`
|
|
902
|
-
: `Issue ${issueKey} unassigned`,
|
|
903
|
-
url: createIssueUrl(issueKey),
|
|
904
|
-
});
|
|
905
|
-
}
|
|
906
|
-
case 'jira_list_transitions': {
|
|
907
|
-
const { issueKey } = a;
|
|
908
|
-
validateIssueKey(issueKey);
|
|
909
|
-
const response = await jiraApi.get(`/issue/${issueKey}/transitions`);
|
|
910
|
-
return createSuccessResponse({
|
|
911
|
-
issueKey,
|
|
912
|
-
transitions: response.data.transitions.map((t) => ({
|
|
913
|
-
id: t.id,
|
|
914
|
-
name: t.name,
|
|
915
|
-
to: {
|
|
916
|
-
id: t.to.id,
|
|
917
|
-
name: t.to.name,
|
|
918
|
-
category: t.to.statusCategory?.name,
|
|
919
|
-
},
|
|
920
|
-
})),
|
|
921
|
-
});
|
|
922
|
-
}
|
|
923
|
-
case 'jira_add_worklog': {
|
|
924
|
-
const { issueKey, timeSpent, comment, started } = a;
|
|
925
|
-
validateIssueKey(issueKey);
|
|
926
|
-
sanitizeString(timeSpent, 50, 'timeSpent');
|
|
927
|
-
const worklogData = { timeSpent };
|
|
928
|
-
if (comment) {
|
|
929
|
-
worklogData.comment = createADFDocument(comment);
|
|
930
|
-
}
|
|
931
|
-
if (started) {
|
|
932
|
-
worklogData.started = started;
|
|
933
|
-
}
|
|
934
|
-
const response = await jiraApi.post(`/issue/${issueKey}/worklog`, worklogData);
|
|
935
|
-
return createSuccessResponse({
|
|
936
|
-
success: true,
|
|
937
|
-
id: response.data.id,
|
|
938
|
-
issueKey,
|
|
939
|
-
timeSpent: response.data.timeSpent,
|
|
940
|
-
author: response.data.author?.displayName,
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
case 'jira_get_comments': {
|
|
944
|
-
const { issueKey, maxResults = 50, orderBy = '-created' } = a;
|
|
945
|
-
validateIssueKey(issueKey);
|
|
946
|
-
const validatedMaxResults = validateMaxResults(maxResults);
|
|
947
|
-
const response = await jiraApi.get(`/issue/${issueKey}/comment`, {
|
|
948
|
-
params: { maxResults: validatedMaxResults, orderBy },
|
|
949
|
-
});
|
|
950
|
-
return createSuccessResponse({
|
|
951
|
-
issueKey,
|
|
952
|
-
total: response.data.total,
|
|
953
|
-
comments: response.data.comments.map((c) => ({
|
|
954
|
-
id: c.id,
|
|
955
|
-
author: c.author?.displayName,
|
|
956
|
-
body: adfToText(c.body),
|
|
957
|
-
created: c.created,
|
|
958
|
-
updated: c.updated,
|
|
959
|
-
})),
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
case 'jira_get_worklogs': {
|
|
963
|
-
const { issueKey } = a;
|
|
964
|
-
validateIssueKey(issueKey);
|
|
965
|
-
const response = await jiraApi.get(`/issue/${issueKey}/worklog`);
|
|
966
|
-
return createSuccessResponse({
|
|
967
|
-
issueKey,
|
|
968
|
-
total: response.data.total,
|
|
969
|
-
worklogs: response.data.worklogs.map((w) => ({
|
|
970
|
-
id: w.id,
|
|
971
|
-
author: w.author?.displayName,
|
|
972
|
-
timeSpent: w.timeSpent,
|
|
973
|
-
timeSpentSeconds: w.timeSpentSeconds,
|
|
974
|
-
started: w.started,
|
|
975
|
-
comment: adfToText(w.comment),
|
|
976
|
-
})),
|
|
977
|
-
});
|
|
978
|
-
}
|
|
979
|
-
case 'jira_list_projects': {
|
|
980
|
-
const { maxResults = 50, query } = a;
|
|
981
|
-
const validatedMaxResults = validateMaxResults(maxResults);
|
|
982
|
-
const params = { maxResults: validatedMaxResults };
|
|
983
|
-
if (query) {
|
|
984
|
-
params.query = sanitizeString(query, 200, 'query');
|
|
985
|
-
}
|
|
986
|
-
const response = await jiraApi.get('/project/search', { params });
|
|
987
|
-
return createSuccessResponse({
|
|
988
|
-
total: response.data.total,
|
|
989
|
-
projects: response.data.values.map((p) => ({
|
|
990
|
-
key: p.key,
|
|
991
|
-
name: p.name,
|
|
992
|
-
projectTypeKey: p.projectTypeKey,
|
|
993
|
-
style: p.style,
|
|
994
|
-
lead: p.lead?.displayName,
|
|
995
|
-
})),
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
case 'jira_get_project_components': {
|
|
999
|
-
const projectKey = a?.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
1000
|
-
const response = await jiraApi.get(`/project/${projectKey}/components`);
|
|
1001
|
-
return createSuccessResponse({
|
|
1002
|
-
projectKey,
|
|
1003
|
-
components: response.data.map((c) => ({
|
|
1004
|
-
id: c.id,
|
|
1005
|
-
name: c.name,
|
|
1006
|
-
description: c.description,
|
|
1007
|
-
lead: c.lead?.displayName,
|
|
1008
|
-
assigneeType: c.assigneeType,
|
|
1009
|
-
})),
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
case 'jira_get_project_versions': {
|
|
1013
|
-
const projectKey = a?.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
1014
|
-
const response = await jiraApi.get(`/project/${projectKey}/versions`);
|
|
1015
|
-
return createSuccessResponse({
|
|
1016
|
-
projectKey,
|
|
1017
|
-
versions: response.data.map((v) => ({
|
|
1018
|
-
id: v.id,
|
|
1019
|
-
name: v.name,
|
|
1020
|
-
description: v.description,
|
|
1021
|
-
released: v.released,
|
|
1022
|
-
archived: v.archived,
|
|
1023
|
-
releaseDate: v.releaseDate,
|
|
1024
|
-
startDate: v.startDate,
|
|
1025
|
-
})),
|
|
1026
|
-
});
|
|
1027
|
-
}
|
|
1028
|
-
case 'jira_get_fields': {
|
|
1029
|
-
const response = await jiraApi.get('/field');
|
|
1030
|
-
return createSuccessResponse({
|
|
1031
|
-
fields: response.data.map((f) => ({
|
|
1032
|
-
id: f.id,
|
|
1033
|
-
name: f.name,
|
|
1034
|
-
custom: f.custom,
|
|
1035
|
-
schema: f.schema,
|
|
1036
|
-
})),
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
1039
|
-
case 'jira_get_issue_types': {
|
|
1040
|
-
const projectKey = a?.projectKey ? validateProjectKey(a.projectKey) : JIRA_PROJECT_KEY;
|
|
1041
|
-
const response = await jiraApi.get(`/issue/createmeta/${projectKey}/issuetypes`);
|
|
1042
|
-
return createSuccessResponse({
|
|
1043
|
-
projectKey,
|
|
1044
|
-
issueTypes: response.data.issueTypes.map((t) => ({
|
|
1045
|
-
id: t.id,
|
|
1046
|
-
name: t.name,
|
|
1047
|
-
subtask: t.subtask,
|
|
1048
|
-
description: t.description,
|
|
1049
|
-
})),
|
|
1050
|
-
});
|
|
1051
|
-
}
|
|
1052
|
-
case 'jira_get_priorities': {
|
|
1053
|
-
const response = await jiraApi.get('/priority/search');
|
|
1054
|
-
return createSuccessResponse({
|
|
1055
|
-
priorities: response.data.values.map((p) => ({
|
|
1056
|
-
id: p.id,
|
|
1057
|
-
name: p.name,
|
|
1058
|
-
description: p.description,
|
|
1059
|
-
iconUrl: p.iconUrl,
|
|
1060
|
-
})),
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
case 'jira_get_link_types': {
|
|
1064
|
-
const response = await jiraApi.get('/issueLinkType');
|
|
1065
|
-
return createSuccessResponse({
|
|
1066
|
-
linkTypes: response.data.issueLinkTypes.map((lt) => ({
|
|
1067
|
-
id: lt.id,
|
|
1068
|
-
name: lt.name,
|
|
1069
|
-
inward: lt.inward,
|
|
1070
|
-
outward: lt.outward,
|
|
1071
|
-
})),
|
|
1072
|
-
});
|
|
1073
|
-
}
|
|
1074
|
-
case 'jira_search_users': {
|
|
1075
|
-
const { query, maxResults = 10 } = a;
|
|
1076
|
-
sanitizeString(query, 200, 'query');
|
|
1077
|
-
const validatedMaxResults = validateMaxResults(maxResults);
|
|
1078
|
-
const response = await jiraApi.get('/user/search', {
|
|
1079
|
-
params: { query, maxResults: validatedMaxResults },
|
|
1080
|
-
});
|
|
1081
|
-
return createSuccessResponse({
|
|
1082
|
-
users: response.data.map((u) => ({
|
|
1083
|
-
accountId: u.accountId,
|
|
1084
|
-
displayName: u.displayName,
|
|
1085
|
-
emailAddress: u.emailAddress,
|
|
1086
|
-
active: u.active,
|
|
1087
|
-
accountType: u.accountType,
|
|
1088
|
-
})),
|
|
1089
|
-
});
|
|
1090
|
-
}
|
|
1091
|
-
default:
|
|
1092
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
1093
|
-
}
|
|
1008
|
+
return await handler(args);
|
|
1094
1009
|
}
|
|
1095
1010
|
catch (error) {
|
|
1096
1011
|
return handleError(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcpio/jira",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server for Jira Cloud API v3 with automatic Markdown-to-ADF conversion",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Volodymyr Press",
|
|
@@ -30,8 +30,7 @@
|
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
33
|
-
"axios": "^1.13.1"
|
|
34
|
-
"dotenv": "^17.2.3"
|
|
33
|
+
"axios": "^1.13.1"
|
|
35
34
|
},
|
|
36
35
|
"engines": {
|
|
37
36
|
"node": ">=18.0.0"
|