@stubbedev/atlassian-mcp 0.3.3 → 0.3.4
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 +6 -1
- package/dist/index.js +29 -2
- package/dist/jira.js +92 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,11 +27,12 @@ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for **s
|
|
|
27
27
|
|
|
28
28
|
| Tool | Description |
|
|
29
29
|
|---|---|
|
|
30
|
-
| `jira_search` | Discover resources: `issues`, `projects`, `issue_types`, `boards`, `sprints`, `board_overview`, or `users` via `resource` param |
|
|
30
|
+
| `jira_search` | Discover resources: `issues`, `projects`, `issue_types`, `boards`, `sprints`, `board_overview`, `versions`, or `users` via `resource` param |
|
|
31
31
|
| `jira_get` | Full details for one issue: summary, description, status, sprint, transitions, comments, and attachment list |
|
|
32
32
|
| `jira_get_attachment` | Fetch a Jira attachment by ID; images are auto-resized via sharp and returned inline so the model can see them, text/JSON inline, larger/binary files via `saveTo` |
|
|
33
33
|
| `jira_mutate` | Create, update, transition, comment, link, add to sprint, or log work — all in one call |
|
|
34
34
|
| `jira_comment` | Add, update, or delete a comment on an issue (`action`: `add` / `update` / `delete`) |
|
|
35
|
+
| `jira_version` | Manage fix versions/releases (`action`: `create` / `update` / `release` / `archive` / `delete`) |
|
|
35
36
|
|
|
36
37
|
### Bitbucket
|
|
37
38
|
|
|
@@ -62,6 +63,10 @@ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for **s
|
|
|
62
63
|
- "what's in the current sprint?" → `jira_search` with `resource=board_overview`
|
|
63
64
|
- "move FOO-123 to In Progress" → `jira_mutate` with `transitionName="In Progress"`
|
|
64
65
|
- "log 2h on FOO-123" → `jira_mutate` with `worklog`
|
|
66
|
+
- "create version 9.1.0 in PAY" → `jira_version` with `action=create`, `projectKey=PAY`, `name=9.1.0`
|
|
67
|
+
- "list releases for PAY" → `jira_search` with `resource=versions`, `project=PAY`
|
|
68
|
+
- "release version 12345" → `jira_version` with `action=release`, `id=12345`
|
|
69
|
+
- "set fix version 9.1.0 on FOO-123" → `jira_mutate` with `update.fixVersion=9.1.0`
|
|
65
70
|
|
|
66
71
|
---
|
|
67
72
|
|
package/dist/index.js
CHANGED
|
@@ -206,11 +206,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
206
206
|
},
|
|
207
207
|
{
|
|
208
208
|
name: 'jira_search',
|
|
209
|
-
description: 'Discover Jira resources. Use when asked "find tickets for...", "what\'s in the backlog", "show me my issues", "list projects", or "which board is for project X". Set resource:\n• "issues" (default) — search by text, JQL, project, status, assignee, issue type, or mine=true for your queue\n• "projects" — list all projects and their keys\n• "issue_types" — valid types and statuses for a project\n• "boards" — list boards (pass project to filter by project key); use this to find the boardId before fetching sprints or board_overview\n• "sprints" — sprints for a board (pass boardId); if you don\'t know the boardId, first use resource=boards\n• "board_overview" — active/future sprints with their issues for a board (pass boardId); use when asked "what\'s in the sprint", "show me the board", or "what\'s everyone working on"\n• "users" — find users by name/email (pass query)',
|
|
209
|
+
description: 'Discover Jira resources. Use when asked "find tickets for...", "what\'s in the backlog", "show me my issues", "list projects", or "which board is for project X". Set resource:\n• "issues" (default) — search by text, JQL, project, status, assignee, issue type, or mine=true for your queue\n• "projects" — list all projects and their keys\n• "issue_types" — valid types and statuses for a project\n• "boards" — list boards (pass project to filter by project key); use this to find the boardId before fetching sprints or board_overview\n• "sprints" — sprints for a board (pass boardId); if you don\'t know the boardId, first use resource=boards\n• "board_overview" — active/future sprints with their issues for a board (pass boardId); use when asked "what\'s in the sprint", "show me the board", or "what\'s everyone working on"\n• "versions" — list fix versions/releases for a project (pass project); use this to find the exact version name or id before setting fixVersion or releasing a version\n• "users" — find users by name/email (pass query)',
|
|
210
210
|
inputSchema: {
|
|
211
211
|
type: 'object',
|
|
212
212
|
properties: {
|
|
213
|
-
resource: { type: 'string', enum: ['issues', 'projects', 'issue_types', 'boards', 'sprints', 'board_overview', 'users'], description: 'What to search (default: issues)' },
|
|
213
|
+
resource: { type: 'string', enum: ['issues', 'projects', 'issue_types', 'boards', 'sprints', 'board_overview', 'versions', 'users'], description: 'What to search (default: issues)' },
|
|
214
214
|
mine: { type: 'boolean', description: 'Return issues assigned to you (resource=issues only)' },
|
|
215
215
|
query: { type: 'string', description: 'Text search or user name query' },
|
|
216
216
|
jql: { type: 'string', description: 'Raw JQL (resource=issues only, overrides other filters)' },
|
|
@@ -331,6 +331,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
331
331
|
},
|
|
332
332
|
required: ['issueKey'],
|
|
333
333
|
},
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: 'jira_version',
|
|
337
|
+
description: 'Manage Jira fix versions (releases). Use when asked to "create version 9.1.0", "release version X", "archive version X", "rename a version", or "delete a version". action defaults to "create". For create pass projectKey + name. For update/release/archive/delete pass id (look it up via jira_search resource=versions). "release" sets released=true and defaults releaseDate to today. Once a version exists you can set it on tickets via jira_mutate update.fixVersion.',
|
|
338
|
+
inputSchema: {
|
|
339
|
+
type: 'object',
|
|
340
|
+
properties: {
|
|
341
|
+
action: { type: 'string', enum: ['create', 'update', 'release', 'archive', 'delete'], description: 'Operation (default: create)' },
|
|
342
|
+
projectKey: { type: 'string', description: 'Jira project code (required for create when not auto-resolvable)' },
|
|
343
|
+
project: { type: 'string', description: 'Alias for projectKey' },
|
|
344
|
+
id: { type: 'string', description: 'Version id (required for update/release/archive/delete; look up via jira_search resource=versions)' },
|
|
345
|
+
name: { type: 'string', description: 'Version name, e.g. "9.1.0" (required for create; optional rename for update)' },
|
|
346
|
+
description: { type: 'string', description: 'Version description (optional)' },
|
|
347
|
+
startDate: { type: 'string', description: 'Start date in YYYY-MM-DD (optional)' },
|
|
348
|
+
releaseDate: { type: 'string', description: 'Release date in YYYY-MM-DD (optional; defaults to today on action=release)' },
|
|
349
|
+
released: { type: 'boolean', description: 'Released flag (optional; action=release forces true)' },
|
|
350
|
+
archived: { type: 'boolean', description: 'Archived flag (optional; action=archive forces true)' },
|
|
351
|
+
},
|
|
352
|
+
},
|
|
334
353
|
}
|
|
335
354
|
] : []),
|
|
336
355
|
...(bitbucket ? [{
|
|
@@ -758,6 +777,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
758
777
|
return await jira.getSprints({ boardId: a.boardId, state: a.sprintState, maxResults: a.maxResults, startAt: a.startAt });
|
|
759
778
|
if (resource === 'board_overview')
|
|
760
779
|
return await jira.boardOverview({ boardId: a.boardId, sprintState: a.sprintState, sprintMaxResults: a.maxResults, sprintStartAt: a.startAt, includeIssues: a.includeIssues, assignee: a.assignee, status: a.status });
|
|
780
|
+
if (resource === 'versions')
|
|
781
|
+
return await jira.listVersions({ projectKey: a.projectKey ?? a.project, maxResults: a.maxResults });
|
|
761
782
|
if (resource === 'users')
|
|
762
783
|
return await jira.searchUsers({ query: a.query ?? '', maxResults: a.maxResults });
|
|
763
784
|
// issues (default)
|
|
@@ -790,6 +811,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
790
811
|
return await jira.deleteComment({ issueKey: a.issueKey, commentId: a.commentId });
|
|
791
812
|
return await jira.addComment({ issueKey: a.issueKey, body: a.body });
|
|
792
813
|
}
|
|
814
|
+
case 'jira_version': {
|
|
815
|
+
if (!jira)
|
|
816
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
817
|
+
const a = normalizeJiraProjectArgs(args);
|
|
818
|
+
return await jira.mutateVersion(a);
|
|
819
|
+
}
|
|
793
820
|
// Bitbucket
|
|
794
821
|
case 'bitbucket_search': {
|
|
795
822
|
if (!bitbucket)
|
package/dist/jira.js
CHANGED
|
@@ -761,4 +761,96 @@ export class JiraClient {
|
|
|
761
761
|
await this.transitionIssueInternal(args.issueKey, transitionId);
|
|
762
762
|
return text(`Transitioned ${args.issueKey} using transition ${transitionId}.\n${this.issueUrl(args.issueKey)}`);
|
|
763
763
|
}
|
|
764
|
+
async listVersions(args) {
|
|
765
|
+
const projectKey = await this.resolveProjectKey(args.projectKey);
|
|
766
|
+
const data = await this.request('GET', `/project/${encodeURIComponent(projectKey)}/versions`);
|
|
767
|
+
if (!data || data.length === 0)
|
|
768
|
+
return text(`No versions in ${projectKey}.`);
|
|
769
|
+
const sorted = [...data].sort((a, b) => {
|
|
770
|
+
if (a.released !== b.released)
|
|
771
|
+
return a.released ? 1 : -1;
|
|
772
|
+
if (a.archived !== b.archived)
|
|
773
|
+
return a.archived ? 1 : -1;
|
|
774
|
+
const ad = a.releaseDate ?? '';
|
|
775
|
+
const bd = b.releaseDate ?? '';
|
|
776
|
+
return bd.localeCompare(ad);
|
|
777
|
+
});
|
|
778
|
+
const limit = args.maxResults ?? sorted.length;
|
|
779
|
+
const shown = sorted.slice(0, limit);
|
|
780
|
+
const lines = shown.map((v, i) => {
|
|
781
|
+
const tags = [];
|
|
782
|
+
if (v.released)
|
|
783
|
+
tags.push('released');
|
|
784
|
+
if (v.archived)
|
|
785
|
+
tags.push('archived');
|
|
786
|
+
const tagStr = tags.length ? ` [${tags.join(', ')}]` : '';
|
|
787
|
+
const dateParts = [v.startDate ? `start ${v.startDate}` : '', v.releaseDate ? `release ${v.releaseDate}` : ''].filter(Boolean);
|
|
788
|
+
const dateStr = dateParts.length ? ` (${dateParts.join(', ')})` : '';
|
|
789
|
+
return `${i + 1}. [${v.id}] ${v.name}${tagStr}${dateStr}`;
|
|
790
|
+
});
|
|
791
|
+
const more = data.length > shown.length ? `\n...and ${data.length - shown.length} more (raise maxResults).` : '';
|
|
792
|
+
return text(`${data.length} version(s) in ${projectKey}:\n${lines.join('\n')}${more}`);
|
|
793
|
+
}
|
|
794
|
+
async mutateVersion(args) {
|
|
795
|
+
const action = args.action ?? 'create';
|
|
796
|
+
if (action === 'create') {
|
|
797
|
+
const projectKey = await this.resolveProjectKey(args.projectKey);
|
|
798
|
+
const name = args.name?.trim();
|
|
799
|
+
if (!name)
|
|
800
|
+
throw new Error('name is required to create a version.');
|
|
801
|
+
const body = { project: projectKey, name };
|
|
802
|
+
if (args.description !== undefined)
|
|
803
|
+
body.description = args.description;
|
|
804
|
+
if (args.releaseDate)
|
|
805
|
+
body.releaseDate = args.releaseDate;
|
|
806
|
+
if (args.startDate)
|
|
807
|
+
body.startDate = args.startDate;
|
|
808
|
+
if (args.released !== undefined)
|
|
809
|
+
body.released = args.released;
|
|
810
|
+
if (args.archived !== undefined)
|
|
811
|
+
body.archived = args.archived;
|
|
812
|
+
const created = await this.request('POST', '/version', body);
|
|
813
|
+
if (!created)
|
|
814
|
+
throw new Error('Jira returned no body when creating version.');
|
|
815
|
+
return text(`Created version [${created.id}] ${created.name} in ${projectKey}.`);
|
|
816
|
+
}
|
|
817
|
+
const id = args.id?.trim();
|
|
818
|
+
if (!id)
|
|
819
|
+
throw new Error(`version id is required for action=${action}.`);
|
|
820
|
+
if (action === 'delete') {
|
|
821
|
+
await this.request('DELETE', `/version/${encodeURIComponent(id)}`);
|
|
822
|
+
return text(`Deleted version ${id}.`);
|
|
823
|
+
}
|
|
824
|
+
const body = {};
|
|
825
|
+
if (args.name !== undefined)
|
|
826
|
+
body.name = args.name;
|
|
827
|
+
if (args.description !== undefined)
|
|
828
|
+
body.description = args.description;
|
|
829
|
+
if (args.startDate !== undefined)
|
|
830
|
+
body.startDate = args.startDate;
|
|
831
|
+
if (args.releaseDate !== undefined)
|
|
832
|
+
body.releaseDate = args.releaseDate;
|
|
833
|
+
if (args.released !== undefined)
|
|
834
|
+
body.released = args.released;
|
|
835
|
+
if (args.archived !== undefined)
|
|
836
|
+
body.archived = args.archived;
|
|
837
|
+
if (action === 'release') {
|
|
838
|
+
body.released = true;
|
|
839
|
+
if (body.releaseDate === undefined)
|
|
840
|
+
body.releaseDate = new Date().toISOString().slice(0, 10);
|
|
841
|
+
}
|
|
842
|
+
else if (action === 'archive') {
|
|
843
|
+
body.archived = true;
|
|
844
|
+
}
|
|
845
|
+
if (Object.keys(body).length === 0) {
|
|
846
|
+
throw new Error('Nothing to update.');
|
|
847
|
+
}
|
|
848
|
+
const updated = await this.request('PUT', `/version/${encodeURIComponent(id)}`, body);
|
|
849
|
+
const label = updated ? `[${updated.id}] ${updated.name}` : id;
|
|
850
|
+
if (action === 'release')
|
|
851
|
+
return text(`Released version ${label} on ${body.releaseDate}.`);
|
|
852
|
+
if (action === 'archive')
|
|
853
|
+
return text(`Archived version ${label}.`);
|
|
854
|
+
return text(`Updated version ${label}.`);
|
|
855
|
+
}
|
|
764
856
|
}
|