org-jira-mcp-adapter 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Jira MCP Adapter
2
+
3
+ Этот пакет предоставляет MCP сервер для работы с Jira (Server / Data Center), адаптированный для вашей команды.
4
+
5
+ ## Как использовать (для команды)
6
+
7
+ Вам не нужно ничего устанавливать или клонировать репозиторий.
8
+ Просто добавьте следующую конфигурацию в настройки MCP вашей IDE (Trae, Cursor, VSCode).
9
+
10
+ ### Конфигурация (JSON)
11
+
12
+ Вставьте этот блок в `mcpServers` в вашем конфигурационном файле (обычно открывается через настройки IDE -> "Edit MCP Settings"):
13
+
14
+ ```json
15
+ {
16
+ "mcpServers": {
17
+ "jira": {
18
+ "command": "npx",
19
+ "args": [
20
+ "-y",
21
+ "org-jira-mcp-adapter"
22
+ ],
23
+ "env": {
24
+ "JIRA_HOST": "jira.example.com",
25
+ "JIRA_API_TOKEN": "your_password_or_pat_token"
26
+ }
27
+ }
28
+ }
29
+ }
30
+ ```
31
+
32
+ > **Важно**: Замените `jira.example.com` и `your_password_or_pat_token` на ваши реальные данные.
33
+
34
+ ### Переменные окружения
35
+
36
+ - `JIRA_HOST`: Адрес вашего Jira сервера (без протокола, например `jira.example.com`).
37
+ - `JIRA_API_TOKEN`: Ваш пароль от Jira или Personal Access Token (PAT).
package/index.js ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ import { connectServer, createMcpServer, formatToolResponse } from '@atlassian-dc-mcp/common';
3
+ import { JiraService, jiraToolSchemas } from './jira-service.js';
4
+ import * as process from 'node:process';
5
+
6
+ const missingEnvVars = JiraService.validateConfig();
7
+
8
+ if (missingEnvVars.length > 0) {
9
+ throw new Error(`Missing required environment variables: ${missingEnvVars.join(', ')}`);
10
+ }
11
+
12
+ // Removing email from constructor as it is not needed for PAT auth
13
+ const jiraService = new JiraService(process.env.JIRA_HOST, process.env.JIRA_API_TOKEN, process.env.JIRA_API_BASE_PATH);
14
+
15
+ const server = createMcpServer({
16
+ name: "atlassian-jira-mcp",
17
+ version: "0.1.0"
18
+ });
19
+
20
+ const jiraInstanceType = "JIRA Data Center edition instance";
21
+
22
+ server.tool("jira_searchIssues", `Search for JIRA issues using JQL in the ${jiraInstanceType}`, jiraToolSchemas.searchIssues, async ({ jql, expand, startAt, maxResults = 10 }) => {
23
+ const result = await jiraService.searchIssues(jql, startAt, expand, maxResults);
24
+ return formatToolResponse(result);
25
+ });
26
+
27
+ server.tool("jira_getIssue", `Get details of a JIRA issue by its key from the ${jiraInstanceType}`, jiraToolSchemas.getIssue, async ({ issueKey, expand }) => {
28
+ const result = await jiraService.getIssue(issueKey, expand);
29
+ return formatToolResponse(result);
30
+ });
31
+
32
+ server.tool('jira_getIssueComments', `Get comments of a JIRA issue by its key from the ${jiraInstanceType}`, jiraToolSchemas.getIssueComments, async ({ issueKey, expand }) => {
33
+ const result = await jiraService.getIssueComments(issueKey, expand);
34
+ return formatToolResponse(result);
35
+ });
36
+
37
+ server.tool("jira_createIssue", `Create a new JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.createIssue, async (params) => {
38
+ const result = await jiraService.createIssue(params);
39
+ return formatToolResponse(result);
40
+ });
41
+
42
+ server.tool("jira_updateIssue", `Update an existing JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.updateIssue, async (params) => {
43
+ const result = await jiraService.updateIssue(params);
44
+ return formatToolResponse(result);
45
+ });
46
+
47
+ server.tool("jira_postIssueComment", `Post a comment on a JIRA issue in the ${jiraInstanceType}`, jiraToolSchemas.postIssueComment, async ({ issueKey, comment }) => {
48
+ const result = await jiraService.postIssueComment(issueKey, comment);
49
+ return formatToolResponse(result);
50
+ });
51
+
52
+ await connectServer(server);
@@ -0,0 +1,128 @@
1
+ import { z } from 'zod';
2
+ import { handleApiOperation } from '@atlassian-dc-mcp/common';
3
+ import { IssueService, OpenAPI, SearchService } from '@atlassian-dc-mcp/jira/build/jira-client/index.js';
4
+
5
+ export class JiraService {
6
+ constructor(host, token, fullBaseUrl, email) {
7
+ if (fullBaseUrl) {
8
+ OpenAPI.BASE = fullBaseUrl;
9
+ } else if (host) {
10
+ const cleanHost = host.replace(/^https?:\/\//, '').replace(/\/$/, '');
11
+ OpenAPI.BASE = `https://${cleanHost}/rest`;
12
+ }
13
+ if (email) {
14
+ OpenAPI.USERNAME = email;
15
+ OpenAPI.PASSWORD = token;
16
+ } else {
17
+ OpenAPI.TOKEN = token;
18
+ }
19
+ OpenAPI.VERSION = '2';
20
+ }
21
+
22
+ async searchIssues(jql, startAt, expand, maxResults = 10) {
23
+ return handleApiOperation(() => {
24
+ return SearchService.searchUsingSearchRequest({
25
+ jql,
26
+ maxResults,
27
+ expand,
28
+ startAt
29
+ });
30
+ }, 'Error searching issues');
31
+ }
32
+
33
+ async getIssue(issueKey, expand) {
34
+ return handleApiOperation(() => IssueService.getIssue(issueKey, expand), 'Error getting issue');
35
+ }
36
+
37
+ async getIssueComments(issueKey, expand) {
38
+ return handleApiOperation(() => IssueService.getComments(issueKey, expand), 'Error getting issue comments');
39
+ }
40
+
41
+ async postIssueComment(issueKey, comment) {
42
+ return handleApiOperation(() => IssueService.addComment(issueKey, undefined, { body: comment }), 'Error posting issue comment');
43
+ }
44
+
45
+ async createIssue(params) {
46
+ return handleApiOperation(async () => {
47
+ const standardFields = {
48
+ project: { key: params.projectId },
49
+ summary: params.summary,
50
+ description: params.description,
51
+ issuetype: { id: params.issueTypeId }
52
+ };
53
+
54
+ const fields = params.customFields
55
+ ? { ...standardFields, ...params.customFields }
56
+ : standardFields;
57
+
58
+ return IssueService.createIssue(true, { fields });
59
+ }, 'Error creating issue');
60
+ }
61
+
62
+ async updateIssue(params) {
63
+ return handleApiOperation(async () => {
64
+ const standardFields = {};
65
+ if (params.summary !== undefined) {
66
+ standardFields.summary = params.summary;
67
+ }
68
+ if (params.description !== undefined) {
69
+ standardFields.description = params.description;
70
+ }
71
+ if (params.issueTypeId !== undefined) {
72
+ standardFields.issuetype = { id: params.issueTypeId };
73
+ }
74
+
75
+ const fields = params.customFields
76
+ ? { ...standardFields, ...params.customFields }
77
+ : standardFields;
78
+
79
+ return IssueService.editIssue(params.issueKey, 'true', { fields });
80
+ }, 'Error updating issue');
81
+ }
82
+
83
+ static validateConfig() {
84
+ const requiredEnvVars = ['JIRA_API_TOKEN'];
85
+ const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);
86
+
87
+ if (!process.env.JIRA_HOST && !process.env.JIRA_API_BASE_PATH) {
88
+ missingVars.push('JIRA_HOST or JIRA_API_BASE_PATH');
89
+ }
90
+
91
+ return missingVars;
92
+ }
93
+ }
94
+
95
+ export const jiraToolSchemas = {
96
+ searchIssues: {
97
+ jql: z.string().describe("JQL query string"),
98
+ maxResults: z.number().optional().describe("Maximum number of results to return"),
99
+ startAt: z.number().optional().describe("Index of the first result to return"),
100
+ expand: z.array(z.string()).optional().describe("Fields to expand")
101
+ },
102
+ getIssue: {
103
+ issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
104
+ expand: z.string().optional().describe("Comma separated fields to expand")
105
+ },
106
+ getIssueComments: {
107
+ issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
108
+ expand: z.string().optional().describe("Comma separated fields to expand")
109
+ },
110
+ postIssueComment: {
111
+ issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
112
+ comment: z.string().describe("Comment text in the format suitable for JIRA DATA CENTER edition (JIRA Wiki Markup).")
113
+ },
114
+ createIssue: {
115
+ projectId: z.string().describe("Project id"),
116
+ summary: z.string().describe("Issue summary"),
117
+ description: z.string().describe("Issue description in the format suitable for JIRA DATA CENTER edition (JIRA Wiki Markup)."),
118
+ issueTypeId: z.string().describe("Issue type id (e.g. id of Task, Bug, Story). Should be found first a correct number for specific JIRA installation."),
119
+ customFields: z.object({}).catchall(z.any()).optional().describe("Optional custom fields as key-value pairs. Examples: {'customfield_10001': 'Custom Value', 'priority': {'id': '1'}, 'assignee': {'name': 'john.doe'}, 'labels': ['urgent', 'bug']}")
120
+ },
121
+ updateIssue: {
122
+ issueKey: z.string().describe("JIRA issue key (e.g., PROJ-123)"),
123
+ summary: z.string().optional().describe("New summary (optional)"),
124
+ description: z.string().optional().describe("New description in JIRA Wiki Markup (optional)"),
125
+ issueTypeId: z.string().optional().describe("New issue type id (optional)"),
126
+ customFields: z.object({}).catchall(z.any()).optional().describe("Optional custom fields to update as key-value pairs. Examples: {'customfield_10001': 'Custom Value', 'priority': {'id': '1'}, 'assignee': {'name': 'john.doe'}, 'labels': ['urgent', 'bug']}")
127
+ }
128
+ };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "org-jira-mcp-adapter",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Jira Data Center/Server (On-Premise)",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "org-jira-mcp-adapter": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "dependencies": {
14
+ "@atlassian-dc-mcp/common": "^0.9.9",
15
+ "@atlassian-dc-mcp/jira": "^0.9.9",
16
+ "zod": "^4.2.1"
17
+ },
18
+ "engines": {
19
+ "node": ">=18.0.0"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "jira",
24
+ "atlassian",
25
+ "server",
26
+ "data-center"
27
+ ],
28
+ "author": "",
29
+ "license": "ISC"
30
+ }