org-jira-mcp-adapter 1.1.0 → 1.2.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/README.md +3 -3
- package/cleanup_ot_690.js +64 -0
- package/index.js +40 -0
- package/jira-service.js +94 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
],
|
|
23
23
|
"env": {
|
|
24
24
|
"JIRA_HOST": "jira.example.com",
|
|
25
|
-
"JIRA_API_TOKEN": "
|
|
25
|
+
"JIRA_API_TOKEN": "your_pat_token"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
> **Важно**: Замените `jira.example.com` и `
|
|
32
|
+
> **Важно**: Замените `jira.example.com` и `your_pat_token` на ваши реальные данные.
|
|
33
33
|
|
|
34
34
|
### Переменные окружения
|
|
35
35
|
|
|
36
36
|
- `JIRA_HOST`: Адрес вашего Jira сервера (без протокола, например `jira.example.com`).
|
|
37
|
-
- `JIRA_API_TOKEN`: Ваш
|
|
37
|
+
- `JIRA_API_TOKEN`: Ваш Personal Access Token (PAT).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { JiraService } from './jira-service.js';
|
|
2
|
+
import * as process from 'node:process';
|
|
3
|
+
|
|
4
|
+
const JIRA_HOST = 'jira.tech.shopstat.ru';
|
|
5
|
+
const JIRA_TOKEN = process.argv[2];
|
|
6
|
+
|
|
7
|
+
if (!JIRA_TOKEN) {
|
|
8
|
+
console.error('Please provide Jira API Token as argument: node cleanup_ot_690.js <TOKEN>');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Clean host for constructor if needed, but JiraService handles it.
|
|
13
|
+
// Passing JIRA_HOST and JIRA_TOKEN.
|
|
14
|
+
const service = new JiraService(JIRA_HOST, JIRA_TOKEN);
|
|
15
|
+
const issueKey = 'OT-690';
|
|
16
|
+
|
|
17
|
+
async function main() {
|
|
18
|
+
console.log(`Starting cleanup for ${issueKey}...`);
|
|
19
|
+
|
|
20
|
+
// 1. Delete Worklogs
|
|
21
|
+
try {
|
|
22
|
+
console.log('Fetching worklogs...');
|
|
23
|
+
const worklogsData = await service.getIssueWorklogs(issueKey);
|
|
24
|
+
// handle both array or object with worklogs property
|
|
25
|
+
const worklogs = Array.isArray(worklogsData) ? worklogsData : (worklogsData.worklogs || []);
|
|
26
|
+
|
|
27
|
+
console.log(`Found ${worklogs.length} worklogs.`);
|
|
28
|
+
|
|
29
|
+
for (const w of worklogs) {
|
|
30
|
+
console.log(`Deleting worklog ${w.id}...`);
|
|
31
|
+
await service.deleteWorklog(issueKey, w.id);
|
|
32
|
+
console.log(`Deleted worklog ${w.id}`);
|
|
33
|
+
}
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error('Error handling worklogs:', e.message);
|
|
36
|
+
if (e.response) {
|
|
37
|
+
console.error('Response data:', e.response.data);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2. Delete Comments
|
|
42
|
+
try {
|
|
43
|
+
console.log('Fetching comments...');
|
|
44
|
+
const commentsData = await service.getIssueComments(issueKey);
|
|
45
|
+
const comments = Array.isArray(commentsData) ? commentsData : (commentsData.comments || []);
|
|
46
|
+
|
|
47
|
+
console.log(`Found ${comments.length} comments.`);
|
|
48
|
+
|
|
49
|
+
for (const c of comments) {
|
|
50
|
+
console.log(`Deleting comment ${c.id}...`);
|
|
51
|
+
await service.deleteComment(issueKey, c.id);
|
|
52
|
+
console.log(`Deleted comment ${c.id}`);
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.error('Error handling comments:', e.message);
|
|
56
|
+
if (e.response) {
|
|
57
|
+
console.error('Response data:', e.response.data);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log('Cleanup complete.');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
main();
|
package/index.js
CHANGED
|
@@ -59,6 +59,36 @@ server.tool("jira_transitionIssue", `Perform a transition on a JIRA issue in the
|
|
|
59
59
|
return formatToolResponse(result);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
server.tool("jira_getVotes", `Get votes for a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.getVotes, async ({ issueKey }) => {
|
|
63
|
+
const result = await jiraService.getVotes(issueKey);
|
|
64
|
+
return formatToolResponse(result);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
server.tool("jira_addVote", `Add vote to a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.addVote, async ({ issueKey }) => {
|
|
68
|
+
const result = await jiraService.addVote(issueKey);
|
|
69
|
+
return formatToolResponse(result);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
server.tool("jira_removeVote", `Remove vote from a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.removeVote, async ({ issueKey }) => {
|
|
73
|
+
const result = await jiraService.removeVote(issueKey);
|
|
74
|
+
return formatToolResponse(result);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
server.tool("jira_getIssueWatchers", `Get watchers for a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.getIssueWatchers, async ({ issueKey }) => {
|
|
78
|
+
const result = await jiraService.getIssueWatchers(issueKey);
|
|
79
|
+
return formatToolResponse(result);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
server.tool("jira_addWatcher", `Add watcher to a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.addWatcher, async ({ issueKey, username }) => {
|
|
83
|
+
const result = await jiraService.addWatcher(issueKey, username);
|
|
84
|
+
return formatToolResponse(result);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
server.tool("jira_removeWatcher", `Remove watcher from a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.removeWatcher, async ({ issueKey, username }) => {
|
|
88
|
+
const result = await jiraService.removeWatcher(issueKey, username);
|
|
89
|
+
return formatToolResponse(result);
|
|
90
|
+
});
|
|
91
|
+
|
|
62
92
|
server.tool("jira_addWorklog", `Add a worklog entry to a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.addWorklog, async ({ issueKey, timeSpent, comment, started }) => {
|
|
63
93
|
const result = await jiraService.addWorklog(issueKey, timeSpent, comment, started);
|
|
64
94
|
return formatToolResponse(result);
|
|
@@ -69,6 +99,16 @@ server.tool("jira_getIssueWorklogs", `Get worklogs for a JIRA issue in the ${jir
|
|
|
69
99
|
return formatToolResponse(result);
|
|
70
100
|
});
|
|
71
101
|
|
|
102
|
+
server.tool("jira_deleteWorklog", `Delete a worklog from a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.deleteWorklog, async ({ issueKey, worklogId }) => {
|
|
103
|
+
const result = await jiraService.deleteWorklog(issueKey, worklogId);
|
|
104
|
+
return formatToolResponse(result);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
server.tool("jira_deleteComment", `Delete a comment from a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.deleteComment, async ({ issueKey, commentId }) => {
|
|
108
|
+
const result = await jiraService.deleteComment(issueKey, commentId);
|
|
109
|
+
return formatToolResponse(result);
|
|
110
|
+
});
|
|
111
|
+
|
|
72
112
|
server.tool("jira_getAllProjects", `Get all visible projects in the ${jiraInstanceType}`, jiraToolSchemas.getAllProjects, async ({ recent }) => {
|
|
73
113
|
const result = await jiraService.getAllProjects(recent);
|
|
74
114
|
return formatToolResponse(result);
|
package/jira-service.js
CHANGED
|
@@ -75,6 +75,61 @@ export class JiraService {
|
|
|
75
75
|
}, 'Error updating issue');
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
async getTransitions(issueKey) {
|
|
79
|
+
return handleApiOperation(() => IssueService.getTransitions(issueKey), 'Error getting transitions');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async transitionIssue(issueKey, transitionId, comment) {
|
|
83
|
+
return handleApiOperation(() => {
|
|
84
|
+
const body = {
|
|
85
|
+
transition: { id: transitionId }
|
|
86
|
+
};
|
|
87
|
+
if (comment) {
|
|
88
|
+
body.update = {
|
|
89
|
+
comment: [{
|
|
90
|
+
add: { body: comment }
|
|
91
|
+
}]
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return IssueService.doTransition(issueKey, body);
|
|
95
|
+
}, 'Error transitioning issue');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async addWorklog(issueKey, timeSpent, comment, started) {
|
|
99
|
+
return handleApiOperation(() => {
|
|
100
|
+
const body = {
|
|
101
|
+
timeSpent,
|
|
102
|
+
comment,
|
|
103
|
+
started: started || new Date().toISOString().replace('Z', '+0000')
|
|
104
|
+
};
|
|
105
|
+
return IssueService.addWorklog(issueKey, undefined, undefined, undefined, body);
|
|
106
|
+
}, 'Error adding worklog');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async getIssueWorklogs(issueKey) {
|
|
110
|
+
return handleApiOperation(() => IssueService.getIssueWorklog(issueKey), 'Error getting worklogs');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async deleteWorklog(issueKey, worklogId) {
|
|
114
|
+
return handleApiOperation(() => IssueService.deleteWorklog(issueKey, worklogId), 'Error deleting worklog');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async deleteComment(issueKey, commentId) {
|
|
118
|
+
return handleApiOperation(() => IssueService.deleteComment(issueKey, commentId), 'Error deleting comment');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async getAllProjects(recent = false) {
|
|
122
|
+
return handleApiOperation(() => ProjectService.getAllProjects(undefined, undefined, recent ? true : undefined, undefined), 'Error getting projects');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async getCurrentUser() {
|
|
126
|
+
return handleApiOperation(() => MyselfService.getUser(), 'Error getting current user');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async findUsers(query, maxResults = 10) {
|
|
130
|
+
return handleApiOperation(() => GroupuserpickerService.findUsersAndGroups(undefined, maxResults, query, undefined, undefined, undefined), 'Error finding users');
|
|
131
|
+
}
|
|
132
|
+
|
|
78
133
|
static validateConfig() {
|
|
79
134
|
const requiredEnvVars = ['JIRA_API_TOKEN'];
|
|
80
135
|
const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);
|
|
@@ -99,23 +154,51 @@ export const jiraToolSchemas = {
|
|
|
99
154
|
expand: z.string().optional().describe("Comma separated fields to expand")
|
|
100
155
|
},
|
|
101
156
|
getTransitions: {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
157
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)")
|
|
158
|
+
},
|
|
159
|
+
transitionIssue: {
|
|
160
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
|
|
161
|
+
transitionId: z.string().describe("Transition ID to perform"),
|
|
162
|
+
comment: z.string().optional().describe("Comment to add with transition")
|
|
163
|
+
},
|
|
164
|
+
getVotes: {
|
|
165
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)")
|
|
166
|
+
},
|
|
167
|
+
addVote: {
|
|
168
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)")
|
|
169
|
+
},
|
|
170
|
+
removeVote: {
|
|
171
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)")
|
|
172
|
+
},
|
|
173
|
+
getIssueWatchers: {
|
|
174
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)")
|
|
175
|
+
},
|
|
176
|
+
addWatcher: {
|
|
177
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
|
|
178
|
+
username: z.string().optional().describe("Username to add as watcher (defaults to current user)")
|
|
179
|
+
},
|
|
180
|
+
removeWatcher: {
|
|
181
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
|
|
182
|
+
username: z.string().describe("Username to remove from watchers")
|
|
183
|
+
},
|
|
184
|
+
addWorklog: {
|
|
110
185
|
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
|
|
111
186
|
timeSpent: z.string().describe("Time spent (e.g. 1h 30m)"),
|
|
112
187
|
comment: z.string().optional().describe("Worklog comment"),
|
|
113
188
|
started: z.string().optional().describe("Start time (ISO 8601 string), defaults to now")
|
|
114
189
|
},
|
|
115
190
|
getIssueWorklogs: {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
191
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)")
|
|
192
|
+
},
|
|
193
|
+
deleteWorklog: {
|
|
194
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
|
|
195
|
+
worklogId: z.string().describe("ID of the worklog to delete")
|
|
196
|
+
},
|
|
197
|
+
deleteComment: {
|
|
198
|
+
issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
|
|
199
|
+
commentId: z.string().describe("ID of the comment to delete")
|
|
200
|
+
},
|
|
201
|
+
getAllProjects: {
|
|
119
202
|
recent: z.boolean().optional().describe("Return only recent projects")
|
|
120
203
|
},
|
|
121
204
|
getCurrentUser: {},
|