@stubbedev/atlassian-mcp 0.2.3 → 0.2.5
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 +34 -60
- package/dist/bitbucket.js +55 -2
- package/dist/index.js +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,84 +8,58 @@ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for **s
|
|
|
8
8
|
|
|
9
9
|
## Tools
|
|
10
10
|
|
|
11
|
-
###
|
|
11
|
+
### Workflow
|
|
12
12
|
|
|
13
13
|
| Tool | Description |
|
|
14
14
|
|---|---|
|
|
15
|
-
| `get_dev_context` |
|
|
15
|
+
| `get_dev_context` | Master entry point: git state + linked Jira ticket + open PR with reviewer/blocker status and next-step hints |
|
|
16
|
+
| `start_work` | Start a Jira ticket: fetches it, creates a local branch (`feature/FOO-123-slug`), and optionally transitions the ticket |
|
|
17
|
+
| `complete_work` | Close out finished work: merges the open PR and transitions the Jira ticket to Done |
|
|
16
18
|
|
|
17
|
-
###
|
|
19
|
+
### Git
|
|
18
20
|
|
|
19
21
|
| Tool | Description |
|
|
20
22
|
|---|---|
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `jira_get_projects` | List all accessible projects |
|
|
24
|
-
| `jira_get_issue_types` | List issue types and their available statuses for a project |
|
|
25
|
-
| `jira_get_sprints` | List sprints for a board (with sprint IDs for assignment) |
|
|
26
|
-
| `jira_get_issue` | Get issue details by key |
|
|
27
|
-
| `jira_issue_overview` | Get one-call issue overview (details, transitions, sprint context, optional comments) |
|
|
28
|
-
| `jira_board_overview` | Get one-call board overview (board info, sprints, optional sprint issues) |
|
|
29
|
-
| `jira_create_issue` | Create a new issue |
|
|
30
|
-
| `jira_update_issue` | Update summary, description, assignee, or priority |
|
|
31
|
-
| `jira_add_issues_to_sprint` | Add one or more issues to a sprint by sprint ID |
|
|
32
|
-
| `jira_mutate_issue` | Bundle create/update/sprint/transition/comment actions into one call |
|
|
33
|
-
| `jira_search_users` | Search for users by name or email |
|
|
34
|
-
| `jira_get_comments` | List comments on an issue |
|
|
35
|
-
| `jira_add_comment` | Add a comment to an issue |
|
|
36
|
-
| `jira_transition_issue` | Move issue status via transition name or transition ID |
|
|
23
|
+
| `git_get_context` | Branch, upstream state, remote URL, recent commits, working tree status, diff stat, and Jira keys in branch name |
|
|
24
|
+
| `git_get_diff` | Diff of uncommitted changes or between two refs; supports paging via `charOffset` |
|
|
37
25
|
|
|
38
|
-
###
|
|
26
|
+
### Jira
|
|
39
27
|
|
|
40
28
|
| Tool | Description |
|
|
41
29
|
|---|---|
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `bitbucket_get_pr_overview` | Get a one-call PR overview: metadata, commits, comments, task-style BLOCKER comments, and optional diff |
|
|
47
|
-
| `bitbucket_get_pr_diff` | Get the code diff for a pull request |
|
|
48
|
-
| `bitbucket_create_pull_request` | Create a new pull request (checks for an existing open PR from the source branch first) |
|
|
49
|
-
| `bitbucket_update_pull_request` | Update pull request title, description, destination branch, or reviewers |
|
|
50
|
-
| `bitbucket_mutate_pull_request` | Create or update a pull request in one call (target by PR ID or source branch) |
|
|
51
|
-
| `bitbucket_approve_pr` | Approve a pull request |
|
|
52
|
-
| `bitbucket_unapprove_pr` | Remove your approval from a pull request |
|
|
53
|
-
| `bitbucket_merge_pr` | Merge a pull request |
|
|
54
|
-
| `bitbucket_decline_pr` | Decline a pull request |
|
|
55
|
-
| `bitbucket_get_pr_comments` | Get PR comment threads in bulk, including task-style BLOCKER comments and blocker counts |
|
|
56
|
-
| `bitbucket_add_pr_comment` | Add a PR comment; when remarking on an existing comment, pass `commentId` so it is posted as a thread reply |
|
|
57
|
-
| `bitbucket_update_pr_comment` | Update comment text/severity (own comments only), resolve or reopen normal threads via `threadResolved`, and resolve/reopen BLOCKER tasks via `state` |
|
|
58
|
-
| `bitbucket_delete_pr_comment` | Delete a PR comment by comment ID |
|
|
59
|
-
| `bitbucket_get_pr_commits` | List commits included in a pull request |
|
|
60
|
-
| `bitbucket_get_branches` | List branches in a repository |
|
|
61
|
-
| `bitbucket_get_file` | Get raw file content at a given ref |
|
|
30
|
+
| `jira_search` | Discover resources: `issues`, `projects`, `issue_types`, `boards`, `sprints`, `board_overview`, or `users` via `resource` param |
|
|
31
|
+
| `jira_get` | Full details for one issue: summary, description, status, sprint, transitions, and comments |
|
|
32
|
+
| `jira_mutate` | Create, update, transition, comment, link, add to sprint, or log work — all in one call |
|
|
33
|
+
| `jira_comment` | Add, update, or delete a comment on an issue (`action`: `add` / `update` / `delete`) |
|
|
62
34
|
|
|
63
|
-
###
|
|
35
|
+
### Bitbucket
|
|
64
36
|
|
|
65
37
|
| Tool | Description |
|
|
66
38
|
|---|---|
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
|
|
71
|
-
|
|
39
|
+
| `bitbucket_search` | Discover resources: `pull_requests` (default), `repos`, or `branches` via `resource` param; `mine=true` for your inbox |
|
|
40
|
+
| `bitbucket_get_pr` | Full PR details: metadata, commits, comments, blockers, build status, and optional diff |
|
|
41
|
+
| `bitbucket_mutate` | Create/update a PR, or perform lifecycle actions: `approve`, `unapprove`, `merge`, `decline` |
|
|
42
|
+
| `bitbucket_comment` | Add, update, or delete a PR comment; supports inline anchors, multiline, suggestions, and BLOCKER severity |
|
|
43
|
+
| `bitbucket_get_file` | Raw file content from Bitbucket at a branch, tag, or commit |
|
|
44
|
+
| `bitbucket_pr_tasks` | Manage PR tasks (checklist items): `list`, `create`, `resolve`, `reopen`, `delete` |
|
|
72
45
|
|
|
73
46
|
### Natural language examples
|
|
74
47
|
|
|
75
|
-
- "
|
|
76
|
-
- "
|
|
77
|
-
- "
|
|
78
|
-
- "
|
|
79
|
-
- "
|
|
80
|
-
- "
|
|
81
|
-
- "
|
|
82
|
-
- "
|
|
83
|
-
- "
|
|
84
|
-
- "resolve this
|
|
85
|
-
- "
|
|
86
|
-
- "
|
|
87
|
-
- "
|
|
88
|
-
- "
|
|
48
|
+
- "what am I working on?" → `get_dev_context`
|
|
49
|
+
- "make a branch for FOO-123" → `start_work`
|
|
50
|
+
- "ship this / merge and close the ticket" → `complete_work`
|
|
51
|
+
- "show my PRs waiting for review" → `bitbucket_search` with `mine=true`
|
|
52
|
+
- "list open PRs for this repo from feature/ABC-123" → `bitbucket_search` with `fromBranch`
|
|
53
|
+
- "give me a full overview of PR 42" → `bitbucket_get_pr`
|
|
54
|
+
- "open a PR from my current branch to master" → `bitbucket_mutate` with `create`
|
|
55
|
+
- "approve / merge / decline PR 42" → `bitbucket_mutate` with `action`
|
|
56
|
+
- "reply to comment 123 on PR 42" → `bitbucket_comment` with `commentId=123`
|
|
57
|
+
- "resolve this blocker on PR 42" → `bitbucket_comment` with `action=update`, `severity=BLOCKER`, `state=RESOLVED`
|
|
58
|
+
- "list PR checklist tasks" → `bitbucket_pr_tasks` with `action=list`
|
|
59
|
+
- "find bugs assigned to me in PAY project" → `jira_search` with `mine=true`, `issueType=Bug`
|
|
60
|
+
- "what's in the current sprint?" → `jira_search` with `resource=board_overview`
|
|
61
|
+
- "move FOO-123 to In Progress" → `jira_mutate` with `transitionName="In Progress"`
|
|
62
|
+
- "log 2h on FOO-123" → `jira_mutate` with `worklog`
|
|
89
63
|
|
|
90
64
|
---
|
|
91
65
|
|
package/dist/bitbucket.js
CHANGED
|
@@ -321,6 +321,19 @@ export class BitbucketClient {
|
|
|
321
321
|
start = data.nextPageStart;
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
|
+
// Fallback: search branches matching filterText and check each for an open PR.
|
|
325
|
+
// Used when exact branch name lookup yields no result (e.g. LLM provides a partial branch name).
|
|
326
|
+
async findOpenPrByBranchFilter(projectKey, repoSlug, filterText) {
|
|
327
|
+
const branches = await this.request('GET', `/projects/${projectKey}/repos/${repoSlug}/branches?limit=25&filterText=${encodeURIComponent(filterText)}`);
|
|
328
|
+
if (!branches?.values?.length)
|
|
329
|
+
return null;
|
|
330
|
+
for (const b of branches.values) {
|
|
331
|
+
const pr = await this.findOpenPrForBranch(projectKey, repoSlug, b.displayId);
|
|
332
|
+
if (pr)
|
|
333
|
+
return pr;
|
|
334
|
+
}
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
324
337
|
async listRepos(args) {
|
|
325
338
|
const { limit = 50, start = 0 } = args;
|
|
326
339
|
const qs = `?limit=${limit}&start=${start}`;
|
|
@@ -396,7 +409,11 @@ export class BitbucketClient {
|
|
|
396
409
|
if (!branch || branch === 'HEAD') {
|
|
397
410
|
throw new Error('Provide prId or fromBranch, or run from a checked-out branch.');
|
|
398
411
|
}
|
|
399
|
-
|
|
412
|
+
let found = await this.findOpenPrForBranch(projectKey, repoSlug, branch);
|
|
413
|
+
if (!found) {
|
|
414
|
+
// Fallback: search branches matching the input text, then check those for open PRs
|
|
415
|
+
found = await this.findOpenPrByBranchFilter(projectKey, repoSlug, branchDisplayId(branch));
|
|
416
|
+
}
|
|
400
417
|
if (!found)
|
|
401
418
|
throw new Error(`No open PR found for branch "${branchDisplayId(branch)}".`);
|
|
402
419
|
prId = found.id;
|
|
@@ -441,7 +458,8 @@ export class BitbucketClient {
|
|
|
441
458
|
if (statuses?.values?.length) {
|
|
442
459
|
const statusLines = statuses.values.map((s) => {
|
|
443
460
|
const icon = s.state === 'SUCCESSFUL' ? '✓' : s.state === 'FAILED' ? '✗' : '…';
|
|
444
|
-
|
|
461
|
+
const urlPart = s.url ? `\n URL: ${s.url}` : '';
|
|
462
|
+
return `${icon} [${s.state}] ${s.name ?? s.key}${s.description ? ` — ${s.description}` : ''}${urlPart}`;
|
|
445
463
|
});
|
|
446
464
|
sections.push(`Build status (${pr.fromRef.latestCommit.slice(0, 8)}):\n${statusLines.join('\n')}`);
|
|
447
465
|
}
|
|
@@ -1010,6 +1028,41 @@ export class BitbucketClient {
|
|
|
1010
1028
|
return text(`Task #${args.taskId} ${newState}.`);
|
|
1011
1029
|
return text(`Task #${updated.id} is now ${updated.state}: "${updated.text}"`);
|
|
1012
1030
|
}
|
|
1031
|
+
async getBuildLog(args) {
|
|
1032
|
+
const { url, maxLines = 150 } = args;
|
|
1033
|
+
const baseUrl = url.replace(/\/+$/, '');
|
|
1034
|
+
// Confirm the URL is Jenkins before doing anything else
|
|
1035
|
+
let probe;
|
|
1036
|
+
try {
|
|
1037
|
+
probe = await fetch(baseUrl, { method: 'HEAD' });
|
|
1038
|
+
}
|
|
1039
|
+
catch (e) {
|
|
1040
|
+
return text(`Network error reaching ${baseUrl}: ${e}`);
|
|
1041
|
+
}
|
|
1042
|
+
const jenkinsVersion = probe.headers.get('X-Jenkins');
|
|
1043
|
+
const fetchUrl = jenkinsVersion ? `${baseUrl}/consoleText` : baseUrl;
|
|
1044
|
+
const label = jenkinsVersion ? `Jenkins ${jenkinsVersion}` : 'CI';
|
|
1045
|
+
let res;
|
|
1046
|
+
try {
|
|
1047
|
+
res = await fetch(fetchUrl);
|
|
1048
|
+
}
|
|
1049
|
+
catch (e) {
|
|
1050
|
+
return text(`Network error fetching build log from ${fetchUrl}: ${e}`);
|
|
1051
|
+
}
|
|
1052
|
+
if (res.status === 401 || res.status === 403) {
|
|
1053
|
+
return text(`${label} returned HTTP ${res.status} for ${fetchUrl}.\n` +
|
|
1054
|
+
`The build log requires authentication. View it directly in your browser.`);
|
|
1055
|
+
}
|
|
1056
|
+
if (!res.ok) {
|
|
1057
|
+
return text(`Failed to fetch build log (HTTP ${res.status}) from ${fetchUrl}.`);
|
|
1058
|
+
}
|
|
1059
|
+
const raw = await res.text();
|
|
1060
|
+
const lines = raw.split('\n');
|
|
1061
|
+
const truncated = lines.length > maxLines;
|
|
1062
|
+
const shown = truncated ? lines.slice(-maxLines) : lines;
|
|
1063
|
+
const prefix = truncated ? `(showing last ${maxLines} of ${lines.length} lines)\n\n` : '';
|
|
1064
|
+
return text(`Build log from ${label} — ${fetchUrl}:\n${prefix}${shown.join('\n')}`);
|
|
1065
|
+
}
|
|
1013
1066
|
async getBuildStatuses(args) {
|
|
1014
1067
|
const data = await this.requestBuildStatus('GET', `/commits/${args.commitSha}`);
|
|
1015
1068
|
if (!data?.values?.length)
|
package/dist/index.js
CHANGED
|
@@ -405,6 +405,18 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
405
405
|
},
|
|
406
406
|
required: ['prId'],
|
|
407
407
|
},
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: 'bitbucket_get_build_log',
|
|
411
|
+
description: 'Fetch CI build console output. Jenkins is detected via the X-Jenkins response header. Use after seeing a failed build URL in bitbucket_get_pr output — pass the URL exactly as shown. Returns the last N lines of the log (default 150).',
|
|
412
|
+
inputSchema: {
|
|
413
|
+
type: 'object',
|
|
414
|
+
properties: {
|
|
415
|
+
url: { type: 'string', description: 'Build URL from CI status, e.g. https://jenkins.example.com/job/my-job/123/' },
|
|
416
|
+
maxLines: { type: 'number', description: 'Max lines to return from the end of the log (default 150)', default: 150 },
|
|
417
|
+
},
|
|
418
|
+
required: ['url'],
|
|
419
|
+
},
|
|
408
420
|
}] : []),
|
|
409
421
|
// ── Combined workflow ─────────────────────────────────────────────────
|
|
410
422
|
...(jira && bitbucket ? [{
|
|
@@ -646,6 +658,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
646
658
|
return await bitbucket.getPrTasks(a);
|
|
647
659
|
return await bitbucket.mutatePrTask({ ...a, action: action });
|
|
648
660
|
}
|
|
661
|
+
case 'bitbucket_get_build_log':
|
|
662
|
+
if (!bitbucket)
|
|
663
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
664
|
+
return await bitbucket.getBuildLog(args);
|
|
649
665
|
case 'complete_work': {
|
|
650
666
|
if (!jira || !bitbucket)
|
|
651
667
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|