@rokealvo/jira-mcp 1.0.0 → 1.1.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.
- package/README.md +1 -1
- package/build/index.js +224 -130
- package/build/index.js.map +1 -1
- package/build/services/jira-api.d.ts +7 -1
- package/build/services/jira-server-api.d.ts +13 -0
- package/build/types/jira.d.ts +11 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ MCP-сервер для работы с Jira из Claude Code, Cline и друг
|
|
|
11
11
|
"mcpServers": {
|
|
12
12
|
"jira": {
|
|
13
13
|
"command": "npx",
|
|
14
|
-
"args": ["-y", "jira-mcp"],
|
|
14
|
+
"args": ["-y", "@rokealvo/jira-mcp"],
|
|
15
15
|
"env": {
|
|
16
16
|
"JIRA_BASE_URL": "https://your-domain.atlassian.net",
|
|
17
17
|
"JIRA_USER_EMAIL": "your@email.com",
|
package/build/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Server as I } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport as _ } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { ListPromptsRequestSchema as b, GetPromptRequestSchema as
|
|
4
|
+
import { ListPromptsRequestSchema as b, GetPromptRequestSchema as A, McpError as c, ErrorCode as p, ListToolsRequestSchema as S, CallToolRequestSchema as J } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
class w {
|
|
6
6
|
baseUrl;
|
|
7
7
|
headers;
|
|
8
8
|
constructor(t, e, s, i = "basic") {
|
|
9
9
|
this.baseUrl = t;
|
|
10
|
-
let
|
|
11
|
-
i === "bearer" ?
|
|
12
|
-
Authorization:
|
|
10
|
+
let a;
|
|
11
|
+
i === "bearer" ? a = `Bearer ${s}` : a = `Basic ${Buffer.from(`${e}:${s}`).toString("base64")}`, this.headers = new Headers({
|
|
12
|
+
Authorization: a,
|
|
13
13
|
Accept: "application/json",
|
|
14
14
|
"Content-Type": "application/json"
|
|
15
15
|
});
|
|
@@ -25,14 +25,14 @@ class w {
|
|
|
25
25
|
else if (i.errorMessage)
|
|
26
26
|
s = i.errorMessage;
|
|
27
27
|
else if (i.errors && typeof i.errors == "object") {
|
|
28
|
-
const
|
|
29
|
-
|
|
28
|
+
const a = Object.entries(i.errors).map(([r, n]) => `${r}: ${n}`).join("; ");
|
|
29
|
+
a && (s = a);
|
|
30
30
|
}
|
|
31
31
|
s === t.statusText && Object.keys(i).length > 0 && (s = JSON.stringify(i));
|
|
32
32
|
} catch {
|
|
33
33
|
try {
|
|
34
|
-
const
|
|
35
|
-
|
|
34
|
+
const r = await t.text();
|
|
35
|
+
r && (s = r.substring(0, 500));
|
|
36
36
|
} catch {
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -47,26 +47,26 @@ class w {
|
|
|
47
47
|
* Looks for nodes that were auto-converted to issue links
|
|
48
48
|
*/
|
|
49
49
|
extractIssueMentions(t, e, s) {
|
|
50
|
-
const i = [],
|
|
51
|
-
if (
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
key:
|
|
50
|
+
const i = [], a = (r) => {
|
|
51
|
+
if (r.type === "inlineCard" && r.attrs?.url) {
|
|
52
|
+
const n = r.attrs.url.match(/\/browse\/([A-Z]+-\d+)/);
|
|
53
|
+
n && i.push({
|
|
54
|
+
key: n[1],
|
|
55
55
|
type: "mention",
|
|
56
56
|
source: e,
|
|
57
57
|
commentId: s
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
r.type === "text" && r.text && (r.text.match(/[A-Z]+-\d+/g) || []).forEach((o) => {
|
|
61
61
|
i.push({
|
|
62
62
|
key: o,
|
|
63
63
|
type: "mention",
|
|
64
64
|
source: e,
|
|
65
65
|
commentId: s
|
|
66
66
|
});
|
|
67
|
-
}),
|
|
67
|
+
}), r.content && r.content.forEach(a);
|
|
68
68
|
};
|
|
69
|
-
return t.forEach(
|
|
69
|
+
return t.forEach(a), [...new Map(i.map((r) => [r.key, r])).values()];
|
|
70
70
|
}
|
|
71
71
|
cleanComment(t) {
|
|
72
72
|
const e = t.body?.content ? this.extractTextContent(t.body.content) : "", s = t.body?.content ? this.extractIssueMentions(t.body.content, "comment", t.id) : [];
|
|
@@ -79,6 +79,18 @@ class w {
|
|
|
79
79
|
mentions: s
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
|
+
cleanAttachment(t) {
|
|
83
|
+
return {
|
|
84
|
+
id: t.id,
|
|
85
|
+
filename: t.filename,
|
|
86
|
+
mimeType: t.mimeType,
|
|
87
|
+
size: t.size,
|
|
88
|
+
created: t.created,
|
|
89
|
+
author: t.author?.displayName,
|
|
90
|
+
content: t.content,
|
|
91
|
+
thumbnail: t.thumbnail
|
|
92
|
+
};
|
|
93
|
+
}
|
|
82
94
|
/**
|
|
83
95
|
* Recursively extracts text content from Atlassian Document Format nodes
|
|
84
96
|
*/
|
|
@@ -104,13 +116,13 @@ class w {
|
|
|
104
116
|
i.length > 0 && (s.relatedIssues = i);
|
|
105
117
|
}
|
|
106
118
|
if (t.fields?.issuelinks?.length > 0) {
|
|
107
|
-
const i = t.fields.issuelinks.map((
|
|
108
|
-
const
|
|
119
|
+
const i = t.fields.issuelinks.map((a) => {
|
|
120
|
+
const r = a.inwardIssue || a.outwardIssue, n = a.type.inward || a.type.outward;
|
|
109
121
|
return {
|
|
110
|
-
key:
|
|
111
|
-
summary:
|
|
122
|
+
key: r.key,
|
|
123
|
+
summary: r.fields?.summary,
|
|
112
124
|
type: "link",
|
|
113
|
-
relationship:
|
|
125
|
+
relationship: n,
|
|
114
126
|
source: "description"
|
|
115
127
|
};
|
|
116
128
|
});
|
|
@@ -131,7 +143,9 @@ class w {
|
|
|
131
143
|
id: i.id,
|
|
132
144
|
key: i.key,
|
|
133
145
|
summary: i.fields?.summary
|
|
134
|
-
}))), s
|
|
146
|
+
}))), t.fields?.attachment?.length > 0 && (s.attachments = t.fields.attachment.map(
|
|
147
|
+
(i) => this.cleanAttachment(i)
|
|
148
|
+
)), s;
|
|
135
149
|
}
|
|
136
150
|
async fetchJson(t, e) {
|
|
137
151
|
const s = await fetch(this.baseUrl + t, {
|
|
@@ -184,18 +198,18 @@ class w {
|
|
|
184
198
|
expand: "names,renderedFields"
|
|
185
199
|
}), s = await this.fetchJson(`/rest/api/3/search?${e}`);
|
|
186
200
|
return await Promise.all(
|
|
187
|
-
s.issues.map(async (
|
|
188
|
-
const
|
|
189
|
-
`/rest/api/3/issue/${
|
|
190
|
-
),
|
|
191
|
-
(
|
|
201
|
+
s.issues.map(async (a) => {
|
|
202
|
+
const r = await this.fetchJson(
|
|
203
|
+
`/rest/api/3/issue/${a.key}/comment`
|
|
204
|
+
), n = this.cleanIssue(a), o = r.comments.map(
|
|
205
|
+
(d) => this.cleanComment(d)
|
|
192
206
|
), u = o.flatMap(
|
|
193
|
-
(
|
|
207
|
+
(d) => d.mentions
|
|
194
208
|
);
|
|
195
|
-
return
|
|
196
|
-
...
|
|
209
|
+
return n.relatedIssues = [
|
|
210
|
+
...n.relatedIssues,
|
|
197
211
|
...u
|
|
198
|
-
],
|
|
212
|
+
], n.comments = o, n;
|
|
199
213
|
})
|
|
200
214
|
);
|
|
201
215
|
}
|
|
@@ -212,7 +226,8 @@ class w {
|
|
|
212
226
|
"parent",
|
|
213
227
|
"subtasks",
|
|
214
228
|
"customfield_10014",
|
|
215
|
-
"issuelinks"
|
|
229
|
+
"issuelinks",
|
|
230
|
+
"attachment"
|
|
216
231
|
].join(","),
|
|
217
232
|
expand: "names,renderedFields"
|
|
218
233
|
});
|
|
@@ -225,24 +240,24 @@ class w {
|
|
|
225
240
|
} catch (o) {
|
|
226
241
|
throw o instanceof Error && o.message.includes("(Status: 404)") ? new Error(`Issue not found: ${t}`) : o;
|
|
227
242
|
}
|
|
228
|
-
const
|
|
243
|
+
const a = this.cleanIssue(s), r = i.comments.map(
|
|
229
244
|
(o) => this.cleanComment(o)
|
|
230
|
-
),
|
|
245
|
+
), n = r.flatMap(
|
|
231
246
|
(o) => o.mentions
|
|
232
247
|
);
|
|
233
|
-
if (
|
|
248
|
+
if (a.relatedIssues = [...a.relatedIssues, ...n], a.comments = r, a.epicLink)
|
|
234
249
|
try {
|
|
235
250
|
const o = await this.fetchJson(
|
|
236
|
-
`/rest/api/3/issue/${
|
|
251
|
+
`/rest/api/3/issue/${a.epicLink.key}?fields=summary`
|
|
237
252
|
);
|
|
238
|
-
|
|
253
|
+
a.epicLink.summary = o.fields?.summary;
|
|
239
254
|
} catch (o) {
|
|
240
255
|
console.error("Failed to fetch epic details:", o);
|
|
241
256
|
}
|
|
242
|
-
return
|
|
257
|
+
return a;
|
|
243
258
|
}
|
|
244
|
-
async createIssue(t, e, s, i,
|
|
245
|
-
const
|
|
259
|
+
async createIssue(t, e, s, i, a) {
|
|
260
|
+
const r = {
|
|
246
261
|
fields: {
|
|
247
262
|
project: {
|
|
248
263
|
key: t
|
|
@@ -252,48 +267,48 @@ class w {
|
|
|
252
267
|
name: e
|
|
253
268
|
},
|
|
254
269
|
...i && { description: i },
|
|
255
|
-
...
|
|
270
|
+
...a
|
|
256
271
|
}
|
|
257
272
|
};
|
|
258
273
|
return this.fetchJson("/rest/api/3/issue", {
|
|
259
274
|
method: "POST",
|
|
260
|
-
body: JSON.stringify(
|
|
275
|
+
body: JSON.stringify(r)
|
|
261
276
|
});
|
|
262
277
|
}
|
|
263
278
|
async getCreateMeta(t, e, s) {
|
|
264
|
-
let
|
|
279
|
+
let a = (await this.fetchJson(
|
|
265
280
|
`/rest/api/3/issue/createmeta/${t}/issuetypes`
|
|
266
281
|
)).issueTypes || [];
|
|
267
|
-
return e && (
|
|
268
|
-
(
|
|
282
|
+
return e && (a = a.filter(
|
|
283
|
+
(n) => n.name.toLowerCase() === e.toLowerCase()
|
|
269
284
|
)), {
|
|
270
285
|
projectKey: t,
|
|
271
286
|
issueTypes: await Promise.all(
|
|
272
|
-
|
|
287
|
+
a.map(async (n) => {
|
|
273
288
|
try {
|
|
274
289
|
let u = (await this.fetchJson(
|
|
275
|
-
`/rest/api/3/issue/createmeta/${t}/issuetypes/${
|
|
290
|
+
`/rest/api/3/issue/createmeta/${t}/issuetypes/${n.id}`
|
|
276
291
|
)).fields || [];
|
|
277
|
-
return s && (u = u.map((
|
|
278
|
-
fieldId:
|
|
279
|
-
name:
|
|
280
|
-
required:
|
|
281
|
-
schema:
|
|
282
|
-
hasDefaultValue:
|
|
283
|
-
allowedValuesCount:
|
|
292
|
+
return s && (u = u.map((d) => ({
|
|
293
|
+
fieldId: d.fieldId,
|
|
294
|
+
name: d.name,
|
|
295
|
+
required: d.required,
|
|
296
|
+
schema: d.schema,
|
|
297
|
+
hasDefaultValue: d.hasDefaultValue,
|
|
298
|
+
allowedValuesCount: d.allowedValues?.length ?? 0
|
|
284
299
|
}))), {
|
|
285
|
-
id:
|
|
286
|
-
name:
|
|
287
|
-
description:
|
|
288
|
-
subtask:
|
|
300
|
+
id: n.id,
|
|
301
|
+
name: n.name,
|
|
302
|
+
description: n.description,
|
|
303
|
+
subtask: n.subtask,
|
|
289
304
|
fields: u
|
|
290
305
|
};
|
|
291
306
|
} catch (o) {
|
|
292
307
|
return {
|
|
293
|
-
id:
|
|
294
|
-
name:
|
|
295
|
-
description:
|
|
296
|
-
subtask:
|
|
308
|
+
id: n.id,
|
|
309
|
+
name: n.name,
|
|
310
|
+
description: n.description,
|
|
311
|
+
subtask: n.subtask,
|
|
297
312
|
fields: [],
|
|
298
313
|
error: o instanceof Error ? o.message : "Failed to fetch fields"
|
|
299
314
|
};
|
|
@@ -305,7 +320,7 @@ class w {
|
|
|
305
320
|
async getFieldOptions(t, e, s) {
|
|
306
321
|
return ((await this.fetchJson(
|
|
307
322
|
`/rest/api/3/issue/createmeta/${t}/issuetypes/${e}`
|
|
308
|
-
)).fields || []).find((
|
|
323
|
+
)).fields || []).find((r) => r.fieldId === s)?.allowedValues ?? [];
|
|
309
324
|
}
|
|
310
325
|
async updateIssue(t, e) {
|
|
311
326
|
await this.fetchJson(`/rest/api/3/issue/${t}`, {
|
|
@@ -352,18 +367,18 @@ class w {
|
|
|
352
367
|
async addAttachment(t, e, s) {
|
|
353
368
|
const i = new FormData();
|
|
354
369
|
i.append("file", new Blob([new Uint8Array(e)]), s);
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
const
|
|
370
|
+
const a = new Headers(this.headers);
|
|
371
|
+
a.delete("Content-Type"), a.set("X-Atlassian-Token", "no-check");
|
|
372
|
+
const r = await fetch(
|
|
358
373
|
`${this.baseUrl}/rest/api/3/issue/${t}/attachments`,
|
|
359
374
|
{
|
|
360
375
|
method: "POST",
|
|
361
|
-
headers:
|
|
376
|
+
headers: a,
|
|
362
377
|
body: i
|
|
363
378
|
}
|
|
364
379
|
);
|
|
365
|
-
|
|
366
|
-
const o = (await
|
|
380
|
+
r.ok || await this.handleFetchError(r);
|
|
381
|
+
const o = (await r.json())[0];
|
|
367
382
|
return {
|
|
368
383
|
id: o.id,
|
|
369
384
|
filename: o.filename
|
|
@@ -395,7 +410,7 @@ class w {
|
|
|
395
410
|
async addCommentToIssue(t, e) {
|
|
396
411
|
const i = {
|
|
397
412
|
body: this.createAdfFromBody(e)
|
|
398
|
-
},
|
|
413
|
+
}, a = await this.fetchJson(
|
|
399
414
|
`/rest/api/3/issue/${t}/comment`,
|
|
400
415
|
{
|
|
401
416
|
method: "POST",
|
|
@@ -403,11 +418,11 @@ class w {
|
|
|
403
418
|
}
|
|
404
419
|
);
|
|
405
420
|
return {
|
|
406
|
-
id:
|
|
407
|
-
author:
|
|
408
|
-
created:
|
|
409
|
-
updated:
|
|
410
|
-
body: this.extractTextContent(
|
|
421
|
+
id: a.id,
|
|
422
|
+
author: a.author.displayName,
|
|
423
|
+
created: a.created,
|
|
424
|
+
updated: a.updated,
|
|
425
|
+
body: this.extractTextContent(a.body.content)
|
|
411
426
|
};
|
|
412
427
|
}
|
|
413
428
|
async getServerInfo() {
|
|
@@ -430,11 +445,11 @@ class w {
|
|
|
430
445
|
query: t,
|
|
431
446
|
maxResults: e.toString()
|
|
432
447
|
});
|
|
433
|
-
return (await this.fetchJson(`/rest/api/3/user/search?${s}`)).map((
|
|
434
|
-
accountId:
|
|
435
|
-
displayName:
|
|
436
|
-
emailAddress:
|
|
437
|
-
active:
|
|
448
|
+
return (await this.fetchJson(`/rest/api/3/user/search?${s}`)).map((a) => ({
|
|
449
|
+
accountId: a.accountId,
|
|
450
|
+
displayName: a.displayName,
|
|
451
|
+
emailAddress: a.emailAddress,
|
|
452
|
+
active: a.active
|
|
438
453
|
}));
|
|
439
454
|
}
|
|
440
455
|
async deleteIssue(t) {
|
|
@@ -444,8 +459,20 @@ class w {
|
|
|
444
459
|
});
|
|
445
460
|
e.ok || await this.handleFetchError(e);
|
|
446
461
|
}
|
|
462
|
+
async getAttachment(t) {
|
|
463
|
+
const e = await this.fetchJson(`/rest/api/3/attachment/${t}`), s = await fetch(e.content, {
|
|
464
|
+
headers: this.headers
|
|
465
|
+
});
|
|
466
|
+
s.ok || await this.handleFetchError(s);
|
|
467
|
+
const i = await s.arrayBuffer();
|
|
468
|
+
return {
|
|
469
|
+
content: Buffer.from(i).toString("base64"),
|
|
470
|
+
filename: e.filename,
|
|
471
|
+
mimeType: e.mimeType
|
|
472
|
+
};
|
|
473
|
+
}
|
|
447
474
|
}
|
|
448
|
-
class
|
|
475
|
+
class T extends w {
|
|
449
476
|
constructor(t, e, s, i = "basic") {
|
|
450
477
|
super(t, e, s, i);
|
|
451
478
|
}
|
|
@@ -460,39 +487,39 @@ class k extends w {
|
|
|
460
487
|
}
|
|
461
488
|
// Override getCreateMeta to use API v2 endpoints for Jira Server 9.0+
|
|
462
489
|
async getCreateMeta(t, e, s) {
|
|
463
|
-
let
|
|
490
|
+
let a = (await this.fetchJson(
|
|
464
491
|
`/rest/api/3/issue/createmeta/${t}/issuetypes`
|
|
465
492
|
)).values || [];
|
|
466
|
-
return e && (
|
|
467
|
-
(
|
|
493
|
+
return e && (a = a.filter(
|
|
494
|
+
(n) => n.name.toLowerCase() === e.toLowerCase()
|
|
468
495
|
)), {
|
|
469
496
|
projectKey: t,
|
|
470
497
|
issueTypes: await Promise.all(
|
|
471
|
-
|
|
498
|
+
a.map(async (n) => {
|
|
472
499
|
try {
|
|
473
500
|
let u = (await this.fetchJson(
|
|
474
|
-
`/rest/api/3/issue/createmeta/${t}/issuetypes/${
|
|
501
|
+
`/rest/api/3/issue/createmeta/${t}/issuetypes/${n.id}`
|
|
475
502
|
)).values || [];
|
|
476
|
-
return s && (u = u.map((
|
|
477
|
-
fieldId:
|
|
478
|
-
name:
|
|
479
|
-
required:
|
|
480
|
-
schema:
|
|
481
|
-
hasDefaultValue:
|
|
482
|
-
allowedValuesCount:
|
|
503
|
+
return s && (u = u.map((d) => ({
|
|
504
|
+
fieldId: d.fieldId,
|
|
505
|
+
name: d.name,
|
|
506
|
+
required: d.required,
|
|
507
|
+
schema: d.schema,
|
|
508
|
+
hasDefaultValue: d.hasDefaultValue,
|
|
509
|
+
allowedValuesCount: d.allowedValues?.length ?? 0
|
|
483
510
|
}))), {
|
|
484
|
-
id:
|
|
485
|
-
name:
|
|
486
|
-
description:
|
|
487
|
-
subtask:
|
|
511
|
+
id: n.id,
|
|
512
|
+
name: n.name,
|
|
513
|
+
description: n.description,
|
|
514
|
+
subtask: n.subtask,
|
|
488
515
|
fields: u
|
|
489
516
|
};
|
|
490
517
|
} catch (o) {
|
|
491
518
|
return {
|
|
492
|
-
id:
|
|
493
|
-
name:
|
|
494
|
-
description:
|
|
495
|
-
subtask:
|
|
519
|
+
id: n.id,
|
|
520
|
+
name: n.name,
|
|
521
|
+
description: n.description,
|
|
522
|
+
subtask: n.subtask,
|
|
496
523
|
fields: [],
|
|
497
524
|
error: o instanceof Error ? o.message : "Failed to fetch fields"
|
|
498
525
|
};
|
|
@@ -505,7 +532,7 @@ class k extends w {
|
|
|
505
532
|
async getFieldOptions(t, e, s) {
|
|
506
533
|
return ((await this.fetchJson(
|
|
507
534
|
`/rest/api/3/issue/createmeta/${t}/issuetypes/${e}`
|
|
508
|
-
)).values || []).find((
|
|
535
|
+
)).values || []).find((r) => r.fieldId === s)?.allowedValues ?? [];
|
|
509
536
|
}
|
|
510
537
|
// Override getUsers for Jira Server (uses 'username' parameter instead of 'query')
|
|
511
538
|
async getUsers(t, e = 50) {
|
|
@@ -513,12 +540,12 @@ class k extends w {
|
|
|
513
540
|
username: t,
|
|
514
541
|
maxResults: e.toString()
|
|
515
542
|
});
|
|
516
|
-
return (await this.fetchJson(`/rest/api/3/user/search?${s}`)).map((
|
|
543
|
+
return (await this.fetchJson(`/rest/api/3/user/search?${s}`)).map((a) => ({
|
|
517
544
|
// Jira Server uses 'name' or 'key' instead of 'accountId'
|
|
518
|
-
accountId:
|
|
519
|
-
displayName:
|
|
520
|
-
emailAddress:
|
|
521
|
-
active:
|
|
545
|
+
accountId: a.key || a.name,
|
|
546
|
+
displayName: a.displayName,
|
|
547
|
+
emailAddress: a.emailAddress,
|
|
548
|
+
active: a.active
|
|
522
549
|
}));
|
|
523
550
|
}
|
|
524
551
|
// Override addCommentToIssue for Jira Server (uses plain text instead of ADF)
|
|
@@ -548,8 +575,32 @@ class k extends w {
|
|
|
548
575
|
});
|
|
549
576
|
e.ok || await this.handleFetchError(e);
|
|
550
577
|
}
|
|
578
|
+
// Override cleanComment for Jira Server (body is plain text, not ADF)
|
|
579
|
+
cleanComment(t) {
|
|
580
|
+
console.error("[DEBUG cleanComment] Raw comment:", JSON.stringify(t, null, 2));
|
|
581
|
+
let e, s = [];
|
|
582
|
+
return typeof t.body == "string" ? (e = t.body, s = this.extractIssueMentionsFromText(e, "comment", t.id)) : t.body?.content ? (e = this.extractTextContent(t.body.content), s = this.extractIssueMentions(t.body.content, "comment", t.id)) : e = "", {
|
|
583
|
+
id: t.id,
|
|
584
|
+
body: e,
|
|
585
|
+
author: t.author?.displayName,
|
|
586
|
+
created: t.created,
|
|
587
|
+
updated: t.updated,
|
|
588
|
+
mentions: s
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
// Extract issue mentions from plain text (for Jira Server)
|
|
592
|
+
extractIssueMentionsFromText(t, e, s) {
|
|
593
|
+
if (!t) return [];
|
|
594
|
+
const i = t.match(/[A-Z]+-\d+/g) || [];
|
|
595
|
+
return [...new Set(i)].map((r) => ({
|
|
596
|
+
key: r,
|
|
597
|
+
type: "mention",
|
|
598
|
+
source: e,
|
|
599
|
+
commentId: s
|
|
600
|
+
}));
|
|
601
|
+
}
|
|
551
602
|
}
|
|
552
|
-
const l = process.env.JIRA_API_TOKEN ?? "", m = process.env.JIRA_BASE_URL ?? "", y = process.env.JIRA_USER_EMAIL ?? "",
|
|
603
|
+
const l = process.env.JIRA_API_TOKEN ?? "", m = process.env.JIRA_BASE_URL ?? "", y = process.env.JIRA_USER_EMAIL ?? "", k = process.env.JIRA_TYPE === "server" ? "server" : "cloud", h = process.env.JIRA_AUTH_TYPE === "bearer" ? "bearer" : "basic";
|
|
553
604
|
if (!l || !m || !y)
|
|
554
605
|
throw new Error(
|
|
555
606
|
"JIRA_API_TOKEN, JIRA_USER_EMAIL and JIRA_BASE_URL environment variables are required"
|
|
@@ -803,7 +854,7 @@ get_users(query) // поиск по имени или email
|
|
|
803
854
|
\`\`\``
|
|
804
855
|
}
|
|
805
856
|
};
|
|
806
|
-
class
|
|
857
|
+
class j {
|
|
807
858
|
server;
|
|
808
859
|
jiraApi;
|
|
809
860
|
constructor() {
|
|
@@ -818,7 +869,7 @@ class T {
|
|
|
818
869
|
prompts: {}
|
|
819
870
|
}
|
|
820
871
|
}
|
|
821
|
-
),
|
|
872
|
+
), k === "server" ? this.jiraApi = new T(
|
|
822
873
|
m,
|
|
823
874
|
y,
|
|
824
875
|
l,
|
|
@@ -839,10 +890,10 @@ class T {
|
|
|
839
890
|
name: t,
|
|
840
891
|
description: e
|
|
841
892
|
}))
|
|
842
|
-
})), this.server.setRequestHandler(
|
|
893
|
+
})), this.server.setRequestHandler(A, async (t) => {
|
|
843
894
|
const { name: e } = t.params, s = g[e];
|
|
844
895
|
if (!s)
|
|
845
|
-
throw new
|
|
896
|
+
throw new c(p.InvalidParams, `Unknown prompt: ${e}`);
|
|
846
897
|
return {
|
|
847
898
|
messages: [
|
|
848
899
|
{
|
|
@@ -854,7 +905,7 @@ class T {
|
|
|
854
905
|
}
|
|
855
906
|
]
|
|
856
907
|
};
|
|
857
|
-
}), this.server.setRequestHandler(
|
|
908
|
+
}), this.server.setRequestHandler(S, async () => ({
|
|
858
909
|
tools: [
|
|
859
910
|
{
|
|
860
911
|
name: "search_issues",
|
|
@@ -1148,6 +1199,21 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1148
1199
|
required: ["issueKey"],
|
|
1149
1200
|
additionalProperties: !1
|
|
1150
1201
|
}
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
name: "get_attachment",
|
|
1205
|
+
description: "Download a JIRA attachment by ID. Returns base64 encoded content. Use get_issue first to see available attachments with their IDs.",
|
|
1206
|
+
inputSchema: {
|
|
1207
|
+
type: "object",
|
|
1208
|
+
properties: {
|
|
1209
|
+
attachmentId: {
|
|
1210
|
+
type: "string",
|
|
1211
|
+
description: "The ID of the attachment to download"
|
|
1212
|
+
}
|
|
1213
|
+
},
|
|
1214
|
+
required: ["attachmentId"],
|
|
1215
|
+
additionalProperties: !1
|
|
1216
|
+
}
|
|
1151
1217
|
}
|
|
1152
1218
|
]
|
|
1153
1219
|
})), this.server.setRequestHandler(J, async (t) => {
|
|
@@ -1156,7 +1222,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1156
1222
|
switch (t.params.name) {
|
|
1157
1223
|
case "search_issues": {
|
|
1158
1224
|
if (!e.searchString || typeof e.searchString != "string")
|
|
1159
|
-
throw new
|
|
1225
|
+
throw new c(
|
|
1160
1226
|
p.InvalidParams,
|
|
1161
1227
|
"Search string is required"
|
|
1162
1228
|
);
|
|
@@ -1169,7 +1235,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1169
1235
|
}
|
|
1170
1236
|
case "get_epic_children": {
|
|
1171
1237
|
if (!e.epicKey || typeof e.epicKey != "string")
|
|
1172
|
-
throw new
|
|
1238
|
+
throw new c(
|
|
1173
1239
|
p.InvalidParams,
|
|
1174
1240
|
"Epic key is required"
|
|
1175
1241
|
);
|
|
@@ -1182,7 +1248,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1182
1248
|
}
|
|
1183
1249
|
case "get_issue": {
|
|
1184
1250
|
if (!e.issueId || typeof e.issueId != "string")
|
|
1185
|
-
throw new
|
|
1251
|
+
throw new c(
|
|
1186
1252
|
p.InvalidParams,
|
|
1187
1253
|
"Issue ID is required"
|
|
1188
1254
|
);
|
|
@@ -1197,7 +1263,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1197
1263
|
}
|
|
1198
1264
|
case "create_issue": {
|
|
1199
1265
|
if (!e.projectKey || typeof e.projectKey != "string" || !e.issueType || typeof e.issueType != "string" || !e.summary || typeof e.summary != "string")
|
|
1200
|
-
throw new
|
|
1266
|
+
throw new c(
|
|
1201
1267
|
p.InvalidParams,
|
|
1202
1268
|
"projectKey, issueType, and summary are required"
|
|
1203
1269
|
);
|
|
@@ -1216,7 +1282,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1216
1282
|
}
|
|
1217
1283
|
case "update_issue": {
|
|
1218
1284
|
if (!e.issueKey || typeof e.issueKey != "string" || !e.fields || typeof e.fields != "object")
|
|
1219
|
-
throw new
|
|
1285
|
+
throw new c(
|
|
1220
1286
|
p.InvalidParams,
|
|
1221
1287
|
"issueKey and fields object are required"
|
|
1222
1288
|
);
|
|
@@ -1235,7 +1301,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1235
1301
|
}
|
|
1236
1302
|
case "get_transitions": {
|
|
1237
1303
|
if (!e.issueKey || typeof e.issueKey != "string")
|
|
1238
|
-
throw new
|
|
1304
|
+
throw new c(
|
|
1239
1305
|
p.InvalidParams,
|
|
1240
1306
|
"Issue key is required"
|
|
1241
1307
|
);
|
|
@@ -1248,7 +1314,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1248
1314
|
}
|
|
1249
1315
|
case "transition_issue": {
|
|
1250
1316
|
if (!e.issueKey || typeof e.issueKey != "string" || !e.transitionId || typeof e.transitionId != "string")
|
|
1251
|
-
throw new
|
|
1317
|
+
throw new c(
|
|
1252
1318
|
p.InvalidParams,
|
|
1253
1319
|
"issueKey and transitionId are required"
|
|
1254
1320
|
);
|
|
@@ -1273,7 +1339,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1273
1339
|
}
|
|
1274
1340
|
case "add_attachment": {
|
|
1275
1341
|
if (!e.issueKey || typeof e.issueKey != "string" || !e.fileContent || typeof e.fileContent != "string" || !e.filename || typeof e.filename != "string")
|
|
1276
|
-
throw new
|
|
1342
|
+
throw new c(
|
|
1277
1343
|
p.InvalidParams,
|
|
1278
1344
|
"issueKey, fileContent, and filename are required"
|
|
1279
1345
|
);
|
|
@@ -1301,7 +1367,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1301
1367
|
}
|
|
1302
1368
|
case "add_comment": {
|
|
1303
1369
|
if (!e.issueIdOrKey || typeof e.issueIdOrKey != "string" || !e.body || typeof e.body != "string")
|
|
1304
|
-
throw new
|
|
1370
|
+
throw new c(
|
|
1305
1371
|
p.InvalidParams,
|
|
1306
1372
|
"issueIdOrKey and body are required"
|
|
1307
1373
|
);
|
|
@@ -1317,7 +1383,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1317
1383
|
}
|
|
1318
1384
|
case "get_create_meta": {
|
|
1319
1385
|
if (!e.projectKey || typeof e.projectKey != "string")
|
|
1320
|
-
throw new
|
|
1386
|
+
throw new c(
|
|
1321
1387
|
p.InvalidParams,
|
|
1322
1388
|
"projectKey is required"
|
|
1323
1389
|
);
|
|
@@ -1334,7 +1400,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1334
1400
|
}
|
|
1335
1401
|
case "get_field_options": {
|
|
1336
1402
|
if (!e.projectKey || typeof e.projectKey != "string" || !e.issueTypeId || typeof e.issueTypeId != "string" || !e.fieldId || typeof e.fieldId != "string")
|
|
1337
|
-
throw new
|
|
1403
|
+
throw new c(
|
|
1338
1404
|
p.InvalidParams,
|
|
1339
1405
|
"projectKey, issueTypeId, and fieldId are required"
|
|
1340
1406
|
);
|
|
@@ -1367,7 +1433,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1367
1433
|
}
|
|
1368
1434
|
case "get_users": {
|
|
1369
1435
|
if (!e.query || typeof e.query != "string")
|
|
1370
|
-
throw new
|
|
1436
|
+
throw new c(
|
|
1371
1437
|
p.InvalidParams,
|
|
1372
1438
|
"query is required"
|
|
1373
1439
|
);
|
|
@@ -1383,7 +1449,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1383
1449
|
}
|
|
1384
1450
|
case "delete_issue": {
|
|
1385
1451
|
if (!e.issueKey || typeof e.issueKey != "string")
|
|
1386
|
-
throw new
|
|
1452
|
+
throw new c(
|
|
1387
1453
|
p.InvalidParams,
|
|
1388
1454
|
"issueKey is required"
|
|
1389
1455
|
);
|
|
@@ -1400,14 +1466,42 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1400
1466
|
]
|
|
1401
1467
|
};
|
|
1402
1468
|
}
|
|
1469
|
+
case "get_attachment": {
|
|
1470
|
+
if (!e.attachmentId || typeof e.attachmentId != "string")
|
|
1471
|
+
throw new c(
|
|
1472
|
+
p.InvalidParams,
|
|
1473
|
+
"attachmentId is required"
|
|
1474
|
+
);
|
|
1475
|
+
const s = await this.jiraApi.getAttachment(e.attachmentId);
|
|
1476
|
+
return s.mimeType.startsWith("image/") ? {
|
|
1477
|
+
content: [
|
|
1478
|
+
{
|
|
1479
|
+
type: "text",
|
|
1480
|
+
text: `Attachment: ${s.filename} (${s.mimeType})`
|
|
1481
|
+
},
|
|
1482
|
+
{
|
|
1483
|
+
type: "image",
|
|
1484
|
+
data: s.content,
|
|
1485
|
+
mimeType: s.mimeType
|
|
1486
|
+
}
|
|
1487
|
+
]
|
|
1488
|
+
} : {
|
|
1489
|
+
content: [
|
|
1490
|
+
{
|
|
1491
|
+
type: "text",
|
|
1492
|
+
text: JSON.stringify(s, null, 2)
|
|
1493
|
+
}
|
|
1494
|
+
]
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1403
1497
|
default:
|
|
1404
|
-
throw new
|
|
1498
|
+
throw new c(
|
|
1405
1499
|
p.MethodNotFound,
|
|
1406
1500
|
`Unknown tool: ${t.params.name}`
|
|
1407
1501
|
);
|
|
1408
1502
|
}
|
|
1409
1503
|
} catch (e) {
|
|
1410
|
-
throw e instanceof
|
|
1504
|
+
throw e instanceof c ? e : new c(
|
|
1411
1505
|
p.InternalError,
|
|
1412
1506
|
e instanceof Error ? e.message : "Unknown error occurred"
|
|
1413
1507
|
);
|
|
@@ -1419,7 +1513,7 @@ Remember to add a comment with cancellation reason first.`,
|
|
|
1419
1513
|
await this.server.connect(t);
|
|
1420
1514
|
}
|
|
1421
1515
|
}
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1516
|
+
const x = new j();
|
|
1517
|
+
x.run().catch(() => {
|
|
1424
1518
|
});
|
|
1425
1519
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/services/jira-api.ts","../src/services/jira-server-api.ts","../src/index.ts"],"sourcesContent":["import {\n AddCommentResponse,\n AdfDoc,\n CleanComment,\n CleanJiraIssue,\n JiraCommentResponse,\n SearchIssuesResponse,\n} from \"../types/jira.js\";\n\nexport class JiraApiService {\n protected baseUrl: string;\n protected headers: Headers;\n\n constructor(baseUrl: string, email: string, apiToken: string, authType: 'basic' | 'bearer' = 'basic') {\n this.baseUrl = baseUrl;\n \n let authHeader: string;\n if (authType === 'bearer') {\n // For Jira Data Center Personal Access Tokens (PATs)\n authHeader = `Bearer ${apiToken}`;\n } else {\n // For Basic authentication with username/password or API token\n const auth = Buffer.from(`${email}:${apiToken}`).toString(\"base64\");\n authHeader = `Basic ${auth}`;\n }\n \n this.headers = new Headers({\n Authorization: authHeader,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n });\n }\n\n protected async handleFetchError(\n response: Response,\n url?: string\n ): Promise<never> {\n if (!response.ok) {\n let message = response.statusText;\n let errorData: any = {};\n try {\n errorData = await response.json();\n\n // Try different error formats that Jira uses\n if (\n Array.isArray(errorData.errorMessages) &&\n errorData.errorMessages.length > 0\n ) {\n // Format: { errorMessages: [\"msg1\", \"msg2\"] }\n message = errorData.errorMessages.join(\"; \");\n } else if (errorData.message) {\n // Format: { message: \"error message\" }\n message = errorData.message;\n } else if (errorData.errorMessage) {\n // Format: { errorMessage: \"error message\" }\n message = errorData.errorMessage;\n } else if (errorData.errors && typeof errorData.errors === \"object\") {\n // Format: { errors: { fieldName: \"error for field\", anotherField: \"another error\" } }\n const errorMessages = Object.entries(errorData.errors)\n .map(([field, msg]) => `${field}: ${msg}`)\n .join(\"; \");\n if (errorMessages) {\n message = errorMessages;\n }\n }\n\n // If we still only have statusText but have error data, stringify it\n if (message === response.statusText && Object.keys(errorData).length > 0) {\n message = JSON.stringify(errorData);\n }\n } catch (e) {\n // Could not parse as JSON, try to get text\n try {\n const text = await response.text();\n if (text) {\n message = text.substring(0, 500); // Limit length\n }\n } catch {\n // Ignore\n }\n }\n\n throw new Error(\n `JIRA API Error: ${message} (Status: ${response.status})`\n );\n }\n\n throw new Error(\"Unknown error occurred during fetch operation.\");\n }\n\n /**\n * Extracts issue mentions from Atlassian document content\n * Looks for nodes that were auto-converted to issue links\n */\n protected extractIssueMentions(\n content: any[],\n source: \"description\" | \"comment\",\n commentId?: string\n ): CleanJiraIssue[\"relatedIssues\"] {\n const mentions: NonNullable<CleanJiraIssue[\"relatedIssues\"]> = [];\n\n const processNode = (node: any) => {\n if (node.type === \"inlineCard\" && node.attrs?.url) {\n const match = node.attrs.url.match(/\\/browse\\/([A-Z]+-\\d+)/);\n if (match) {\n mentions.push({\n key: match[1],\n type: \"mention\",\n source,\n commentId,\n });\n }\n }\n\n if (node.type === \"text\" && node.text) {\n const matches = node.text.match(/[A-Z]+-\\d+/g) || [];\n matches.forEach((key: string) => {\n mentions.push({\n key,\n type: \"mention\",\n source,\n commentId,\n });\n });\n }\n\n if (node.content) {\n node.content.forEach(processNode);\n }\n };\n\n content.forEach(processNode);\n return [...new Map(mentions.map((m) => [m.key, m])).values()];\n }\n\n protected cleanComment(comment: {\n id: string;\n body?: {\n content?: any[];\n };\n author?: {\n displayName?: string;\n };\n created: string;\n updated: string;\n }): CleanComment {\n const body = comment.body?.content\n ? this.extractTextContent(comment.body.content)\n : \"\";\n const mentions = comment.body?.content\n ? this.extractIssueMentions(comment.body.content, \"comment\", comment.id)\n : [];\n\n return {\n id: comment.id,\n body,\n author: comment.author?.displayName,\n created: comment.created,\n updated: comment.updated,\n mentions: mentions,\n };\n }\n\n /**\n * Recursively extracts text content from Atlassian Document Format nodes\n */\n protected extractTextContent(content: any[]): string {\n if (!Array.isArray(content)) return \"\";\n\n return content\n .map((node) => {\n if (node.type === \"text\") {\n return node.text || \"\";\n }\n if (node.content) {\n return this.extractTextContent(node.content);\n }\n return \"\";\n })\n .join(\"\");\n }\n\n protected cleanIssue(issue: any): CleanJiraIssue {\n const description = issue.fields?.description?.content\n ? this.extractTextContent(issue.fields.description.content)\n : \"\";\n\n const cleanedIssue: CleanJiraIssue = {\n id: issue.id,\n key: issue.key,\n summary: issue.fields?.summary,\n status: issue.fields?.status?.name,\n created: issue.fields?.created,\n updated: issue.fields?.updated,\n description,\n relatedIssues: [],\n };\n\n if (issue.fields?.description?.content) {\n const mentions = this.extractIssueMentions(\n issue.fields.description.content,\n \"description\"\n );\n if (mentions.length > 0) {\n cleanedIssue.relatedIssues = mentions;\n }\n }\n\n if (issue.fields?.issuelinks?.length > 0) {\n const links = issue.fields.issuelinks.map((link: any) => {\n const linkedIssue = link.inwardIssue || link.outwardIssue;\n const relationship = link.type.inward || link.type.outward;\n return {\n key: linkedIssue.key,\n summary: linkedIssue.fields?.summary,\n type: \"link\" as const,\n relationship,\n source: \"description\" as const,\n };\n });\n\n cleanedIssue.relatedIssues = [\n ...(cleanedIssue.relatedIssues || []),\n ...links,\n ];\n }\n\n if (issue.fields?.parent) {\n cleanedIssue.parent = {\n id: issue.fields.parent.id,\n key: issue.fields.parent.key,\n summary: issue.fields.parent.fields?.summary,\n };\n }\n\n if (issue.fields?.customfield_10014) {\n cleanedIssue.epicLink = {\n id: issue.fields.customfield_10014,\n key: issue.fields.customfield_10014,\n summary: undefined,\n };\n }\n\n if (issue.fields?.subtasks?.length > 0) {\n cleanedIssue.children = issue.fields.subtasks.map((subtask: any) => ({\n id: subtask.id,\n key: subtask.key,\n summary: subtask.fields?.summary,\n }));\n }\n\n return cleanedIssue;\n }\n\n protected async fetchJson<T>(url: string, init?: RequestInit): Promise<T> {\n const response = await fetch(this.baseUrl + url, {\n ...init,\n headers: this.headers,\n });\n\n if (!response.ok) {\n await this.handleFetchError(response, url);\n }\n\n return response.json();\n }\n\n async searchIssues(searchString: string): Promise<SearchIssuesResponse> {\n const params = new URLSearchParams({\n jql: searchString,\n maxResults: \"50\",\n fields: [\n \"id\",\n \"key\",\n \"summary\",\n \"description\",\n \"status\",\n \"created\",\n \"updated\",\n \"parent\",\n \"subtasks\",\n \"customfield_10014\",\n \"issuelinks\",\n ].join(\",\"),\n expand: \"names,renderedFields\",\n });\n\n const data = await this.fetchJson<any>(`/rest/api/3/search?${params}`);\n\n return {\n total: data.total,\n issues: data.issues.map((issue: any) => this.cleanIssue(issue)),\n };\n }\n\n async getEpicChildren(epicKey: string): Promise<CleanJiraIssue[]> {\n const params = new URLSearchParams({\n jql: `\"Epic Link\" = ${epicKey}`,\n maxResults: \"100\",\n fields: [\n \"id\",\n \"key\",\n \"summary\",\n \"description\",\n \"status\",\n \"created\",\n \"updated\",\n \"parent\",\n \"subtasks\",\n \"customfield_10014\",\n \"issuelinks\",\n ].join(\",\"),\n expand: \"names,renderedFields\",\n });\n\n const data = await this.fetchJson<any>(`/rest/api/3/search?${params}`);\n\n const issuesWithComments = await Promise.all(\n data.issues.map(async (issue: any) => {\n const commentsData = await this.fetchJson<any>(\n `/rest/api/3/issue/${issue.key}/comment`\n );\n const cleanedIssue = this.cleanIssue(issue);\n const comments = commentsData.comments.map((comment: any) =>\n this.cleanComment(comment)\n );\n\n const commentMentions = comments.flatMap(\n (comment: CleanComment) => comment.mentions\n );\n cleanedIssue.relatedIssues = [\n ...cleanedIssue.relatedIssues,\n ...commentMentions,\n ];\n\n cleanedIssue.comments = comments;\n return cleanedIssue;\n })\n );\n\n return issuesWithComments;\n }\n\n async getIssueWithComments(issueId: string): Promise<CleanJiraIssue> {\n const params = new URLSearchParams({\n fields: [\n \"id\",\n \"key\",\n \"summary\",\n \"description\",\n \"status\",\n \"created\",\n \"updated\",\n \"parent\",\n \"subtasks\",\n \"customfield_10014\",\n \"issuelinks\",\n ].join(\",\"),\n expand: \"names,renderedFields\",\n });\n\n let issueData, commentsData;\n try {\n [issueData, commentsData] = await Promise.all([\n this.fetchJson<any>(`/rest/api/3/issue/${issueId}?${params}`),\n this.fetchJson<any>(`/rest/api/3/issue/${issueId}/comment`),\n ]);\n } catch (error: any) {\n if (error instanceof Error && error.message.includes(\"(Status: 404)\")) {\n throw new Error(`Issue not found: ${issueId}`);\n }\n\n throw error;\n }\n\n const issue = this.cleanIssue(issueData);\n const comments = commentsData.comments.map((comment: any) =>\n this.cleanComment(comment)\n );\n\n const commentMentions = comments.flatMap(\n (comment: CleanComment) => comment.mentions\n );\n issue.relatedIssues = [...issue.relatedIssues, ...commentMentions];\n\n issue.comments = comments;\n\n if (issue.epicLink) {\n try {\n const epicData = await this.fetchJson<any>(\n `/rest/api/3/issue/${issue.epicLink.key}?fields=summary`\n );\n issue.epicLink.summary = epicData.fields?.summary;\n } catch (error) {\n console.error(\"Failed to fetch epic details:\", error);\n }\n }\n\n return issue;\n }\n\n async createIssue(\n projectKey: string,\n issueType: string,\n summary: string,\n description?: string,\n fields?: Record<string, any>\n ): Promise<{ id: string; key: string }> {\n const payload = {\n fields: {\n project: {\n key: projectKey,\n },\n summary,\n issuetype: {\n name: issueType,\n },\n ...(description && { description }),\n ...fields,\n },\n };\n\n return this.fetchJson<{ id: string; key: string }>(\"/rest/api/3/issue\", {\n method: \"POST\",\n body: JSON.stringify(payload),\n });\n }\n\n async getCreateMeta(\n projectKey: string,\n issueTypeName?: string,\n compact?: boolean\n ): Promise<any> {\n // Get available issue types for the project\n const issueTypesData = await this.fetchJson<{ issueTypes: Array<{ id: string; name: string; description?: string; subtask: boolean }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes`\n );\n\n let issueTypes = issueTypesData.issueTypes || [];\n\n // Filter by issue type name if provided\n if (issueTypeName) {\n issueTypes = issueTypes.filter(\n (it) => it.name.toLowerCase() === issueTypeName.toLowerCase()\n );\n }\n\n // Get fields for each issue type\n const result = {\n projectKey,\n issueTypes: await Promise.all(\n issueTypes.map(async (issueType) => {\n try {\n const fieldsData = await this.fetchJson<{ fields: Array<{ fieldId: string; name: string; required: boolean; schema?: any; allowedValues?: any[]; hasDefaultValue?: boolean }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes/${issueType.id}`\n );\n\n let fields = fieldsData.fields || [];\n\n // In compact mode, replace allowedValues with count\n if (compact) {\n fields = fields.map((f) => ({\n fieldId: f.fieldId,\n name: f.name,\n required: f.required,\n schema: f.schema,\n hasDefaultValue: f.hasDefaultValue,\n allowedValuesCount: f.allowedValues?.length ?? 0,\n }));\n }\n\n return {\n id: issueType.id,\n name: issueType.name,\n description: issueType.description,\n subtask: issueType.subtask,\n fields,\n };\n } catch (error) {\n return {\n id: issueType.id,\n name: issueType.name,\n description: issueType.description,\n subtask: issueType.subtask,\n fields: [],\n error: error instanceof Error ? error.message : \"Failed to fetch fields\",\n };\n }\n })\n ),\n };\n\n return result;\n }\n\n async getFieldOptions(\n projectKey: string,\n issueTypeId: string,\n fieldId: string\n ): Promise<any[]> {\n const fieldsData = await this.fetchJson<{ fields: Array<{ fieldId: string; allowedValues?: any[] }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes/${issueTypeId}`\n );\n\n const field = (fieldsData.fields || []).find((f) => f.fieldId === fieldId);\n return field?.allowedValues ?? [];\n }\n\n async updateIssue(\n issueKey: string,\n fields: Record<string, any>\n ): Promise<void> {\n await this.fetchJson(`/rest/api/3/issue/${issueKey}`, {\n method: \"PUT\",\n body: JSON.stringify({ fields }),\n });\n }\n\n async getTransitions(\n issueKey: string\n ): Promise<Array<{ id: string; name: string; to: { name: string } }>> {\n const data = await this.fetchJson<any>(\n `/rest/api/3/issue/${issueKey}/transitions`\n );\n return data.transitions;\n }\n\n async transitionIssue(\n issueKey: string,\n transitionId: string,\n comment?: string\n ): Promise<void> {\n const payload: any = {\n transition: { id: transitionId },\n };\n\n if (comment) {\n payload.update = {\n comment: [\n {\n add: {\n body: {\n type: \"doc\",\n version: 1,\n content: [\n {\n type: \"paragraph\",\n content: [\n {\n type: \"text\",\n text: comment,\n },\n ],\n },\n ],\n },\n },\n },\n ],\n };\n }\n\n await this.fetchJson(`/rest/api/3/issue/${issueKey}/transitions`, {\n method: \"POST\",\n body: JSON.stringify(payload),\n });\n }\n\n async addAttachment(\n issueKey: string,\n file: Buffer,\n filename: string\n ): Promise<{ id: string; filename: string }> {\n const formData = new FormData();\n formData.append(\"file\", new Blob([new Uint8Array(file)]), filename);\n\n const headers = new Headers(this.headers);\n headers.delete(\"Content-Type\");\n headers.set(\"X-Atlassian-Token\", \"no-check\");\n\n const response = await fetch(\n `${this.baseUrl}/rest/api/3/issue/${issueKey}/attachments`,\n {\n method: \"POST\",\n headers,\n body: formData,\n }\n );\n\n if (!response.ok) {\n await this.handleFetchError(response);\n }\n\n const data = await response.json();\n\n const attachment = data[0];\n return {\n id: attachment.id,\n filename: attachment.filename,\n };\n }\n\n /**\n * Converts plain text to a basic Atlassian Document Format (ADF) structure.\n */\n private createAdfFromBody(text: string): AdfDoc {\n return {\n version: 1,\n type: \"doc\",\n content: [\n {\n type: \"paragraph\",\n content: [\n {\n type: \"text\",\n text: text,\n },\n ],\n },\n ],\n };\n }\n\n /**\n * Adds a comment to a JIRA issue.\n */\n async addCommentToIssue(\n issueIdOrKey: string,\n body: string\n ): Promise<AddCommentResponse> {\n const adfBody = this.createAdfFromBody(body);\n\n const payload = {\n body: adfBody,\n };\n\n const response = await this.fetchJson<JiraCommentResponse>(\n `/rest/api/3/issue/${issueIdOrKey}/comment`,\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n }\n );\n\n return {\n id: response.id,\n author: response.author.displayName,\n created: response.created,\n updated: response.updated,\n body: this.extractTextContent(response.body.content),\n };\n }\n\n async getServerInfo(): Promise<{\n version: string;\n versionNumbers: number[];\n deploymentType: string;\n buildNumber: number;\n serverTitle: string;\n }> {\n return this.fetchJson(`/rest/api/3/serverInfo`);\n }\n\n async getProjects(): Promise<Array<{\n id: string;\n key: string;\n name: string;\n projectTypeKey: string;\n lead?: { displayName: string; accountId?: string };\n }>> {\n const data = await this.fetchJson<any[]>(`/rest/api/3/project`);\n return data.map((project) => ({\n id: project.id,\n key: project.key,\n name: project.name,\n projectTypeKey: project.projectTypeKey,\n lead: project.lead ? {\n displayName: project.lead.displayName,\n accountId: project.lead.accountId,\n } : undefined,\n }));\n }\n\n async getUsers(query: string, maxResults: number = 50): Promise<Array<{\n accountId: string;\n displayName: string;\n emailAddress?: string;\n active: boolean;\n }>> {\n const params = new URLSearchParams({\n query,\n maxResults: maxResults.toString(),\n });\n const data = await this.fetchJson<any[]>(`/rest/api/3/user/search?${params}`);\n return data.map((user) => ({\n accountId: user.accountId,\n displayName: user.displayName,\n emailAddress: user.emailAddress,\n active: user.active,\n }));\n }\n\n async deleteIssue(issueKey: string): Promise<void> {\n const response = await fetch(`${this.baseUrl}/rest/api/3/issue/${issueKey}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n\n if (!response.ok) {\n await this.handleFetchError(response);\n }\n }\n}\n","// JiraServerApiService: Jira Server (Data Center) implementation\n// This class should override methods as needed for Jira Server differences\nimport { JiraApiService } from \"./jira-api.js\";\n\nexport class JiraServerApiService extends JiraApiService {\n constructor(baseUrl: string, email: string, apiToken: string, authType: 'basic' | 'bearer' = 'basic') {\n // For Jira Server/Data Center:\n // - Basic Auth: username/password or API token (traditional method)\n // - Bearer Auth: Personal Access Tokens (PATs) available in Data Center 8.14.0+\n super(baseUrl, email, apiToken, authType);\n }\n\n // Example: Override fetchJson to use /rest/api/2/ instead of /rest/api/3/\n protected overrideApiPath(path: string): string {\n // Replace /rest/api/3/ with /rest/api/2/ for Jira Server\n return path.replace(\"/rest/api/3/\", \"/rest/api/2/\");\n }\n\n // Override fetchJson to use the correct API path\n protected async fetchJson<T>(url: string, init?: RequestInit): Promise<T> {\n const serverUrl = this.overrideApiPath(url);\n return super.fetchJson<T>(serverUrl, init);\n }\n\n // Override getCreateMeta to use API v2 endpoints for Jira Server 9.0+\n async getCreateMeta(\n projectKey: string,\n issueTypeName?: string,\n compact?: boolean\n ): Promise<any> {\n // For Jira Server 9.0+, use the new paginated endpoints (same structure as Cloud but API v2)\n const issueTypesData = await this.fetchJson<{ values: Array<{ id: string; name: string; description?: string; subtask: boolean }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes`\n );\n\n let issueTypes = issueTypesData.values || [];\n\n // Filter by issue type name if provided\n if (issueTypeName) {\n issueTypes = issueTypes.filter(\n (it) => it.name.toLowerCase() === issueTypeName.toLowerCase()\n );\n }\n\n // Get fields for each issue type\n const result = {\n projectKey,\n issueTypes: await Promise.all(\n issueTypes.map(async (issueType) => {\n try {\n const fieldsData = await this.fetchJson<{ values: Array<{ fieldId: string; name: string; required: boolean; schema?: any; allowedValues?: any[]; hasDefaultValue?: boolean }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes/${issueType.id}`\n );\n\n let fields = fieldsData.values || [];\n\n // In compact mode, replace allowedValues with count\n if (compact) {\n fields = fields.map((f) => ({\n fieldId: f.fieldId,\n name: f.name,\n required: f.required,\n schema: f.schema,\n hasDefaultValue: f.hasDefaultValue,\n allowedValuesCount: f.allowedValues?.length ?? 0,\n }));\n }\n\n return {\n id: issueType.id,\n name: issueType.name,\n description: issueType.description,\n subtask: issueType.subtask,\n fields,\n };\n } catch (error) {\n return {\n id: issueType.id,\n name: issueType.name,\n description: issueType.description,\n subtask: issueType.subtask,\n fields: [],\n error: error instanceof Error ? error.message : \"Failed to fetch fields\",\n };\n }\n })\n ),\n };\n\n return result;\n }\n\n // Override getFieldOptions for Jira Server (uses 'values' instead of 'fields')\n async getFieldOptions(\n projectKey: string,\n issueTypeId: string,\n fieldId: string\n ): Promise<any[]> {\n const fieldsData = await this.fetchJson<{ values: Array<{ fieldId: string; allowedValues?: any[] }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes/${issueTypeId}`\n );\n\n const field = (fieldsData.values || []).find((f) => f.fieldId === fieldId);\n return field?.allowedValues ?? [];\n }\n\n // Override getUsers for Jira Server (uses 'username' parameter instead of 'query')\n async getUsers(query: string, maxResults: number = 50): Promise<Array<{\n accountId: string;\n displayName: string;\n emailAddress?: string;\n active: boolean;\n }>> {\n const params = new URLSearchParams({\n username: query,\n maxResults: maxResults.toString(),\n });\n const data = await this.fetchJson<any[]>(`/rest/api/3/user/search?${params}`);\n return data.map((user) => ({\n // Jira Server uses 'name' or 'key' instead of 'accountId'\n accountId: user.key || user.name,\n displayName: user.displayName,\n emailAddress: user.emailAddress,\n active: user.active,\n }));\n }\n\n // Override addCommentToIssue for Jira Server (uses plain text instead of ADF)\n async addCommentToIssue(\n issueIdOrKey: string,\n body: string\n ): Promise<{\n id: string;\n author: string;\n created: string;\n updated: string;\n body: string;\n }> {\n // Jira Server API v2 expects plain text body, not ADF\n const payload = {\n body: body,\n };\n\n const response = await this.fetchJson<{\n id: string;\n author: { displayName: string };\n created: string;\n updated: string;\n body: string;\n }>(\n `/rest/api/3/issue/${issueIdOrKey}/comment`,\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n }\n );\n\n return {\n id: response.id,\n author: response.author.displayName,\n created: response.created,\n updated: response.updated,\n body: response.body,\n };\n }\n\n // Override deleteIssue for Jira Server (uses API v2)\n async deleteIssue(issueKey: string): Promise<void> {\n const response = await fetch(`${this.baseUrl}/rest/api/2/issue/${issueKey}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n\n if (!response.ok) {\n await this.handleFetchError(response);\n }\n }\n}\n","#!/usr/bin/env node\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListToolsRequestSchema,\n ListPromptsRequestSchema,\n GetPromptRequestSchema,\n McpError,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { JiraApiService } from \"./services/jira-api.js\";\nimport { JiraServerApiService } from \"./services/jira-server-api.js\";\n\nconst JIRA_API_TOKEN = process.env.JIRA_API_TOKEN ?? \"\";\nconst JIRA_BASE_URL = process.env.JIRA_BASE_URL ?? \"\";\nconst JIRA_USER_EMAIL = process.env.JIRA_USER_EMAIL ?? \"\";\nconst JIRA_TYPE = (process.env.JIRA_TYPE === \"server\" ? \"server\" : \"cloud\") as\n | \"cloud\"\n | \"server\";\nconst JIRA_AUTH_TYPE = (process.env.JIRA_AUTH_TYPE === \"bearer\" ? \"bearer\" : \"basic\") as\n | \"basic\"\n | \"bearer\";\n\nif (!JIRA_API_TOKEN || !JIRA_BASE_URL || !JIRA_USER_EMAIL) {\n throw new Error(\n \"JIRA_API_TOKEN, JIRA_USER_EMAIL and JIRA_BASE_URL environment variables are required\",\n );\n}\n\n// ============================================================================\n// PROMPTS - Инструкции и регламенты для агентов\n// ============================================================================\n\nconst JIRA_PROMPTS: Record<string, { description: string; content: string }> = {\n jira_guidelines: {\n description: \"Общие правила и регламенты работы с Jira в компании\",\n content: `# Регламент работы с Jira\n\n## Общие принципы\n- Все изменения статусов должны отражать реальное состояние работы\n- Комментарии пишутся на русском языке\n- При любых изменениях, затрагивающих других участников, оставляй комментарий\n\n## Статусы и переходы\n\n### Workflow задачи:\n1. **Бэклог** — задача запланирована, но работа не начата\n2. **Очередь** — задача в очереди на выполнение\n3. **В работе** — код в процессе написания\n4. **Ревью** — код готов к проверке, создан PR/MR\n5. **Local test** — задача на локальном тестировании\n6. **Dev** — код в ветке dev\n7. **Stage (не проверено)** — код в ветке stage, тестирование не проведено\n8. **Релиз-кандидат** — проверено на stage, готово к релизу\n9. **Тест на проде** — код на проде, ожидает проверки\n10. **Готово на проде** — задача завершена и проверена\n\n### Особые статусы:\n- **Заблокирована** — работа приостановлена (указать причину в комментарии)\n- **Отложено** — задача отложена на неопределённый срок\n- **Отмена** — задача отменена (ОБЯЗАТЕЛЬНО с комментарием о причине!)\n\n## Создание задач\n- Summary должен быть кратким и информативным (не более 100 символов)\n- Description должен содержать достаточно информации для понимания задачи\n- Обязательно указывай компонент (component) для правильной маршрутизации\n- Для багов: шаги воспроизведения, ожидаемый и фактический результат\n\n## Отмена задач\n- ЗАПРЕЩЕНО отменять задачу без указания причины в комментарии\n- Причина должна быть информативной: \"дубликат IM3-1234\", \"неактуально после релиза X\", и т.д.\n\n## Удаление задач\n- Удаление задач запрещено политикой безопасности\n- Вместо удаления используй статус \"Отмена\" с комментарием\n\n## Комментарии\n- Используй комментарии для важной информации, которая должна остаться в истории\n- При блокировке — указывай причину и ссылку на блокирующую задачу\n- При передаче задачи — описывай текущее состояние и что осталось сделать`,\n },\n\n create_issue_workflow: {\n description: \"Пошаговый процесс создания задачи в Jira\",\n content: `# Создание задачи в Jira\n\n## Перед созданием ОБЯЗАТЕЛЬНО уточни у пользователя:\n\n### Основная информация:\n1. **Проект** — в каком проекте создать (IM3, SD, FT, ETPP и т.д.)\n2. **Тип задачи** — Баг, Story, Task, Sub-task\n3. **Краткое описание** (summary) — что нужно сделать, до 100 символов\n4. **Подробное описание** (description) — детали задачи\n\n### Для багов дополнительно запроси:\n- Шаги воспроизведения (пронумерованный список)\n- Ожидаемый результат\n- Фактический результат\n- Окружение (браузер, ОС, URL) — если релевантно\n- Скриншоты или логи — если есть\n\n### Для Story/Task уточни:\n- Критерии приёмки (acceptance criteria)\n- Связанные задачи (если есть)\n\n## Порядок действий:\n\n1. **Получи схему полей:**\n \\`\\`\\`\n get_create_meta(projectKey, issueTypeName, compact=true)\n \\`\\`\\`\n\n2. **Для обязательных полей с вариантами получи опции:**\n \\`\\`\\`\n get_field_options(projectKey, issueTypeId, fieldId)\n \\`\\`\\`\n\n3. **Покажи пользователю** итоговые данные перед созданием\n\n4. **Создай задачу:**\n \\`\\`\\`\n create_issue(projectKey, issueType, summary, description, fields)\n \\`\\`\\`\n\n5. **Верни пользователю** ключ и ссылку на созданную задачу\n\n## Пример описания бага:\n\n\\`\\`\\`\nШаги воспроизведения:\n1. Открыть страницу /checkout\n2. Добавить товар в корзину\n3. Нажать \"Оформить заказ\"\n\nОжидаемый результат:\nОткрывается форма оформления заказа\n\nФактический результат:\nСтраница показывает ошибку 500\n\nОкружение:\n- URL: https://example.com/checkout\n- Браузер: Chrome 120\n- ОС: Windows 11\n\\`\\`\\``,\n },\n\n cancel_issue_workflow: {\n description: \"Процесс отмены задачи (требует указания причины)\",\n content: `# Отмена задачи в Jira\n\n## ⚠️ ВАЖНО: Задачу НЕЛЬЗЯ отменять без причины!\n\n## Порядок действий:\n\n### 1. Спроси причину отмены\nПрежде чем отменять задачу, ОБЯЗАТЕЛЬНО спроси у пользователя:\n- \"Почему задача отменяется?\"\n- \"Укажите причину отмены для комментария\"\n\n### 2. Примеры корректных причин:\n- \"Дубликат задачи IM3-1234\"\n- \"Неактуально после релиза версии 2.5\"\n- \"Функционал реализован в рамках IM3-5678\"\n- \"Отменено по решению Product Owner (имя)\"\n- \"Требования изменились, создана новая задача IM3-9999\"\n\n### 3. Добавь комментарий с причиной:\n\\`\\`\\`\nadd_comment(issueKey, \"Причина отмены: <причина от пользователя>\")\n\\`\\`\\`\n\n### 4. Получи ID перехода \"Отмена\":\n\\`\\`\\`\nget_transitions(issueKey)\n// Найди переход с name=\"Отмена\"\n\\`\\`\\`\n\n### 5. Выполни переход:\n\\`\\`\\`\ntransition_issue(issueKey, transitionId)\n\\`\\`\\`\n\n## Запрещено:\n- ❌ Отменять задачу без комментария с причиной\n- ❌ Использовать причины типа \"не нужно\", \"отмена\" без пояснения\n\n## Если пользователь не хочет указывать причину:\nОбъясни, что это требование регламента компании, и причина важна для:\n- Истории проекта\n- Понимания, почему задача не была выполнена\n- Аудита и отчётности`,\n },\n\n transition_issue_workflow: {\n description: \"Правила смены статусов задач\",\n content: `# Смена статуса задачи в Jira\n\n## Общие правила:\n- Статус должен отражать реальное состояние работы\n- Не пропускай промежуточные статусы без веской причины\n- При переходе в финальные статусы убедись, что работа действительно завершена\n\n## Особые случаи:\n\n### Переход в \"Отмена\":\n⚠️ ОБЯЗАТЕЛЬНО сначала добавь комментарий с причиной отмены!\nСм. prompt \"cancel_issue_workflow\" для подробностей.\n\n### Переход в \"Заблокирована\":\n- Укажи в комментарии причину блокировки\n- Добавь ссылку на блокирующую задачу (если есть)\n- Пример: \"Заблокировано: ждём завершения IM3-1234 (интеграция с API)\"\n\n### Переход в \"Ревью\":\n- Должен существовать Pull Request / Merge Request\n- Желательно добавить ссылку на PR в комментарий\n\n### Переход в \"Dev\" / \"Stage\":\n- Код должен быть смержен в соответствующую ветку\n- Задача задеплоена на окружение\n\n### Переход в \"Тест на проде\":\n- Код задеплоен на production\n- Задача готова к финальной проверке\n\n### Переход в \"Готово на проде\":\n- Задача проверена на production\n- Все acceptance criteria выполнены\n- Нет открытых sub-tasks\n\n## Получение доступных переходов:\n\\`\\`\\`\nget_transitions(issueKey)\n// Вернёт список доступных переходов с их ID\n\\`\\`\\`\n\n## Выполнение перехода:\n\\`\\`\\`\ntransition_issue(issueKey, transitionId, comment?)\n// comment — опциональный комментарий к переходу\n\\`\\`\\``,\n },\n\n update_issue_workflow: {\n description: \"Правила обновления задач\",\n content: `# Обновление задачи в Jira\n\n## Что можно обновлять:\n- summary — краткое описание\n- description — подробное описание\n- assignee — исполнитель\n- priority — приоритет\n- labels — метки\n- components — компоненты\n- customfield_* — кастомные поля\n\n## Правила:\n\n### При изменении исполнителя (assignee):\n- Если задача в работе — согласуй с текущим исполнителем\n- Добавь комментарий о причине переназначения\n\n### При изменении приоритета:\n- Повышение приоритета — укажи причину в комментарии\n- Понижение приоритета — согласуй с автором задачи\n\n### При изменении описания:\n- Не удаляй важную информацию\n- Если меняешь суть задачи — лучше создай новую\n\n## Пример обновления:\n\\`\\`\\`\nupdate_issue(issueKey, {\n summary: \"Новый заголовок\",\n priority: { name: \"High\" },\n assignee: { name: \"username\" }\n})\n\\`\\`\\`\n\n## Для поиска пользователей:\n\\`\\`\\`\nget_users(query) // поиск по имени или email\n\\`\\`\\``,\n },\n};\n\nclass JiraServer {\n private server: Server;\n private jiraApi: JiraApiService;\n\n constructor() {\n this.server = new Server(\n {\n name: \"jira-mcp\",\n version: \"0.3.0\",\n },\n {\n capabilities: {\n tools: {},\n prompts: {},\n },\n },\n );\n\n if (JIRA_TYPE === \"server\") {\n this.jiraApi = new JiraServerApiService(\n JIRA_BASE_URL,\n JIRA_USER_EMAIL,\n JIRA_API_TOKEN,\n JIRA_AUTH_TYPE,\n );\n } else {\n this.jiraApi = new JiraApiService(\n JIRA_BASE_URL,\n JIRA_USER_EMAIL,\n JIRA_API_TOKEN,\n JIRA_AUTH_TYPE,\n );\n }\n\n this.setupToolHandlers();\n\n this.server.onerror = (error) => {};\n process.on(\"SIGINT\", async () => {\n await this.server.close();\n process.exit(0);\n });\n }\n\n private setupToolHandlers() {\n // ========================================================================\n // PROMPTS HANDLERS\n // ========================================================================\n\n this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({\n prompts: Object.entries(JIRA_PROMPTS).map(([name, { description }]) => ({\n name,\n description,\n })),\n }));\n\n this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const { name } = request.params;\n const prompt = JIRA_PROMPTS[name];\n\n if (!prompt) {\n throw new McpError(ErrorCode.InvalidParams, `Unknown prompt: ${name}`);\n }\n\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: prompt.content,\n },\n },\n ],\n };\n });\n\n // ========================================================================\n // TOOLS HANDLERS\n // ========================================================================\n\n this.server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: \"search_issues\",\n description: \"Search JIRA issues using JQL\",\n inputSchema: {\n type: \"object\",\n properties: {\n searchString: {\n type: \"string\",\n description: \"JQL search string\",\n },\n },\n required: [\"searchString\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_epic_children\",\n description:\n \"Get all child issues in an epic including their comments\",\n inputSchema: {\n type: \"object\",\n properties: {\n epicKey: {\n type: \"string\",\n description: \"The key of the epic issue\",\n },\n },\n required: [\"epicKey\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_issue\",\n description:\n \"Get detailed information about a specific JIRA issue including comments\",\n inputSchema: {\n type: \"object\",\n properties: {\n issueId: {\n type: \"string\",\n description: \"The ID or key of the JIRA issue\",\n },\n },\n required: [\"issueId\"],\n additionalProperties: false,\n },\n },\n {\n name: \"create_issue\",\n description: `Create a new JIRA issue.\n\n📋 BEFORE CREATING: Ask user for project, issue type, summary, and description.\nFor bugs: also ask for reproduction steps, expected/actual results.\nUse get_create_meta first to check required fields.\nSee prompt \"create_issue_workflow\" for detailed guidelines.`,\n inputSchema: {\n type: \"object\",\n properties: {\n projectKey: {\n type: \"string\",\n description: \"The project key where the issue will be created\",\n },\n issueType: {\n type: \"string\",\n description:\n 'The type of issue to create (e.g., \"Bug\", \"Story\", \"Task\")',\n },\n summary: {\n type: \"string\",\n description: \"The issue summary/title\",\n },\n description: {\n type: \"string\",\n description: \"The issue description\",\n },\n fields: {\n type: \"object\",\n description: \"Additional fields to set on the issue\",\n additionalProperties: true,\n },\n },\n required: [\"projectKey\", \"issueType\", \"summary\"],\n additionalProperties: false,\n },\n },\n {\n name: \"update_issue\",\n description: `Update an existing JIRA issue.\n\n📝 When changing assignee or priority, consider adding a comment explaining why.\nSee prompt \"update_issue_workflow\" for guidelines.`,\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to update\",\n },\n fields: {\n type: \"object\",\n description: \"Fields to update on the issue\",\n additionalProperties: true,\n },\n },\n required: [\"issueKey\", \"fields\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_transitions\",\n description: \"Get available status transitions for a JIRA issue\",\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to get transitions for\",\n },\n },\n required: [\"issueKey\"],\n additionalProperties: false,\n },\n },\n {\n name: \"transition_issue\",\n description: `Change the status of a JIRA issue by performing a transition.\n\n⚠️ CANCELLATION RULE: When transitioning to \"Отмена\" (Cancel), you MUST first:\n1. Ask user for cancellation reason\n2. Add comment with the reason using add_comment\n3. Only then perform the transition\nSee prompt \"cancel_issue_workflow\" for details.`,\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to transition\",\n },\n transitionId: {\n type: \"string\",\n description: \"The ID of the transition to perform\",\n },\n comment: {\n type: \"string\",\n description: \"Optional comment to add with the transition\",\n },\n },\n required: [\"issueKey\", \"transitionId\"],\n additionalProperties: false,\n },\n },\n {\n name: \"add_attachment\",\n description: \"Add a file attachment to a JIRA issue\",\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to add attachment to\",\n },\n fileContent: {\n type: \"string\",\n description: \"Base64 encoded content of the file\",\n },\n filename: {\n type: \"string\",\n description: \"Name of the file to be attached\",\n },\n },\n required: [\"issueKey\", \"fileContent\", \"filename\"],\n additionalProperties: false,\n },\n },\n {\n name: \"add_comment\",\n description: \"Add a comment to a JIRA issue\",\n inputSchema: {\n type: \"object\",\n properties: {\n issueIdOrKey: {\n type: \"string\",\n description: \"The ID or key of the issue to add the comment to\",\n },\n body: {\n type: \"string\",\n description: \"The content of the comment (plain text)\",\n },\n },\n required: [\"issueIdOrKey\", \"body\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_create_meta\",\n description: \"Get metadata for creating issues in a project, including available issue types and required fields. Use compact=true to get a smaller response with field counts instead of full allowedValues arrays.\",\n inputSchema: {\n type: \"object\",\n properties: {\n projectKey: {\n type: \"string\",\n description: \"The project key to get creation metadata for\",\n },\n issueTypeName: {\n type: \"string\",\n description: \"Optional: filter to a specific issue type name\",\n },\n compact: {\n type: \"boolean\",\n description: \"If true, returns allowedValuesCount instead of full allowedValues arrays (reduces response size significantly)\",\n },\n },\n required: [\"projectKey\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_field_options\",\n description: \"Get allowed values for a specific field when creating issues. Use this after get_create_meta with compact=true to fetch options for fields that have allowedValuesCount > 0.\",\n inputSchema: {\n type: \"object\",\n properties: {\n projectKey: {\n type: \"string\",\n description: \"The project key\",\n },\n issueTypeId: {\n type: \"string\",\n description: \"The issue type ID (from get_create_meta response)\",\n },\n fieldId: {\n type: \"string\",\n description: \"The field ID to get allowed values for (e.g., 'components', 'customfield_12405')\",\n },\n },\n required: [\"projectKey\", \"issueTypeId\", \"fieldId\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_server_info\",\n description: \"Get JIRA server information including version and deployment type\",\n inputSchema: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: \"get_projects\",\n description: \"Get all JIRA projects accessible to the current user\",\n inputSchema: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: \"get_users\",\n description: \"Search for JIRA users by name or email. Useful for finding assignees.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n description: \"Search query (name or email)\",\n },\n maxResults: {\n type: \"number\",\n description: \"Maximum number of results to return (default: 50)\",\n },\n },\n required: [\"query\"],\n additionalProperties: false,\n },\n },\n {\n name: \"delete_issue\",\n description: `Delete a JIRA issue permanently.\n\n⚠️ NOTE: Most users don't have delete permissions.\nIf deletion fails, use transition to \"Отмена\" (Cancel) status instead.\nRemember to add a comment with cancellation reason first.`,\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to delete (e.g., IM3-1234)\",\n },\n },\n required: [\"issueKey\"],\n additionalProperties: false,\n },\n },\n ],\n }));\n\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n try {\n const args = request.params.arguments as Record<string, any>;\n\n switch (request.params.name) {\n case \"search_issues\": {\n if (!args.searchString || typeof args.searchString !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"Search string is required\",\n );\n }\n const response = await this.jiraApi.searchIssues(args.searchString);\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"get_epic_children\": {\n if (!args.epicKey || typeof args.epicKey !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"Epic key is required\",\n );\n }\n const response = await this.jiraApi.getEpicChildren(args.epicKey);\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"get_issue\": {\n if (!args.issueId || typeof args.issueId !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"Issue ID is required\",\n );\n }\n const response = await this.jiraApi.getIssueWithComments(\n args.issueId,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"create_issue\": {\n // Basic validation\n if (\n !args.projectKey ||\n typeof args.projectKey !== \"string\" ||\n !args.issueType ||\n typeof args.issueType !== \"string\" ||\n !args.summary ||\n typeof args.summary !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"projectKey, issueType, and summary are required\",\n );\n }\n const response = await this.jiraApi.createIssue(\n args.projectKey,\n args.issueType,\n args.summary,\n args.description as string | undefined,\n args.fields as Record<string, any> | undefined,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"update_issue\": {\n if (\n !args.issueKey ||\n typeof args.issueKey !== \"string\" ||\n !args.fields ||\n typeof args.fields !== \"object\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueKey and fields object are required\",\n );\n }\n await this.jiraApi.updateIssue(args.issueKey, args.fields);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { message: `Issue ${args.issueKey} updated successfully` },\n null,\n 2,\n ),\n },\n ],\n };\n }\n case \"get_transitions\": {\n if (!args.issueKey || typeof args.issueKey !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"Issue key is required\",\n );\n }\n const response = await this.jiraApi.getTransitions(args.issueKey);\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"transition_issue\": {\n if (\n !args.issueKey ||\n typeof args.issueKey !== \"string\" ||\n !args.transitionId ||\n typeof args.transitionId !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueKey and transitionId are required\",\n );\n }\n await this.jiraApi.transitionIssue(\n args.issueKey,\n args.transitionId,\n args.comment as string | undefined,\n );\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n message: `Issue ${args.issueKey} transitioned successfully${args.comment ? \" with comment\" : \"\"}`,\n },\n null,\n 2,\n ),\n },\n ],\n };\n }\n case \"add_attachment\": {\n if (\n !args.issueKey ||\n typeof args.issueKey !== \"string\" ||\n !args.fileContent ||\n typeof args.fileContent !== \"string\" ||\n !args.filename ||\n typeof args.filename !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueKey, fileContent, and filename are required\",\n );\n }\n const fileBuffer = Buffer.from(args.fileContent, \"base64\");\n const result = await this.jiraApi.addAttachment(\n args.issueKey,\n fileBuffer,\n args.filename,\n );\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n message: `File ${args.filename} attached successfully to issue ${args.issueKey}`,\n attachmentId: result.id,\n filename: result.filename,\n },\n null,\n 2,\n ),\n },\n ],\n };\n }\n case \"add_comment\": {\n if (\n !args.issueIdOrKey ||\n typeof args.issueIdOrKey !== \"string\" ||\n !args.body ||\n typeof args.body !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueIdOrKey and body are required\",\n );\n }\n const response = await this.jiraApi.addCommentToIssue(\n args.issueIdOrKey,\n args.body,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"get_create_meta\": {\n if (!args.projectKey || typeof args.projectKey !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"projectKey is required\",\n );\n }\n const meta = await this.jiraApi.getCreateMeta(\n args.projectKey,\n args.issueTypeName as string | undefined,\n args.compact as boolean | undefined,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(meta, null, 2) },\n ],\n };\n }\n case \"get_field_options\": {\n if (\n !args.projectKey ||\n typeof args.projectKey !== \"string\" ||\n !args.issueTypeId ||\n typeof args.issueTypeId !== \"string\" ||\n !args.fieldId ||\n typeof args.fieldId !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"projectKey, issueTypeId, and fieldId are required\",\n );\n }\n const options = await this.jiraApi.getFieldOptions(\n args.projectKey,\n args.issueTypeId,\n args.fieldId,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(options, null, 2) },\n ],\n };\n }\n case \"get_server_info\": {\n const serverInfo = await this.jiraApi.getServerInfo();\n return {\n content: [\n { type: \"text\", text: JSON.stringify(serverInfo, null, 2) },\n ],\n };\n }\n case \"get_projects\": {\n const projects = await this.jiraApi.getProjects();\n return {\n content: [\n { type: \"text\", text: JSON.stringify(projects, null, 2) },\n ],\n };\n }\n case \"get_users\": {\n if (!args.query || typeof args.query !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"query is required\",\n );\n }\n const users = await this.jiraApi.getUsers(\n args.query,\n args.maxResults as number | undefined,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(users, null, 2) },\n ],\n };\n }\n case \"delete_issue\": {\n if (!args.issueKey || typeof args.issueKey !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueKey is required\",\n );\n }\n await this.jiraApi.deleteIssue(args.issueKey);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { message: `Issue ${args.issueKey} deleted successfully` },\n null,\n 2,\n ),\n },\n ],\n };\n }\n default:\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n } catch (error) {\n // Keep generic error handling\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n error instanceof Error ? error.message : \"Unknown error occurred\",\n );\n }\n });\n }\n\n async run() {\n const transport = new StdioServerTransport();\n await this.server.connect(transport);\n // JIRA MCP server running on stdio\n }\n}\n\nconst server = new JiraServer();\nserver.run().catch(() => {});\n"],"names":["JiraApiService","baseUrl","email","apiToken","authType","authHeader","response","url","message","errorData","errorMessages","field","msg","text","content","source","commentId","mentions","processNode","node","match","key","m","comment","body","issue","description","cleanedIssue","links","link","linkedIssue","relationship","subtask","init","searchString","params","data","epicKey","commentsData","comments","commentMentions","issueId","issueData","error","epicData","projectKey","issueType","summary","fields","payload","issueTypeName","compact","issueTypes","it","f","issueTypeId","fieldId","issueKey","transitionId","file","filename","formData","headers","attachment","issueIdOrKey","project","query","maxResults","user","JiraServerApiService","path","serverUrl","JIRA_API_TOKEN","JIRA_BASE_URL","JIRA_USER_EMAIL","JIRA_TYPE","JIRA_AUTH_TYPE","JIRA_PROMPTS","JiraServer","Server","ListPromptsRequestSchema","name","GetPromptRequestSchema","request","prompt","McpError","ErrorCode","ListToolsRequestSchema","CallToolRequestSchema","args","fileBuffer","result","meta","options","serverInfo","projects","users","transport","StdioServerTransport","server"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/services/jira-api.ts","../src/services/jira-server-api.ts","../src/index.ts"],"sourcesContent":["import {\n AddCommentResponse,\n AdfDoc,\n CleanAttachment,\n CleanComment,\n CleanJiraIssue,\n JiraCommentResponse,\n SearchIssuesResponse,\n} from \"../types/jira.js\";\n\nexport class JiraApiService {\n protected baseUrl: string;\n protected headers: Headers;\n\n constructor(baseUrl: string, email: string, apiToken: string, authType: 'basic' | 'bearer' = 'basic') {\n this.baseUrl = baseUrl;\n \n let authHeader: string;\n if (authType === 'bearer') {\n // For Jira Data Center Personal Access Tokens (PATs)\n authHeader = `Bearer ${apiToken}`;\n } else {\n // For Basic authentication with username/password or API token\n const auth = Buffer.from(`${email}:${apiToken}`).toString(\"base64\");\n authHeader = `Basic ${auth}`;\n }\n \n this.headers = new Headers({\n Authorization: authHeader,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n });\n }\n\n protected async handleFetchError(\n response: Response,\n url?: string\n ): Promise<never> {\n if (!response.ok) {\n let message = response.statusText;\n let errorData: any = {};\n try {\n errorData = await response.json();\n\n // Try different error formats that Jira uses\n if (\n Array.isArray(errorData.errorMessages) &&\n errorData.errorMessages.length > 0\n ) {\n // Format: { errorMessages: [\"msg1\", \"msg2\"] }\n message = errorData.errorMessages.join(\"; \");\n } else if (errorData.message) {\n // Format: { message: \"error message\" }\n message = errorData.message;\n } else if (errorData.errorMessage) {\n // Format: { errorMessage: \"error message\" }\n message = errorData.errorMessage;\n } else if (errorData.errors && typeof errorData.errors === \"object\") {\n // Format: { errors: { fieldName: \"error for field\", anotherField: \"another error\" } }\n const errorMessages = Object.entries(errorData.errors)\n .map(([field, msg]) => `${field}: ${msg}`)\n .join(\"; \");\n if (errorMessages) {\n message = errorMessages;\n }\n }\n\n // If we still only have statusText but have error data, stringify it\n if (message === response.statusText && Object.keys(errorData).length > 0) {\n message = JSON.stringify(errorData);\n }\n } catch (e) {\n // Could not parse as JSON, try to get text\n try {\n const text = await response.text();\n if (text) {\n message = text.substring(0, 500); // Limit length\n }\n } catch {\n // Ignore\n }\n }\n\n throw new Error(\n `JIRA API Error: ${message} (Status: ${response.status})`\n );\n }\n\n throw new Error(\"Unknown error occurred during fetch operation.\");\n }\n\n /**\n * Extracts issue mentions from Atlassian document content\n * Looks for nodes that were auto-converted to issue links\n */\n protected extractIssueMentions(\n content: any[],\n source: \"description\" | \"comment\",\n commentId?: string\n ): CleanJiraIssue[\"relatedIssues\"] {\n const mentions: NonNullable<CleanJiraIssue[\"relatedIssues\"]> = [];\n\n const processNode = (node: any) => {\n if (node.type === \"inlineCard\" && node.attrs?.url) {\n const match = node.attrs.url.match(/\\/browse\\/([A-Z]+-\\d+)/);\n if (match) {\n mentions.push({\n key: match[1],\n type: \"mention\",\n source,\n commentId,\n });\n }\n }\n\n if (node.type === \"text\" && node.text) {\n const matches = node.text.match(/[A-Z]+-\\d+/g) || [];\n matches.forEach((key: string) => {\n mentions.push({\n key,\n type: \"mention\",\n source,\n commentId,\n });\n });\n }\n\n if (node.content) {\n node.content.forEach(processNode);\n }\n };\n\n content.forEach(processNode);\n return [...new Map(mentions.map((m) => [m.key, m])).values()];\n }\n\n protected cleanComment(comment: {\n id: string;\n body?: {\n content?: any[];\n };\n author?: {\n displayName?: string;\n };\n created: string;\n updated: string;\n }): CleanComment {\n const body = comment.body?.content\n ? this.extractTextContent(comment.body.content)\n : \"\";\n const mentions = comment.body?.content\n ? this.extractIssueMentions(comment.body.content, \"comment\", comment.id)\n : [];\n\n return {\n id: comment.id,\n body,\n author: comment.author?.displayName,\n created: comment.created,\n updated: comment.updated,\n mentions: mentions,\n };\n }\n\n protected cleanAttachment(attachment: any): CleanAttachment {\n return {\n id: attachment.id,\n filename: attachment.filename,\n mimeType: attachment.mimeType,\n size: attachment.size,\n created: attachment.created,\n author: attachment.author?.displayName,\n content: attachment.content,\n thumbnail: attachment.thumbnail,\n };\n }\n\n /**\n * Recursively extracts text content from Atlassian Document Format nodes\n */\n protected extractTextContent(content: any[]): string {\n if (!Array.isArray(content)) return \"\";\n\n return content\n .map((node) => {\n if (node.type === \"text\") {\n return node.text || \"\";\n }\n if (node.content) {\n return this.extractTextContent(node.content);\n }\n return \"\";\n })\n .join(\"\");\n }\n\n protected cleanIssue(issue: any): CleanJiraIssue {\n const description = issue.fields?.description?.content\n ? this.extractTextContent(issue.fields.description.content)\n : \"\";\n\n const cleanedIssue: CleanJiraIssue = {\n id: issue.id,\n key: issue.key,\n summary: issue.fields?.summary,\n status: issue.fields?.status?.name,\n created: issue.fields?.created,\n updated: issue.fields?.updated,\n description,\n relatedIssues: [],\n };\n\n if (issue.fields?.description?.content) {\n const mentions = this.extractIssueMentions(\n issue.fields.description.content,\n \"description\"\n );\n if (mentions.length > 0) {\n cleanedIssue.relatedIssues = mentions;\n }\n }\n\n if (issue.fields?.issuelinks?.length > 0) {\n const links = issue.fields.issuelinks.map((link: any) => {\n const linkedIssue = link.inwardIssue || link.outwardIssue;\n const relationship = link.type.inward || link.type.outward;\n return {\n key: linkedIssue.key,\n summary: linkedIssue.fields?.summary,\n type: \"link\" as const,\n relationship,\n source: \"description\" as const,\n };\n });\n\n cleanedIssue.relatedIssues = [\n ...(cleanedIssue.relatedIssues || []),\n ...links,\n ];\n }\n\n if (issue.fields?.parent) {\n cleanedIssue.parent = {\n id: issue.fields.parent.id,\n key: issue.fields.parent.key,\n summary: issue.fields.parent.fields?.summary,\n };\n }\n\n if (issue.fields?.customfield_10014) {\n cleanedIssue.epicLink = {\n id: issue.fields.customfield_10014,\n key: issue.fields.customfield_10014,\n summary: undefined,\n };\n }\n\n if (issue.fields?.subtasks?.length > 0) {\n cleanedIssue.children = issue.fields.subtasks.map((subtask: any) => ({\n id: subtask.id,\n key: subtask.key,\n summary: subtask.fields?.summary,\n }));\n }\n\n if (issue.fields?.attachment?.length > 0) {\n cleanedIssue.attachments = issue.fields.attachment.map((a: any) =>\n this.cleanAttachment(a)\n );\n }\n\n return cleanedIssue;\n }\n\n protected async fetchJson<T>(url: string, init?: RequestInit): Promise<T> {\n const response = await fetch(this.baseUrl + url, {\n ...init,\n headers: this.headers,\n });\n\n if (!response.ok) {\n await this.handleFetchError(response, url);\n }\n\n return response.json();\n }\n\n async searchIssues(searchString: string): Promise<SearchIssuesResponse> {\n const params = new URLSearchParams({\n jql: searchString,\n maxResults: \"50\",\n fields: [\n \"id\",\n \"key\",\n \"summary\",\n \"description\",\n \"status\",\n \"created\",\n \"updated\",\n \"parent\",\n \"subtasks\",\n \"customfield_10014\",\n \"issuelinks\",\n ].join(\",\"),\n expand: \"names,renderedFields\",\n });\n\n const data = await this.fetchJson<any>(`/rest/api/3/search?${params}`);\n\n return {\n total: data.total,\n issues: data.issues.map((issue: any) => this.cleanIssue(issue)),\n };\n }\n\n async getEpicChildren(epicKey: string): Promise<CleanJiraIssue[]> {\n const params = new URLSearchParams({\n jql: `\"Epic Link\" = ${epicKey}`,\n maxResults: \"100\",\n fields: [\n \"id\",\n \"key\",\n \"summary\",\n \"description\",\n \"status\",\n \"created\",\n \"updated\",\n \"parent\",\n \"subtasks\",\n \"customfield_10014\",\n \"issuelinks\",\n ].join(\",\"),\n expand: \"names,renderedFields\",\n });\n\n const data = await this.fetchJson<any>(`/rest/api/3/search?${params}`);\n\n const issuesWithComments = await Promise.all(\n data.issues.map(async (issue: any) => {\n const commentsData = await this.fetchJson<any>(\n `/rest/api/3/issue/${issue.key}/comment`\n );\n const cleanedIssue = this.cleanIssue(issue);\n const comments = commentsData.comments.map((comment: any) =>\n this.cleanComment(comment)\n );\n\n const commentMentions = comments.flatMap(\n (comment: CleanComment) => comment.mentions\n );\n cleanedIssue.relatedIssues = [\n ...cleanedIssue.relatedIssues,\n ...commentMentions,\n ];\n\n cleanedIssue.comments = comments;\n return cleanedIssue;\n })\n );\n\n return issuesWithComments;\n }\n\n async getIssueWithComments(issueId: string): Promise<CleanJiraIssue> {\n const params = new URLSearchParams({\n fields: [\n \"id\",\n \"key\",\n \"summary\",\n \"description\",\n \"status\",\n \"created\",\n \"updated\",\n \"parent\",\n \"subtasks\",\n \"customfield_10014\",\n \"issuelinks\",\n \"attachment\",\n ].join(\",\"),\n expand: \"names,renderedFields\",\n });\n\n let issueData, commentsData;\n try {\n [issueData, commentsData] = await Promise.all([\n this.fetchJson<any>(`/rest/api/3/issue/${issueId}?${params}`),\n this.fetchJson<any>(`/rest/api/3/issue/${issueId}/comment`),\n ]);\n } catch (error: any) {\n if (error instanceof Error && error.message.includes(\"(Status: 404)\")) {\n throw new Error(`Issue not found: ${issueId}`);\n }\n\n throw error;\n }\n\n const issue = this.cleanIssue(issueData);\n const comments = commentsData.comments.map((comment: any) =>\n this.cleanComment(comment)\n );\n\n const commentMentions = comments.flatMap(\n (comment: CleanComment) => comment.mentions\n );\n issue.relatedIssues = [...issue.relatedIssues, ...commentMentions];\n\n issue.comments = comments;\n\n if (issue.epicLink) {\n try {\n const epicData = await this.fetchJson<any>(\n `/rest/api/3/issue/${issue.epicLink.key}?fields=summary`\n );\n issue.epicLink.summary = epicData.fields?.summary;\n } catch (error) {\n console.error(\"Failed to fetch epic details:\", error);\n }\n }\n\n return issue;\n }\n\n async createIssue(\n projectKey: string,\n issueType: string,\n summary: string,\n description?: string,\n fields?: Record<string, any>\n ): Promise<{ id: string; key: string }> {\n const payload = {\n fields: {\n project: {\n key: projectKey,\n },\n summary,\n issuetype: {\n name: issueType,\n },\n ...(description && { description }),\n ...fields,\n },\n };\n\n return this.fetchJson<{ id: string; key: string }>(\"/rest/api/3/issue\", {\n method: \"POST\",\n body: JSON.stringify(payload),\n });\n }\n\n async getCreateMeta(\n projectKey: string,\n issueTypeName?: string,\n compact?: boolean\n ): Promise<any> {\n // Get available issue types for the project\n const issueTypesData = await this.fetchJson<{ issueTypes: Array<{ id: string; name: string; description?: string; subtask: boolean }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes`\n );\n\n let issueTypes = issueTypesData.issueTypes || [];\n\n // Filter by issue type name if provided\n if (issueTypeName) {\n issueTypes = issueTypes.filter(\n (it) => it.name.toLowerCase() === issueTypeName.toLowerCase()\n );\n }\n\n // Get fields for each issue type\n const result = {\n projectKey,\n issueTypes: await Promise.all(\n issueTypes.map(async (issueType) => {\n try {\n const fieldsData = await this.fetchJson<{ fields: Array<{ fieldId: string; name: string; required: boolean; schema?: any; allowedValues?: any[]; hasDefaultValue?: boolean }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes/${issueType.id}`\n );\n\n let fields = fieldsData.fields || [];\n\n // In compact mode, replace allowedValues with count\n if (compact) {\n fields = fields.map((f) => ({\n fieldId: f.fieldId,\n name: f.name,\n required: f.required,\n schema: f.schema,\n hasDefaultValue: f.hasDefaultValue,\n allowedValuesCount: f.allowedValues?.length ?? 0,\n }));\n }\n\n return {\n id: issueType.id,\n name: issueType.name,\n description: issueType.description,\n subtask: issueType.subtask,\n fields,\n };\n } catch (error) {\n return {\n id: issueType.id,\n name: issueType.name,\n description: issueType.description,\n subtask: issueType.subtask,\n fields: [],\n error: error instanceof Error ? error.message : \"Failed to fetch fields\",\n };\n }\n })\n ),\n };\n\n return result;\n }\n\n async getFieldOptions(\n projectKey: string,\n issueTypeId: string,\n fieldId: string\n ): Promise<any[]> {\n const fieldsData = await this.fetchJson<{ fields: Array<{ fieldId: string; allowedValues?: any[] }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes/${issueTypeId}`\n );\n\n const field = (fieldsData.fields || []).find((f) => f.fieldId === fieldId);\n return field?.allowedValues ?? [];\n }\n\n async updateIssue(\n issueKey: string,\n fields: Record<string, any>\n ): Promise<void> {\n await this.fetchJson(`/rest/api/3/issue/${issueKey}`, {\n method: \"PUT\",\n body: JSON.stringify({ fields }),\n });\n }\n\n async getTransitions(\n issueKey: string\n ): Promise<Array<{ id: string; name: string; to: { name: string } }>> {\n const data = await this.fetchJson<any>(\n `/rest/api/3/issue/${issueKey}/transitions`\n );\n return data.transitions;\n }\n\n async transitionIssue(\n issueKey: string,\n transitionId: string,\n comment?: string\n ): Promise<void> {\n const payload: any = {\n transition: { id: transitionId },\n };\n\n if (comment) {\n payload.update = {\n comment: [\n {\n add: {\n body: {\n type: \"doc\",\n version: 1,\n content: [\n {\n type: \"paragraph\",\n content: [\n {\n type: \"text\",\n text: comment,\n },\n ],\n },\n ],\n },\n },\n },\n ],\n };\n }\n\n await this.fetchJson(`/rest/api/3/issue/${issueKey}/transitions`, {\n method: \"POST\",\n body: JSON.stringify(payload),\n });\n }\n\n async addAttachment(\n issueKey: string,\n file: Buffer,\n filename: string\n ): Promise<{ id: string; filename: string }> {\n const formData = new FormData();\n formData.append(\"file\", new Blob([new Uint8Array(file)]), filename);\n\n const headers = new Headers(this.headers);\n headers.delete(\"Content-Type\");\n headers.set(\"X-Atlassian-Token\", \"no-check\");\n\n const response = await fetch(\n `${this.baseUrl}/rest/api/3/issue/${issueKey}/attachments`,\n {\n method: \"POST\",\n headers,\n body: formData,\n }\n );\n\n if (!response.ok) {\n await this.handleFetchError(response);\n }\n\n const data = await response.json();\n\n const attachment = data[0];\n return {\n id: attachment.id,\n filename: attachment.filename,\n };\n }\n\n /**\n * Converts plain text to a basic Atlassian Document Format (ADF) structure.\n */\n private createAdfFromBody(text: string): AdfDoc {\n return {\n version: 1,\n type: \"doc\",\n content: [\n {\n type: \"paragraph\",\n content: [\n {\n type: \"text\",\n text: text,\n },\n ],\n },\n ],\n };\n }\n\n /**\n * Adds a comment to a JIRA issue.\n */\n async addCommentToIssue(\n issueIdOrKey: string,\n body: string\n ): Promise<AddCommentResponse> {\n const adfBody = this.createAdfFromBody(body);\n\n const payload = {\n body: adfBody,\n };\n\n const response = await this.fetchJson<JiraCommentResponse>(\n `/rest/api/3/issue/${issueIdOrKey}/comment`,\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n }\n );\n\n return {\n id: response.id,\n author: response.author.displayName,\n created: response.created,\n updated: response.updated,\n body: this.extractTextContent(response.body.content),\n };\n }\n\n async getServerInfo(): Promise<{\n version: string;\n versionNumbers: number[];\n deploymentType: string;\n buildNumber: number;\n serverTitle: string;\n }> {\n return this.fetchJson(`/rest/api/3/serverInfo`);\n }\n\n async getProjects(): Promise<Array<{\n id: string;\n key: string;\n name: string;\n projectTypeKey: string;\n lead?: { displayName: string; accountId?: string };\n }>> {\n const data = await this.fetchJson<any[]>(`/rest/api/3/project`);\n return data.map((project) => ({\n id: project.id,\n key: project.key,\n name: project.name,\n projectTypeKey: project.projectTypeKey,\n lead: project.lead ? {\n displayName: project.lead.displayName,\n accountId: project.lead.accountId,\n } : undefined,\n }));\n }\n\n async getUsers(query: string, maxResults: number = 50): Promise<Array<{\n accountId: string;\n displayName: string;\n emailAddress?: string;\n active: boolean;\n }>> {\n const params = new URLSearchParams({\n query,\n maxResults: maxResults.toString(),\n });\n const data = await this.fetchJson<any[]>(`/rest/api/3/user/search?${params}`);\n return data.map((user) => ({\n accountId: user.accountId,\n displayName: user.displayName,\n emailAddress: user.emailAddress,\n active: user.active,\n }));\n }\n\n async deleteIssue(issueKey: string): Promise<void> {\n const response = await fetch(`${this.baseUrl}/rest/api/3/issue/${issueKey}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n\n if (!response.ok) {\n await this.handleFetchError(response);\n }\n }\n\n async getAttachment(attachmentId: string): Promise<{\n content: string; // base64 encoded\n filename: string;\n mimeType: string;\n }> {\n // First, get attachment metadata to get the content URL\n const metadata = await this.fetchJson<{\n id: string;\n filename: string;\n mimeType: string;\n content: string;\n }>(`/rest/api/3/attachment/${attachmentId}`);\n\n // Fetch the actual content from the content URL\n const response = await fetch(metadata.content, {\n headers: this.headers,\n });\n\n if (!response.ok) {\n await this.handleFetchError(response);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const base64 = Buffer.from(arrayBuffer).toString(\"base64\");\n\n return {\n content: base64,\n filename: metadata.filename,\n mimeType: metadata.mimeType,\n };\n }\n}\n","// JiraServerApiService: Jira Server (Data Center) implementation\n// This class should override methods as needed for Jira Server differences\nimport { JiraApiService } from \"./jira-api.js\";\nimport type { CleanComment, CleanJiraIssue } from \"../types/jira.js\";\n\nexport class JiraServerApiService extends JiraApiService {\n constructor(baseUrl: string, email: string, apiToken: string, authType: 'basic' | 'bearer' = 'basic') {\n // For Jira Server/Data Center:\n // - Basic Auth: username/password or API token (traditional method)\n // - Bearer Auth: Personal Access Tokens (PATs) available in Data Center 8.14.0+\n super(baseUrl, email, apiToken, authType);\n }\n\n // Example: Override fetchJson to use /rest/api/2/ instead of /rest/api/3/\n protected overrideApiPath(path: string): string {\n // Replace /rest/api/3/ with /rest/api/2/ for Jira Server\n return path.replace(\"/rest/api/3/\", \"/rest/api/2/\");\n }\n\n // Override fetchJson to use the correct API path\n protected async fetchJson<T>(url: string, init?: RequestInit): Promise<T> {\n const serverUrl = this.overrideApiPath(url);\n return super.fetchJson<T>(serverUrl, init);\n }\n\n // Override getCreateMeta to use API v2 endpoints for Jira Server 9.0+\n async getCreateMeta(\n projectKey: string,\n issueTypeName?: string,\n compact?: boolean\n ): Promise<any> {\n // For Jira Server 9.0+, use the new paginated endpoints (same structure as Cloud but API v2)\n const issueTypesData = await this.fetchJson<{ values: Array<{ id: string; name: string; description?: string; subtask: boolean }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes`\n );\n\n let issueTypes = issueTypesData.values || [];\n\n // Filter by issue type name if provided\n if (issueTypeName) {\n issueTypes = issueTypes.filter(\n (it) => it.name.toLowerCase() === issueTypeName.toLowerCase()\n );\n }\n\n // Get fields for each issue type\n const result = {\n projectKey,\n issueTypes: await Promise.all(\n issueTypes.map(async (issueType) => {\n try {\n const fieldsData = await this.fetchJson<{ values: Array<{ fieldId: string; name: string; required: boolean; schema?: any; allowedValues?: any[]; hasDefaultValue?: boolean }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes/${issueType.id}`\n );\n\n let fields = fieldsData.values || [];\n\n // In compact mode, replace allowedValues with count\n if (compact) {\n fields = fields.map((f) => ({\n fieldId: f.fieldId,\n name: f.name,\n required: f.required,\n schema: f.schema,\n hasDefaultValue: f.hasDefaultValue,\n allowedValuesCount: f.allowedValues?.length ?? 0,\n }));\n }\n\n return {\n id: issueType.id,\n name: issueType.name,\n description: issueType.description,\n subtask: issueType.subtask,\n fields,\n };\n } catch (error) {\n return {\n id: issueType.id,\n name: issueType.name,\n description: issueType.description,\n subtask: issueType.subtask,\n fields: [],\n error: error instanceof Error ? error.message : \"Failed to fetch fields\",\n };\n }\n })\n ),\n };\n\n return result;\n }\n\n // Override getFieldOptions for Jira Server (uses 'values' instead of 'fields')\n async getFieldOptions(\n projectKey: string,\n issueTypeId: string,\n fieldId: string\n ): Promise<any[]> {\n const fieldsData = await this.fetchJson<{ values: Array<{ fieldId: string; allowedValues?: any[] }> }>(\n `/rest/api/3/issue/createmeta/${projectKey}/issuetypes/${issueTypeId}`\n );\n\n const field = (fieldsData.values || []).find((f) => f.fieldId === fieldId);\n return field?.allowedValues ?? [];\n }\n\n // Override getUsers for Jira Server (uses 'username' parameter instead of 'query')\n async getUsers(query: string, maxResults: number = 50): Promise<Array<{\n accountId: string;\n displayName: string;\n emailAddress?: string;\n active: boolean;\n }>> {\n const params = new URLSearchParams({\n username: query,\n maxResults: maxResults.toString(),\n });\n const data = await this.fetchJson<any[]>(`/rest/api/3/user/search?${params}`);\n return data.map((user) => ({\n // Jira Server uses 'name' or 'key' instead of 'accountId'\n accountId: user.key || user.name,\n displayName: user.displayName,\n emailAddress: user.emailAddress,\n active: user.active,\n }));\n }\n\n // Override addCommentToIssue for Jira Server (uses plain text instead of ADF)\n async addCommentToIssue(\n issueIdOrKey: string,\n body: string\n ): Promise<{\n id: string;\n author: string;\n created: string;\n updated: string;\n body: string;\n }> {\n // Jira Server API v2 expects plain text body, not ADF\n const payload = {\n body: body,\n };\n\n const response = await this.fetchJson<{\n id: string;\n author: { displayName: string };\n created: string;\n updated: string;\n body: string;\n }>(\n `/rest/api/3/issue/${issueIdOrKey}/comment`,\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n }\n );\n\n return {\n id: response.id,\n author: response.author.displayName,\n created: response.created,\n updated: response.updated,\n body: response.body,\n };\n }\n\n // Override deleteIssue for Jira Server (uses API v2)\n async deleteIssue(issueKey: string): Promise<void> {\n const response = await fetch(`${this.baseUrl}/rest/api/2/issue/${issueKey}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n\n if (!response.ok) {\n await this.handleFetchError(response);\n }\n }\n\n // Override cleanComment for Jira Server (body is plain text, not ADF)\n protected cleanComment(comment: {\n id: string;\n body?: string | { content?: any[] };\n author?: {\n displayName?: string;\n };\n created: string;\n updated: string;\n }): CleanComment {\n // Debug: log raw comment to understand API response format\n console.error(\"[DEBUG cleanComment] Raw comment:\", JSON.stringify(comment, null, 2));\n\n // Jira Server API v2 returns body as plain text string\n // Jira Cloud API v3 returns body as ADF object with content array\n let body: string;\n let mentions: CleanJiraIssue[\"relatedIssues\"] = [];\n\n if (typeof comment.body === \"string\") {\n // Jira Server: body is plain text\n body = comment.body;\n // Extract issue mentions from plain text (e.g., PROJECT-123)\n mentions = this.extractIssueMentionsFromText(body, \"comment\", comment.id);\n } else if (comment.body?.content) {\n // Jira Cloud: body is ADF object\n body = this.extractTextContent(comment.body.content);\n mentions = this.extractIssueMentions(comment.body.content, \"comment\", comment.id);\n } else {\n body = \"\";\n }\n\n return {\n id: comment.id,\n body,\n author: comment.author?.displayName,\n created: comment.created,\n updated: comment.updated,\n mentions,\n };\n }\n\n // Extract issue mentions from plain text (for Jira Server)\n protected extractIssueMentionsFromText(\n text: string,\n source: \"description\" | \"comment\",\n commentId?: string\n ): CleanJiraIssue[\"relatedIssues\"] {\n if (!text) return [];\n\n const matches = text.match(/[A-Z]+-\\d+/g) || [];\n const uniqueKeys = [...new Set(matches)];\n\n return uniqueKeys.map((key) => ({\n key,\n type: \"mention\" as const,\n source,\n commentId,\n }));\n }\n}\n","#!/usr/bin/env node\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListToolsRequestSchema,\n ListPromptsRequestSchema,\n GetPromptRequestSchema,\n McpError,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { JiraApiService } from \"./services/jira-api.js\";\nimport { JiraServerApiService } from \"./services/jira-server-api.js\";\n\nconst JIRA_API_TOKEN = process.env.JIRA_API_TOKEN ?? \"\";\nconst JIRA_BASE_URL = process.env.JIRA_BASE_URL ?? \"\";\nconst JIRA_USER_EMAIL = process.env.JIRA_USER_EMAIL ?? \"\";\nconst JIRA_TYPE = (process.env.JIRA_TYPE === \"server\" ? \"server\" : \"cloud\") as\n | \"cloud\"\n | \"server\";\nconst JIRA_AUTH_TYPE = (process.env.JIRA_AUTH_TYPE === \"bearer\" ? \"bearer\" : \"basic\") as\n | \"basic\"\n | \"bearer\";\n\nif (!JIRA_API_TOKEN || !JIRA_BASE_URL || !JIRA_USER_EMAIL) {\n throw new Error(\n \"JIRA_API_TOKEN, JIRA_USER_EMAIL and JIRA_BASE_URL environment variables are required\",\n );\n}\n\n// ============================================================================\n// PROMPTS - Инструкции и регламенты для агентов\n// ============================================================================\n\nconst JIRA_PROMPTS: Record<string, { description: string; content: string }> = {\n jira_guidelines: {\n description: \"Общие правила и регламенты работы с Jira в компании\",\n content: `# Регламент работы с Jira\n\n## Общие принципы\n- Все изменения статусов должны отражать реальное состояние работы\n- Комментарии пишутся на русском языке\n- При любых изменениях, затрагивающих других участников, оставляй комментарий\n\n## Статусы и переходы\n\n### Workflow задачи:\n1. **Бэклог** — задача запланирована, но работа не начата\n2. **Очередь** — задача в очереди на выполнение\n3. **В работе** — код в процессе написания\n4. **Ревью** — код готов к проверке, создан PR/MR\n5. **Local test** — задача на локальном тестировании\n6. **Dev** — код в ветке dev\n7. **Stage (не проверено)** — код в ветке stage, тестирование не проведено\n8. **Релиз-кандидат** — проверено на stage, готово к релизу\n9. **Тест на проде** — код на проде, ожидает проверки\n10. **Готово на проде** — задача завершена и проверена\n\n### Особые статусы:\n- **Заблокирована** — работа приостановлена (указать причину в комментарии)\n- **Отложено** — задача отложена на неопределённый срок\n- **Отмена** — задача отменена (ОБЯЗАТЕЛЬНО с комментарием о причине!)\n\n## Создание задач\n- Summary должен быть кратким и информативным (не более 100 символов)\n- Description должен содержать достаточно информации для понимания задачи\n- Обязательно указывай компонент (component) для правильной маршрутизации\n- Для багов: шаги воспроизведения, ожидаемый и фактический результат\n\n## Отмена задач\n- ЗАПРЕЩЕНО отменять задачу без указания причины в комментарии\n- Причина должна быть информативной: \"дубликат IM3-1234\", \"неактуально после релиза X\", и т.д.\n\n## Удаление задач\n- Удаление задач запрещено политикой безопасности\n- Вместо удаления используй статус \"Отмена\" с комментарием\n\n## Комментарии\n- Используй комментарии для важной информации, которая должна остаться в истории\n- При блокировке — указывай причину и ссылку на блокирующую задачу\n- При передаче задачи — описывай текущее состояние и что осталось сделать`,\n },\n\n create_issue_workflow: {\n description: \"Пошаговый процесс создания задачи в Jira\",\n content: `# Создание задачи в Jira\n\n## Перед созданием ОБЯЗАТЕЛЬНО уточни у пользователя:\n\n### Основная информация:\n1. **Проект** — в каком проекте создать (IM3, SD, FT, ETPP и т.д.)\n2. **Тип задачи** — Баг, Story, Task, Sub-task\n3. **Краткое описание** (summary) — что нужно сделать, до 100 символов\n4. **Подробное описание** (description) — детали задачи\n\n### Для багов дополнительно запроси:\n- Шаги воспроизведения (пронумерованный список)\n- Ожидаемый результат\n- Фактический результат\n- Окружение (браузер, ОС, URL) — если релевантно\n- Скриншоты или логи — если есть\n\n### Для Story/Task уточни:\n- Критерии приёмки (acceptance criteria)\n- Связанные задачи (если есть)\n\n## Порядок действий:\n\n1. **Получи схему полей:**\n \\`\\`\\`\n get_create_meta(projectKey, issueTypeName, compact=true)\n \\`\\`\\`\n\n2. **Для обязательных полей с вариантами получи опции:**\n \\`\\`\\`\n get_field_options(projectKey, issueTypeId, fieldId)\n \\`\\`\\`\n\n3. **Покажи пользователю** итоговые данные перед созданием\n\n4. **Создай задачу:**\n \\`\\`\\`\n create_issue(projectKey, issueType, summary, description, fields)\n \\`\\`\\`\n\n5. **Верни пользователю** ключ и ссылку на созданную задачу\n\n## Пример описания бага:\n\n\\`\\`\\`\nШаги воспроизведения:\n1. Открыть страницу /checkout\n2. Добавить товар в корзину\n3. Нажать \"Оформить заказ\"\n\nОжидаемый результат:\nОткрывается форма оформления заказа\n\nФактический результат:\nСтраница показывает ошибку 500\n\nОкружение:\n- URL: https://example.com/checkout\n- Браузер: Chrome 120\n- ОС: Windows 11\n\\`\\`\\``,\n },\n\n cancel_issue_workflow: {\n description: \"Процесс отмены задачи (требует указания причины)\",\n content: `# Отмена задачи в Jira\n\n## ⚠️ ВАЖНО: Задачу НЕЛЬЗЯ отменять без причины!\n\n## Порядок действий:\n\n### 1. Спроси причину отмены\nПрежде чем отменять задачу, ОБЯЗАТЕЛЬНО спроси у пользователя:\n- \"Почему задача отменяется?\"\n- \"Укажите причину отмены для комментария\"\n\n### 2. Примеры корректных причин:\n- \"Дубликат задачи IM3-1234\"\n- \"Неактуально после релиза версии 2.5\"\n- \"Функционал реализован в рамках IM3-5678\"\n- \"Отменено по решению Product Owner (имя)\"\n- \"Требования изменились, создана новая задача IM3-9999\"\n\n### 3. Добавь комментарий с причиной:\n\\`\\`\\`\nadd_comment(issueKey, \"Причина отмены: <причина от пользователя>\")\n\\`\\`\\`\n\n### 4. Получи ID перехода \"Отмена\":\n\\`\\`\\`\nget_transitions(issueKey)\n// Найди переход с name=\"Отмена\"\n\\`\\`\\`\n\n### 5. Выполни переход:\n\\`\\`\\`\ntransition_issue(issueKey, transitionId)\n\\`\\`\\`\n\n## Запрещено:\n- ❌ Отменять задачу без комментария с причиной\n- ❌ Использовать причины типа \"не нужно\", \"отмена\" без пояснения\n\n## Если пользователь не хочет указывать причину:\nОбъясни, что это требование регламента компании, и причина важна для:\n- Истории проекта\n- Понимания, почему задача не была выполнена\n- Аудита и отчётности`,\n },\n\n transition_issue_workflow: {\n description: \"Правила смены статусов задач\",\n content: `# Смена статуса задачи в Jira\n\n## Общие правила:\n- Статус должен отражать реальное состояние работы\n- Не пропускай промежуточные статусы без веской причины\n- При переходе в финальные статусы убедись, что работа действительно завершена\n\n## Особые случаи:\n\n### Переход в \"Отмена\":\n⚠️ ОБЯЗАТЕЛЬНО сначала добавь комментарий с причиной отмены!\nСм. prompt \"cancel_issue_workflow\" для подробностей.\n\n### Переход в \"Заблокирована\":\n- Укажи в комментарии причину блокировки\n- Добавь ссылку на блокирующую задачу (если есть)\n- Пример: \"Заблокировано: ждём завершения IM3-1234 (интеграция с API)\"\n\n### Переход в \"Ревью\":\n- Должен существовать Pull Request / Merge Request\n- Желательно добавить ссылку на PR в комментарий\n\n### Переход в \"Dev\" / \"Stage\":\n- Код должен быть смержен в соответствующую ветку\n- Задача задеплоена на окружение\n\n### Переход в \"Тест на проде\":\n- Код задеплоен на production\n- Задача готова к финальной проверке\n\n### Переход в \"Готово на проде\":\n- Задача проверена на production\n- Все acceptance criteria выполнены\n- Нет открытых sub-tasks\n\n## Получение доступных переходов:\n\\`\\`\\`\nget_transitions(issueKey)\n// Вернёт список доступных переходов с их ID\n\\`\\`\\`\n\n## Выполнение перехода:\n\\`\\`\\`\ntransition_issue(issueKey, transitionId, comment?)\n// comment — опциональный комментарий к переходу\n\\`\\`\\``,\n },\n\n update_issue_workflow: {\n description: \"Правила обновления задач\",\n content: `# Обновление задачи в Jira\n\n## Что можно обновлять:\n- summary — краткое описание\n- description — подробное описание\n- assignee — исполнитель\n- priority — приоритет\n- labels — метки\n- components — компоненты\n- customfield_* — кастомные поля\n\n## Правила:\n\n### При изменении исполнителя (assignee):\n- Если задача в работе — согласуй с текущим исполнителем\n- Добавь комментарий о причине переназначения\n\n### При изменении приоритета:\n- Повышение приоритета — укажи причину в комментарии\n- Понижение приоритета — согласуй с автором задачи\n\n### При изменении описания:\n- Не удаляй важную информацию\n- Если меняешь суть задачи — лучше создай новую\n\n## Пример обновления:\n\\`\\`\\`\nupdate_issue(issueKey, {\n summary: \"Новый заголовок\",\n priority: { name: \"High\" },\n assignee: { name: \"username\" }\n})\n\\`\\`\\`\n\n## Для поиска пользователей:\n\\`\\`\\`\nget_users(query) // поиск по имени или email\n\\`\\`\\``,\n },\n};\n\nclass JiraServer {\n private server: Server;\n private jiraApi: JiraApiService;\n\n constructor() {\n this.server = new Server(\n {\n name: \"jira-mcp\",\n version: \"0.3.0\",\n },\n {\n capabilities: {\n tools: {},\n prompts: {},\n },\n },\n );\n\n if (JIRA_TYPE === \"server\") {\n this.jiraApi = new JiraServerApiService(\n JIRA_BASE_URL,\n JIRA_USER_EMAIL,\n JIRA_API_TOKEN,\n JIRA_AUTH_TYPE,\n );\n } else {\n this.jiraApi = new JiraApiService(\n JIRA_BASE_URL,\n JIRA_USER_EMAIL,\n JIRA_API_TOKEN,\n JIRA_AUTH_TYPE,\n );\n }\n\n this.setupToolHandlers();\n\n this.server.onerror = (error) => {};\n process.on(\"SIGINT\", async () => {\n await this.server.close();\n process.exit(0);\n });\n }\n\n private setupToolHandlers() {\n // ========================================================================\n // PROMPTS HANDLERS\n // ========================================================================\n\n this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({\n prompts: Object.entries(JIRA_PROMPTS).map(([name, { description }]) => ({\n name,\n description,\n })),\n }));\n\n this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const { name } = request.params;\n const prompt = JIRA_PROMPTS[name];\n\n if (!prompt) {\n throw new McpError(ErrorCode.InvalidParams, `Unknown prompt: ${name}`);\n }\n\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: prompt.content,\n },\n },\n ],\n };\n });\n\n // ========================================================================\n // TOOLS HANDLERS\n // ========================================================================\n\n this.server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: \"search_issues\",\n description: \"Search JIRA issues using JQL\",\n inputSchema: {\n type: \"object\",\n properties: {\n searchString: {\n type: \"string\",\n description: \"JQL search string\",\n },\n },\n required: [\"searchString\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_epic_children\",\n description:\n \"Get all child issues in an epic including their comments\",\n inputSchema: {\n type: \"object\",\n properties: {\n epicKey: {\n type: \"string\",\n description: \"The key of the epic issue\",\n },\n },\n required: [\"epicKey\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_issue\",\n description:\n \"Get detailed information about a specific JIRA issue including comments\",\n inputSchema: {\n type: \"object\",\n properties: {\n issueId: {\n type: \"string\",\n description: \"The ID or key of the JIRA issue\",\n },\n },\n required: [\"issueId\"],\n additionalProperties: false,\n },\n },\n {\n name: \"create_issue\",\n description: `Create a new JIRA issue.\n\n📋 BEFORE CREATING: Ask user for project, issue type, summary, and description.\nFor bugs: also ask for reproduction steps, expected/actual results.\nUse get_create_meta first to check required fields.\nSee prompt \"create_issue_workflow\" for detailed guidelines.`,\n inputSchema: {\n type: \"object\",\n properties: {\n projectKey: {\n type: \"string\",\n description: \"The project key where the issue will be created\",\n },\n issueType: {\n type: \"string\",\n description:\n 'The type of issue to create (e.g., \"Bug\", \"Story\", \"Task\")',\n },\n summary: {\n type: \"string\",\n description: \"The issue summary/title\",\n },\n description: {\n type: \"string\",\n description: \"The issue description\",\n },\n fields: {\n type: \"object\",\n description: \"Additional fields to set on the issue\",\n additionalProperties: true,\n },\n },\n required: [\"projectKey\", \"issueType\", \"summary\"],\n additionalProperties: false,\n },\n },\n {\n name: \"update_issue\",\n description: `Update an existing JIRA issue.\n\n📝 When changing assignee or priority, consider adding a comment explaining why.\nSee prompt \"update_issue_workflow\" for guidelines.`,\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to update\",\n },\n fields: {\n type: \"object\",\n description: \"Fields to update on the issue\",\n additionalProperties: true,\n },\n },\n required: [\"issueKey\", \"fields\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_transitions\",\n description: \"Get available status transitions for a JIRA issue\",\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to get transitions for\",\n },\n },\n required: [\"issueKey\"],\n additionalProperties: false,\n },\n },\n {\n name: \"transition_issue\",\n description: `Change the status of a JIRA issue by performing a transition.\n\n⚠️ CANCELLATION RULE: When transitioning to \"Отмена\" (Cancel), you MUST first:\n1. Ask user for cancellation reason\n2. Add comment with the reason using add_comment\n3. Only then perform the transition\nSee prompt \"cancel_issue_workflow\" for details.`,\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to transition\",\n },\n transitionId: {\n type: \"string\",\n description: \"The ID of the transition to perform\",\n },\n comment: {\n type: \"string\",\n description: \"Optional comment to add with the transition\",\n },\n },\n required: [\"issueKey\", \"transitionId\"],\n additionalProperties: false,\n },\n },\n {\n name: \"add_attachment\",\n description: \"Add a file attachment to a JIRA issue\",\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to add attachment to\",\n },\n fileContent: {\n type: \"string\",\n description: \"Base64 encoded content of the file\",\n },\n filename: {\n type: \"string\",\n description: \"Name of the file to be attached\",\n },\n },\n required: [\"issueKey\", \"fileContent\", \"filename\"],\n additionalProperties: false,\n },\n },\n {\n name: \"add_comment\",\n description: \"Add a comment to a JIRA issue\",\n inputSchema: {\n type: \"object\",\n properties: {\n issueIdOrKey: {\n type: \"string\",\n description: \"The ID or key of the issue to add the comment to\",\n },\n body: {\n type: \"string\",\n description: \"The content of the comment (plain text)\",\n },\n },\n required: [\"issueIdOrKey\", \"body\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_create_meta\",\n description: \"Get metadata for creating issues in a project, including available issue types and required fields. Use compact=true to get a smaller response with field counts instead of full allowedValues arrays.\",\n inputSchema: {\n type: \"object\",\n properties: {\n projectKey: {\n type: \"string\",\n description: \"The project key to get creation metadata for\",\n },\n issueTypeName: {\n type: \"string\",\n description: \"Optional: filter to a specific issue type name\",\n },\n compact: {\n type: \"boolean\",\n description: \"If true, returns allowedValuesCount instead of full allowedValues arrays (reduces response size significantly)\",\n },\n },\n required: [\"projectKey\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_field_options\",\n description: \"Get allowed values for a specific field when creating issues. Use this after get_create_meta with compact=true to fetch options for fields that have allowedValuesCount > 0.\",\n inputSchema: {\n type: \"object\",\n properties: {\n projectKey: {\n type: \"string\",\n description: \"The project key\",\n },\n issueTypeId: {\n type: \"string\",\n description: \"The issue type ID (from get_create_meta response)\",\n },\n fieldId: {\n type: \"string\",\n description: \"The field ID to get allowed values for (e.g., 'components', 'customfield_12405')\",\n },\n },\n required: [\"projectKey\", \"issueTypeId\", \"fieldId\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_server_info\",\n description: \"Get JIRA server information including version and deployment type\",\n inputSchema: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: \"get_projects\",\n description: \"Get all JIRA projects accessible to the current user\",\n inputSchema: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: \"get_users\",\n description: \"Search for JIRA users by name or email. Useful for finding assignees.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n description: \"Search query (name or email)\",\n },\n maxResults: {\n type: \"number\",\n description: \"Maximum number of results to return (default: 50)\",\n },\n },\n required: [\"query\"],\n additionalProperties: false,\n },\n },\n {\n name: \"delete_issue\",\n description: `Delete a JIRA issue permanently.\n\n⚠️ NOTE: Most users don't have delete permissions.\nIf deletion fails, use transition to \"Отмена\" (Cancel) status instead.\nRemember to add a comment with cancellation reason first.`,\n inputSchema: {\n type: \"object\",\n properties: {\n issueKey: {\n type: \"string\",\n description: \"The key of the issue to delete (e.g., IM3-1234)\",\n },\n },\n required: [\"issueKey\"],\n additionalProperties: false,\n },\n },\n {\n name: \"get_attachment\",\n description: \"Download a JIRA attachment by ID. Returns base64 encoded content. Use get_issue first to see available attachments with their IDs.\",\n inputSchema: {\n type: \"object\",\n properties: {\n attachmentId: {\n type: \"string\",\n description: \"The ID of the attachment to download\",\n },\n },\n required: [\"attachmentId\"],\n additionalProperties: false,\n },\n },\n ],\n }));\n\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n try {\n const args = request.params.arguments as Record<string, any>;\n\n switch (request.params.name) {\n case \"search_issues\": {\n if (!args.searchString || typeof args.searchString !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"Search string is required\",\n );\n }\n const response = await this.jiraApi.searchIssues(args.searchString);\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"get_epic_children\": {\n if (!args.epicKey || typeof args.epicKey !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"Epic key is required\",\n );\n }\n const response = await this.jiraApi.getEpicChildren(args.epicKey);\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"get_issue\": {\n if (!args.issueId || typeof args.issueId !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"Issue ID is required\",\n );\n }\n const response = await this.jiraApi.getIssueWithComments(\n args.issueId,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"create_issue\": {\n // Basic validation\n if (\n !args.projectKey ||\n typeof args.projectKey !== \"string\" ||\n !args.issueType ||\n typeof args.issueType !== \"string\" ||\n !args.summary ||\n typeof args.summary !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"projectKey, issueType, and summary are required\",\n );\n }\n const response = await this.jiraApi.createIssue(\n args.projectKey,\n args.issueType,\n args.summary,\n args.description as string | undefined,\n args.fields as Record<string, any> | undefined,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"update_issue\": {\n if (\n !args.issueKey ||\n typeof args.issueKey !== \"string\" ||\n !args.fields ||\n typeof args.fields !== \"object\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueKey and fields object are required\",\n );\n }\n await this.jiraApi.updateIssue(args.issueKey, args.fields);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { message: `Issue ${args.issueKey} updated successfully` },\n null,\n 2,\n ),\n },\n ],\n };\n }\n case \"get_transitions\": {\n if (!args.issueKey || typeof args.issueKey !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"Issue key is required\",\n );\n }\n const response = await this.jiraApi.getTransitions(args.issueKey);\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"transition_issue\": {\n if (\n !args.issueKey ||\n typeof args.issueKey !== \"string\" ||\n !args.transitionId ||\n typeof args.transitionId !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueKey and transitionId are required\",\n );\n }\n await this.jiraApi.transitionIssue(\n args.issueKey,\n args.transitionId,\n args.comment as string | undefined,\n );\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n message: `Issue ${args.issueKey} transitioned successfully${args.comment ? \" with comment\" : \"\"}`,\n },\n null,\n 2,\n ),\n },\n ],\n };\n }\n case \"add_attachment\": {\n if (\n !args.issueKey ||\n typeof args.issueKey !== \"string\" ||\n !args.fileContent ||\n typeof args.fileContent !== \"string\" ||\n !args.filename ||\n typeof args.filename !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueKey, fileContent, and filename are required\",\n );\n }\n const fileBuffer = Buffer.from(args.fileContent, \"base64\");\n const result = await this.jiraApi.addAttachment(\n args.issueKey,\n fileBuffer,\n args.filename,\n );\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n message: `File ${args.filename} attached successfully to issue ${args.issueKey}`,\n attachmentId: result.id,\n filename: result.filename,\n },\n null,\n 2,\n ),\n },\n ],\n };\n }\n case \"add_comment\": {\n if (\n !args.issueIdOrKey ||\n typeof args.issueIdOrKey !== \"string\" ||\n !args.body ||\n typeof args.body !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueIdOrKey and body are required\",\n );\n }\n const response = await this.jiraApi.addCommentToIssue(\n args.issueIdOrKey,\n args.body,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(response, null, 2) },\n ],\n };\n }\n case \"get_create_meta\": {\n if (!args.projectKey || typeof args.projectKey !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"projectKey is required\",\n );\n }\n const meta = await this.jiraApi.getCreateMeta(\n args.projectKey,\n args.issueTypeName as string | undefined,\n args.compact as boolean | undefined,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(meta, null, 2) },\n ],\n };\n }\n case \"get_field_options\": {\n if (\n !args.projectKey ||\n typeof args.projectKey !== \"string\" ||\n !args.issueTypeId ||\n typeof args.issueTypeId !== \"string\" ||\n !args.fieldId ||\n typeof args.fieldId !== \"string\"\n ) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"projectKey, issueTypeId, and fieldId are required\",\n );\n }\n const options = await this.jiraApi.getFieldOptions(\n args.projectKey,\n args.issueTypeId,\n args.fieldId,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(options, null, 2) },\n ],\n };\n }\n case \"get_server_info\": {\n const serverInfo = await this.jiraApi.getServerInfo();\n return {\n content: [\n { type: \"text\", text: JSON.stringify(serverInfo, null, 2) },\n ],\n };\n }\n case \"get_projects\": {\n const projects = await this.jiraApi.getProjects();\n return {\n content: [\n { type: \"text\", text: JSON.stringify(projects, null, 2) },\n ],\n };\n }\n case \"get_users\": {\n if (!args.query || typeof args.query !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"query is required\",\n );\n }\n const users = await this.jiraApi.getUsers(\n args.query,\n args.maxResults as number | undefined,\n );\n return {\n content: [\n { type: \"text\", text: JSON.stringify(users, null, 2) },\n ],\n };\n }\n case \"delete_issue\": {\n if (!args.issueKey || typeof args.issueKey !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"issueKey is required\",\n );\n }\n await this.jiraApi.deleteIssue(args.issueKey);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { message: `Issue ${args.issueKey} deleted successfully` },\n null,\n 2,\n ),\n },\n ],\n };\n }\n case \"get_attachment\": {\n if (!args.attachmentId || typeof args.attachmentId !== \"string\") {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"attachmentId is required\",\n );\n }\n const attachment = await this.jiraApi.getAttachment(args.attachmentId);\n\n // Check if it's an image type that Claude can view\n const isImage = attachment.mimeType.startsWith(\"image/\");\n\n if (isImage) {\n return {\n content: [\n {\n type: \"text\",\n text: `Attachment: ${attachment.filename} (${attachment.mimeType})`,\n },\n {\n type: \"image\",\n data: attachment.content,\n mimeType: attachment.mimeType,\n } as any,\n ],\n };\n }\n\n // For non-image files, return base64 as text\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(attachment, null, 2),\n },\n ],\n };\n }\n default:\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n } catch (error) {\n // Keep generic error handling\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n error instanceof Error ? error.message : \"Unknown error occurred\",\n );\n }\n });\n }\n\n async run() {\n const transport = new StdioServerTransport();\n await this.server.connect(transport);\n // JIRA MCP server running on stdio\n }\n}\n\nconst server = new JiraServer();\nserver.run().catch(() => {});\n"],"names":["JiraApiService","baseUrl","email","apiToken","authType","authHeader","response","url","message","errorData","errorMessages","field","msg","text","content","source","commentId","mentions","processNode","node","match","key","m","comment","body","attachment","issue","description","cleanedIssue","links","link","linkedIssue","relationship","subtask","a","init","searchString","params","data","epicKey","commentsData","comments","commentMentions","issueId","issueData","error","epicData","projectKey","issueType","summary","fields","payload","issueTypeName","compact","issueTypes","it","f","issueTypeId","fieldId","issueKey","transitionId","file","filename","formData","headers","issueIdOrKey","project","query","maxResults","user","attachmentId","metadata","arrayBuffer","JiraServerApiService","path","serverUrl","matches","JIRA_API_TOKEN","JIRA_BASE_URL","JIRA_USER_EMAIL","JIRA_TYPE","JIRA_AUTH_TYPE","JIRA_PROMPTS","JiraServer","Server","ListPromptsRequestSchema","name","GetPromptRequestSchema","request","prompt","McpError","ErrorCode","ListToolsRequestSchema","CallToolRequestSchema","args","fileBuffer","result","meta","options","serverInfo","projects","users","transport","StdioServerTransport","server"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AddCommentResponse, CleanComment, CleanJiraIssue, SearchIssuesResponse } from '../types/jira.js';
|
|
1
|
+
import { AddCommentResponse, CleanAttachment, CleanComment, CleanJiraIssue, SearchIssuesResponse } from '../types/jira.js';
|
|
2
2
|
export declare class JiraApiService {
|
|
3
3
|
protected baseUrl: string;
|
|
4
4
|
protected headers: Headers;
|
|
@@ -20,6 +20,7 @@ export declare class JiraApiService {
|
|
|
20
20
|
created: string;
|
|
21
21
|
updated: string;
|
|
22
22
|
}): CleanComment;
|
|
23
|
+
protected cleanAttachment(attachment: any): CleanAttachment;
|
|
23
24
|
/**
|
|
24
25
|
* Recursively extracts text content from Atlassian Document Format nodes
|
|
25
26
|
*/
|
|
@@ -80,4 +81,9 @@ export declare class JiraApiService {
|
|
|
80
81
|
active: boolean;
|
|
81
82
|
}>>;
|
|
82
83
|
deleteIssue(issueKey: string): Promise<void>;
|
|
84
|
+
getAttachment(attachmentId: string): Promise<{
|
|
85
|
+
content: string;
|
|
86
|
+
filename: string;
|
|
87
|
+
mimeType: string;
|
|
88
|
+
}>;
|
|
83
89
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JiraApiService } from './jira-api.js';
|
|
2
|
+
import { CleanComment, CleanJiraIssue } from '../types/jira.js';
|
|
2
3
|
export declare class JiraServerApiService extends JiraApiService {
|
|
3
4
|
constructor(baseUrl: string, email: string, apiToken: string, authType?: 'basic' | 'bearer');
|
|
4
5
|
protected overrideApiPath(path: string): string;
|
|
@@ -19,4 +20,16 @@ export declare class JiraServerApiService extends JiraApiService {
|
|
|
19
20
|
body: string;
|
|
20
21
|
}>;
|
|
21
22
|
deleteIssue(issueKey: string): Promise<void>;
|
|
23
|
+
protected cleanComment(comment: {
|
|
24
|
+
id: string;
|
|
25
|
+
body?: string | {
|
|
26
|
+
content?: any[];
|
|
27
|
+
};
|
|
28
|
+
author?: {
|
|
29
|
+
displayName?: string;
|
|
30
|
+
};
|
|
31
|
+
created: string;
|
|
32
|
+
updated: string;
|
|
33
|
+
}): CleanComment;
|
|
34
|
+
protected extractIssueMentionsFromText(text: string, source: "description" | "comment", commentId?: string): CleanJiraIssue["relatedIssues"];
|
|
22
35
|
}
|
package/build/types/jira.d.ts
CHANGED
|
@@ -6,6 +6,16 @@ export interface CleanComment {
|
|
|
6
6
|
updated: string;
|
|
7
7
|
mentions: NonNullable<CleanJiraIssue["relatedIssues"]>;
|
|
8
8
|
}
|
|
9
|
+
export interface CleanAttachment {
|
|
10
|
+
id: string;
|
|
11
|
+
filename: string;
|
|
12
|
+
mimeType: string;
|
|
13
|
+
size: number;
|
|
14
|
+
created: string;
|
|
15
|
+
author?: string;
|
|
16
|
+
content: string;
|
|
17
|
+
thumbnail?: string;
|
|
18
|
+
}
|
|
9
19
|
export interface CleanJiraIssue {
|
|
10
20
|
id: string;
|
|
11
21
|
key: string;
|
|
@@ -15,6 +25,7 @@ export interface CleanJiraIssue {
|
|
|
15
25
|
updated: string | undefined;
|
|
16
26
|
description: string;
|
|
17
27
|
comments?: CleanComment[];
|
|
28
|
+
attachments?: CleanAttachment[];
|
|
18
29
|
parent?: {
|
|
19
30
|
id: string;
|
|
20
31
|
key: string;
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokealvo/jira-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "JIRA MCP Server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"jira-mcp": "./build/index.js"
|
|
8
8
|
},
|
|
9
|
-
"files": [
|
|
9
|
+
"files": [
|
|
10
|
+
"build"
|
|
11
|
+
],
|
|
10
12
|
"publishConfig": {
|
|
11
13
|
"access": "public"
|
|
12
14
|
},
|