org-jira-mcp-adapter 1.0.0 → 1.2.0

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.
@@ -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
@@ -49,4 +49,79 @@ server.tool("jira_postIssueComment", `Post a comment on a JIRA issue in the ${ji
49
49
  return formatToolResponse(result);
50
50
  });
51
51
 
52
+ server.tool("jira_getTransitions", `Get available transitions for a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.getTransitions, async ({ issueKey }) => {
53
+ const result = await jiraService.getTransitions(issueKey);
54
+ return formatToolResponse(result);
55
+ });
56
+
57
+ server.tool("jira_transitionIssue", `Perform a transition on a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.transitionIssue, async ({ issueKey, transitionId, comment }) => {
58
+ const result = await jiraService.transitionIssue(issueKey, transitionId, comment);
59
+ return formatToolResponse(result);
60
+ });
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
+
92
+ server.tool("jira_addWorklog", `Add a worklog entry to a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.addWorklog, async ({ issueKey, timeSpent, comment, started }) => {
93
+ const result = await jiraService.addWorklog(issueKey, timeSpent, comment, started);
94
+ return formatToolResponse(result);
95
+ });
96
+
97
+ server.tool("jira_getIssueWorklogs", `Get worklogs for a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.getIssueWorklogs, async ({ issueKey }) => {
98
+ const result = await jiraService.getIssueWorklogs(issueKey);
99
+ return formatToolResponse(result);
100
+ });
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
+
112
+ server.tool("jira_getAllProjects", `Get all visible projects in the ${jiraInstanceType}`, jiraToolSchemas.getAllProjects, async ({ recent }) => {
113
+ const result = await jiraService.getAllProjects(recent);
114
+ return formatToolResponse(result);
115
+ });
116
+
117
+ server.tool("jira_getCurrentUser", `Get the currently logged in user in the ${jiraInstanceType}`, jiraToolSchemas.getCurrentUser, async () => {
118
+ const result = await jiraService.getCurrentUser();
119
+ return formatToolResponse(result);
120
+ });
121
+
122
+ server.tool("jira_findUsers", `Find users in the ${jiraInstanceType}`, jiraToolSchemas.findUsers, async ({ query, maxResults }) => {
123
+ const result = await jiraService.findUsers(query, maxResults);
124
+ return formatToolResponse(result);
125
+ });
126
+
52
127
  await connectServer(server);
package/jira-service.js CHANGED
@@ -3,19 +3,14 @@ import { handleApiOperation } from '@atlassian-dc-mcp/common';
3
3
  import { IssueService, OpenAPI, SearchService } from '@atlassian-dc-mcp/jira/build/jira-client/index.js';
4
4
 
5
5
  export class JiraService {
6
- constructor(host, token, fullBaseUrl, email) {
6
+ constructor(host, token, fullBaseUrl) {
7
7
  if (fullBaseUrl) {
8
8
  OpenAPI.BASE = fullBaseUrl;
9
9
  } else if (host) {
10
10
  const cleanHost = host.replace(/^https?:\/\//, '').replace(/\/$/, '');
11
11
  OpenAPI.BASE = `https://${cleanHost}/rest`;
12
12
  }
13
- if (email) {
14
- OpenAPI.USERNAME = email;
15
- OpenAPI.PASSWORD = token;
16
- } else {
17
- OpenAPI.TOKEN = token;
18
- }
13
+ OpenAPI.TOKEN = token;
19
14
  OpenAPI.VERSION = '2';
20
15
  }
21
16
 
@@ -80,6 +75,61 @@ export class JiraService {
80
75
  }, 'Error updating issue');
81
76
  }
82
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
+
83
133
  static validateConfig() {
84
134
  const requiredEnvVars = ['JIRA_API_TOKEN'];
85
135
  const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);
@@ -103,6 +153,59 @@ export const jiraToolSchemas = {
103
153
  issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
104
154
  expand: z.string().optional().describe("Comma separated fields to expand")
105
155
  },
156
+ getTransitions: {
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: {
185
+ issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
186
+ timeSpent: z.string().describe("Time spent (e.g. 1h 30m)"),
187
+ comment: z.string().optional().describe("Worklog comment"),
188
+ started: z.string().optional().describe("Start time (ISO 8601 string), defaults to now")
189
+ },
190
+ getIssueWorklogs: {
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: {
202
+ recent: z.boolean().optional().describe("Return only recent projects")
203
+ },
204
+ getCurrentUser: {},
205
+ findUsers: {
206
+ query: z.string().describe("Search query (username, name, email)"),
207
+ maxResults: z.number().optional().describe("Max results")
208
+ },
106
209
  getIssueComments: {
107
210
  issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
108
211
  expand: z.string().optional().describe("Comma separated fields to expand")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "org-jira-mcp-adapter",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "MCP server for Jira Data Center/Server (On-Premise)",
5
5
  "type": "module",
6
6
  "main": "index.js",