@stubbedev/atlassian-mcp 0.0.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/README.md +300 -0
- package/atlassian-mcp.schema.json +48 -0
- package/dist/bitbucket.js +337 -0
- package/dist/config.js +54 -0
- package/dist/context.js +98 -0
- package/dist/git.js +86 -0
- package/dist/index.js +568 -0
- package/dist/jira.js +189 -0
- package/package.json +40 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import 'dotenv/config';
|
|
3
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { loadConfig } from './config.js';
|
|
7
|
+
import { JiraClient } from './jira.js';
|
|
8
|
+
import { BitbucketClient } from './bitbucket.js';
|
|
9
|
+
import { getContext, getCommits, getDiff } from './git.js';
|
|
10
|
+
import { getDevContext, createPrFromContext } from './context.js';
|
|
11
|
+
const config = loadConfig();
|
|
12
|
+
const jira = new JiraClient(config.jira.url, config.jira.token);
|
|
13
|
+
const bitbucket = new BitbucketClient(config.bitbucket.url, config.bitbucket.token);
|
|
14
|
+
const server = new Server({ name: 'atlassian-mcp', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
15
|
+
server.onerror = (error) => console.error('[MCP Error]', error);
|
|
16
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
17
|
+
tools: [
|
|
18
|
+
// ── Context ───────────────────────────────────────────────────────────
|
|
19
|
+
{
|
|
20
|
+
name: 'get_dev_context',
|
|
21
|
+
description: 'One-shot developer context: reads the current git branch, fetches any linked Jira tickets detected in the branch name, and finds the open Bitbucket PR for the branch — all in a single call.',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
repoPath: { type: 'string', description: 'Path to the git repository (defaults to cwd)' },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
// ── Jira ──────────────────────────────────────────────────────────────
|
|
30
|
+
{
|
|
31
|
+
name: 'jira_search_issues',
|
|
32
|
+
description: 'Search Jira issues. Use `query` for plain-text search (searches summary, description, and comments using Lucene full-text). Use `jql` for advanced queries. Combine `query` with `project`, `status`, `assignee`, or `issueType` to narrow results without writing JQL.',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
query: { type: 'string', description: 'Plain-text search across summary, description, and comments (fuzzy, stemmed)' },
|
|
37
|
+
jql: { type: 'string', description: 'Raw JQL query — overrides all other filters when provided' },
|
|
38
|
+
project: { type: 'string', description: 'Filter by project key, e.g. "FOO"' },
|
|
39
|
+
status: { type: 'string', description: 'Filter by status name, e.g. "In Progress"' },
|
|
40
|
+
assignee: { type: 'string', description: 'Filter by assignee username' },
|
|
41
|
+
issueType: { type: 'string', description: 'Filter by issue type, e.g. "Bug", "Story"' },
|
|
42
|
+
maxResults: { type: 'number', description: 'Max results per page (default 20)', default: 20 },
|
|
43
|
+
startAt: { type: 'number', description: 'Offset for pagination (default 0)', default: 0 },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'jira_my_issues',
|
|
49
|
+
description: 'List issues assigned to the current user, ordered by last updated',
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
maxResults: { type: 'number', description: 'Max results per page (default 20)', default: 20 },
|
|
54
|
+
startAt: { type: 'number', description: 'Offset for pagination (default 0)', default: 0 },
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'jira_get_projects',
|
|
60
|
+
description: 'List all accessible Jira projects',
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: {
|
|
64
|
+
maxResults: { type: 'number', description: 'Max results (default 50)', default: 50 },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'jira_get_issue_types',
|
|
70
|
+
description: 'List issue types and their available statuses for a project — use before jira_create_issue to see valid options',
|
|
71
|
+
inputSchema: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
projectKey: { type: 'string', description: 'Jira project key' },
|
|
75
|
+
},
|
|
76
|
+
required: ['projectKey'],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'jira_search_users',
|
|
81
|
+
description: 'Search for Jira users by name or username — use to find the correct username before assigning an issue',
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
query: { type: 'string', description: 'Name or username to search for' },
|
|
86
|
+
maxResults: { type: 'number', description: 'Max results (default 10)', default: 10 },
|
|
87
|
+
},
|
|
88
|
+
required: ['query'],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'jira_get_issue',
|
|
93
|
+
description: 'Get details of a Jira issue by key',
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
issueKey: { type: 'string', description: 'Jira issue key, e.g. "FOO-123"' },
|
|
98
|
+
},
|
|
99
|
+
required: ['issueKey'],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'jira_create_issue',
|
|
104
|
+
description: 'Create a new Jira issue',
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
projectKey: { type: 'string', description: 'Jira project key' },
|
|
109
|
+
issueType: { type: 'string', description: 'Issue type name, e.g. "Bug", "Story", "Task"' },
|
|
110
|
+
summary: { type: 'string', description: 'Issue title' },
|
|
111
|
+
description: { type: 'string', description: 'Issue description (optional)' },
|
|
112
|
+
assignee: { type: 'string', description: 'Username to assign to (optional)' },
|
|
113
|
+
priority: { type: 'string', description: 'Priority name, e.g. "High" (optional)' },
|
|
114
|
+
},
|
|
115
|
+
required: ['projectKey', 'issueType', 'summary'],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'jira_update_issue',
|
|
120
|
+
description: 'Update fields on an existing Jira issue (summary, description, assignee, priority)',
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
properties: {
|
|
124
|
+
issueKey: { type: 'string', description: 'Jira issue key' },
|
|
125
|
+
summary: { type: 'string', description: 'New summary (optional)' },
|
|
126
|
+
description: { type: 'string', description: 'New description (optional)' },
|
|
127
|
+
assignee: { type: 'string', description: 'New assignee username, or empty string to unassign (optional)' },
|
|
128
|
+
priority: { type: 'string', description: 'New priority name (optional)' },
|
|
129
|
+
},
|
|
130
|
+
required: ['issueKey'],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'jira_get_comments',
|
|
135
|
+
description: 'Get comments on a Jira issue',
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: 'object',
|
|
138
|
+
properties: {
|
|
139
|
+
issueKey: { type: 'string', description: 'Jira issue key' },
|
|
140
|
+
maxResults: { type: 'number', description: 'Max comments per page (default 50)', default: 50 },
|
|
141
|
+
startAt: { type: 'number', description: 'Offset for pagination (default 0)', default: 0 },
|
|
142
|
+
},
|
|
143
|
+
required: ['issueKey'],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'jira_add_comment',
|
|
148
|
+
description: 'Add a comment to a Jira issue',
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: {
|
|
152
|
+
issueKey: { type: 'string', description: 'Jira issue key' },
|
|
153
|
+
body: { type: 'string', description: 'Comment text (plain text or Jira wiki markup)' },
|
|
154
|
+
},
|
|
155
|
+
required: ['issueKey', 'body'],
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'jira_get_transitions',
|
|
160
|
+
description: 'List available status transitions for a Jira issue',
|
|
161
|
+
inputSchema: {
|
|
162
|
+
type: 'object',
|
|
163
|
+
properties: {
|
|
164
|
+
issueKey: { type: 'string', description: 'Jira issue key' },
|
|
165
|
+
},
|
|
166
|
+
required: ['issueKey'],
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'jira_transition_issue',
|
|
171
|
+
description: 'Change the status of a Jira issue (get transition IDs from jira_get_transitions)',
|
|
172
|
+
inputSchema: {
|
|
173
|
+
type: 'object',
|
|
174
|
+
properties: {
|
|
175
|
+
issueKey: { type: 'string', description: 'Jira issue key' },
|
|
176
|
+
transitionId: { type: 'string', description: 'Transition ID from jira_get_transitions' },
|
|
177
|
+
},
|
|
178
|
+
required: ['issueKey', 'transitionId'],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
// ── Bitbucket ─────────────────────────────────────────────────────────
|
|
182
|
+
{
|
|
183
|
+
name: 'bitbucket_create_pr_from_context',
|
|
184
|
+
description: 'Create a Bitbucket pull request using the current git repo — auto-detects project, repo, and branch from the git remote. Only requires a title.',
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: 'object',
|
|
187
|
+
properties: {
|
|
188
|
+
title: { type: 'string', description: 'PR title' },
|
|
189
|
+
description: { type: 'string', description: 'PR description (optional)' },
|
|
190
|
+
toBranch: { type: 'string', description: 'Target branch (default: master)' },
|
|
191
|
+
reviewers: { type: 'array', items: { type: 'string' }, description: 'Reviewer usernames (optional)' },
|
|
192
|
+
repoPath: { type: 'string', description: 'Path to the git repository (defaults to cwd)' },
|
|
193
|
+
},
|
|
194
|
+
required: ['title'],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: 'bitbucket_list_repos',
|
|
199
|
+
description: 'List Bitbucket repositories, optionally filtered by project key',
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: 'object',
|
|
202
|
+
properties: {
|
|
203
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (optional)' },
|
|
204
|
+
limit: { type: 'number', description: 'Max repos per page (default 50)', default: 50 },
|
|
205
|
+
start: { type: 'number', description: 'Offset for pagination (default 0)', default: 0 },
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'bitbucket_list_pull_requests',
|
|
211
|
+
description: 'List pull requests for a repository',
|
|
212
|
+
inputSchema: {
|
|
213
|
+
type: 'object',
|
|
214
|
+
properties: {
|
|
215
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
216
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
217
|
+
state: { type: 'string', enum: ['OPEN', 'MERGED', 'DECLINED', 'ALL'], description: 'PR state filter (default OPEN)', default: 'OPEN' },
|
|
218
|
+
limit: { type: 'number', description: 'Max PRs per page (default 25)', default: 25 },
|
|
219
|
+
start: { type: 'number', description: 'Offset for pagination (default 0)', default: 0 },
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: 'bitbucket_my_prs',
|
|
225
|
+
description: 'List pull requests in your inbox (authored by you or awaiting your review)',
|
|
226
|
+
inputSchema: {
|
|
227
|
+
type: 'object',
|
|
228
|
+
properties: {
|
|
229
|
+
limit: { type: 'number', description: 'Max PRs per page (default 25)', default: 25 },
|
|
230
|
+
start: { type: 'number', description: 'Offset for pagination (default 0)', default: 0 },
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: 'bitbucket_get_pull_request',
|
|
236
|
+
description: 'Get details of a specific pull request',
|
|
237
|
+
inputSchema: {
|
|
238
|
+
type: 'object',
|
|
239
|
+
properties: {
|
|
240
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
241
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
242
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
243
|
+
},
|
|
244
|
+
required: ['prId'],
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'bitbucket_get_pr_diff',
|
|
249
|
+
description: 'Get the code diff for a pull request',
|
|
250
|
+
inputSchema: {
|
|
251
|
+
type: 'object',
|
|
252
|
+
properties: {
|
|
253
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
254
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
255
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
256
|
+
},
|
|
257
|
+
required: ['prId'],
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'bitbucket_get_pr_commits',
|
|
262
|
+
description: 'List commits included in a pull request',
|
|
263
|
+
inputSchema: {
|
|
264
|
+
type: 'object',
|
|
265
|
+
properties: {
|
|
266
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
267
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
268
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
269
|
+
limit: { type: 'number', description: 'Max commits (default 25)', default: 25 },
|
|
270
|
+
},
|
|
271
|
+
required: ['prId'],
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: 'bitbucket_create_pull_request',
|
|
276
|
+
description: 'Create a new pull request (project and repo can be auto-detected from git remote)',
|
|
277
|
+
inputSchema: {
|
|
278
|
+
type: 'object',
|
|
279
|
+
properties: {
|
|
280
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
281
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
282
|
+
title: { type: 'string', description: 'PR title' },
|
|
283
|
+
description: { type: 'string', description: 'PR description (optional)' },
|
|
284
|
+
fromBranch: { type: 'string', description: 'Source branch name' },
|
|
285
|
+
toBranch: { type: 'string', description: 'Target branch name (default: master)' },
|
|
286
|
+
reviewers: { type: 'array', items: { type: 'string' }, description: 'Reviewer usernames (optional)' },
|
|
287
|
+
},
|
|
288
|
+
required: ['title', 'fromBranch'],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'bitbucket_approve_pr',
|
|
293
|
+
description: 'Approve a pull request',
|
|
294
|
+
inputSchema: {
|
|
295
|
+
type: 'object',
|
|
296
|
+
properties: {
|
|
297
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
298
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
299
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
300
|
+
},
|
|
301
|
+
required: ['prId'],
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: 'bitbucket_unapprove_pr',
|
|
306
|
+
description: 'Retract your approval from a pull request',
|
|
307
|
+
inputSchema: {
|
|
308
|
+
type: 'object',
|
|
309
|
+
properties: {
|
|
310
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
311
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
312
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
313
|
+
},
|
|
314
|
+
required: ['prId'],
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: 'bitbucket_decline_pr',
|
|
319
|
+
description: 'Decline a pull request',
|
|
320
|
+
inputSchema: {
|
|
321
|
+
type: 'object',
|
|
322
|
+
properties: {
|
|
323
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
324
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
325
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
326
|
+
message: { type: 'string', description: 'Optional decline message' },
|
|
327
|
+
},
|
|
328
|
+
required: ['prId'],
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: 'bitbucket_merge_pr',
|
|
333
|
+
description: 'Merge a pull request',
|
|
334
|
+
inputSchema: {
|
|
335
|
+
type: 'object',
|
|
336
|
+
properties: {
|
|
337
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
338
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
339
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
340
|
+
mergeStrategy: { type: 'string', enum: ['MERGE_COMMIT', 'SQUASH', 'FAST_FORWARD'], description: 'Merge strategy (uses repo default if omitted)' },
|
|
341
|
+
message: { type: 'string', description: 'Custom merge commit message (optional)' },
|
|
342
|
+
},
|
|
343
|
+
required: ['prId'],
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: 'bitbucket_get_pr_comments',
|
|
348
|
+
description: 'Get pull request comment threads with comment IDs and states (needed for replies and resolving)',
|
|
349
|
+
inputSchema: {
|
|
350
|
+
type: 'object',
|
|
351
|
+
properties: {
|
|
352
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
353
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
354
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
355
|
+
state: { type: 'string', enum: ['OPEN', 'RESOLVED', 'PENDING'], description: 'Filter comment state (default OPEN)', default: 'OPEN' },
|
|
356
|
+
limit: { type: 'number', description: 'Max items per page (default 50)', default: 50 },
|
|
357
|
+
start: { type: 'number', description: 'Offset for pagination (default 0)', default: 0 },
|
|
358
|
+
},
|
|
359
|
+
required: ['prId'],
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: 'bitbucket_add_pr_comment',
|
|
364
|
+
description: 'Add a top-level comment or reply to an existing comment in a pull request',
|
|
365
|
+
inputSchema: {
|
|
366
|
+
type: 'object',
|
|
367
|
+
properties: {
|
|
368
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
369
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
370
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
371
|
+
parentCommentId: { type: 'number', description: 'Parent comment ID for reply mode (optional)' },
|
|
372
|
+
text: { type: 'string', description: 'Comment text' },
|
|
373
|
+
},
|
|
374
|
+
required: ['prId', 'text'],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: 'bitbucket_update_pr_comment',
|
|
379
|
+
description: 'Update comment text, state, and/or severity (convert comment <-> task)',
|
|
380
|
+
inputSchema: {
|
|
381
|
+
type: 'object',
|
|
382
|
+
properties: {
|
|
383
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
384
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
385
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
386
|
+
commentId: { type: 'number', description: 'Comment ID to update' },
|
|
387
|
+
text: { type: 'string', description: 'New comment text (optional)' },
|
|
388
|
+
state: { type: 'string', enum: ['OPEN', 'RESOLVED'], description: 'Comment state (optional)' },
|
|
389
|
+
severity: { type: 'string', enum: ['NORMAL', 'BLOCKER'], description: 'Comment severity (optional, BLOCKER creates a task)' },
|
|
390
|
+
},
|
|
391
|
+
required: ['prId', 'commentId'],
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: 'bitbucket_delete_pr_comment',
|
|
396
|
+
description: 'Delete a pull request comment by ID',
|
|
397
|
+
inputSchema: {
|
|
398
|
+
type: 'object',
|
|
399
|
+
properties: {
|
|
400
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
401
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
402
|
+
prId: { type: 'number', description: 'Pull request ID' },
|
|
403
|
+
commentId: { type: 'number', description: 'Comment ID to delete' },
|
|
404
|
+
},
|
|
405
|
+
required: ['prId', 'commentId'],
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: 'bitbucket_get_branches',
|
|
410
|
+
description: 'List branches in a repository',
|
|
411
|
+
inputSchema: {
|
|
412
|
+
type: 'object',
|
|
413
|
+
properties: {
|
|
414
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
415
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
416
|
+
filter: { type: 'string', description: 'Filter branches by name (optional)' },
|
|
417
|
+
limit: { type: 'number', description: 'Max branches per page (default 25)', default: 25 },
|
|
418
|
+
start: { type: 'number', description: 'Offset for pagination (default 0)', default: 0 },
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
name: 'bitbucket_get_file',
|
|
424
|
+
description: 'Get the raw content of a file from a repository',
|
|
425
|
+
inputSchema: {
|
|
426
|
+
type: 'object',
|
|
427
|
+
properties: {
|
|
428
|
+
projectKey: { type: 'string', description: 'Bitbucket project key (auto-detected from git remote if omitted)' },
|
|
429
|
+
repoSlug: { type: 'string', description: 'Repository slug (auto-detected from git remote if omitted)' },
|
|
430
|
+
path: { type: 'string', description: 'File path, e.g. "src/index.ts"' },
|
|
431
|
+
ref: { type: 'string', description: 'Branch, tag, or commit hash (defaults to default branch)' },
|
|
432
|
+
},
|
|
433
|
+
required: ['path'],
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
// ── Git ───────────────────────────────────────────────────────────────
|
|
437
|
+
{
|
|
438
|
+
name: 'git_get_context',
|
|
439
|
+
description: 'Get git context for a local repo: current branch, remote URL, recent commits, working tree status, and any Jira keys detected in the branch name',
|
|
440
|
+
inputSchema: {
|
|
441
|
+
type: 'object',
|
|
442
|
+
properties: {
|
|
443
|
+
repoPath: { type: 'string', description: 'Path to the git repository (defaults to cwd)' },
|
|
444
|
+
commitLimit: { type: 'number', description: 'Number of recent commits to show (default 10)', default: 10 },
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
name: 'git_get_commits',
|
|
450
|
+
description: 'Get commit history for a branch with author and message details',
|
|
451
|
+
inputSchema: {
|
|
452
|
+
type: 'object',
|
|
453
|
+
properties: {
|
|
454
|
+
repoPath: { type: 'string', description: 'Path to the git repository (defaults to cwd)' },
|
|
455
|
+
limit: { type: 'number', description: 'Max commits to return (default 20)', default: 20 },
|
|
456
|
+
branch: { type: 'string', description: 'Branch name to log (defaults to current branch)' },
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: 'git_get_diff',
|
|
462
|
+
description: 'Get git diff — uncommitted changes (vs HEAD) or between two refs',
|
|
463
|
+
inputSchema: {
|
|
464
|
+
type: 'object',
|
|
465
|
+
properties: {
|
|
466
|
+
repoPath: { type: 'string', description: 'Path to the git repository (defaults to cwd)' },
|
|
467
|
+
fromRef: { type: 'string', description: 'Base ref or commit (optional)' },
|
|
468
|
+
toRef: { type: 'string', description: 'Target ref or commit (optional, requires fromRef)' },
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
}));
|
|
474
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
475
|
+
const { name, arguments: args = {} } = request.params;
|
|
476
|
+
try {
|
|
477
|
+
switch (name) {
|
|
478
|
+
// Context
|
|
479
|
+
case 'get_dev_context':
|
|
480
|
+
return await getDevContext(args, jira, bitbucket);
|
|
481
|
+
// Jira
|
|
482
|
+
case 'jira_search_issues':
|
|
483
|
+
return await jira.searchIssues(args);
|
|
484
|
+
case 'jira_my_issues':
|
|
485
|
+
return await jira.myIssues(args);
|
|
486
|
+
case 'jira_get_projects':
|
|
487
|
+
return await jira.getProjects(args);
|
|
488
|
+
case 'jira_get_issue_types':
|
|
489
|
+
return await jira.getIssueTypes(args);
|
|
490
|
+
case 'jira_search_users':
|
|
491
|
+
return await jira.searchUsers(args);
|
|
492
|
+
case 'jira_get_issue':
|
|
493
|
+
return await jira.getIssue(args);
|
|
494
|
+
case 'jira_create_issue':
|
|
495
|
+
return await jira.createIssue(args);
|
|
496
|
+
case 'jira_update_issue':
|
|
497
|
+
return await jira.updateIssue(args);
|
|
498
|
+
case 'jira_get_comments':
|
|
499
|
+
return await jira.getComments(args);
|
|
500
|
+
case 'jira_add_comment':
|
|
501
|
+
return await jira.addComment(args);
|
|
502
|
+
case 'jira_get_transitions':
|
|
503
|
+
return await jira.getTransitions(args);
|
|
504
|
+
case 'jira_transition_issue':
|
|
505
|
+
return await jira.transitionIssue(args);
|
|
506
|
+
// Bitbucket
|
|
507
|
+
case 'bitbucket_create_pr_from_context':
|
|
508
|
+
return await createPrFromContext(args, bitbucket);
|
|
509
|
+
case 'bitbucket_list_repos':
|
|
510
|
+
return await bitbucket.listRepos(args);
|
|
511
|
+
case 'bitbucket_list_pull_requests':
|
|
512
|
+
return await bitbucket.listPullRequests(args);
|
|
513
|
+
case 'bitbucket_my_prs':
|
|
514
|
+
return await bitbucket.myPrs(args);
|
|
515
|
+
case 'bitbucket_get_pull_request':
|
|
516
|
+
return await bitbucket.getPullRequest(args);
|
|
517
|
+
case 'bitbucket_get_pr_diff':
|
|
518
|
+
return await bitbucket.getPrDiff(args);
|
|
519
|
+
case 'bitbucket_get_pr_commits':
|
|
520
|
+
return await bitbucket.getPrCommits(args);
|
|
521
|
+
case 'bitbucket_create_pull_request':
|
|
522
|
+
return await bitbucket.createPullRequest(args);
|
|
523
|
+
case 'bitbucket_approve_pr':
|
|
524
|
+
return await bitbucket.approvePr(args);
|
|
525
|
+
case 'bitbucket_unapprove_pr':
|
|
526
|
+
return await bitbucket.unapprovePr(args);
|
|
527
|
+
case 'bitbucket_decline_pr':
|
|
528
|
+
return await bitbucket.declinePr(args);
|
|
529
|
+
case 'bitbucket_merge_pr':
|
|
530
|
+
return await bitbucket.mergePr(args);
|
|
531
|
+
case 'bitbucket_get_pr_comments':
|
|
532
|
+
return await bitbucket.getPrComments(args);
|
|
533
|
+
case 'bitbucket_add_pr_comment':
|
|
534
|
+
return await bitbucket.addPrComment(args);
|
|
535
|
+
case 'bitbucket_update_pr_comment':
|
|
536
|
+
return await bitbucket.updatePrComment(args);
|
|
537
|
+
case 'bitbucket_delete_pr_comment':
|
|
538
|
+
return await bitbucket.deletePrComment(args);
|
|
539
|
+
case 'bitbucket_get_branches':
|
|
540
|
+
return await bitbucket.getBranches(args);
|
|
541
|
+
case 'bitbucket_get_file':
|
|
542
|
+
return await bitbucket.getFile(args);
|
|
543
|
+
// Git
|
|
544
|
+
case 'git_get_context':
|
|
545
|
+
return getContext(args);
|
|
546
|
+
case 'git_get_commits':
|
|
547
|
+
return getCommits(args);
|
|
548
|
+
case 'git_get_diff':
|
|
549
|
+
return getDiff(args);
|
|
550
|
+
default:
|
|
551
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
catch (err) {
|
|
555
|
+
if (err instanceof McpError)
|
|
556
|
+
throw err;
|
|
557
|
+
return {
|
|
558
|
+
content: [{ type: 'text', text: `Error: ${err.message}` }],
|
|
559
|
+
isError: true,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
process.on('SIGINT', async () => {
|
|
564
|
+
await server.close();
|
|
565
|
+
process.exit(0);
|
|
566
|
+
});
|
|
567
|
+
const transport = new StdioServerTransport();
|
|
568
|
+
await server.connect(transport);
|