@stubbedev/atlassian-mcp 0.1.19 → 0.2.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/dist/bitbucket.js +171 -6
- package/dist/config.js +24 -17
- package/dist/context.js +35 -7
- package/dist/git.js +106 -3
- package/dist/index.js +631 -741
- package/dist/jira.js +72 -1
- package/package.json +12 -7
package/dist/jira.js
CHANGED
|
@@ -180,6 +180,12 @@ export class JiraClient {
|
|
|
180
180
|
fields.assignee = { name: args.assignee };
|
|
181
181
|
if (args.priority)
|
|
182
182
|
fields.priority = { name: args.priority };
|
|
183
|
+
if (args.labels?.length)
|
|
184
|
+
fields.labels = args.labels;
|
|
185
|
+
if (args.fixVersion)
|
|
186
|
+
fields.fixVersions = [{ name: args.fixVersion }];
|
|
187
|
+
if (args.parent)
|
|
188
|
+
fields.parent = { key: args.parent };
|
|
183
189
|
return this.request('POST', '/issue', { fields });
|
|
184
190
|
}
|
|
185
191
|
async updateIssueFieldsInternal(args) {
|
|
@@ -192,6 +198,10 @@ export class JiraClient {
|
|
|
192
198
|
fields.assignee = { name: args.assignee };
|
|
193
199
|
if (args.priority !== undefined)
|
|
194
200
|
fields.priority = { name: args.priority };
|
|
201
|
+
if (args.labels !== undefined)
|
|
202
|
+
fields.labels = args.labels;
|
|
203
|
+
if (args.fixVersion !== undefined)
|
|
204
|
+
fields.fixVersions = args.fixVersion ? [{ name: args.fixVersion }] : [];
|
|
195
205
|
if (Object.keys(fields).length === 0)
|
|
196
206
|
return false;
|
|
197
207
|
await this.request('PUT', `/issue/${args.issueKey}`, { fields });
|
|
@@ -319,6 +329,16 @@ export class JiraClient {
|
|
|
319
329
|
.map((u, i) => `${i + 1}. ${u.displayName} (${u.name}) — ${u.emailAddress}`);
|
|
320
330
|
return text(`${lines.length} user(s) found:\n${lines.join('\n')}`);
|
|
321
331
|
}
|
|
332
|
+
async getIssueFields(issueKey) {
|
|
333
|
+
const data = await this.request('GET', `/issue/${issueKey}?fields=summary,status,issuetype`);
|
|
334
|
+
if (!data)
|
|
335
|
+
throw new Error(`Issue ${issueKey} not found`);
|
|
336
|
+
return {
|
|
337
|
+
summary: data.fields.summary,
|
|
338
|
+
status: data.fields.status.name,
|
|
339
|
+
type: data.fields.issuetype.name,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
322
342
|
async getIssue(args) {
|
|
323
343
|
const fields = 'summary,description,status,assignee,priority,issuetype,labels,components';
|
|
324
344
|
const data = await this.request('GET', `/issue/${args.issueKey}?fields=${fields}`);
|
|
@@ -346,7 +366,7 @@ export class JiraClient {
|
|
|
346
366
|
const includeSprint = args.includeSprint ?? true;
|
|
347
367
|
const commentsMaxResults = args.commentsMaxResults ?? 10;
|
|
348
368
|
const commentsStartAt = args.commentsStartAt ?? 0;
|
|
349
|
-
const fields = 'summary,description,status,assignee,priority,issuetype,labels,components';
|
|
369
|
+
const fields = 'summary,description,status,assignee,priority,issuetype,labels,components,parent,fixVersions,issuelinks,subtasks';
|
|
350
370
|
const issue = await this.request('GET', `/issue/${args.issueKey}?fields=${fields}`);
|
|
351
371
|
if (!issue)
|
|
352
372
|
return text('Issue not found.');
|
|
@@ -360,6 +380,18 @@ export class JiraClient {
|
|
|
360
380
|
`Assignee: ${f.assignee?.displayName ?? 'Unassigned'}`,
|
|
361
381
|
`Labels: ${f.labels?.join(', ') || 'None'}`,
|
|
362
382
|
`Components: ${f.components?.map((c) => c.name).join(', ') || 'None'}`,
|
|
383
|
+
...(f.parent ? [`Parent: [${f.parent.key}] ${f.parent.fields.summary} (${f.parent.fields.issuetype.name})`] : []),
|
|
384
|
+
...(f.fixVersions?.length ? [`Fix Vers: ${f.fixVersions.map((v) => v.name).join(', ')}`] : []),
|
|
385
|
+
...(f.subtasks?.length ? [`Subtasks: ${f.subtasks.map((s) => `[${s.key}] ${s.fields.summary} (${s.fields.status.name})`).join(', ')}`] : []),
|
|
386
|
+
...(f.issuelinks?.length ? [
|
|
387
|
+
`Links: ${f.issuelinks.map((l) => {
|
|
388
|
+
if (l.outwardIssue)
|
|
389
|
+
return `${l.type.outward} → [${l.outwardIssue.key}] ${l.outwardIssue.fields.summary}`;
|
|
390
|
+
if (l.inwardIssue)
|
|
391
|
+
return `${l.type.inward} ← [${l.inwardIssue.key}] ${l.inwardIssue.fields.summary}`;
|
|
392
|
+
return l.type.name;
|
|
393
|
+
}).join('; ')}`,
|
|
394
|
+
] : []),
|
|
363
395
|
];
|
|
364
396
|
if (includeSprint) {
|
|
365
397
|
try {
|
|
@@ -550,6 +582,10 @@ export class JiraClient {
|
|
|
550
582
|
await this.addIssuesToSprintInternal(args.sprintId, [issueKey]);
|
|
551
583
|
actions.push(`added to sprint ${args.sprintId}`);
|
|
552
584
|
}
|
|
585
|
+
if (args.removeFromSprint) {
|
|
586
|
+
await this.requestAgile('POST', '/backlog/issue', { issues: [issueKey] });
|
|
587
|
+
actions.push('moved to backlog');
|
|
588
|
+
}
|
|
553
589
|
if (args.transitionId || args.transitionName) {
|
|
554
590
|
const transitionId = await this.resolveTransitionId(issueKey, args.transitionId, args.transitionName);
|
|
555
591
|
await this.transitionIssueInternal(issueKey, transitionId);
|
|
@@ -559,6 +595,24 @@ export class JiraClient {
|
|
|
559
595
|
await this.request('POST', `/issue/${issueKey}/comment`, { body: validateCommentBody(args.comment) });
|
|
560
596
|
actions.push('added comment');
|
|
561
597
|
}
|
|
598
|
+
if (args.link) {
|
|
599
|
+
const dir = args.link.direction ?? 'outward';
|
|
600
|
+
await this.request('POST', '/issueLink', {
|
|
601
|
+
type: { name: args.link.linkType },
|
|
602
|
+
outwardIssue: { key: dir === 'outward' ? issueKey : args.link.targetIssueKey },
|
|
603
|
+
inwardIssue: { key: dir === 'outward' ? args.link.targetIssueKey : issueKey },
|
|
604
|
+
});
|
|
605
|
+
actions.push(`linked ${args.link.linkType} → ${args.link.targetIssueKey}`);
|
|
606
|
+
}
|
|
607
|
+
if (args.worklog) {
|
|
608
|
+
const wBody = { timeSpent: args.worklog.timeSpent };
|
|
609
|
+
if (args.worklog.comment)
|
|
610
|
+
wBody.comment = args.worklog.comment;
|
|
611
|
+
if (args.worklog.started)
|
|
612
|
+
wBody.started = args.worklog.started;
|
|
613
|
+
await this.request('POST', `/issue/${issueKey}/worklog`, wBody);
|
|
614
|
+
actions.push(`logged ${args.worklog.timeSpent}`);
|
|
615
|
+
}
|
|
562
616
|
if (actions.length === 0) {
|
|
563
617
|
return text('Nothing to mutate.');
|
|
564
618
|
}
|
|
@@ -606,6 +660,23 @@ export class JiraClient {
|
|
|
606
660
|
await this.request('DELETE', path);
|
|
607
661
|
return text(`Comment ${commentId} deleted from ${args.issueKey}.`);
|
|
608
662
|
}
|
|
663
|
+
async getBoards(args) {
|
|
664
|
+
const params = new URLSearchParams({
|
|
665
|
+
maxResults: String(args.maxResults ?? 25),
|
|
666
|
+
startAt: String(args.startAt ?? 0),
|
|
667
|
+
});
|
|
668
|
+
if (args.projectKey)
|
|
669
|
+
params.set('projectKeyOrId', args.projectKey);
|
|
670
|
+
const data = await this.requestAgile('GET', `/board?${params}`);
|
|
671
|
+
if (!data || data.values.length === 0)
|
|
672
|
+
return text('No boards found.');
|
|
673
|
+
const lines = data.values.map((b, i) => {
|
|
674
|
+
const projectHint = b.location?.projectKey ? ` [${b.location.projectKey}]` : '';
|
|
675
|
+
return `${(args.startAt ?? 0) + i + 1}. [${b.id}] ${b.name} (${b.type})${projectHint} | ${this.boardUrl(b.id)}`;
|
|
676
|
+
});
|
|
677
|
+
const page = data.isLast ? '' : ` (use startAt=${(args.startAt ?? 0) + data.values.length} for next page)`;
|
|
678
|
+
return text(`${data.values.length} board(s)${page}:\n${lines.join('\n')}`);
|
|
679
|
+
}
|
|
609
680
|
async transitionIssue(args) {
|
|
610
681
|
const transitionId = await this.resolveTransitionId(args.issueKey, args.transitionId, args.transitionName);
|
|
611
682
|
await this.transitionIssueInternal(args.issueKey, transitionId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stubbedev/atlassian-mcp",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "MCP server for self-hosted Jira and Bitbucket",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,20 +14,25 @@
|
|
|
14
14
|
"atlassian-mcp": "dist/index.js"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"prepare": "tsc",
|
|
18
17
|
"build": "tsc",
|
|
19
18
|
"start": "node dist/index.js",
|
|
20
|
-
"dev": "
|
|
19
|
+
"dev": "tsx src/index.ts",
|
|
20
|
+
"release:patch": "npm version patch && git push origin && git push origin --tags && gh release create $(git describe --tags) --generate-notes",
|
|
21
|
+
"release:minor": "npm version minor && git push origin && git push origin --tags && gh release create $(git describe --tags) --generate-notes",
|
|
22
|
+
"release:major": "npm version major && git push origin && git push origin --tags && gh release create $(git describe --tags) --generate-notes",
|
|
21
23
|
"smoke": "npm run build && npm run smoke:tools",
|
|
22
|
-
"smoke:tools": "printf '%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}' | node dist/index.js"
|
|
24
|
+
"smoke:tools": "printf '%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}' | node dist/index.js",
|
|
25
|
+
"smoke:validate": "printf '%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}' | node dist/index.js | node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const r=JSON.parse(d.trim());if(r.error)throw new Error('server error: '+r.error.message);const t=r.result?.tools;if(!Array.isArray(t)||!t.length)throw new Error('no tools in response');const bad=t.filter(x=>!x.name||!x.inputSchema);if(bad.length)throw new Error('malformed tools: '+bad.map(x=>x.name??'(unnamed)').join(', '));console.log('smoke OK — '+t.length+' tool(s): '+t.map(x=>x.name).join(', '))}catch(e){console.error('smoke FAIL:',e.message);process.exit(1)}})\"",
|
|
26
|
+
"prerelease": "npm run build && npm run smoke:validate && ncu"
|
|
23
27
|
},
|
|
24
28
|
"dependencies": {
|
|
25
29
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
26
|
-
"dotenv": "^
|
|
30
|
+
"dotenv": "^17.4.2"
|
|
27
31
|
},
|
|
28
32
|
"devDependencies": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
33
|
+
"@types/node": "^25.6.0",
|
|
34
|
+
"npm-check-updates": "^21.0.2",
|
|
35
|
+
"typescript": "^6.0.3"
|
|
31
36
|
},
|
|
32
37
|
"engines": {
|
|
33
38
|
"node": ">=18.0.0"
|