@ecubelabs/atlassian-mcp 1.15.3-next.1 → 1.16.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.
@@ -1,6 +1,15 @@
1
1
  import { z } from 'zod';
2
2
  import { JiraService } from './libs/jira-client.js';
3
3
  import { formatToolResponse, saveBinaryToTempFile } from './libs/response-formatter.js';
4
+ const toDefaultAssignee = (user) => user
5
+ ? {
6
+ accountId: user.accountId,
7
+ displayName: user.displayName,
8
+ emailAddress: user.emailAddress,
9
+ active: user.active,
10
+ }
11
+ : null;
12
+ const describeDefaultAssignee = (assignee, assigneeType) => assignee ? `${assignee.displayName} (${assignee.accountId})` : `none (assigneeType: ${assigneeType ?? 'unknown'})`;
4
13
  export const registerJiraTools = (server) => {
5
14
  // Initialize Jira service
6
15
  const jiraService = new JiraService();
@@ -318,6 +327,76 @@ export const registerJiraTools = (server) => {
318
327
  };
319
328
  }
320
329
  });
330
+ server.tool('get-project', 'Get a single Jira project, including its default assignee configuration. ' +
331
+ 'Unlike get-projects (which uses the project search endpoint and omits it), this returns "assigneeType". ' +
332
+ 'The "defaultAssignee" field is derived by this tool: the project lead when assigneeType is PROJECT_LEAD, ' +
333
+ 'and null when it is UNASSIGNED. For component-level defaults use get-project-components.', {
334
+ projectIdOrKey: z.string().describe('Project ID or key (e.g. PROJ or 10001)'),
335
+ expand: z
336
+ .array(z.enum(['description', 'issueTypes', 'lead', 'projectKeys', 'issueTypeHierarchy']))
337
+ .optional()
338
+ .describe('Additional fields to include: description, issueTypes, lead, projectKeys, issueTypeHierarchy'),
339
+ properties: z.array(z.string()).optional().describe('Project property keys to include in the response'),
340
+ }, async ({ projectIdOrKey, expand, properties }) => {
341
+ try {
342
+ const project = await jiraService.getProject(projectIdOrKey, { expand, properties });
343
+ const defaultAssignee = project.assigneeType === 'PROJECT_LEAD' ? toDefaultAssignee(project.lead) : null;
344
+ return formatToolResponse({ ...project, defaultAssignee }, {
345
+ toolName: 'get-project',
346
+ summary: `Project ${project.key}: ${project.name} | Default assignee: ${describeDefaultAssignee(defaultAssignee, project.assigneeType)}`,
347
+ });
348
+ }
349
+ catch (error) {
350
+ return {
351
+ content: [
352
+ {
353
+ type: 'text',
354
+ text: `Failed to retrieve project: ${error instanceof Error ? error.message : String(error)}`,
355
+ },
356
+ ],
357
+ };
358
+ }
359
+ });
360
+ server.tool('get-project-components', 'Get the components of a Jira project together with their default assignee. Each entry exposes a ' +
361
+ '"defaultAssignee" derived from Jira\'s realAssignee, which already resolves PROJECT_DEFAULT down to the ' +
362
+ 'project-level setting; isAssigneeTypeValid is false when the configured assigneeType cannot be applied. ' +
363
+ 'Set includeRaw to true to get the full unmodified API objects instead of the slim view.', {
364
+ projectIdOrKey: z.string().describe('Project ID or key (e.g. PROJ or 10001)'),
365
+ includeRaw: z
366
+ .boolean()
367
+ .optional()
368
+ .describe('Return the full component objects from the API instead of the slim default-assignee view. Default: false'),
369
+ }, async ({ projectIdOrKey, includeRaw }) => {
370
+ try {
371
+ const components = await jiraService.getProjectComponents(projectIdOrKey);
372
+ const invalidCount = components.filter((component) => component.isAssigneeTypeValid === false).length;
373
+ const summary = `${components.length} component(s) in ${projectIdOrKey}` +
374
+ (invalidCount > 0 ? ` | ${invalidCount} with an invalid assigneeType` : '');
375
+ if (includeRaw) {
376
+ return formatToolResponse(components, { toolName: 'get-project-components', summary });
377
+ }
378
+ const slimComponents = components.map((component) => ({
379
+ id: component.id,
380
+ name: component.name,
381
+ description: component.description,
382
+ assigneeType: component.assigneeType,
383
+ realAssigneeType: component.realAssigneeType,
384
+ isAssigneeTypeValid: component.isAssigneeTypeValid,
385
+ defaultAssignee: toDefaultAssignee(component.realAssignee),
386
+ }));
387
+ return formatToolResponse(slimComponents, { toolName: 'get-project-components', summary });
388
+ }
389
+ catch (error) {
390
+ return {
391
+ content: [
392
+ {
393
+ type: 'text',
394
+ text: `Failed to retrieve project components: ${error instanceof Error ? error.message : String(error)}`,
395
+ },
396
+ ],
397
+ };
398
+ }
399
+ });
321
400
  server.tool('get-create-metadata-issue-types', 'Get issue types available for creating issues in a project', {
322
401
  projectIdOrKey: z.string().describe('Project ID (numeric) or project key (e.g. PROJ)'),
323
402
  }, async ({ projectIdOrKey }) => {
@@ -114,6 +114,25 @@ export class JiraService extends BaseApiService {
114
114
  }
115
115
  return this.makeRequest(() => this.client.get('/project/search', { params }));
116
116
  }
117
+ /**
118
+ * 프로젝트 단건 조회 (기본 담당자 설정인 assigneeType, lead 포함)
119
+ * @see https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-projects/#api-rest-api-3-project-projectidorkey-get
120
+ */
121
+ async getProject(projectIdOrKey, options) {
122
+ const params = {};
123
+ if (options?.expand)
124
+ params.expand = options.expand.join(',');
125
+ if (options?.properties)
126
+ params.properties = options.properties.join(',');
127
+ return this.makeRequest(() => this.client.get(`/project/${projectIdOrKey}`, { params }));
128
+ }
129
+ /**
130
+ * 프로젝트 컴포넌트 목록 조회 (컴포넌트별 기본 담당자인 realAssignee 포함)
131
+ * @see https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-components/#api-rest-api-3-project-projectidorkey-components-get
132
+ */
133
+ async getProjectComponents(projectIdOrKey) {
134
+ return this.makeRequest(() => this.client.get(`/project/${projectIdOrKey}/components`));
135
+ }
117
136
  /**
118
137
  * 이슈 댓글 목록 조회
119
138
  * @see https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-get
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecubelabs/atlassian-mcp",
3
- "version": "1.15.3-next.1",
3
+ "version": "1.16.0",
4
4
  "bin": "./dist/index.js",
5
5
  "repository": {
6
6
  "url": "https://github.com/Ecube-Labs/skynet.git"
@@ -38,6 +38,5 @@
38
38
  "semantic-release-yarn": "^3.0.2",
39
39
  "ts-node": "^10.9.2",
40
40
  "typescript": "^5.9.3"
41
- },
42
- "stableVersion": "1.0.0"
41
+ }
43
42
  }