@nexus2520/jira-mcp-server 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,19 +1,21 @@
1
1
  # Jira MCP Server
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/@nexus2520%2Fjira-mcp-server.svg)](https://www.npmjs.com/package/@nexus2520/jira-mcp-server)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
5
 
5
- A Model Context Protocol (MCP) server for Jira API integration. This server enables AI assistants like Claude to interact with Jira Cloud instances for issue management, search, comments, and workflow transitions.
6
+ A Model Context Protocol (MCP) server for Jira API integration. This server enables AI assistants like Claude to interact with Jira Cloud instances for issue management, search, comments, workflow transitions, and attachment handling.
6
7
 
7
8
  ## Features
8
9
 
9
10
  - **Issue Management**: Get, create, update, and assign Jira issues with custom field support
10
11
  - **JQL Search**: Search issues using Jira Query Language
11
- - **Comments**: Add and retrieve comments on issues (supports mentions and links)
12
+ - **Comments**: Add and retrieve comments on issues
12
13
  - **Workflow**: Get available transitions and change issue status
13
14
  - **Metadata Discovery**: Get field requirements and allowed values for projects
14
15
  - **User Search**: Find users by email or name for assignments
15
16
  - **Projects**: List all accessible projects
16
- - **API Token Authentication**: Secure authentication using email + API token
17
+ - **Attachments**: List, upload, delete attachments and retrieve their content — text files returned as text, images rendered inline via Claude vision
18
+ - **Token Efficient**: 5 compound tools instead of 16 flat tools — ~50% fewer tokens per session
17
19
 
18
20
  ## Installation
19
21
 
@@ -51,8 +53,6 @@ npm install -g @nexus2520/jira-mcp-server
51
53
 
52
54
  ### Environment Variables
53
55
 
54
- The server requires the following environment variables:
55
-
56
56
  - `JIRA_EMAIL`: Your Atlassian account email
57
57
  - `JIRA_API_TOKEN`: Your Jira API token
58
58
  - `JIRA_BASE_URL`: Your Jira instance URL (e.g., `https://yourcompany.atlassian.net`)
@@ -91,12 +91,10 @@ Add the following to your Claude Desktop MCP settings file:
91
91
  {
92
92
  "mcpServers": {
93
93
  "jira": {
94
- "name": "jira",
95
94
  "command": "node",
96
95
  "args": [
97
96
  "/absolute/path/to/jira-mcp-server/build/index.js"
98
97
  ],
99
- "transport": "stdio",
100
98
  "env": {
101
99
  "JIRA_EMAIL": "your-email@company.com",
102
100
  "JIRA_API_TOKEN": "your-api-token-here",
@@ -115,130 +113,152 @@ Add the following to your Claude Desktop MCP settings file:
115
113
  4. Copy the generated token
116
114
  5. Use it in your configuration
117
115
 
116
+ ## Tool Architecture
117
+
118
+ This server exposes **5 compound tools** — each with an `action` parameter that selects the operation. This design reduces the token overhead of tool definitions by ~50% compared to 16 flat tools, leaving more context for actual work.
119
+
120
+ | Tool | Actions | Description |
121
+ |------|---------|-------------|
122
+ | `jira_issues` | `get` `create` `update` `assign` | Full issue lifecycle management |
123
+ | `jira_search` | `issues` `projects` `users` `create_metadata` | Search and discovery |
124
+ | `jira_comments` | `get` `add` | Read and write comments |
125
+ | `jira_workflow` | `get_transitions` `transition` | Status transitions |
126
+ | `jira_attachments` | `list` `get_content` `upload` `delete` | File attachments |
127
+
128
+ ---
129
+
118
130
  ## Available Tools
119
131
 
120
- ### Issue Management
132
+ ### `jira_issues`
121
133
 
122
- #### `get_issue`
123
- Get detailed information about a Jira issue.
134
+ Manage the full lifecycle of Jira issues.
124
135
 
125
- **Parameters**:
126
- - `issueKey` (required): The issue key (e.g., "PROJ-123")
136
+ **Required**: `action`
127
137
 
128
- **Example**:
138
+ | Action | Description | Required params | Optional params |
139
+ |--------|-------------|-----------------|-----------------|
140
+ | `get` | Fetch full issue details | `issueKey` | — |
141
+ | `create` | Create a new issue | `projectKey`, `summary`, `issueType` | `description`, `priority`, `assignee`, `labels`, `customFields` |
142
+ | `update` | Edit fields on an existing issue | `issueKey` | `summary`, `description`, `priority`, `assignee`, `labels`, `customFields` |
143
+ | `assign` | Set or clear the assignee | `issueKey`, `assignee` | — |
144
+
145
+ **Tips**:
146
+ - Always call `jira_search` with `action=create_metadata` before creating issues to discover required custom fields and allowed values.
147
+ - Pass `assignee: "-1"` to unassign an issue.
148
+ - `description` accepts plain text or an [Atlassian Document Format (ADF)](https://developer.atlassian.com/cloud/jira/platform/apis/document/structure/) object.
149
+ - `customFields` is a key-value map: `{"customfield_10000": "value"}`.
150
+
151
+ **Examples**:
129
152
  ```
130
- Get details for issue PROJ-123
153
+ Get details for PROJ-123
154
+ Create a Bug in project PROJ with summary "Login button broken"
155
+ Update PROJ-123 priority to High
156
+ Assign PROJ-123 to john.doe@company.com
131
157
  ```
132
158
 
133
- #### `create_issue`
134
- Create a new Jira issue.
159
+ ---
135
160
 
136
- **Important**: Always use `get_create_metadata` first to discover required fields, custom fields, and allowed values.
161
+ ### `jira_search`
137
162
 
138
- **Parameters**:
139
- - `projectKey` (required): Project key (e.g., "PROJ", "DEV")
140
- - `summary` (required): Issue title
141
- - `issueType` (required): Type (e.g., "Bug", "Task", "Story")
142
- - `description` (optional): Issue description
143
- - `priority` (optional): Priority name
144
- - `assignee` (optional): Assignee account ID or email
145
- - `labels` (optional): Array of labels
146
- - `customFields` (optional): Custom fields object
163
+ Search and discover Jira resources.
147
164
 
148
- **Example**:
149
- ```
150
- Create a bug in project PROJ with summary "Login button not working" and description "Users cannot log in"
151
- ```
165
+ **Required**: `action`
152
166
 
153
- #### `update_issue`
154
- Update fields of an existing issue.
167
+ | Action | Description | Required params | Optional params |
168
+ |--------|-------------|-----------------|-----------------|
169
+ | `issues` | Search issues via JQL | `jql` | `maxResults` |
170
+ | `projects` | List all accessible projects | — | `maxResults` |
171
+ | `users` | Find users by name or email | `query` | `maxResults` |
172
+ | `create_metadata` | Get field requirements for creating issues | `projectKey` | `issueType` |
155
173
 
156
- **Tip**: Use `get_create_metadata` to discover available custom fields and their allowed values.
174
+ **Common JQL examples**:
175
+ ```
176
+ project = PROJ AND status = Open
177
+ assignee = currentUser() AND status != Done
178
+ priority = High AND created >= -7d
179
+ ```
157
180
 
158
- **Parameters**:
159
- - `issueKey` (required): Issue to update
160
- - `summary` (optional): New summary
161
- - `description` (optional): New description
162
- - `priority` (optional): New priority
163
- - `assignee` (optional): New assignee
164
- - `labels` (optional): New labels array
165
- - `customFields` (optional): Custom fields object
181
+ **Tips**:
182
+ - Use `create_metadata` before `jira_issues` `create` to understand what fields are required for a project/issue type.
183
+ - Use `users` to look up account IDs for assignments — pass the returned account ID or email to `jira_issues` `assign`.
166
184
 
167
- #### `assign_issue`
168
- Assign an issue to a user.
185
+ ---
169
186
 
170
- **Parameters**:
171
- - `issueKey` (required): Issue to assign
172
- - `assignee` (required): User account ID, email, or "-1" to unassign
187
+ ### `jira_comments`
173
188
 
174
- ### Metadata & Discovery
189
+ Read and write comments on a Jira issue.
175
190
 
176
- #### `get_create_metadata`
177
- Get field requirements and metadata for creating issues in a project.
191
+ **Required**: `action`, `issueKey`
178
192
 
179
- **Parameters**:
180
- - `projectKey` (required): Project key
181
- - `issueType` (optional): Filter by specific issue type
193
+ | Action | Description | Required params |
194
+ |--------|-------------|-----------------|
195
+ | `get` | Fetch all comments on an issue | — |
196
+ | `add` | Post a new comment | `comment` |
182
197
 
183
- #### `search_users`
184
- Search for users by name or email to get their account ID.
198
+ `comment` accepts plain text or an ADF object.
185
199
 
186
- **Parameters**:
187
- - `query` (required): Search query (email or name)
188
- - `maxResults` (optional): Max results (default: 50)
200
+ **Examples**:
201
+ ```
202
+ Get all comments on PROJ-123
203
+ Add a comment to PROJ-123: "Fixed in PR #456"
204
+ ```
189
205
 
190
- ### Search
206
+ ---
191
207
 
192
- #### `search_issues`
193
- Search for issues using JQL. Returns issue keys and titles.
208
+ ### `jira_workflow`
194
209
 
195
- **Parameters**:
196
- - `jql` (required): JQL query string
197
- - `maxResults` (optional): Max results (default: 50)
210
+ Manage issue status transitions.
198
211
 
199
- **Example JQL queries**:
200
- - `"project = PROJ AND status = Open"`
201
- - `"assignee = currentUser() AND status != Done"`
202
- - `"priority = High AND created >= -7d"`
212
+ **Required**: `action`, `issueKey`
203
213
 
204
- #### `list_projects`
205
- List all accessible projects.
214
+ | Action | Description | Required params | Optional params |
215
+ |--------|-------------|-----------------|-----------------|
216
+ | `get_transitions` | List available status transitions | — | — |
217
+ | `transition` | Move issue to a new status | `transitionId` | `comment` |
206
218
 
207
- **Parameters**:
208
- - `maxResults` (optional): Max results (default: 50)
219
+ **Tip**: Always call `get_transitions` first — transition IDs vary per project and issue type. The `transitionId` from the response is what you pass to `transition`.
209
220
 
210
- ### Comments
221
+ **Examples**:
222
+ ```
223
+ Get available transitions for PROJ-123
224
+ Move PROJ-123 to "In Progress" (use get_transitions first to find the ID)
225
+ ```
211
226
 
212
- #### `add_comment`
213
- Add a comment to an issue.
227
+ ---
214
228
 
215
- **Parameters**:
216
- - `issueKey` (required): Issue to comment on
217
- - `comment` (required): Comment text
229
+ ### `jira_attachments`
218
230
 
219
- #### `get_comments`
220
- Get all comments for an issue.
231
+ Manage file attachments on Jira issues.
221
232
 
222
- **Parameters**:
223
- - `issueKey` (required): Issue key
233
+ **Required**: `action`
224
234
 
225
- ### Workflow Transitions
235
+ | Action | Description | Required params | Optional params |
236
+ |--------|-------------|-----------------|-----------------|
237
+ | `list` | List all attachments with metadata | `issueKey` | — |
238
+ | `get_content` | Download and return file content | `attachmentId` | `mimeType` |
239
+ | `upload` | Attach a local file to an issue | `issueKey`, `filePath` | `fileName` |
240
+ | `delete` | Remove an attachment by ID | `attachmentId` | — |
226
241
 
227
- #### `get_transitions`
228
- Get available status transitions for an issue.
242
+ **Content types returned by `get_content`**:
243
+ - **Text files** (`text/*`, `application/json`, `application/xml`): returned as readable text
244
+ - **Images** (`image/*`): returned as base64 — Claude will render them inline
245
+ - **Other types** (PDF, zip, etc.): returns file metadata with a descriptive message
229
246
 
230
- **Parameters**:
231
- - `issueKey` (required): Issue key
247
+ **Tips**:
248
+ - Use `list` first to get attachment IDs before calling `get_content` or `delete`.
249
+ - `fileName` in `upload` overrides the filename shown in Jira (defaults to the file's basename).
232
250
 
233
- #### `transition_issue`
234
- Change the status of an issue.
251
+ **Examples**:
252
+ ```
253
+ List attachments on PROJ-123
254
+ Get the content of attachment 136904
255
+ Upload /tmp/report.pdf to PROJ-123
256
+ Delete attachment 136904
257
+ ```
235
258
 
236
- **Parameters**:
237
- - `issueKey` (required): Issue to transition
238
- - `transitionId` (required): Transition ID (from get_transitions)
239
- - `comment` (optional): Comment to add with transition
259
+ ---
240
260
 
241
- ### API Reference
261
+ ## API Reference
242
262
 
243
263
  This server uses the [Jira REST API v3](https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/).
244
264
 
@@ -252,7 +272,7 @@ Make sure you've set the environment variables in your MCP configuration.
252
272
 
253
273
  - Verify your API token is correct
254
274
  - Ensure your email matches your Atlassian account
255
- - Check that your JIRA_BASE_URL doesn't have a trailing slash
275
+ - Check that your `JIRA_BASE_URL` doesn't have a trailing slash
256
276
 
257
277
  ### Permission errors
258
278
 
@@ -0,0 +1,65 @@
1
+ import { JiraApiClient } from '../utils/api-client.js';
2
+ export declare class AttachmentHandlers {
3
+ private apiClient;
4
+ constructor(apiClient: JiraApiClient);
5
+ handleListAttachments(args: any): Promise<{
6
+ content: {
7
+ type: string;
8
+ text: string;
9
+ }[];
10
+ isError?: undefined;
11
+ } | {
12
+ content: {
13
+ type: string;
14
+ text: string;
15
+ }[];
16
+ isError: boolean;
17
+ }>;
18
+ handleGetAttachmentContent(args: any): Promise<{
19
+ content: ({
20
+ type: string;
21
+ text: string;
22
+ data?: undefined;
23
+ mimeType?: undefined;
24
+ } | {
25
+ type: string;
26
+ data: string;
27
+ mimeType: string;
28
+ text?: undefined;
29
+ })[];
30
+ isError?: undefined;
31
+ } | {
32
+ content: {
33
+ type: string;
34
+ text: string;
35
+ }[];
36
+ isError: boolean;
37
+ }>;
38
+ handleUploadAttachment(args: any): Promise<{
39
+ content: {
40
+ type: string;
41
+ text: string;
42
+ }[];
43
+ isError?: undefined;
44
+ } | {
45
+ content: {
46
+ type: string;
47
+ text: string;
48
+ }[];
49
+ isError: boolean;
50
+ }>;
51
+ handleDeleteAttachment(args: any): Promise<{
52
+ content: {
53
+ type: string;
54
+ text: string;
55
+ }[];
56
+ isError?: undefined;
57
+ } | {
58
+ content: {
59
+ type: string;
60
+ text: string;
61
+ }[];
62
+ isError: boolean;
63
+ }>;
64
+ }
65
+ //# sourceMappingURL=attachment-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment-handlers.d.ts","sourceRoot":"","sources":["../../src/handlers/attachment-handlers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,qBAAa,kBAAkB;IACjB,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,aAAa;IAEtC,qBAAqB,CAAC,IAAI,EAAE,GAAG;;;;;;;;;;;;;IAgC/B,0BAA0B,CAAC,IAAI,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;IAsEpC,sBAAsB,CAAC,IAAI,EAAE,GAAG;;;;;;;;;;;;;IAwChC,sBAAsB,CAAC,IAAI,EAAE,GAAG;;;;;;;;;;;;;CAqCvC"}
@@ -0,0 +1,176 @@
1
+ import { existsSync } from 'fs';
2
+ import { basename } from 'path';
3
+ import { JiraFormatters } from '../utils/formatters.js';
4
+ export class AttachmentHandlers {
5
+ apiClient;
6
+ constructor(apiClient) {
7
+ this.apiClient = apiClient;
8
+ }
9
+ async handleListAttachments(args) {
10
+ try {
11
+ const { issueKey } = args;
12
+ if (!issueKey) {
13
+ throw new Error('issueKey is required');
14
+ }
15
+ const issue = await this.apiClient.get(`/issue/${issueKey}`, { fields: 'attachment' });
16
+ const attachments = issue.fields?.attachment || [];
17
+ return {
18
+ content: [
19
+ {
20
+ type: 'text',
21
+ text: JiraFormatters.formatAttachments(attachments),
22
+ },
23
+ ],
24
+ };
25
+ }
26
+ catch (error) {
27
+ return {
28
+ content: [
29
+ {
30
+ type: 'text',
31
+ text: JiraFormatters.formatError(error),
32
+ },
33
+ ],
34
+ isError: true,
35
+ };
36
+ }
37
+ }
38
+ async handleGetAttachmentContent(args) {
39
+ try {
40
+ const { attachmentId } = args;
41
+ if (!attachmentId) {
42
+ throw new Error('attachmentId is required');
43
+ }
44
+ // Fetch metadata to get filename and MIME type
45
+ const metadata = await this.apiClient.get(`/attachment/${attachmentId}`);
46
+ const filename = metadata.filename || 'unknown';
47
+ const mimeType = args.mimeType || metadata.mimeType || 'application/octet-stream';
48
+ const sizeBytes = metadata.size || 0;
49
+ const sizeKB = (sizeBytes / 1024).toFixed(1);
50
+ // Download the actual file bytes
51
+ const { data, contentType } = await this.apiClient.downloadAttachment(attachmentId);
52
+ const effectiveMime = contentType.split(';')[0].trim() || mimeType;
53
+ if (effectiveMime.startsWith('text/') || effectiveMime === 'application/json' || effectiveMime === 'application/xml') {
54
+ const text = data.toString('utf-8');
55
+ return {
56
+ content: [
57
+ {
58
+ type: 'text',
59
+ text: `**Attachment**: ${filename} (${effectiveMime}, ${sizeKB} KB)\n\n${text}`,
60
+ },
61
+ ],
62
+ };
63
+ }
64
+ if (effectiveMime.startsWith('image/')) {
65
+ const base64String = data.toString('base64');
66
+ return {
67
+ content: [
68
+ {
69
+ type: 'text',
70
+ text: `**Attachment**: ${filename} (${effectiveMime}, ${sizeKB} KB)`,
71
+ },
72
+ {
73
+ type: 'image',
74
+ data: base64String,
75
+ mimeType: effectiveMime,
76
+ },
77
+ ],
78
+ };
79
+ }
80
+ // Unsupported type (PDF, zip, etc.)
81
+ return {
82
+ content: [
83
+ {
84
+ type: 'text',
85
+ text: `**Attachment**: ${filename}\n**Type**: ${effectiveMime}\n**Size**: ${sizeKB} KB\n\nThis attachment cannot be displayed as text or image. Download it directly from Jira.`,
86
+ },
87
+ ],
88
+ };
89
+ }
90
+ catch (error) {
91
+ return {
92
+ content: [
93
+ {
94
+ type: 'text',
95
+ text: JiraFormatters.formatError(error),
96
+ },
97
+ ],
98
+ isError: true,
99
+ };
100
+ }
101
+ }
102
+ async handleUploadAttachment(args) {
103
+ try {
104
+ const { issueKey, filePath, fileName } = args;
105
+ if (!issueKey) {
106
+ throw new Error('issueKey is required');
107
+ }
108
+ if (!filePath) {
109
+ throw new Error('filePath is required');
110
+ }
111
+ if (!existsSync(filePath)) {
112
+ throw new Error(`File not found: ${filePath}`);
113
+ }
114
+ const name = fileName || basename(filePath);
115
+ const result = await this.apiClient.uploadAttachment(issueKey, filePath, name);
116
+ const attachment = result[0];
117
+ const sizeKB = attachment.size ? (attachment.size / 1024).toFixed(1) : 'unknown';
118
+ return {
119
+ content: [
120
+ {
121
+ type: 'text',
122
+ text: `Attachment uploaded successfully to ${issueKey}.\n\n**ID**: ${attachment.id}\n**Filename**: ${attachment.filename}\n**Size**: ${sizeKB} KB\n**MIME type**: ${attachment.mimeType || 'unknown'}`,
123
+ },
124
+ ],
125
+ };
126
+ }
127
+ catch (error) {
128
+ return {
129
+ content: [
130
+ {
131
+ type: 'text',
132
+ text: JiraFormatters.formatError(error),
133
+ },
134
+ ],
135
+ isError: true,
136
+ };
137
+ }
138
+ }
139
+ async handleDeleteAttachment(args) {
140
+ try {
141
+ const { attachmentId } = args;
142
+ if (!attachmentId) {
143
+ throw new Error('attachmentId is required');
144
+ }
145
+ try {
146
+ await this.apiClient.delete(`/attachment/${attachmentId}`);
147
+ }
148
+ catch (err) {
149
+ if (err.message?.includes('(404)')) {
150
+ throw new Error(`Attachment not found: ${attachmentId}`);
151
+ }
152
+ throw err;
153
+ }
154
+ return {
155
+ content: [
156
+ {
157
+ type: 'text',
158
+ text: `Attachment ${attachmentId} deleted successfully.`,
159
+ },
160
+ ],
161
+ };
162
+ }
163
+ catch (error) {
164
+ return {
165
+ content: [
166
+ {
167
+ type: 'text',
168
+ text: JiraFormatters.formatError(error),
169
+ },
170
+ ],
171
+ isError: true,
172
+ };
173
+ }
174
+ }
175
+ }
176
+ //# sourceMappingURL=attachment-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment-handlers.js","sourceRoot":"","sources":["../../src/handlers/attachment-handlers.ts"],"names":[],"mappings":"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"}
package/build/index.js CHANGED
@@ -10,6 +10,7 @@ import { TransitionHandlers } from './handlers/transition-handlers.js';
10
10
  import { ProjectHandlers } from './handlers/project-handlers.js';
11
11
  import { MetadataHandlers } from './handlers/metadata-handlers.js';
12
12
  import { UserHandlers } from './handlers/user-handlers.js';
13
+ import { AttachmentHandlers } from './handlers/attachment-handlers.js';
13
14
  import { toolDefinitions } from './tools/definitions.js';
14
15
  // Get environment variables
15
16
  const JIRA_EMAIL = process.env.JIRA_EMAIL;
@@ -36,6 +37,7 @@ class JiraMCPServer {
36
37
  projectHandlers;
37
38
  metadataHandlers;
38
39
  userHandlers;
40
+ attachmentHandlers;
39
41
  constructor() {
40
42
  this.server = new Server({
41
43
  name: 'jira-mcp-server',
@@ -55,6 +57,7 @@ class JiraMCPServer {
55
57
  this.transitionHandlers = new TransitionHandlers(this.apiClient);
56
58
  this.projectHandlers = new ProjectHandlers(this.apiClient);
57
59
  this.metadataHandlers = new MetadataHandlers(this.apiClient);
60
+ this.attachmentHandlers = new AttachmentHandlers(this.apiClient);
58
61
  this.setupToolHandlers();
59
62
  // Error handling
60
63
  this.server.onerror = (error) => console.error('[MCP Error]', error);
@@ -71,36 +74,52 @@ class JiraMCPServer {
71
74
  // Handle tool calls
72
75
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
73
76
  switch (request.params.name) {
74
- // Issue Management tools
75
- case 'get_issue':
76
- return this.issueHandlers.handleGetIssue(request.params.arguments);
77
- case 'create_issue':
78
- return this.issueHandlers.handleCreateIssue(request.params.arguments);
79
- case 'update_issue':
80
- return this.issueHandlers.handleUpdateIssue(request.params.arguments);
81
- case 'assign_issue':
82
- return this.issueHandlers.handleAssignIssue(request.params.arguments);
83
- // Search tools
84
- case 'search_issues':
85
- return this.searchHandlers.handleSearchIssues(request.params.arguments);
86
- case 'list_projects':
87
- return this.projectHandlers.handleListProjects(request.params.arguments);
88
- // Metadata tools
89
- case 'get_create_metadata':
90
- return this.metadataHandlers.handleGetCreateMetadata(request.params.arguments);
91
- // User tools
92
- case 'search_users':
93
- return this.userHandlers.handleSearchUsers(request.params.arguments);
94
- // Comment tools
95
- case 'add_comment':
96
- return this.commentHandlers.handleAddComment(request.params.arguments);
97
- case 'get_comments':
98
- return this.commentHandlers.handleGetComments(request.params.arguments);
99
- // Transition tools
100
- case 'get_transitions':
101
- return this.transitionHandlers.handleGetTransitions(request.params.arguments);
102
- case 'transition_issue':
103
- return this.transitionHandlers.handleTransitionIssue(request.params.arguments);
77
+ case 'jira_issues': {
78
+ const args = request.params.arguments;
79
+ switch (args.action) {
80
+ case 'get': return this.issueHandlers.handleGetIssue(args);
81
+ case 'create': return this.issueHandlers.handleCreateIssue(args);
82
+ case 'update': return this.issueHandlers.handleUpdateIssue(args);
83
+ case 'assign': return this.issueHandlers.handleAssignIssue(args);
84
+ default: throw new McpError(ErrorCode.InvalidParams, `Unknown action: ${args.action}`);
85
+ }
86
+ }
87
+ case 'jira_search': {
88
+ const args = request.params.arguments;
89
+ switch (args.action) {
90
+ case 'issues': return this.searchHandlers.handleSearchIssues(args);
91
+ case 'projects': return this.projectHandlers.handleListProjects(args);
92
+ case 'users': return this.userHandlers.handleSearchUsers(args);
93
+ case 'create_metadata': return this.metadataHandlers.handleGetCreateMetadata(args);
94
+ default: throw new McpError(ErrorCode.InvalidParams, `Unknown action: ${args.action}`);
95
+ }
96
+ }
97
+ case 'jira_comments': {
98
+ const args = request.params.arguments;
99
+ switch (args.action) {
100
+ case 'get': return this.commentHandlers.handleGetComments(args);
101
+ case 'add': return this.commentHandlers.handleAddComment(args);
102
+ default: throw new McpError(ErrorCode.InvalidParams, `Unknown action: ${args.action}`);
103
+ }
104
+ }
105
+ case 'jira_workflow': {
106
+ const args = request.params.arguments;
107
+ switch (args.action) {
108
+ case 'get_transitions': return this.transitionHandlers.handleGetTransitions(args);
109
+ case 'transition': return this.transitionHandlers.handleTransitionIssue(args);
110
+ default: throw new McpError(ErrorCode.InvalidParams, `Unknown action: ${args.action}`);
111
+ }
112
+ }
113
+ case 'jira_attachments': {
114
+ const args = request.params.arguments;
115
+ switch (args.action) {
116
+ case 'list': return this.attachmentHandlers.handleListAttachments(args);
117
+ case 'get_content': return this.attachmentHandlers.handleGetAttachmentContent(args);
118
+ case 'upload': return this.attachmentHandlers.handleUploadAttachment(args);
119
+ case 'delete': return this.attachmentHandlers.handleDeleteAttachment(args);
120
+ default: throw new McpError(ErrorCode.InvalidParams, `Unknown action: ${args.action}`);
121
+ }
122
+ }
104
123
  default:
105
124
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
106
125
  }