@rokealvo/jira-mcp 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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 S, McpError as d, ErrorCode as p, ListToolsRequestSchema as A, CallToolRequestSchema as J } from "@modelcontextprotocol/sdk/types.js";
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 r;
11
- i === "bearer" ? r = `Bearer ${s}` : r = `Basic ${Buffer.from(`${e}:${s}`).toString("base64")}`, this.headers = new Headers({
12
- Authorization: r,
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 r = Object.entries(i.errors).map(([n, a]) => `${n}: ${a}`).join("; ");
29
- r && (s = r);
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 n = await t.text();
35
- n && (s = n.substring(0, 500));
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 = [], r = (n) => {
51
- if (n.type === "inlineCard" && n.attrs?.url) {
52
- const a = n.attrs.url.match(/\/browse\/([A-Z]+-\d+)/);
53
- a && i.push({
54
- key: a[1],
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
- n.type === "text" && n.text && (n.text.match(/[A-Z]+-\d+/g) || []).forEach((o) => {
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
- }), n.content && n.content.forEach(r);
67
+ }), r.content && r.content.forEach(a);
68
68
  };
69
- return t.forEach(r), [...new Map(i.map((n) => [n.key, n])).values()];
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
  */
@@ -86,7 +98,9 @@ class w {
86
98
  return Array.isArray(t) ? t.map((e) => e.type === "text" ? e.text || "" : e.content ? this.extractTextContent(e.content) : "").join("") : "";
87
99
  }
88
100
  cleanIssue(t) {
89
- const e = t.fields?.description?.content ? this.extractTextContent(t.fields.description.content) : "", s = {
101
+ let e = "";
102
+ t.fields?.description && (typeof t.fields.description == "string" ? e = t.fields.description : t.fields.description.content && (e = this.extractTextContent(t.fields.description.content)));
103
+ const s = {
90
104
  id: t.id,
91
105
  key: t.key,
92
106
  summary: t.fields?.summary,
@@ -104,13 +118,13 @@ class w {
104
118
  i.length > 0 && (s.relatedIssues = i);
105
119
  }
106
120
  if (t.fields?.issuelinks?.length > 0) {
107
- const i = t.fields.issuelinks.map((r) => {
108
- const n = r.inwardIssue || r.outwardIssue, a = r.type.inward || r.type.outward;
121
+ const i = t.fields.issuelinks.map((a) => {
122
+ const r = a.inwardIssue || a.outwardIssue, n = a.type.inward || a.type.outward;
109
123
  return {
110
- key: n.key,
111
- summary: n.fields?.summary,
124
+ key: r.key,
125
+ summary: r.fields?.summary,
112
126
  type: "link",
113
- relationship: a,
127
+ relationship: n,
114
128
  source: "description"
115
129
  };
116
130
  });
@@ -131,7 +145,9 @@ class w {
131
145
  id: i.id,
132
146
  key: i.key,
133
147
  summary: i.fields?.summary
134
- }))), s;
148
+ }))), t.fields?.attachment?.length > 0 && (s.attachments = t.fields.attachment.map(
149
+ (i) => this.cleanAttachment(i)
150
+ )), s;
135
151
  }
136
152
  async fetchJson(t, e) {
137
153
  const s = await fetch(this.baseUrl + t, {
@@ -184,18 +200,18 @@ class w {
184
200
  expand: "names,renderedFields"
185
201
  }), s = await this.fetchJson(`/rest/api/3/search?${e}`);
186
202
  return await Promise.all(
187
- s.issues.map(async (r) => {
188
- const n = await this.fetchJson(
189
- `/rest/api/3/issue/${r.key}/comment`
190
- ), a = this.cleanIssue(r), o = n.comments.map(
191
- (c) => this.cleanComment(c)
203
+ s.issues.map(async (a) => {
204
+ const r = await this.fetchJson(
205
+ `/rest/api/3/issue/${a.key}/comment`
206
+ ), n = this.cleanIssue(a), o = r.comments.map(
207
+ (d) => this.cleanComment(d)
192
208
  ), u = o.flatMap(
193
- (c) => c.mentions
209
+ (d) => d.mentions
194
210
  );
195
- return a.relatedIssues = [
196
- ...a.relatedIssues,
211
+ return n.relatedIssues = [
212
+ ...n.relatedIssues,
197
213
  ...u
198
- ], a.comments = o, a;
214
+ ], n.comments = o, n;
199
215
  })
200
216
  );
201
217
  }
@@ -212,7 +228,8 @@ class w {
212
228
  "parent",
213
229
  "subtasks",
214
230
  "customfield_10014",
215
- "issuelinks"
231
+ "issuelinks",
232
+ "attachment"
216
233
  ].join(","),
217
234
  expand: "names,renderedFields"
218
235
  });
@@ -225,24 +242,24 @@ class w {
225
242
  } catch (o) {
226
243
  throw o instanceof Error && o.message.includes("(Status: 404)") ? new Error(`Issue not found: ${t}`) : o;
227
244
  }
228
- const r = this.cleanIssue(s), n = i.comments.map(
245
+ const a = this.cleanIssue(s), r = i.comments.map(
229
246
  (o) => this.cleanComment(o)
230
- ), a = n.flatMap(
247
+ ), n = r.flatMap(
231
248
  (o) => o.mentions
232
249
  );
233
- if (r.relatedIssues = [...r.relatedIssues, ...a], r.comments = n, r.epicLink)
250
+ if (a.relatedIssues = [...a.relatedIssues, ...n], a.comments = r, a.epicLink)
234
251
  try {
235
252
  const o = await this.fetchJson(
236
- `/rest/api/3/issue/${r.epicLink.key}?fields=summary`
253
+ `/rest/api/3/issue/${a.epicLink.key}?fields=summary`
237
254
  );
238
- r.epicLink.summary = o.fields?.summary;
255
+ a.epicLink.summary = o.fields?.summary;
239
256
  } catch (o) {
240
257
  console.error("Failed to fetch epic details:", o);
241
258
  }
242
- return r;
259
+ return a;
243
260
  }
244
- async createIssue(t, e, s, i, r) {
245
- const n = {
261
+ async createIssue(t, e, s, i, a) {
262
+ const r = {
246
263
  fields: {
247
264
  project: {
248
265
  key: t
@@ -252,48 +269,48 @@ class w {
252
269
  name: e
253
270
  },
254
271
  ...i && { description: i },
255
- ...r
272
+ ...a
256
273
  }
257
274
  };
258
275
  return this.fetchJson("/rest/api/3/issue", {
259
276
  method: "POST",
260
- body: JSON.stringify(n)
277
+ body: JSON.stringify(r)
261
278
  });
262
279
  }
263
280
  async getCreateMeta(t, e, s) {
264
- let r = (await this.fetchJson(
281
+ let a = (await this.fetchJson(
265
282
  `/rest/api/3/issue/createmeta/${t}/issuetypes`
266
283
  )).issueTypes || [];
267
- return e && (r = r.filter(
268
- (a) => a.name.toLowerCase() === e.toLowerCase()
284
+ return e && (a = a.filter(
285
+ (n) => n.name.toLowerCase() === e.toLowerCase()
269
286
  )), {
270
287
  projectKey: t,
271
288
  issueTypes: await Promise.all(
272
- r.map(async (a) => {
289
+ a.map(async (n) => {
273
290
  try {
274
291
  let u = (await this.fetchJson(
275
- `/rest/api/3/issue/createmeta/${t}/issuetypes/${a.id}`
292
+ `/rest/api/3/issue/createmeta/${t}/issuetypes/${n.id}`
276
293
  )).fields || [];
277
- return s && (u = u.map((c) => ({
278
- fieldId: c.fieldId,
279
- name: c.name,
280
- required: c.required,
281
- schema: c.schema,
282
- hasDefaultValue: c.hasDefaultValue,
283
- allowedValuesCount: c.allowedValues?.length ?? 0
294
+ return s && (u = u.map((d) => ({
295
+ fieldId: d.fieldId,
296
+ name: d.name,
297
+ required: d.required,
298
+ schema: d.schema,
299
+ hasDefaultValue: d.hasDefaultValue,
300
+ allowedValuesCount: d.allowedValues?.length ?? 0
284
301
  }))), {
285
- id: a.id,
286
- name: a.name,
287
- description: a.description,
288
- subtask: a.subtask,
302
+ id: n.id,
303
+ name: n.name,
304
+ description: n.description,
305
+ subtask: n.subtask,
289
306
  fields: u
290
307
  };
291
308
  } catch (o) {
292
309
  return {
293
- id: a.id,
294
- name: a.name,
295
- description: a.description,
296
- subtask: a.subtask,
310
+ id: n.id,
311
+ name: n.name,
312
+ description: n.description,
313
+ subtask: n.subtask,
297
314
  fields: [],
298
315
  error: o instanceof Error ? o.message : "Failed to fetch fields"
299
316
  };
@@ -305,7 +322,7 @@ class w {
305
322
  async getFieldOptions(t, e, s) {
306
323
  return ((await this.fetchJson(
307
324
  `/rest/api/3/issue/createmeta/${t}/issuetypes/${e}`
308
- )).fields || []).find((n) => n.fieldId === s)?.allowedValues ?? [];
325
+ )).fields || []).find((r) => r.fieldId === s)?.allowedValues ?? [];
309
326
  }
310
327
  async updateIssue(t, e) {
311
328
  await this.fetchJson(`/rest/api/3/issue/${t}`, {
@@ -352,18 +369,18 @@ class w {
352
369
  async addAttachment(t, e, s) {
353
370
  const i = new FormData();
354
371
  i.append("file", new Blob([new Uint8Array(e)]), s);
355
- const r = new Headers(this.headers);
356
- r.delete("Content-Type"), r.set("X-Atlassian-Token", "no-check");
357
- const n = await fetch(
372
+ const a = new Headers(this.headers);
373
+ a.delete("Content-Type"), a.set("X-Atlassian-Token", "no-check");
374
+ const r = await fetch(
358
375
  `${this.baseUrl}/rest/api/3/issue/${t}/attachments`,
359
376
  {
360
377
  method: "POST",
361
- headers: r,
378
+ headers: a,
362
379
  body: i
363
380
  }
364
381
  );
365
- n.ok || await this.handleFetchError(n);
366
- const o = (await n.json())[0];
382
+ r.ok || await this.handleFetchError(r);
383
+ const o = (await r.json())[0];
367
384
  return {
368
385
  id: o.id,
369
386
  filename: o.filename
@@ -395,7 +412,7 @@ class w {
395
412
  async addCommentToIssue(t, e) {
396
413
  const i = {
397
414
  body: this.createAdfFromBody(e)
398
- }, r = await this.fetchJson(
415
+ }, a = await this.fetchJson(
399
416
  `/rest/api/3/issue/${t}/comment`,
400
417
  {
401
418
  method: "POST",
@@ -403,11 +420,11 @@ class w {
403
420
  }
404
421
  );
405
422
  return {
406
- id: r.id,
407
- author: r.author.displayName,
408
- created: r.created,
409
- updated: r.updated,
410
- body: this.extractTextContent(r.body.content)
423
+ id: a.id,
424
+ author: a.author.displayName,
425
+ created: a.created,
426
+ updated: a.updated,
427
+ body: this.extractTextContent(a.body.content)
411
428
  };
412
429
  }
413
430
  async getServerInfo() {
@@ -430,11 +447,11 @@ class w {
430
447
  query: t,
431
448
  maxResults: e.toString()
432
449
  });
433
- return (await this.fetchJson(`/rest/api/3/user/search?${s}`)).map((r) => ({
434
- accountId: r.accountId,
435
- displayName: r.displayName,
436
- emailAddress: r.emailAddress,
437
- active: r.active
450
+ return (await this.fetchJson(`/rest/api/3/user/search?${s}`)).map((a) => ({
451
+ accountId: a.accountId,
452
+ displayName: a.displayName,
453
+ emailAddress: a.emailAddress,
454
+ active: a.active
438
455
  }));
439
456
  }
440
457
  async deleteIssue(t) {
@@ -444,8 +461,20 @@ class w {
444
461
  });
445
462
  e.ok || await this.handleFetchError(e);
446
463
  }
464
+ async getAttachment(t) {
465
+ const e = await this.fetchJson(`/rest/api/3/attachment/${t}`), s = await fetch(e.content, {
466
+ headers: this.headers
467
+ });
468
+ s.ok || await this.handleFetchError(s);
469
+ const i = await s.arrayBuffer();
470
+ return {
471
+ content: Buffer.from(i).toString("base64"),
472
+ filename: e.filename,
473
+ mimeType: e.mimeType
474
+ };
475
+ }
447
476
  }
448
- class k extends w {
477
+ class T extends w {
449
478
  constructor(t, e, s, i = "basic") {
450
479
  super(t, e, s, i);
451
480
  }
@@ -460,39 +489,39 @@ class k extends w {
460
489
  }
461
490
  // Override getCreateMeta to use API v2 endpoints for Jira Server 9.0+
462
491
  async getCreateMeta(t, e, s) {
463
- let r = (await this.fetchJson(
492
+ let a = (await this.fetchJson(
464
493
  `/rest/api/3/issue/createmeta/${t}/issuetypes`
465
494
  )).values || [];
466
- return e && (r = r.filter(
467
- (a) => a.name.toLowerCase() === e.toLowerCase()
495
+ return e && (a = a.filter(
496
+ (n) => n.name.toLowerCase() === e.toLowerCase()
468
497
  )), {
469
498
  projectKey: t,
470
499
  issueTypes: await Promise.all(
471
- r.map(async (a) => {
500
+ a.map(async (n) => {
472
501
  try {
473
502
  let u = (await this.fetchJson(
474
- `/rest/api/3/issue/createmeta/${t}/issuetypes/${a.id}`
503
+ `/rest/api/3/issue/createmeta/${t}/issuetypes/${n.id}`
475
504
  )).values || [];
476
- return s && (u = u.map((c) => ({
477
- fieldId: c.fieldId,
478
- name: c.name,
479
- required: c.required,
480
- schema: c.schema,
481
- hasDefaultValue: c.hasDefaultValue,
482
- allowedValuesCount: c.allowedValues?.length ?? 0
505
+ return s && (u = u.map((d) => ({
506
+ fieldId: d.fieldId,
507
+ name: d.name,
508
+ required: d.required,
509
+ schema: d.schema,
510
+ hasDefaultValue: d.hasDefaultValue,
511
+ allowedValuesCount: d.allowedValues?.length ?? 0
483
512
  }))), {
484
- id: a.id,
485
- name: a.name,
486
- description: a.description,
487
- subtask: a.subtask,
513
+ id: n.id,
514
+ name: n.name,
515
+ description: n.description,
516
+ subtask: n.subtask,
488
517
  fields: u
489
518
  };
490
519
  } catch (o) {
491
520
  return {
492
- id: a.id,
493
- name: a.name,
494
- description: a.description,
495
- subtask: a.subtask,
521
+ id: n.id,
522
+ name: n.name,
523
+ description: n.description,
524
+ subtask: n.subtask,
496
525
  fields: [],
497
526
  error: o instanceof Error ? o.message : "Failed to fetch fields"
498
527
  };
@@ -505,7 +534,7 @@ class k extends w {
505
534
  async getFieldOptions(t, e, s) {
506
535
  return ((await this.fetchJson(
507
536
  `/rest/api/3/issue/createmeta/${t}/issuetypes/${e}`
508
- )).values || []).find((n) => n.fieldId === s)?.allowedValues ?? [];
537
+ )).values || []).find((r) => r.fieldId === s)?.allowedValues ?? [];
509
538
  }
510
539
  // Override getUsers for Jira Server (uses 'username' parameter instead of 'query')
511
540
  async getUsers(t, e = 50) {
@@ -513,12 +542,12 @@ class k extends w {
513
542
  username: t,
514
543
  maxResults: e.toString()
515
544
  });
516
- return (await this.fetchJson(`/rest/api/3/user/search?${s}`)).map((r) => ({
545
+ return (await this.fetchJson(`/rest/api/3/user/search?${s}`)).map((a) => ({
517
546
  // Jira Server uses 'name' or 'key' instead of 'accountId'
518
- accountId: r.key || r.name,
519
- displayName: r.displayName,
520
- emailAddress: r.emailAddress,
521
- active: r.active
547
+ accountId: a.key || a.name,
548
+ displayName: a.displayName,
549
+ emailAddress: a.emailAddress,
550
+ active: a.active
522
551
  }));
523
552
  }
524
553
  // Override addCommentToIssue for Jira Server (uses plain text instead of ADF)
@@ -548,8 +577,32 @@ class k extends w {
548
577
  });
549
578
  e.ok || await this.handleFetchError(e);
550
579
  }
580
+ // Override cleanComment for Jira Server (body is plain text, not ADF)
581
+ cleanComment(t) {
582
+ console.error("[DEBUG cleanComment] Raw comment:", JSON.stringify(t, null, 2));
583
+ let e, s = [];
584
+ 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 = "", {
585
+ id: t.id,
586
+ body: e,
587
+ author: t.author?.displayName,
588
+ created: t.created,
589
+ updated: t.updated,
590
+ mentions: s
591
+ };
592
+ }
593
+ // Extract issue mentions from plain text (for Jira Server)
594
+ extractIssueMentionsFromText(t, e, s) {
595
+ if (!t) return [];
596
+ const i = t.match(/[A-Z]+-\d+/g) || [];
597
+ return [...new Set(i)].map((r) => ({
598
+ key: r,
599
+ type: "mention",
600
+ source: e,
601
+ commentId: s
602
+ }));
603
+ }
551
604
  }
552
- const l = process.env.JIRA_API_TOKEN ?? "", m = process.env.JIRA_BASE_URL ?? "", y = process.env.JIRA_USER_EMAIL ?? "", j = process.env.JIRA_TYPE === "server" ? "server" : "cloud", h = process.env.JIRA_AUTH_TYPE === "bearer" ? "bearer" : "basic";
605
+ 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
606
  if (!l || !m || !y)
554
607
  throw new Error(
555
608
  "JIRA_API_TOKEN, JIRA_USER_EMAIL and JIRA_BASE_URL environment variables are required"
@@ -803,7 +856,7 @@ get_users(query) // поиск по имени или email
803
856
  \`\`\``
804
857
  }
805
858
  };
806
- class T {
859
+ class j {
807
860
  server;
808
861
  jiraApi;
809
862
  constructor() {
@@ -818,7 +871,7 @@ class T {
818
871
  prompts: {}
819
872
  }
820
873
  }
821
- ), j === "server" ? this.jiraApi = new k(
874
+ ), k === "server" ? this.jiraApi = new T(
822
875
  m,
823
876
  y,
824
877
  l,
@@ -839,10 +892,10 @@ class T {
839
892
  name: t,
840
893
  description: e
841
894
  }))
842
- })), this.server.setRequestHandler(S, async (t) => {
895
+ })), this.server.setRequestHandler(A, async (t) => {
843
896
  const { name: e } = t.params, s = g[e];
844
897
  if (!s)
845
- throw new d(p.InvalidParams, `Unknown prompt: ${e}`);
898
+ throw new c(p.InvalidParams, `Unknown prompt: ${e}`);
846
899
  return {
847
900
  messages: [
848
901
  {
@@ -854,7 +907,7 @@ class T {
854
907
  }
855
908
  ]
856
909
  };
857
- }), this.server.setRequestHandler(A, async () => ({
910
+ }), this.server.setRequestHandler(S, async () => ({
858
911
  tools: [
859
912
  {
860
913
  name: "search_issues",
@@ -1148,6 +1201,21 @@ Remember to add a comment with cancellation reason first.`,
1148
1201
  required: ["issueKey"],
1149
1202
  additionalProperties: !1
1150
1203
  }
1204
+ },
1205
+ {
1206
+ name: "get_attachment",
1207
+ description: "Download a JIRA attachment by ID. Returns base64 encoded content. Use get_issue first to see available attachments with their IDs.",
1208
+ inputSchema: {
1209
+ type: "object",
1210
+ properties: {
1211
+ attachmentId: {
1212
+ type: "string",
1213
+ description: "The ID of the attachment to download"
1214
+ }
1215
+ },
1216
+ required: ["attachmentId"],
1217
+ additionalProperties: !1
1218
+ }
1151
1219
  }
1152
1220
  ]
1153
1221
  })), this.server.setRequestHandler(J, async (t) => {
@@ -1156,7 +1224,7 @@ Remember to add a comment with cancellation reason first.`,
1156
1224
  switch (t.params.name) {
1157
1225
  case "search_issues": {
1158
1226
  if (!e.searchString || typeof e.searchString != "string")
1159
- throw new d(
1227
+ throw new c(
1160
1228
  p.InvalidParams,
1161
1229
  "Search string is required"
1162
1230
  );
@@ -1169,7 +1237,7 @@ Remember to add a comment with cancellation reason first.`,
1169
1237
  }
1170
1238
  case "get_epic_children": {
1171
1239
  if (!e.epicKey || typeof e.epicKey != "string")
1172
- throw new d(
1240
+ throw new c(
1173
1241
  p.InvalidParams,
1174
1242
  "Epic key is required"
1175
1243
  );
@@ -1182,7 +1250,7 @@ Remember to add a comment with cancellation reason first.`,
1182
1250
  }
1183
1251
  case "get_issue": {
1184
1252
  if (!e.issueId || typeof e.issueId != "string")
1185
- throw new d(
1253
+ throw new c(
1186
1254
  p.InvalidParams,
1187
1255
  "Issue ID is required"
1188
1256
  );
@@ -1197,7 +1265,7 @@ Remember to add a comment with cancellation reason first.`,
1197
1265
  }
1198
1266
  case "create_issue": {
1199
1267
  if (!e.projectKey || typeof e.projectKey != "string" || !e.issueType || typeof e.issueType != "string" || !e.summary || typeof e.summary != "string")
1200
- throw new d(
1268
+ throw new c(
1201
1269
  p.InvalidParams,
1202
1270
  "projectKey, issueType, and summary are required"
1203
1271
  );
@@ -1216,7 +1284,7 @@ Remember to add a comment with cancellation reason first.`,
1216
1284
  }
1217
1285
  case "update_issue": {
1218
1286
  if (!e.issueKey || typeof e.issueKey != "string" || !e.fields || typeof e.fields != "object")
1219
- throw new d(
1287
+ throw new c(
1220
1288
  p.InvalidParams,
1221
1289
  "issueKey and fields object are required"
1222
1290
  );
@@ -1235,7 +1303,7 @@ Remember to add a comment with cancellation reason first.`,
1235
1303
  }
1236
1304
  case "get_transitions": {
1237
1305
  if (!e.issueKey || typeof e.issueKey != "string")
1238
- throw new d(
1306
+ throw new c(
1239
1307
  p.InvalidParams,
1240
1308
  "Issue key is required"
1241
1309
  );
@@ -1248,7 +1316,7 @@ Remember to add a comment with cancellation reason first.`,
1248
1316
  }
1249
1317
  case "transition_issue": {
1250
1318
  if (!e.issueKey || typeof e.issueKey != "string" || !e.transitionId || typeof e.transitionId != "string")
1251
- throw new d(
1319
+ throw new c(
1252
1320
  p.InvalidParams,
1253
1321
  "issueKey and transitionId are required"
1254
1322
  );
@@ -1273,7 +1341,7 @@ Remember to add a comment with cancellation reason first.`,
1273
1341
  }
1274
1342
  case "add_attachment": {
1275
1343
  if (!e.issueKey || typeof e.issueKey != "string" || !e.fileContent || typeof e.fileContent != "string" || !e.filename || typeof e.filename != "string")
1276
- throw new d(
1344
+ throw new c(
1277
1345
  p.InvalidParams,
1278
1346
  "issueKey, fileContent, and filename are required"
1279
1347
  );
@@ -1301,7 +1369,7 @@ Remember to add a comment with cancellation reason first.`,
1301
1369
  }
1302
1370
  case "add_comment": {
1303
1371
  if (!e.issueIdOrKey || typeof e.issueIdOrKey != "string" || !e.body || typeof e.body != "string")
1304
- throw new d(
1372
+ throw new c(
1305
1373
  p.InvalidParams,
1306
1374
  "issueIdOrKey and body are required"
1307
1375
  );
@@ -1317,7 +1385,7 @@ Remember to add a comment with cancellation reason first.`,
1317
1385
  }
1318
1386
  case "get_create_meta": {
1319
1387
  if (!e.projectKey || typeof e.projectKey != "string")
1320
- throw new d(
1388
+ throw new c(
1321
1389
  p.InvalidParams,
1322
1390
  "projectKey is required"
1323
1391
  );
@@ -1334,7 +1402,7 @@ Remember to add a comment with cancellation reason first.`,
1334
1402
  }
1335
1403
  case "get_field_options": {
1336
1404
  if (!e.projectKey || typeof e.projectKey != "string" || !e.issueTypeId || typeof e.issueTypeId != "string" || !e.fieldId || typeof e.fieldId != "string")
1337
- throw new d(
1405
+ throw new c(
1338
1406
  p.InvalidParams,
1339
1407
  "projectKey, issueTypeId, and fieldId are required"
1340
1408
  );
@@ -1367,7 +1435,7 @@ Remember to add a comment with cancellation reason first.`,
1367
1435
  }
1368
1436
  case "get_users": {
1369
1437
  if (!e.query || typeof e.query != "string")
1370
- throw new d(
1438
+ throw new c(
1371
1439
  p.InvalidParams,
1372
1440
  "query is required"
1373
1441
  );
@@ -1383,7 +1451,7 @@ Remember to add a comment with cancellation reason first.`,
1383
1451
  }
1384
1452
  case "delete_issue": {
1385
1453
  if (!e.issueKey || typeof e.issueKey != "string")
1386
- throw new d(
1454
+ throw new c(
1387
1455
  p.InvalidParams,
1388
1456
  "issueKey is required"
1389
1457
  );
@@ -1400,14 +1468,42 @@ Remember to add a comment with cancellation reason first.`,
1400
1468
  ]
1401
1469
  };
1402
1470
  }
1471
+ case "get_attachment": {
1472
+ if (!e.attachmentId || typeof e.attachmentId != "string")
1473
+ throw new c(
1474
+ p.InvalidParams,
1475
+ "attachmentId is required"
1476
+ );
1477
+ const s = await this.jiraApi.getAttachment(e.attachmentId);
1478
+ return s.mimeType.startsWith("image/") ? {
1479
+ content: [
1480
+ {
1481
+ type: "text",
1482
+ text: `Attachment: ${s.filename} (${s.mimeType})`
1483
+ },
1484
+ {
1485
+ type: "image",
1486
+ data: s.content,
1487
+ mimeType: s.mimeType
1488
+ }
1489
+ ]
1490
+ } : {
1491
+ content: [
1492
+ {
1493
+ type: "text",
1494
+ text: JSON.stringify(s, null, 2)
1495
+ }
1496
+ ]
1497
+ };
1498
+ }
1403
1499
  default:
1404
- throw new d(
1500
+ throw new c(
1405
1501
  p.MethodNotFound,
1406
1502
  `Unknown tool: ${t.params.name}`
1407
1503
  );
1408
1504
  }
1409
1505
  } catch (e) {
1410
- throw e instanceof d ? e : new d(
1506
+ throw e instanceof c ? e : new c(
1411
1507
  p.InternalError,
1412
1508
  e instanceof Error ? e.message : "Unknown error occurred"
1413
1509
  );
@@ -1419,7 +1515,7 @@ Remember to add a comment with cancellation reason first.`,
1419
1515
  await this.server.connect(t);
1420
1516
  }
1421
1517
  }
1422
- const v = new T();
1423
- v.run().catch(() => {
1518
+ const x = new j();
1519
+ x.run().catch(() => {
1424
1520
  });
1425
1521
  //# sourceMappingURL=index.js.map
@@ -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 let description = \"\";\n if (issue.fields?.description) {\n if (typeof issue.fields.description === \"string\") {\n // Jira Server: plain text\n description = issue.fields.description;\n } else if (issue.fields.description.content) {\n // Jira Cloud: ADF format\n description = this.extractTextContent(issue.fields.description.content);\n }\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
  }
@@ -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.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "JIRA MCP Server",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "jira-mcp": "./build/index.js"
8
8
  },
9
- "files": ["build"],
9
+ "files": [
10
+ "build"
11
+ ],
10
12
  "publishConfig": {
11
13
  "access": "public"
12
14
  },