@stubbedev/atlassian-mcp 0.1.5 → 0.1.6
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 +17 -9
- package/dist/jira.js +36 -14
- package/package.json +1 -1
package/dist/bitbucket.js
CHANGED
|
@@ -158,6 +158,13 @@ export class BitbucketClient {
|
|
|
158
158
|
Accept: 'application/json',
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
|
+
pullRequestUrl(projectKey, repoSlug, prId, pr) {
|
|
162
|
+
const apiUrl = pr?.links?.self?.[0]?.href?.trim();
|
|
163
|
+
if (apiUrl) {
|
|
164
|
+
return apiUrl;
|
|
165
|
+
}
|
|
166
|
+
return `${this.baseUrl}/projects/${encodeURIComponent(projectKey)}/repos/${encodeURIComponent(repoSlug)}/pull-requests/${prId}`;
|
|
167
|
+
}
|
|
161
168
|
resolveProjectAndRepo(projectKey, repoSlug) {
|
|
162
169
|
if (projectKey && repoSlug)
|
|
163
170
|
return { projectKey, repoSlug };
|
|
@@ -375,20 +382,21 @@ export class BitbucketClient {
|
|
|
375
382
|
const data = await this.request('POST', `/projects/${projectKey}/repos/${repoSlug}/pull-requests`, body);
|
|
376
383
|
if (!data)
|
|
377
384
|
return text('Pull request created.');
|
|
378
|
-
const url = data.
|
|
379
|
-
return text(`Created PR #${data.id}: "${data.title}"
|
|
385
|
+
const url = this.pullRequestUrl(projectKey, repoSlug, data.id, data);
|
|
386
|
+
return text(`Created PR #${data.id}: "${data.title}"\n${url}`);
|
|
380
387
|
}
|
|
381
388
|
async approvePr(args) {
|
|
382
389
|
const { projectKey, repoSlug } = this.resolveProjectAndRepo(args.projectKey, args.repoSlug);
|
|
383
390
|
const data = await this.request('POST', `/projects/${projectKey}/repos/${repoSlug}/pull-requests/${args.prId}/approve`);
|
|
391
|
+
const url = this.pullRequestUrl(projectKey, repoSlug, args.prId);
|
|
384
392
|
if (!data)
|
|
385
|
-
return text(`Approved PR #${args.prId}
|
|
386
|
-
return text(`Approved PR #${args.prId} as ${data.user.displayName}
|
|
393
|
+
return text(`Approved PR #${args.prId}.\n${url}`);
|
|
394
|
+
return text(`Approved PR #${args.prId} as ${data.user.displayName}.\n${url}`);
|
|
387
395
|
}
|
|
388
396
|
async unapprovePr(args) {
|
|
389
397
|
const { projectKey, repoSlug } = this.resolveProjectAndRepo(args.projectKey, args.repoSlug);
|
|
390
398
|
await this.request('DELETE', `/projects/${projectKey}/repos/${repoSlug}/pull-requests/${args.prId}/approve`);
|
|
391
|
-
return text(`Approval removed from PR #${args.prId}
|
|
399
|
+
return text(`Approval removed from PR #${args.prId}.\n${this.pullRequestUrl(projectKey, repoSlug, args.prId)}`);
|
|
392
400
|
}
|
|
393
401
|
async declinePr(args) {
|
|
394
402
|
const { projectKey, repoSlug } = this.resolveProjectAndRepo(args.projectKey, args.repoSlug);
|
|
@@ -400,8 +408,8 @@ export class BitbucketClient {
|
|
|
400
408
|
body.message = args.message;
|
|
401
409
|
const data = await this.request('POST', `/projects/${projectKey}/repos/${repoSlug}/pull-requests/${args.prId}/decline`, body);
|
|
402
410
|
if (!data)
|
|
403
|
-
return text(`Declined PR #${args.prId}
|
|
404
|
-
return text(`Declined PR #${data.id}: "${data.title}"
|
|
411
|
+
return text(`Declined PR #${args.prId}.\n${this.pullRequestUrl(projectKey, repoSlug, args.prId)}`);
|
|
412
|
+
return text(`Declined PR #${data.id}: "${data.title}".\n${this.pullRequestUrl(projectKey, repoSlug, data.id, data)}`);
|
|
405
413
|
}
|
|
406
414
|
async mergePr(args) {
|
|
407
415
|
const { projectKey, repoSlug } = this.resolveProjectAndRepo(args.projectKey, args.repoSlug);
|
|
@@ -415,8 +423,8 @@ export class BitbucketClient {
|
|
|
415
423
|
body.message = args.message;
|
|
416
424
|
const data = await this.request('POST', `/projects/${projectKey}/repos/${repoSlug}/pull-requests/${args.prId}/merge`, body);
|
|
417
425
|
if (!data)
|
|
418
|
-
return text(`Merged PR #${args.prId}
|
|
419
|
-
return text(`Merged PR #${data.id}: "${data.title}" (${data.fromRef.displayId} → ${data.toRef.displayId})
|
|
426
|
+
return text(`Merged PR #${args.prId}.\n${this.pullRequestUrl(projectKey, repoSlug, args.prId)}`);
|
|
427
|
+
return text(`Merged PR #${data.id}: "${data.title}" (${data.fromRef.displayId} → ${data.toRef.displayId}).\n${this.pullRequestUrl(projectKey, repoSlug, data.id, data)}`);
|
|
420
428
|
}
|
|
421
429
|
async getBranches(args) {
|
|
422
430
|
const { projectKey, repoSlug } = this.resolveProjectAndRepo(args.projectKey, args.repoSlug);
|
package/dist/jira.js
CHANGED
|
@@ -98,6 +98,18 @@ export class JiraClient {
|
|
|
98
98
|
Accept: 'application/json',
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
|
+
issueUrl(issueKey) {
|
|
102
|
+
return `${this.baseUrl}/browse/${encodeURIComponent(issueKey)}`;
|
|
103
|
+
}
|
|
104
|
+
projectUrl(projectKey) {
|
|
105
|
+
return `${this.baseUrl}/projects/${encodeURIComponent(projectKey)}`;
|
|
106
|
+
}
|
|
107
|
+
boardUrl(boardId) {
|
|
108
|
+
return `${this.baseUrl}/secure/RapidBoard.jspa?rapidView=${boardId}`;
|
|
109
|
+
}
|
|
110
|
+
sprintUrl(boardId, sprintId) {
|
|
111
|
+
return `${this.boardUrl(boardId)}&sprint=${sprintId}`;
|
|
112
|
+
}
|
|
101
113
|
async requestWithBase(apiBase, method, path, body) {
|
|
102
114
|
const url = `${this.baseUrl}${apiBase}${path}`;
|
|
103
115
|
const opts = { method, headers: this.headers };
|
|
@@ -208,7 +220,7 @@ export class JiraClient {
|
|
|
208
220
|
return text('No results.');
|
|
209
221
|
const lines = data.issues.map((i, idx) => {
|
|
210
222
|
const assignee = i.fields.assignee?.displayName ?? 'Unassigned';
|
|
211
|
-
return `${startAt + idx + 1}. [${i.key}] ${i.fields.summary} | ${i.fields.status.name} | ${assignee}`;
|
|
223
|
+
return `${startAt + idx + 1}. [${i.key}] ${i.fields.summary} | ${i.fields.status.name} | ${assignee} | ${this.issueUrl(i.key)}`;
|
|
212
224
|
});
|
|
213
225
|
const page = pagination(data.total, startAt, data.issues.length);
|
|
214
226
|
return text(`Found ${data.total} issues${page}:\n${lines.join('\n')}`);
|
|
@@ -225,7 +237,7 @@ export class JiraClient {
|
|
|
225
237
|
const data = await this.request('GET', `/project?maxResults=${limit}`);
|
|
226
238
|
if (!data || data.length === 0)
|
|
227
239
|
return text('No projects found.');
|
|
228
|
-
const lines = data.map((p, i) => `${i + 1}. [${p.key}] ${p.name} (${p.projectTypeKey})`);
|
|
240
|
+
const lines = data.map((p, i) => `${i + 1}. [${p.key}] ${p.name} (${p.projectTypeKey}) | ${this.projectUrl(p.key)}`);
|
|
229
241
|
return text(`${data.length} project(s):\n${lines.join('\n')}`);
|
|
230
242
|
}
|
|
231
243
|
async getIssueTypes(args) {
|
|
@@ -253,11 +265,11 @@ export class JiraClient {
|
|
|
253
265
|
const lines = data.values.map((s, i) => {
|
|
254
266
|
const window = [s.startDate?.slice(0, 10), s.endDate?.slice(0, 10)].filter(Boolean).join(' -> ');
|
|
255
267
|
const goal = s.goal?.trim() ? ` | Goal: ${s.goal}` : '';
|
|
256
|
-
return `${startAt + i + 1}. [${s.id}] ${s.name} | ${s.state}${window ? ` | ${window}` : ''}${goal}`;
|
|
268
|
+
return `${startAt + i + 1}. [${s.id}] ${s.name} | ${s.state}${window ? ` | ${window}` : ''}${goal} | ${this.sprintUrl(boardId, s.id)}`;
|
|
257
269
|
});
|
|
258
270
|
const rangeEnd = startAt + data.values.length;
|
|
259
271
|
const page = data.isLast ? '' : ` (showing ${startAt + 1}-${rangeEnd}, use startAt=${rangeEnd} for next page)`;
|
|
260
|
-
return text(`Sprints for board ${boardId}${page}:\n${lines.join('\n')}`);
|
|
272
|
+
return text(`Sprints for board ${boardId}${page}:\nBoard URL: ${this.boardUrl(boardId)}\n${lines.join('\n')}`);
|
|
261
273
|
}
|
|
262
274
|
async searchUsers(args) {
|
|
263
275
|
const params = new URLSearchParams({
|
|
@@ -305,6 +317,7 @@ export class JiraClient {
|
|
|
305
317
|
const f = issue.fields;
|
|
306
318
|
const lines = [
|
|
307
319
|
`Issue: ${issue.key} — ${f.summary}`,
|
|
320
|
+
`URL: ${this.issueUrl(issue.key)}`,
|
|
308
321
|
`Status: ${f.status.name}`,
|
|
309
322
|
`Type: ${f.issuetype.name}`,
|
|
310
323
|
`Priority: ${f.priority?.name ?? 'None'}`,
|
|
@@ -400,12 +413,13 @@ export class JiraClient {
|
|
|
400
413
|
const issueBySprint = new Map(sprintIssueData.map((entry) => [entry.sprintId, entry.issues]));
|
|
401
414
|
const lines = [
|
|
402
415
|
`Board: [${args.boardId}] ${board?.name ?? '(unknown)'} | ${board?.type ?? '(unknown type)'}`,
|
|
416
|
+
`URL: ${this.boardUrl(args.boardId)}`,
|
|
403
417
|
`Sprints: ${sprints.values.length}`,
|
|
404
418
|
'',
|
|
405
419
|
];
|
|
406
420
|
sprints.values.forEach((sprint, idx) => {
|
|
407
421
|
const window = [sprint.startDate?.slice(0, 10), sprint.endDate?.slice(0, 10)].filter(Boolean).join(' -> ');
|
|
408
|
-
lines.push(`${sprintStartAt + idx + 1}. [${sprint.id}] ${sprint.name} | ${sprint.state}${window ? ` | ${window}` : ''}`);
|
|
422
|
+
lines.push(`${sprintStartAt + idx + 1}. [${sprint.id}] ${sprint.name} | ${sprint.state}${window ? ` | ${window}` : ''} | ${this.sprintUrl(args.boardId, sprint.id)}`);
|
|
409
423
|
if (sprint.goal?.trim()) {
|
|
410
424
|
lines.push(` Goal: ${sprint.goal}`);
|
|
411
425
|
}
|
|
@@ -415,7 +429,7 @@ export class JiraClient {
|
|
|
415
429
|
lines.push(` Issues: ${issueData?.total ?? 0}`);
|
|
416
430
|
for (const issue of issues) {
|
|
417
431
|
const assignee = issue.fields.assignee?.displayName ?? 'Unassigned';
|
|
418
|
-
lines.push(` - [${issue.key}] ${issue.fields.summary} | ${issue.fields.status.name} | ${assignee}`);
|
|
432
|
+
lines.push(` - [${issue.key}] ${issue.fields.summary} | ${issue.fields.status.name} | ${assignee} | ${this.issueUrl(issue.key)}`);
|
|
419
433
|
}
|
|
420
434
|
if ((issueData?.total ?? 0) > issues.length) {
|
|
421
435
|
lines.push(` ...and ${(issueData?.total ?? 0) - issues.length} more (adjust issueStartAt/issueMaxResults).`);
|
|
@@ -433,11 +447,12 @@ export class JiraClient {
|
|
|
433
447
|
const data = await this.createIssueInternal(args);
|
|
434
448
|
if (!data)
|
|
435
449
|
return text('Issue created.');
|
|
450
|
+
const url = this.issueUrl(data.key);
|
|
436
451
|
if (args.sprintId !== undefined) {
|
|
437
452
|
await this.addIssuesToSprintInternal(args.sprintId, [data.key]);
|
|
438
|
-
return text(`Created ${data.key} and added it to sprint ${args.sprintId}
|
|
453
|
+
return text(`Created ${data.key} and added it to sprint ${args.sprintId}.\n${url}`);
|
|
439
454
|
}
|
|
440
|
-
return text(`Created ${data.key}
|
|
455
|
+
return text(`Created ${data.key}.\n${url}`);
|
|
441
456
|
}
|
|
442
457
|
async updateIssue(args) {
|
|
443
458
|
const hasFieldUpdates = await this.updateIssueFieldsInternal(args);
|
|
@@ -447,12 +462,12 @@ export class JiraClient {
|
|
|
447
462
|
await this.addIssuesToSprintInternal(args.sprintId, [args.issueKey]);
|
|
448
463
|
}
|
|
449
464
|
if (hasFieldUpdates && args.sprintId !== undefined) {
|
|
450
|
-
return text(`Updated ${args.issueKey} and added it to sprint ${args.sprintId}
|
|
465
|
+
return text(`Updated ${args.issueKey} and added it to sprint ${args.sprintId}.\n${this.issueUrl(args.issueKey)}`);
|
|
451
466
|
}
|
|
452
467
|
if (hasFieldUpdates) {
|
|
453
|
-
return text(`Updated ${args.issueKey}
|
|
468
|
+
return text(`Updated ${args.issueKey}.\n${this.issueUrl(args.issueKey)}`);
|
|
454
469
|
}
|
|
455
|
-
return text(`Added ${args.issueKey} to sprint ${args.sprintId}
|
|
470
|
+
return text(`Added ${args.issueKey} to sprint ${args.sprintId}.\n${this.issueUrl(args.issueKey)}`);
|
|
456
471
|
}
|
|
457
472
|
async addIssuesToSprint(args) {
|
|
458
473
|
const keys = new Set();
|
|
@@ -468,7 +483,14 @@ export class JiraClient {
|
|
|
468
483
|
}
|
|
469
484
|
const issueKeys = Array.from(keys);
|
|
470
485
|
await this.addIssuesToSprintInternal(args.sprintId, issueKeys);
|
|
471
|
-
|
|
486
|
+
if (issueKeys.length === 1) {
|
|
487
|
+
return text(`Added ${issueKeys[0]} to sprint ${args.sprintId}.\n${this.issueUrl(issueKeys[0])}`);
|
|
488
|
+
}
|
|
489
|
+
const lines = [
|
|
490
|
+
`Added ${issueKeys.length} issue(s) to sprint ${args.sprintId}.`,
|
|
491
|
+
...issueKeys.map((issueKey) => `${issueKey}: ${this.issueUrl(issueKey)}`),
|
|
492
|
+
];
|
|
493
|
+
return text(lines.join('\n'));
|
|
472
494
|
}
|
|
473
495
|
async mutateIssue(args) {
|
|
474
496
|
let issueKey = args.issueKey?.trim();
|
|
@@ -504,7 +526,7 @@ export class JiraClient {
|
|
|
504
526
|
if (actions.length === 0) {
|
|
505
527
|
return text('Nothing to mutate.');
|
|
506
528
|
}
|
|
507
|
-
return text(`Mutated ${issueKey}: ${actions.join(', ')}
|
|
529
|
+
return text(`Mutated ${issueKey}: ${actions.join(', ')}.\n${this.issueUrl(issueKey)}`);
|
|
508
530
|
}
|
|
509
531
|
async getComments(args) {
|
|
510
532
|
const { issueKey, maxResults = 50, startAt = 0 } = args;
|
|
@@ -525,6 +547,6 @@ export class JiraClient {
|
|
|
525
547
|
async transitionIssue(args) {
|
|
526
548
|
const transitionId = await this.resolveTransitionId(args.issueKey, args.transitionId, args.transitionName);
|
|
527
549
|
await this.transitionIssueInternal(args.issueKey, transitionId);
|
|
528
|
-
return text(`Transitioned ${args.issueKey} using transition ${transitionId}
|
|
550
|
+
return text(`Transitioned ${args.issueKey} using transition ${transitionId}.\n${this.issueUrl(args.issueKey)}`);
|
|
529
551
|
}
|
|
530
552
|
}
|