mcp-server-bitbucket 0.11.0
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/Dockerfile +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2976 -0
- package/docker-entrypoint.sh +6 -0
- package/package.json +57 -0
- package/smithery.yaml +47 -0
- package/src/client.ts +1102 -0
- package/src/index.ts +149 -0
- package/src/prompts.ts +208 -0
- package/src/resources.ts +160 -0
- package/src/settings.ts +63 -0
- package/src/tools/branches.ts +69 -0
- package/src/tools/commits.ts +186 -0
- package/src/tools/deployments.ts +100 -0
- package/src/tools/index.ts +112 -0
- package/src/tools/permissions.ts +205 -0
- package/src/tools/pipelines.ts +301 -0
- package/src/tools/projects.ts +63 -0
- package/src/tools/pull-requests.ts +321 -0
- package/src/tools/repositories.ts +208 -0
- package/src/tools/restrictions.ts +94 -0
- package/src/tools/source.ts +78 -0
- package/src/tools/tags.ts +88 -0
- package/src/tools/webhooks.ts +121 -0
- package/src/types.ts +369 -0
- package/src/utils.ts +83 -0
- package/tsconfig.json +25 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2976 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import {
|
|
7
|
+
CallToolRequestSchema,
|
|
8
|
+
ListToolsRequestSchema,
|
|
9
|
+
ListResourcesRequestSchema,
|
|
10
|
+
ReadResourceRequestSchema,
|
|
11
|
+
ListPromptsRequestSchema,
|
|
12
|
+
GetPromptRequestSchema
|
|
13
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
+
|
|
15
|
+
// src/settings.ts
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
var settingsSchema = z.object({
|
|
18
|
+
bitbucketWorkspace: z.string().min(1, "BITBUCKET_WORKSPACE is required"),
|
|
19
|
+
bitbucketEmail: z.string().min(1, "BITBUCKET_EMAIL is required"),
|
|
20
|
+
bitbucketApiToken: z.string().min(1, "BITBUCKET_API_TOKEN is required"),
|
|
21
|
+
apiTimeout: z.number().min(1).max(300).default(30),
|
|
22
|
+
maxRetries: z.number().min(0).max(10).default(3),
|
|
23
|
+
outputFormat: z.enum(["json", "toon"]).default("json")
|
|
24
|
+
});
|
|
25
|
+
var cachedSettings = null;
|
|
26
|
+
function getSettings() {
|
|
27
|
+
if (cachedSettings) {
|
|
28
|
+
return cachedSettings;
|
|
29
|
+
}
|
|
30
|
+
const rawSettings = {
|
|
31
|
+
bitbucketWorkspace: process.env.BITBUCKET_WORKSPACE || "",
|
|
32
|
+
bitbucketEmail: process.env.BITBUCKET_EMAIL || "",
|
|
33
|
+
bitbucketApiToken: process.env.BITBUCKET_API_TOKEN || "",
|
|
34
|
+
apiTimeout: parseInt(process.env.API_TIMEOUT || "30", 10),
|
|
35
|
+
maxRetries: parseInt(process.env.MAX_RETRIES || "3", 10),
|
|
36
|
+
outputFormat: process.env.OUTPUT_FORMAT || "json"
|
|
37
|
+
};
|
|
38
|
+
const result = settingsSchema.safeParse(rawSettings);
|
|
39
|
+
if (!result.success) {
|
|
40
|
+
const errors = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
41
|
+
throw new Error(`Configuration error: ${errors}`);
|
|
42
|
+
}
|
|
43
|
+
cachedSettings = result.data;
|
|
44
|
+
return cachedSettings;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/client.ts
|
|
48
|
+
import axios from "axios";
|
|
49
|
+
|
|
50
|
+
// src/utils.ts
|
|
51
|
+
function ensureUuidBraces(uuid) {
|
|
52
|
+
if (!uuid) return uuid;
|
|
53
|
+
if (uuid.startsWith("{") && uuid.endsWith("}")) {
|
|
54
|
+
return uuid;
|
|
55
|
+
}
|
|
56
|
+
return `{${uuid}}`;
|
|
57
|
+
}
|
|
58
|
+
function truncateHash(hash) {
|
|
59
|
+
if (!hash) return "";
|
|
60
|
+
return hash.substring(0, 7);
|
|
61
|
+
}
|
|
62
|
+
function sanitizeSearchTerm(term) {
|
|
63
|
+
return term.replace(/["\\]/g, "").trim();
|
|
64
|
+
}
|
|
65
|
+
function validateLimit(limit, maxLimit = 100) {
|
|
66
|
+
if (limit < 1) return 1;
|
|
67
|
+
if (limit > maxLimit) return maxLimit;
|
|
68
|
+
return limit;
|
|
69
|
+
}
|
|
70
|
+
function notFoundResponse(type, identifier) {
|
|
71
|
+
return {
|
|
72
|
+
error: `${type} '${identifier}' not found`,
|
|
73
|
+
found: false
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function sleep(ms) {
|
|
77
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/client.ts
|
|
81
|
+
var BitbucketError = class extends Error {
|
|
82
|
+
constructor(message, statusCode, method, path) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.statusCode = statusCode;
|
|
85
|
+
this.method = method;
|
|
86
|
+
this.path = path;
|
|
87
|
+
this.name = "BitbucketError";
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var BitbucketClient = class _BitbucketClient {
|
|
91
|
+
static BASE_URL = "https://api.bitbucket.org/2.0";
|
|
92
|
+
static INITIAL_BACKOFF = 1e3;
|
|
93
|
+
// ms
|
|
94
|
+
workspace;
|
|
95
|
+
client;
|
|
96
|
+
maxRetries;
|
|
97
|
+
constructor() {
|
|
98
|
+
const settings = getSettings();
|
|
99
|
+
this.workspace = settings.bitbucketWorkspace;
|
|
100
|
+
this.maxRetries = settings.maxRetries;
|
|
101
|
+
this.client = axios.create({
|
|
102
|
+
baseURL: _BitbucketClient.BASE_URL,
|
|
103
|
+
timeout: settings.apiTimeout * 1e3,
|
|
104
|
+
auth: {
|
|
105
|
+
username: settings.bitbucketEmail,
|
|
106
|
+
password: settings.bitbucketApiToken
|
|
107
|
+
},
|
|
108
|
+
headers: {
|
|
109
|
+
"Content-Type": "application/json"
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Build repository endpoint path
|
|
115
|
+
*/
|
|
116
|
+
repoPath(repoSlug, ...parts) {
|
|
117
|
+
const base = `repositories/${this.workspace}/${repoSlug}`;
|
|
118
|
+
return parts.length > 0 ? `${base}/${parts.join("/")}` : base;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Make an API request with retry logic for rate limiting
|
|
122
|
+
*/
|
|
123
|
+
async request(method, path, data, params) {
|
|
124
|
+
let backoff = _BitbucketClient.INITIAL_BACKOFF;
|
|
125
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
126
|
+
try {
|
|
127
|
+
const response = await this.client.request({
|
|
128
|
+
method,
|
|
129
|
+
url: path,
|
|
130
|
+
data,
|
|
131
|
+
params
|
|
132
|
+
});
|
|
133
|
+
return response.data;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (axios.isAxiosError(error)) {
|
|
136
|
+
const axiosError = error;
|
|
137
|
+
if (axiosError.response?.status === 404) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
if (axiosError.response?.status === 429) {
|
|
141
|
+
if (attempt < this.maxRetries) {
|
|
142
|
+
const retryAfter = axiosError.response.headers["retry-after"];
|
|
143
|
+
const waitTime = retryAfter ? parseInt(retryAfter, 10) * 1e3 : backoff;
|
|
144
|
+
await sleep(waitTime);
|
|
145
|
+
backoff *= 2;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
throw new BitbucketError(
|
|
149
|
+
`Rate limited after ${this.maxRetries} retries`,
|
|
150
|
+
429,
|
|
151
|
+
method,
|
|
152
|
+
path
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
const statusCode = axiosError.response?.status;
|
|
156
|
+
const errorText = JSON.stringify(axiosError.response?.data || axiosError.message).substring(0, 500);
|
|
157
|
+
throw new BitbucketError(
|
|
158
|
+
`API error ${statusCode}: ${errorText}`,
|
|
159
|
+
statusCode,
|
|
160
|
+
method,
|
|
161
|
+
path
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
throw new BitbucketError(`Unexpected error in request`, void 0, method, path);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Make a request that returns plain text
|
|
171
|
+
*/
|
|
172
|
+
async requestText(path) {
|
|
173
|
+
let backoff = _BitbucketClient.INITIAL_BACKOFF;
|
|
174
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
175
|
+
try {
|
|
176
|
+
const response = await this.client.get(path, {
|
|
177
|
+
responseType: "text"
|
|
178
|
+
});
|
|
179
|
+
return response.data;
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (axios.isAxiosError(error)) {
|
|
182
|
+
if (error.response?.status === 404) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
if (error.response?.status === 429) {
|
|
186
|
+
if (attempt < this.maxRetries) {
|
|
187
|
+
const retryAfter = error.response.headers["retry-after"];
|
|
188
|
+
const waitTime = retryAfter ? parseInt(retryAfter, 10) * 1e3 : backoff;
|
|
189
|
+
await sleep(waitTime);
|
|
190
|
+
backoff *= 2;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
throw new BitbucketError(`Request failed: ${error.response?.status}`);
|
|
195
|
+
}
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Helper for paginated list endpoints
|
|
203
|
+
*/
|
|
204
|
+
async paginatedList(endpoint, options = {}) {
|
|
205
|
+
const { limit = 50, maxPage = 100, ...extraParams } = options;
|
|
206
|
+
const params = {
|
|
207
|
+
pagelen: Math.min(limit, maxPage),
|
|
208
|
+
...extraParams
|
|
209
|
+
};
|
|
210
|
+
Object.keys(params).forEach((key) => {
|
|
211
|
+
if (params[key] === void 0) {
|
|
212
|
+
delete params[key];
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
const result = await this.request("GET", endpoint, void 0, params);
|
|
216
|
+
return result?.values || [];
|
|
217
|
+
}
|
|
218
|
+
// ==================== REPOSITORIES ====================
|
|
219
|
+
async getRepository(repoSlug) {
|
|
220
|
+
return this.request("GET", this.repoPath(repoSlug));
|
|
221
|
+
}
|
|
222
|
+
async createRepository(repoSlug, options = {}) {
|
|
223
|
+
const payload = {
|
|
224
|
+
scm: "git",
|
|
225
|
+
is_private: options.isPrivate ?? true
|
|
226
|
+
};
|
|
227
|
+
if (options.projectKey) {
|
|
228
|
+
payload.project = { key: options.projectKey };
|
|
229
|
+
}
|
|
230
|
+
if (options.description) {
|
|
231
|
+
payload.description = options.description;
|
|
232
|
+
}
|
|
233
|
+
const result = await this.request("POST", this.repoPath(repoSlug), payload);
|
|
234
|
+
if (!result) {
|
|
235
|
+
throw new BitbucketError(`Failed to create repository: ${repoSlug}`);
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
async deleteRepository(repoSlug) {
|
|
240
|
+
await this.request("DELETE", this.repoPath(repoSlug));
|
|
241
|
+
}
|
|
242
|
+
async listRepositories(options = {}) {
|
|
243
|
+
const params = {
|
|
244
|
+
pagelen: Math.min(options.limit || 50, 100)
|
|
245
|
+
};
|
|
246
|
+
const qParts = [];
|
|
247
|
+
if (options.projectKey) {
|
|
248
|
+
qParts.push(`project.key="${options.projectKey}"`);
|
|
249
|
+
}
|
|
250
|
+
if (options.query) {
|
|
251
|
+
qParts.push(options.query);
|
|
252
|
+
}
|
|
253
|
+
if (qParts.length > 0) {
|
|
254
|
+
params.q = qParts.join(" AND ");
|
|
255
|
+
}
|
|
256
|
+
const result = await this.request(
|
|
257
|
+
"GET",
|
|
258
|
+
`repositories/${this.workspace}`,
|
|
259
|
+
void 0,
|
|
260
|
+
params
|
|
261
|
+
);
|
|
262
|
+
return result?.values || [];
|
|
263
|
+
}
|
|
264
|
+
async updateRepository(repoSlug, options) {
|
|
265
|
+
const payload = {};
|
|
266
|
+
if (options.projectKey !== void 0) {
|
|
267
|
+
payload.project = { key: options.projectKey };
|
|
268
|
+
}
|
|
269
|
+
if (options.isPrivate !== void 0) {
|
|
270
|
+
payload.is_private = options.isPrivate;
|
|
271
|
+
}
|
|
272
|
+
if (options.description !== void 0) {
|
|
273
|
+
payload.description = options.description;
|
|
274
|
+
}
|
|
275
|
+
if (options.name !== void 0) {
|
|
276
|
+
payload.name = options.name;
|
|
277
|
+
}
|
|
278
|
+
if (Object.keys(payload).length === 0) {
|
|
279
|
+
throw new BitbucketError("No fields to update");
|
|
280
|
+
}
|
|
281
|
+
const result = await this.request("PUT", this.repoPath(repoSlug), payload);
|
|
282
|
+
if (!result) {
|
|
283
|
+
throw new BitbucketError(`Failed to update repository: ${repoSlug}`);
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
// ==================== PULL REQUESTS ====================
|
|
288
|
+
async createPullRequest(repoSlug, options) {
|
|
289
|
+
const payload = {
|
|
290
|
+
title: options.title,
|
|
291
|
+
source: { branch: { name: options.sourceBranch } },
|
|
292
|
+
destination: { branch: { name: options.destinationBranch || "main" } },
|
|
293
|
+
close_source_branch: options.closeSourceBranch ?? true
|
|
294
|
+
};
|
|
295
|
+
if (options.description) {
|
|
296
|
+
payload.description = options.description;
|
|
297
|
+
}
|
|
298
|
+
if (options.reviewers && options.reviewers.length > 0) {
|
|
299
|
+
payload.reviewers = options.reviewers.map(
|
|
300
|
+
(r) => r.startsWith("{") ? { uuid: r } : { account_id: r }
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
const result = await this.request(
|
|
304
|
+
"POST",
|
|
305
|
+
this.repoPath(repoSlug, "pullrequests"),
|
|
306
|
+
payload
|
|
307
|
+
);
|
|
308
|
+
if (!result) {
|
|
309
|
+
throw new BitbucketError(`Failed to create PR: ${options.sourceBranch} -> ${options.destinationBranch || "main"}`);
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
313
|
+
async getPullRequest(repoSlug, prId) {
|
|
314
|
+
return this.request("GET", this.repoPath(repoSlug, "pullrequests", String(prId)));
|
|
315
|
+
}
|
|
316
|
+
async listPullRequests(repoSlug, options = {}) {
|
|
317
|
+
return this.paginatedList(this.repoPath(repoSlug, "pullrequests"), {
|
|
318
|
+
limit: options.limit || 50,
|
|
319
|
+
maxPage: 50,
|
|
320
|
+
state: options.state || "OPEN"
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
async mergePullRequest(repoSlug, prId, options = {}) {
|
|
324
|
+
const payload = {
|
|
325
|
+
type: options.mergeStrategy || "merge_commit",
|
|
326
|
+
close_source_branch: options.closeSourceBranch ?? true
|
|
327
|
+
};
|
|
328
|
+
if (options.message) {
|
|
329
|
+
payload.message = options.message;
|
|
330
|
+
}
|
|
331
|
+
const result = await this.request(
|
|
332
|
+
"POST",
|
|
333
|
+
this.repoPath(repoSlug, "pullrequests", String(prId), "merge"),
|
|
334
|
+
payload
|
|
335
|
+
);
|
|
336
|
+
if (!result) {
|
|
337
|
+
throw new BitbucketError(`Failed to merge PR #${prId}`);
|
|
338
|
+
}
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
async listPrComments(repoSlug, prId, options = {}) {
|
|
342
|
+
return this.paginatedList(
|
|
343
|
+
this.repoPath(repoSlug, "pullrequests", String(prId), "comments"),
|
|
344
|
+
{ limit: options.limit || 50 }
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
async addPrComment(repoSlug, prId, content, inline) {
|
|
348
|
+
const payload = {
|
|
349
|
+
content: { raw: content }
|
|
350
|
+
};
|
|
351
|
+
if (inline) {
|
|
352
|
+
payload.inline = inline;
|
|
353
|
+
}
|
|
354
|
+
const result = await this.request(
|
|
355
|
+
"POST",
|
|
356
|
+
this.repoPath(repoSlug, "pullrequests", String(prId), "comments"),
|
|
357
|
+
payload
|
|
358
|
+
);
|
|
359
|
+
if (!result) {
|
|
360
|
+
throw new BitbucketError(`Failed to add comment to PR #${prId}`);
|
|
361
|
+
}
|
|
362
|
+
return result;
|
|
363
|
+
}
|
|
364
|
+
async approvePr(repoSlug, prId) {
|
|
365
|
+
const result = await this.request(
|
|
366
|
+
"POST",
|
|
367
|
+
this.repoPath(repoSlug, "pullrequests", String(prId), "approve")
|
|
368
|
+
);
|
|
369
|
+
if (!result) {
|
|
370
|
+
throw new BitbucketError(`Failed to approve PR #${prId}`);
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
async unapprovePr(repoSlug, prId) {
|
|
375
|
+
await this.request("DELETE", this.repoPath(repoSlug, "pullrequests", String(prId), "approve"));
|
|
376
|
+
}
|
|
377
|
+
async requestChangesPr(repoSlug, prId) {
|
|
378
|
+
const result = await this.request(
|
|
379
|
+
"POST",
|
|
380
|
+
this.repoPath(repoSlug, "pullrequests", String(prId), "request-changes")
|
|
381
|
+
);
|
|
382
|
+
if (!result) {
|
|
383
|
+
throw new BitbucketError(`Failed to request changes on PR #${prId}`);
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
async declinePr(repoSlug, prId) {
|
|
388
|
+
const result = await this.request(
|
|
389
|
+
"POST",
|
|
390
|
+
this.repoPath(repoSlug, "pullrequests", String(prId), "decline")
|
|
391
|
+
);
|
|
392
|
+
if (!result) {
|
|
393
|
+
throw new BitbucketError(`Failed to decline PR #${prId}`);
|
|
394
|
+
}
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
async getPrDiff(repoSlug, prId) {
|
|
398
|
+
return await this.requestText(this.repoPath(repoSlug, "pullrequests", String(prId), "diff")) || "";
|
|
399
|
+
}
|
|
400
|
+
// ==================== PIPELINES ====================
|
|
401
|
+
/**
|
|
402
|
+
* Build the pipeline target object based on options.
|
|
403
|
+
* Supports branch triggers, commit triggers, and custom pipelines.
|
|
404
|
+
*/
|
|
405
|
+
buildPipelineTarget(options) {
|
|
406
|
+
if (options.branch && options.commit) {
|
|
407
|
+
throw new BitbucketError("Cannot specify both branch and commit - they are mutually exclusive");
|
|
408
|
+
}
|
|
409
|
+
if (options.commit) {
|
|
410
|
+
const target2 = {
|
|
411
|
+
type: "pipeline_commit_target",
|
|
412
|
+
commit: { hash: options.commit }
|
|
413
|
+
};
|
|
414
|
+
if (options.customPipeline) {
|
|
415
|
+
target2.selector = {
|
|
416
|
+
type: "custom",
|
|
417
|
+
pattern: options.customPipeline
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
return target2;
|
|
421
|
+
}
|
|
422
|
+
const target = {
|
|
423
|
+
type: "pipeline_ref_target",
|
|
424
|
+
ref_type: "branch",
|
|
425
|
+
ref_name: options.branch || "main"
|
|
426
|
+
};
|
|
427
|
+
if (options.customPipeline) {
|
|
428
|
+
target.selector = {
|
|
429
|
+
type: "custom",
|
|
430
|
+
pattern: options.customPipeline
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
return target;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Normalize pipeline variables to the array format expected by the API.
|
|
437
|
+
* Supports both array format (with secured flag) and simple object format.
|
|
438
|
+
*/
|
|
439
|
+
normalizePipelineVariables(variables) {
|
|
440
|
+
if (!variables) {
|
|
441
|
+
return void 0;
|
|
442
|
+
}
|
|
443
|
+
if (Array.isArray(variables)) {
|
|
444
|
+
return variables.map((v) => ({
|
|
445
|
+
key: v.key,
|
|
446
|
+
value: v.value,
|
|
447
|
+
...v.secured !== void 0 && { secured: v.secured }
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
450
|
+
return Object.entries(variables).map(([key, value]) => ({
|
|
451
|
+
key,
|
|
452
|
+
value
|
|
453
|
+
}));
|
|
454
|
+
}
|
|
455
|
+
async triggerPipeline(repoSlug, options = {}) {
|
|
456
|
+
const payload = {
|
|
457
|
+
target: this.buildPipelineTarget(options)
|
|
458
|
+
};
|
|
459
|
+
const normalizedVariables = this.normalizePipelineVariables(options.variables);
|
|
460
|
+
if (normalizedVariables && normalizedVariables.length > 0) {
|
|
461
|
+
payload.variables = normalizedVariables;
|
|
462
|
+
}
|
|
463
|
+
const result = await this.request(
|
|
464
|
+
"POST",
|
|
465
|
+
`${this.repoPath(repoSlug, "pipelines")}/`,
|
|
466
|
+
payload
|
|
467
|
+
);
|
|
468
|
+
const targetDesc = options.commit ? `commit ${options.commit}` : options.branch || "main";
|
|
469
|
+
const pipelineDesc = options.customPipeline ? `custom:${options.customPipeline}` : "default";
|
|
470
|
+
if (!result) {
|
|
471
|
+
throw new BitbucketError(`Failed to trigger ${pipelineDesc} pipeline on ${targetDesc}`);
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
async getPipeline(repoSlug, pipelineUuid) {
|
|
476
|
+
return this.request("GET", this.repoPath(repoSlug, "pipelines", ensureUuidBraces(pipelineUuid)));
|
|
477
|
+
}
|
|
478
|
+
async listPipelines(repoSlug, options = {}) {
|
|
479
|
+
return this.paginatedList(`${this.repoPath(repoSlug, "pipelines")}/`, {
|
|
480
|
+
limit: options.limit || 10,
|
|
481
|
+
sort: "-created_on"
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
async getPipelineSteps(repoSlug, pipelineUuid) {
|
|
485
|
+
return this.paginatedList(
|
|
486
|
+
`${this.repoPath(repoSlug, "pipelines", ensureUuidBraces(pipelineUuid), "steps")}/`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
async getPipelineLogs(repoSlug, pipelineUuid, stepUuid) {
|
|
490
|
+
const path = this.repoPath(
|
|
491
|
+
repoSlug,
|
|
492
|
+
"pipelines",
|
|
493
|
+
ensureUuidBraces(pipelineUuid),
|
|
494
|
+
"steps",
|
|
495
|
+
ensureUuidBraces(stepUuid),
|
|
496
|
+
"log"
|
|
497
|
+
);
|
|
498
|
+
return await this.requestText(path) || "";
|
|
499
|
+
}
|
|
500
|
+
async stopPipeline(repoSlug, pipelineUuid) {
|
|
501
|
+
await this.request(
|
|
502
|
+
"POST",
|
|
503
|
+
this.repoPath(repoSlug, "pipelines", ensureUuidBraces(pipelineUuid), "stopPipeline")
|
|
504
|
+
);
|
|
505
|
+
const result = await this.getPipeline(repoSlug, pipelineUuid);
|
|
506
|
+
return result || { uuid: pipelineUuid, state: { name: "STOPPED" } };
|
|
507
|
+
}
|
|
508
|
+
// ==================== PIPELINE VARIABLES ====================
|
|
509
|
+
async listPipelineVariables(repoSlug, options = {}) {
|
|
510
|
+
return this.paginatedList(
|
|
511
|
+
this.repoPath(repoSlug, "pipelines_config", "variables"),
|
|
512
|
+
{ limit: options.limit || 50 }
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
async getPipelineVariable(repoSlug, variableUuid) {
|
|
516
|
+
return this.request(
|
|
517
|
+
"GET",
|
|
518
|
+
this.repoPath(repoSlug, "pipelines_config", "variables", ensureUuidBraces(variableUuid))
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
async createPipelineVariable(repoSlug, key, value, secured = false) {
|
|
522
|
+
const result = await this.request(
|
|
523
|
+
"POST",
|
|
524
|
+
`${this.repoPath(repoSlug, "pipelines_config", "variables")}/`,
|
|
525
|
+
{ key, value, secured }
|
|
526
|
+
);
|
|
527
|
+
if (!result) {
|
|
528
|
+
throw new BitbucketError("Failed to create pipeline variable");
|
|
529
|
+
}
|
|
530
|
+
return result;
|
|
531
|
+
}
|
|
532
|
+
async updatePipelineVariable(repoSlug, variableUuid, value) {
|
|
533
|
+
const result = await this.request(
|
|
534
|
+
"PUT",
|
|
535
|
+
this.repoPath(repoSlug, "pipelines_config", "variables", ensureUuidBraces(variableUuid)),
|
|
536
|
+
{ value }
|
|
537
|
+
);
|
|
538
|
+
if (!result) {
|
|
539
|
+
throw new BitbucketError("Failed to update pipeline variable");
|
|
540
|
+
}
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
async deletePipelineVariable(repoSlug, variableUuid) {
|
|
544
|
+
await this.request(
|
|
545
|
+
"DELETE",
|
|
546
|
+
this.repoPath(repoSlug, "pipelines_config", "variables", ensureUuidBraces(variableUuid))
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
// ==================== BRANCHES ====================
|
|
550
|
+
async listBranches(repoSlug, options = {}) {
|
|
551
|
+
return this.paginatedList(this.repoPath(repoSlug, "refs", "branches"), {
|
|
552
|
+
limit: options.limit || 50
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
async getBranch(repoSlug, branchName) {
|
|
556
|
+
return this.request("GET", this.repoPath(repoSlug, "refs", "branches", branchName));
|
|
557
|
+
}
|
|
558
|
+
// ==================== COMMITS ====================
|
|
559
|
+
async listCommits(repoSlug, options = {}) {
|
|
560
|
+
return this.paginatedList(this.repoPath(repoSlug, "commits"), {
|
|
561
|
+
limit: options.limit || 20,
|
|
562
|
+
include: options.branch,
|
|
563
|
+
path: options.path
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
async getCommit(repoSlug, commit) {
|
|
567
|
+
return this.request("GET", this.repoPath(repoSlug, "commit", commit));
|
|
568
|
+
}
|
|
569
|
+
async compareCommits(repoSlug, base, head) {
|
|
570
|
+
return this.request("GET", this.repoPath(repoSlug, "diffstat", `${base}..${head}`));
|
|
571
|
+
}
|
|
572
|
+
async getCommitStatuses(repoSlug, commit, options = {}) {
|
|
573
|
+
return this.paginatedList(this.repoPath(repoSlug, "commit", commit, "statuses"), {
|
|
574
|
+
limit: options.limit || 20
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
async createCommitStatus(repoSlug, commit, options) {
|
|
578
|
+
const payload = {
|
|
579
|
+
state: options.state,
|
|
580
|
+
key: options.key
|
|
581
|
+
};
|
|
582
|
+
if (options.url) payload.url = options.url;
|
|
583
|
+
if (options.name) payload.name = options.name;
|
|
584
|
+
if (options.description) payload.description = options.description;
|
|
585
|
+
const result = await this.request(
|
|
586
|
+
"POST",
|
|
587
|
+
this.repoPath(repoSlug, "commit", commit, "statuses", "build"),
|
|
588
|
+
payload
|
|
589
|
+
);
|
|
590
|
+
if (!result) {
|
|
591
|
+
throw new BitbucketError(`Failed to create status for commit ${commit}`);
|
|
592
|
+
}
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
// ==================== PROJECTS ====================
|
|
596
|
+
async listProjects(options = {}) {
|
|
597
|
+
return this.paginatedList(`workspaces/${this.workspace}/projects`, {
|
|
598
|
+
limit: options.limit || 50
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
async getProject(projectKey) {
|
|
602
|
+
return this.request("GET", `workspaces/${this.workspace}/projects/${projectKey}`);
|
|
603
|
+
}
|
|
604
|
+
// ==================== DEPLOYMENTS ====================
|
|
605
|
+
async listEnvironments(repoSlug, options = {}) {
|
|
606
|
+
return this.paginatedList(this.repoPath(repoSlug, "environments"), {
|
|
607
|
+
limit: options.limit || 20
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
async getEnvironment(repoSlug, environmentUuid) {
|
|
611
|
+
return this.request(
|
|
612
|
+
"GET",
|
|
613
|
+
this.repoPath(repoSlug, "environments", ensureUuidBraces(environmentUuid))
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
async listDeploymentHistory(repoSlug, environmentUuid, options = {}) {
|
|
617
|
+
return this.paginatedList(this.repoPath(repoSlug, "deployments"), {
|
|
618
|
+
limit: options.limit || 20,
|
|
619
|
+
environment: ensureUuidBraces(environmentUuid),
|
|
620
|
+
sort: "-state.started_on"
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
// ==================== WEBHOOKS ====================
|
|
624
|
+
async listWebhooks(repoSlug, options = {}) {
|
|
625
|
+
return this.paginatedList(this.repoPath(repoSlug, "hooks"), {
|
|
626
|
+
limit: options.limit || 50
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
async createWebhook(repoSlug, options) {
|
|
630
|
+
const payload = {
|
|
631
|
+
url: options.url,
|
|
632
|
+
events: options.events,
|
|
633
|
+
active: options.active ?? true
|
|
634
|
+
};
|
|
635
|
+
if (options.description) {
|
|
636
|
+
payload.description = options.description;
|
|
637
|
+
}
|
|
638
|
+
const result = await this.request(
|
|
639
|
+
"POST",
|
|
640
|
+
this.repoPath(repoSlug, "hooks"),
|
|
641
|
+
payload
|
|
642
|
+
);
|
|
643
|
+
if (!result) {
|
|
644
|
+
throw new BitbucketError("Failed to create webhook");
|
|
645
|
+
}
|
|
646
|
+
return result;
|
|
647
|
+
}
|
|
648
|
+
async getWebhook(repoSlug, webhookUid) {
|
|
649
|
+
return this.request("GET", this.repoPath(repoSlug, "hooks", ensureUuidBraces(webhookUid)));
|
|
650
|
+
}
|
|
651
|
+
async deleteWebhook(repoSlug, webhookUid) {
|
|
652
|
+
await this.request("DELETE", this.repoPath(repoSlug, "hooks", ensureUuidBraces(webhookUid)));
|
|
653
|
+
}
|
|
654
|
+
// ==================== TAGS ====================
|
|
655
|
+
async listTags(repoSlug, options = {}) {
|
|
656
|
+
return this.paginatedList(this.repoPath(repoSlug, "refs", "tags"), {
|
|
657
|
+
limit: options.limit || 50,
|
|
658
|
+
sort: "-target.date"
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
async createTag(repoSlug, name, target, message) {
|
|
662
|
+
const payload = {
|
|
663
|
+
name,
|
|
664
|
+
target: { hash: target }
|
|
665
|
+
};
|
|
666
|
+
if (message) {
|
|
667
|
+
payload.message = message;
|
|
668
|
+
}
|
|
669
|
+
const result = await this.request(
|
|
670
|
+
"POST",
|
|
671
|
+
this.repoPath(repoSlug, "refs", "tags"),
|
|
672
|
+
payload
|
|
673
|
+
);
|
|
674
|
+
if (!result) {
|
|
675
|
+
throw new BitbucketError(`Failed to create tag ${name}`);
|
|
676
|
+
}
|
|
677
|
+
return result;
|
|
678
|
+
}
|
|
679
|
+
async deleteTag(repoSlug, tagName) {
|
|
680
|
+
await this.request("DELETE", this.repoPath(repoSlug, "refs", "tags", tagName));
|
|
681
|
+
}
|
|
682
|
+
// ==================== BRANCH RESTRICTIONS ====================
|
|
683
|
+
async listBranchRestrictions(repoSlug, options = {}) {
|
|
684
|
+
return this.paginatedList(this.repoPath(repoSlug, "branch-restrictions"), {
|
|
685
|
+
limit: options.limit || 50
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
async createBranchRestriction(repoSlug, options) {
|
|
689
|
+
const payload = {
|
|
690
|
+
kind: options.kind,
|
|
691
|
+
branch_match_kind: options.branchMatchKind || "glob"
|
|
692
|
+
};
|
|
693
|
+
if (options.branchMatchKind === "glob" && options.pattern) {
|
|
694
|
+
payload.pattern = options.pattern;
|
|
695
|
+
}
|
|
696
|
+
if (options.branchMatchKind === "branching_model" && options.branchType) {
|
|
697
|
+
payload.branch_type = options.branchType;
|
|
698
|
+
}
|
|
699
|
+
if (options.value !== void 0) {
|
|
700
|
+
payload.value = options.value;
|
|
701
|
+
}
|
|
702
|
+
const result = await this.request(
|
|
703
|
+
"POST",
|
|
704
|
+
this.repoPath(repoSlug, "branch-restrictions"),
|
|
705
|
+
payload
|
|
706
|
+
);
|
|
707
|
+
if (!result) {
|
|
708
|
+
throw new BitbucketError(`Failed to create branch restriction ${options.kind}`);
|
|
709
|
+
}
|
|
710
|
+
return result;
|
|
711
|
+
}
|
|
712
|
+
async deleteBranchRestriction(repoSlug, restrictionId) {
|
|
713
|
+
await this.request(
|
|
714
|
+
"DELETE",
|
|
715
|
+
this.repoPath(repoSlug, "branch-restrictions", String(restrictionId))
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
// ==================== SOURCE ====================
|
|
719
|
+
async getFileContent(repoSlug, path, ref = "main") {
|
|
720
|
+
return this.requestText(this.repoPath(repoSlug, "src", ref, path));
|
|
721
|
+
}
|
|
722
|
+
async listDirectory(repoSlug, path = "", options = {}) {
|
|
723
|
+
const endpoint = path ? this.repoPath(repoSlug, "src", options.ref || "main", path) : this.repoPath(repoSlug, "src", options.ref || "main");
|
|
724
|
+
return this.paginatedList(endpoint, { limit: options.limit || 100 });
|
|
725
|
+
}
|
|
726
|
+
// ==================== PERMISSIONS ====================
|
|
727
|
+
async listUserPermissions(repoSlug, options = {}) {
|
|
728
|
+
return this.paginatedList(this.repoPath(repoSlug, "permissions-config", "users"), {
|
|
729
|
+
limit: options.limit || 50
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
async getUserPermission(repoSlug, selectedUser) {
|
|
733
|
+
return this.request(
|
|
734
|
+
"GET",
|
|
735
|
+
this.repoPath(repoSlug, "permissions-config", "users", selectedUser)
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
async updateUserPermission(repoSlug, selectedUser, permission) {
|
|
739
|
+
const result = await this.request(
|
|
740
|
+
"PUT",
|
|
741
|
+
this.repoPath(repoSlug, "permissions-config", "users", selectedUser),
|
|
742
|
+
{ permission }
|
|
743
|
+
);
|
|
744
|
+
if (!result) {
|
|
745
|
+
throw new BitbucketError(`Failed to update permission for user ${selectedUser}`);
|
|
746
|
+
}
|
|
747
|
+
return result;
|
|
748
|
+
}
|
|
749
|
+
async deleteUserPermission(repoSlug, selectedUser) {
|
|
750
|
+
await this.request(
|
|
751
|
+
"DELETE",
|
|
752
|
+
this.repoPath(repoSlug, "permissions-config", "users", selectedUser)
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
async listGroupPermissions(repoSlug, options = {}) {
|
|
756
|
+
return this.paginatedList(this.repoPath(repoSlug, "permissions-config", "groups"), {
|
|
757
|
+
limit: options.limit || 50
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
async getGroupPermission(repoSlug, groupSlug) {
|
|
761
|
+
return this.request(
|
|
762
|
+
"GET",
|
|
763
|
+
this.repoPath(repoSlug, "permissions-config", "groups", groupSlug)
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
async updateGroupPermission(repoSlug, groupSlug, permission) {
|
|
767
|
+
const result = await this.request(
|
|
768
|
+
"PUT",
|
|
769
|
+
this.repoPath(repoSlug, "permissions-config", "groups", groupSlug),
|
|
770
|
+
{ permission }
|
|
771
|
+
);
|
|
772
|
+
if (!result) {
|
|
773
|
+
throw new BitbucketError(`Failed to update permission for group ${groupSlug}`);
|
|
774
|
+
}
|
|
775
|
+
return result;
|
|
776
|
+
}
|
|
777
|
+
async deleteGroupPermission(repoSlug, groupSlug) {
|
|
778
|
+
await this.request(
|
|
779
|
+
"DELETE",
|
|
780
|
+
this.repoPath(repoSlug, "permissions-config", "groups", groupSlug)
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
// ==================== UTILITIES ====================
|
|
784
|
+
extractPrUrl(pr) {
|
|
785
|
+
return pr.links?.html?.href || "";
|
|
786
|
+
}
|
|
787
|
+
extractCloneUrls(repo) {
|
|
788
|
+
const urls = {};
|
|
789
|
+
for (const link of repo.links?.clone || []) {
|
|
790
|
+
const name = (link.name || "").toLowerCase();
|
|
791
|
+
if (name === "https" || name === "ssh") {
|
|
792
|
+
urls[name] = link.href || "";
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
urls.html = repo.links?.html?.href || "";
|
|
796
|
+
return urls;
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
var clientInstance = null;
|
|
800
|
+
function getClient() {
|
|
801
|
+
if (!clientInstance) {
|
|
802
|
+
clientInstance = new BitbucketClient();
|
|
803
|
+
}
|
|
804
|
+
return clientInstance;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// src/tools/repositories.ts
|
|
808
|
+
var definitions = [
|
|
809
|
+
{
|
|
810
|
+
name: "get_repository",
|
|
811
|
+
description: "Get information about a Bitbucket repository.",
|
|
812
|
+
inputSchema: {
|
|
813
|
+
type: "object",
|
|
814
|
+
properties: {
|
|
815
|
+
repo_slug: {
|
|
816
|
+
type: "string",
|
|
817
|
+
description: 'Repository slug (e.g., "anzsic_classifier")'
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
required: ["repo_slug"]
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
name: "create_repository",
|
|
825
|
+
description: "Create a new Bitbucket repository.",
|
|
826
|
+
inputSchema: {
|
|
827
|
+
type: "object",
|
|
828
|
+
properties: {
|
|
829
|
+
repo_slug: {
|
|
830
|
+
type: "string",
|
|
831
|
+
description: "Repository slug (lowercase, no spaces)"
|
|
832
|
+
},
|
|
833
|
+
project_key: {
|
|
834
|
+
type: "string",
|
|
835
|
+
description: "Project key to create repo under (optional)"
|
|
836
|
+
},
|
|
837
|
+
is_private: {
|
|
838
|
+
type: "boolean",
|
|
839
|
+
description: "Whether repository is private (default: true)",
|
|
840
|
+
default: true
|
|
841
|
+
},
|
|
842
|
+
description: {
|
|
843
|
+
type: "string",
|
|
844
|
+
description: "Repository description",
|
|
845
|
+
default: ""
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
required: ["repo_slug"]
|
|
849
|
+
}
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
name: "delete_repository",
|
|
853
|
+
description: "Delete a Bitbucket repository. WARNING: This action is irreversible!",
|
|
854
|
+
inputSchema: {
|
|
855
|
+
type: "object",
|
|
856
|
+
properties: {
|
|
857
|
+
repo_slug: {
|
|
858
|
+
type: "string",
|
|
859
|
+
description: "Repository slug to delete"
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
required: ["repo_slug"]
|
|
863
|
+
}
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
name: "list_repositories",
|
|
867
|
+
description: "List and search repositories in the workspace.",
|
|
868
|
+
inputSchema: {
|
|
869
|
+
type: "object",
|
|
870
|
+
properties: {
|
|
871
|
+
project_key: {
|
|
872
|
+
type: "string",
|
|
873
|
+
description: "Filter by project key (optional)"
|
|
874
|
+
},
|
|
875
|
+
search: {
|
|
876
|
+
type: "string",
|
|
877
|
+
description: "Simple search term for repository name (optional). Uses fuzzy matching."
|
|
878
|
+
},
|
|
879
|
+
query: {
|
|
880
|
+
type: "string",
|
|
881
|
+
description: 'Advanced Bitbucket query syntax (optional). Examples: name ~ "api", is_private = false'
|
|
882
|
+
},
|
|
883
|
+
limit: {
|
|
884
|
+
type: "number",
|
|
885
|
+
description: "Maximum number of results (default: 50, max: 100)",
|
|
886
|
+
default: 50
|
|
887
|
+
}
|
|
888
|
+
},
|
|
889
|
+
required: []
|
|
890
|
+
}
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
name: "update_repository",
|
|
894
|
+
description: "Update repository settings (project, visibility, description, name).",
|
|
895
|
+
inputSchema: {
|
|
896
|
+
type: "object",
|
|
897
|
+
properties: {
|
|
898
|
+
repo_slug: {
|
|
899
|
+
type: "string",
|
|
900
|
+
description: "Repository slug"
|
|
901
|
+
},
|
|
902
|
+
project_key: {
|
|
903
|
+
type: "string",
|
|
904
|
+
description: "Move to different project (optional)"
|
|
905
|
+
},
|
|
906
|
+
is_private: {
|
|
907
|
+
type: "boolean",
|
|
908
|
+
description: "Change visibility (optional)"
|
|
909
|
+
},
|
|
910
|
+
description: {
|
|
911
|
+
type: "string",
|
|
912
|
+
description: "Update description (optional)"
|
|
913
|
+
},
|
|
914
|
+
name: {
|
|
915
|
+
type: "string",
|
|
916
|
+
description: "Rename repository (optional)"
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
required: ["repo_slug"]
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
];
|
|
923
|
+
var handlers = {
|
|
924
|
+
get_repository: async (args) => {
|
|
925
|
+
const client = getClient();
|
|
926
|
+
const result = await client.getRepository(args.repo_slug);
|
|
927
|
+
if (!result) {
|
|
928
|
+
return notFoundResponse("Repository", args.repo_slug);
|
|
929
|
+
}
|
|
930
|
+
return {
|
|
931
|
+
name: result.name,
|
|
932
|
+
full_name: result.full_name,
|
|
933
|
+
private: result.is_private,
|
|
934
|
+
project: result.project?.key,
|
|
935
|
+
description: result.description || "",
|
|
936
|
+
main_branch: result.mainbranch?.name,
|
|
937
|
+
clone_urls: client.extractCloneUrls(result),
|
|
938
|
+
created: result.created_on,
|
|
939
|
+
updated: result.updated_on
|
|
940
|
+
};
|
|
941
|
+
},
|
|
942
|
+
create_repository: async (args) => {
|
|
943
|
+
const client = getClient();
|
|
944
|
+
const result = await client.createRepository(args.repo_slug, {
|
|
945
|
+
projectKey: args.project_key,
|
|
946
|
+
isPrivate: args.is_private,
|
|
947
|
+
description: args.description
|
|
948
|
+
});
|
|
949
|
+
return {
|
|
950
|
+
name: result.name,
|
|
951
|
+
full_name: result.full_name,
|
|
952
|
+
clone_urls: client.extractCloneUrls(result)
|
|
953
|
+
};
|
|
954
|
+
},
|
|
955
|
+
delete_repository: async (args) => {
|
|
956
|
+
const client = getClient();
|
|
957
|
+
await client.deleteRepository(args.repo_slug);
|
|
958
|
+
return {};
|
|
959
|
+
},
|
|
960
|
+
list_repositories: async (args) => {
|
|
961
|
+
const client = getClient();
|
|
962
|
+
let effectiveQuery = args.query;
|
|
963
|
+
if (args.search && !args.query) {
|
|
964
|
+
const safeSearch = sanitizeSearchTerm(args.search);
|
|
965
|
+
effectiveQuery = `name ~ "${safeSearch}"`;
|
|
966
|
+
}
|
|
967
|
+
const repos = await client.listRepositories({
|
|
968
|
+
projectKey: args.project_key,
|
|
969
|
+
query: effectiveQuery,
|
|
970
|
+
limit: validateLimit(args.limit || 50)
|
|
971
|
+
});
|
|
972
|
+
return {
|
|
973
|
+
repositories: repos.map((r) => ({
|
|
974
|
+
name: r.name,
|
|
975
|
+
full_name: r.full_name,
|
|
976
|
+
private: r.is_private,
|
|
977
|
+
project: r.project?.key,
|
|
978
|
+
description: r.description || ""
|
|
979
|
+
}))
|
|
980
|
+
};
|
|
981
|
+
},
|
|
982
|
+
update_repository: async (args) => {
|
|
983
|
+
const client = getClient();
|
|
984
|
+
const result = await client.updateRepository(args.repo_slug, {
|
|
985
|
+
projectKey: args.project_key,
|
|
986
|
+
isPrivate: args.is_private,
|
|
987
|
+
description: args.description,
|
|
988
|
+
name: args.name
|
|
989
|
+
});
|
|
990
|
+
return {
|
|
991
|
+
name: result.name,
|
|
992
|
+
full_name: result.full_name,
|
|
993
|
+
project: result.project?.key,
|
|
994
|
+
private: result.is_private,
|
|
995
|
+
description: result.description || ""
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
// src/types.ts
|
|
1001
|
+
var PRState = /* @__PURE__ */ ((PRState2) => {
|
|
1002
|
+
PRState2["OPEN"] = "OPEN";
|
|
1003
|
+
PRState2["MERGED"] = "MERGED";
|
|
1004
|
+
PRState2["DECLINED"] = "DECLINED";
|
|
1005
|
+
PRState2["SUPERSEDED"] = "SUPERSEDED";
|
|
1006
|
+
return PRState2;
|
|
1007
|
+
})(PRState || {});
|
|
1008
|
+
var MergeStrategy = /* @__PURE__ */ ((MergeStrategy2) => {
|
|
1009
|
+
MergeStrategy2["MERGE_COMMIT"] = "merge_commit";
|
|
1010
|
+
MergeStrategy2["SQUASH"] = "squash";
|
|
1011
|
+
MergeStrategy2["FAST_FORWARD"] = "fast_forward";
|
|
1012
|
+
return MergeStrategy2;
|
|
1013
|
+
})(MergeStrategy || {});
|
|
1014
|
+
var CommitStatusState = /* @__PURE__ */ ((CommitStatusState2) => {
|
|
1015
|
+
CommitStatusState2["SUCCESSFUL"] = "SUCCESSFUL";
|
|
1016
|
+
CommitStatusState2["FAILED"] = "FAILED";
|
|
1017
|
+
CommitStatusState2["INPROGRESS"] = "INPROGRESS";
|
|
1018
|
+
CommitStatusState2["STOPPED"] = "STOPPED";
|
|
1019
|
+
return CommitStatusState2;
|
|
1020
|
+
})(CommitStatusState || {});
|
|
1021
|
+
|
|
1022
|
+
// src/tools/pull-requests.ts
|
|
1023
|
+
var definitions2 = [
|
|
1024
|
+
{
|
|
1025
|
+
name: "create_pull_request",
|
|
1026
|
+
description: "Create a pull request in a Bitbucket repository.",
|
|
1027
|
+
inputSchema: {
|
|
1028
|
+
type: "object",
|
|
1029
|
+
properties: {
|
|
1030
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1031
|
+
title: { type: "string", description: "PR title" },
|
|
1032
|
+
source_branch: { type: "string", description: "Source branch name" },
|
|
1033
|
+
destination_branch: { type: "string", description: "Target branch (default: main)", default: "main" },
|
|
1034
|
+
description: { type: "string", description: "PR description in markdown", default: "" },
|
|
1035
|
+
close_source_branch: { type: "boolean", description: "Delete source branch after merge", default: true }
|
|
1036
|
+
},
|
|
1037
|
+
required: ["repo_slug", "title", "source_branch"]
|
|
1038
|
+
}
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
name: "get_pull_request",
|
|
1042
|
+
description: "Get information about a pull request.",
|
|
1043
|
+
inputSchema: {
|
|
1044
|
+
type: "object",
|
|
1045
|
+
properties: {
|
|
1046
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1047
|
+
pr_id: { type: "number", description: "Pull request ID" }
|
|
1048
|
+
},
|
|
1049
|
+
required: ["repo_slug", "pr_id"]
|
|
1050
|
+
}
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
name: "list_pull_requests",
|
|
1054
|
+
description: "List pull requests in a repository.",
|
|
1055
|
+
inputSchema: {
|
|
1056
|
+
type: "object",
|
|
1057
|
+
properties: {
|
|
1058
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1059
|
+
state: { type: "string", description: "Filter by state: OPEN, MERGED, DECLINED, SUPERSEDED", default: "OPEN" },
|
|
1060
|
+
limit: { type: "number", description: "Maximum results (default: 20, max: 100)", default: 20 }
|
|
1061
|
+
},
|
|
1062
|
+
required: ["repo_slug"]
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
name: "merge_pull_request",
|
|
1067
|
+
description: "Merge a pull request.",
|
|
1068
|
+
inputSchema: {
|
|
1069
|
+
type: "object",
|
|
1070
|
+
properties: {
|
|
1071
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1072
|
+
pr_id: { type: "number", description: "Pull request ID" },
|
|
1073
|
+
merge_strategy: { type: "string", description: "merge_commit, squash, or fast_forward", default: "merge_commit" },
|
|
1074
|
+
close_source_branch: { type: "boolean", description: "Delete source branch after merge", default: true },
|
|
1075
|
+
message: { type: "string", description: "Optional merge commit message" }
|
|
1076
|
+
},
|
|
1077
|
+
required: ["repo_slug", "pr_id"]
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
{
|
|
1081
|
+
name: "list_pr_comments",
|
|
1082
|
+
description: "List comments on a pull request.",
|
|
1083
|
+
inputSchema: {
|
|
1084
|
+
type: "object",
|
|
1085
|
+
properties: {
|
|
1086
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1087
|
+
pr_id: { type: "number", description: "Pull request ID" },
|
|
1088
|
+
limit: { type: "number", description: "Maximum results (default: 50)", default: 50 }
|
|
1089
|
+
},
|
|
1090
|
+
required: ["repo_slug", "pr_id"]
|
|
1091
|
+
}
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
name: "add_pr_comment",
|
|
1095
|
+
description: "Add a comment to a pull request. Can add general or inline comments.",
|
|
1096
|
+
inputSchema: {
|
|
1097
|
+
type: "object",
|
|
1098
|
+
properties: {
|
|
1099
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1100
|
+
pr_id: { type: "number", description: "Pull request ID" },
|
|
1101
|
+
content: { type: "string", description: "Comment content (markdown supported)" },
|
|
1102
|
+
file_path: { type: "string", description: "File path for inline comment (optional)" },
|
|
1103
|
+
line: { type: "number", description: "Line number for inline comment (optional, requires file_path)" }
|
|
1104
|
+
},
|
|
1105
|
+
required: ["repo_slug", "pr_id", "content"]
|
|
1106
|
+
}
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
name: "approve_pr",
|
|
1110
|
+
description: "Approve a pull request.",
|
|
1111
|
+
inputSchema: {
|
|
1112
|
+
type: "object",
|
|
1113
|
+
properties: {
|
|
1114
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1115
|
+
pr_id: { type: "number", description: "Pull request ID" }
|
|
1116
|
+
},
|
|
1117
|
+
required: ["repo_slug", "pr_id"]
|
|
1118
|
+
}
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
name: "unapprove_pr",
|
|
1122
|
+
description: "Remove your approval from a pull request.",
|
|
1123
|
+
inputSchema: {
|
|
1124
|
+
type: "object",
|
|
1125
|
+
properties: {
|
|
1126
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1127
|
+
pr_id: { type: "number", description: "Pull request ID" }
|
|
1128
|
+
},
|
|
1129
|
+
required: ["repo_slug", "pr_id"]
|
|
1130
|
+
}
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
name: "request_changes_pr",
|
|
1134
|
+
description: "Request changes on a pull request.",
|
|
1135
|
+
inputSchema: {
|
|
1136
|
+
type: "object",
|
|
1137
|
+
properties: {
|
|
1138
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1139
|
+
pr_id: { type: "number", description: "Pull request ID" }
|
|
1140
|
+
},
|
|
1141
|
+
required: ["repo_slug", "pr_id"]
|
|
1142
|
+
}
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
name: "decline_pr",
|
|
1146
|
+
description: "Decline (close without merging) a pull request.",
|
|
1147
|
+
inputSchema: {
|
|
1148
|
+
type: "object",
|
|
1149
|
+
properties: {
|
|
1150
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1151
|
+
pr_id: { type: "number", description: "Pull request ID" }
|
|
1152
|
+
},
|
|
1153
|
+
required: ["repo_slug", "pr_id"]
|
|
1154
|
+
}
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
name: "get_pr_diff",
|
|
1158
|
+
description: "Get the diff of a pull request.",
|
|
1159
|
+
inputSchema: {
|
|
1160
|
+
type: "object",
|
|
1161
|
+
properties: {
|
|
1162
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1163
|
+
pr_id: { type: "number", description: "Pull request ID" }
|
|
1164
|
+
},
|
|
1165
|
+
required: ["repo_slug", "pr_id"]
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
];
|
|
1169
|
+
var handlers2 = {
|
|
1170
|
+
create_pull_request: async (args) => {
|
|
1171
|
+
const client = getClient();
|
|
1172
|
+
const result = await client.createPullRequest(args.repo_slug, {
|
|
1173
|
+
title: args.title,
|
|
1174
|
+
sourceBranch: args.source_branch,
|
|
1175
|
+
destinationBranch: args.destination_branch || "main",
|
|
1176
|
+
description: args.description,
|
|
1177
|
+
closeSourceBranch: args.close_source_branch ?? true
|
|
1178
|
+
});
|
|
1179
|
+
return {
|
|
1180
|
+
id: result.id,
|
|
1181
|
+
title: result.title,
|
|
1182
|
+
state: result.state,
|
|
1183
|
+
url: client.extractPrUrl(result)
|
|
1184
|
+
};
|
|
1185
|
+
},
|
|
1186
|
+
get_pull_request: async (args) => {
|
|
1187
|
+
const client = getClient();
|
|
1188
|
+
const result = await client.getPullRequest(args.repo_slug, args.pr_id);
|
|
1189
|
+
if (!result) {
|
|
1190
|
+
return notFoundResponse("PR", `#${args.pr_id}`);
|
|
1191
|
+
}
|
|
1192
|
+
return {
|
|
1193
|
+
id: result.id,
|
|
1194
|
+
title: result.title,
|
|
1195
|
+
description: result.description,
|
|
1196
|
+
state: result.state,
|
|
1197
|
+
author: result.author?.display_name,
|
|
1198
|
+
source_branch: result.source?.branch?.name,
|
|
1199
|
+
destination_branch: result.destination?.branch?.name,
|
|
1200
|
+
reviewers: result.reviewers?.map((r) => r.display_name) || [],
|
|
1201
|
+
url: client.extractPrUrl(result),
|
|
1202
|
+
created: result.created_on,
|
|
1203
|
+
updated: result.updated_on
|
|
1204
|
+
};
|
|
1205
|
+
},
|
|
1206
|
+
list_pull_requests: async (args) => {
|
|
1207
|
+
const client = getClient();
|
|
1208
|
+
const state = (args.state || "OPEN").toUpperCase();
|
|
1209
|
+
const validState = Object.values(PRState).includes(state) ? state : "OPEN";
|
|
1210
|
+
const prs = await client.listPullRequests(args.repo_slug, {
|
|
1211
|
+
state: validState,
|
|
1212
|
+
limit: validateLimit(args.limit || 20)
|
|
1213
|
+
});
|
|
1214
|
+
return {
|
|
1215
|
+
pull_requests: prs.map((pr) => ({
|
|
1216
|
+
id: pr.id,
|
|
1217
|
+
title: pr.title,
|
|
1218
|
+
state: pr.state,
|
|
1219
|
+
author: pr.author?.display_name,
|
|
1220
|
+
source_branch: pr.source?.branch?.name,
|
|
1221
|
+
destination_branch: pr.destination?.branch?.name,
|
|
1222
|
+
url: client.extractPrUrl(pr)
|
|
1223
|
+
}))
|
|
1224
|
+
};
|
|
1225
|
+
},
|
|
1226
|
+
merge_pull_request: async (args) => {
|
|
1227
|
+
const client = getClient();
|
|
1228
|
+
const strategy = (args.merge_strategy || "merge_commit").toLowerCase();
|
|
1229
|
+
const validStrategy = Object.values(MergeStrategy).includes(strategy) ? strategy : "merge_commit";
|
|
1230
|
+
const result = await client.mergePullRequest(args.repo_slug, args.pr_id, {
|
|
1231
|
+
mergeStrategy: validStrategy,
|
|
1232
|
+
closeSourceBranch: args.close_source_branch ?? true,
|
|
1233
|
+
message: args.message
|
|
1234
|
+
});
|
|
1235
|
+
return {
|
|
1236
|
+
id: result.id,
|
|
1237
|
+
state: result.state,
|
|
1238
|
+
merge_commit: result.merge_commit?.hash,
|
|
1239
|
+
url: client.extractPrUrl(result)
|
|
1240
|
+
};
|
|
1241
|
+
},
|
|
1242
|
+
list_pr_comments: async (args) => {
|
|
1243
|
+
const client = getClient();
|
|
1244
|
+
const comments = await client.listPrComments(args.repo_slug, args.pr_id, {
|
|
1245
|
+
limit: validateLimit(args.limit || 50)
|
|
1246
|
+
});
|
|
1247
|
+
return {
|
|
1248
|
+
pr_id: args.pr_id,
|
|
1249
|
+
comments: comments.map((c) => ({
|
|
1250
|
+
id: c.id,
|
|
1251
|
+
content: c.content?.raw || "",
|
|
1252
|
+
author: c.user?.display_name,
|
|
1253
|
+
created: c.created_on,
|
|
1254
|
+
inline: c.inline ? { path: c.inline.path, line: c.inline.to } : void 0
|
|
1255
|
+
}))
|
|
1256
|
+
};
|
|
1257
|
+
},
|
|
1258
|
+
add_pr_comment: async (args) => {
|
|
1259
|
+
const client = getClient();
|
|
1260
|
+
let inline;
|
|
1261
|
+
if (args.file_path && args.line) {
|
|
1262
|
+
inline = { path: args.file_path, to: args.line };
|
|
1263
|
+
}
|
|
1264
|
+
const result = await client.addPrComment(
|
|
1265
|
+
args.repo_slug,
|
|
1266
|
+
args.pr_id,
|
|
1267
|
+
args.content,
|
|
1268
|
+
inline
|
|
1269
|
+
);
|
|
1270
|
+
return {
|
|
1271
|
+
id: result.id,
|
|
1272
|
+
content: result.content?.raw || "",
|
|
1273
|
+
inline
|
|
1274
|
+
};
|
|
1275
|
+
},
|
|
1276
|
+
approve_pr: async (args) => {
|
|
1277
|
+
const client = getClient();
|
|
1278
|
+
const result = await client.approvePr(args.repo_slug, args.pr_id);
|
|
1279
|
+
return {
|
|
1280
|
+
pr_id: args.pr_id,
|
|
1281
|
+
approved_by: result.user?.display_name
|
|
1282
|
+
};
|
|
1283
|
+
},
|
|
1284
|
+
unapprove_pr: async (args) => {
|
|
1285
|
+
const client = getClient();
|
|
1286
|
+
await client.unapprovePr(args.repo_slug, args.pr_id);
|
|
1287
|
+
return { pr_id: args.pr_id };
|
|
1288
|
+
},
|
|
1289
|
+
request_changes_pr: async (args) => {
|
|
1290
|
+
const client = getClient();
|
|
1291
|
+
const result = await client.requestChangesPr(args.repo_slug, args.pr_id);
|
|
1292
|
+
return {
|
|
1293
|
+
pr_id: args.pr_id,
|
|
1294
|
+
requested_by: result.user?.display_name
|
|
1295
|
+
};
|
|
1296
|
+
},
|
|
1297
|
+
decline_pr: async (args) => {
|
|
1298
|
+
const client = getClient();
|
|
1299
|
+
const result = await client.declinePr(args.repo_slug, args.pr_id);
|
|
1300
|
+
return {
|
|
1301
|
+
pr_id: args.pr_id,
|
|
1302
|
+
state: result.state
|
|
1303
|
+
};
|
|
1304
|
+
},
|
|
1305
|
+
get_pr_diff: async (args) => {
|
|
1306
|
+
const client = getClient();
|
|
1307
|
+
const diff = await client.getPrDiff(args.repo_slug, args.pr_id);
|
|
1308
|
+
if (!diff) {
|
|
1309
|
+
return { error: `PR #${args.pr_id} not found or has no diff` };
|
|
1310
|
+
}
|
|
1311
|
+
const maxLength = 5e4;
|
|
1312
|
+
const truncated = diff.length > maxLength;
|
|
1313
|
+
return {
|
|
1314
|
+
pr_id: args.pr_id,
|
|
1315
|
+
diff: truncated ? diff.substring(0, maxLength) : diff,
|
|
1316
|
+
truncated,
|
|
1317
|
+
total_length: diff.length
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
|
|
1322
|
+
// src/tools/pipelines.ts
|
|
1323
|
+
var definitions3 = [
|
|
1324
|
+
{
|
|
1325
|
+
name: "trigger_pipeline",
|
|
1326
|
+
description: 'Trigger a pipeline run. Supports custom pipelines (from "custom:" section in bitbucket-pipelines.yml) and commit-based triggers.',
|
|
1327
|
+
inputSchema: {
|
|
1328
|
+
type: "object",
|
|
1329
|
+
properties: {
|
|
1330
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1331
|
+
branch: { type: "string", description: "Branch to run pipeline on (default: main). Mutually exclusive with commit.", default: "main" },
|
|
1332
|
+
commit: { type: "string", description: "Commit hash to run pipeline on. Mutually exclusive with branch." },
|
|
1333
|
+
custom_pipeline: { type: "string", description: 'Name of custom pipeline from "custom:" section (e.g., "deploy-staging", "dry-run")' },
|
|
1334
|
+
variables: {
|
|
1335
|
+
type: "array",
|
|
1336
|
+
description: "Pipeline variables. Can be array of {key, value, secured?} or simple {key: value} object for backwards compatibility.",
|
|
1337
|
+
items: {
|
|
1338
|
+
type: "object",
|
|
1339
|
+
properties: {
|
|
1340
|
+
key: { type: "string", description: "Variable name" },
|
|
1341
|
+
value: { type: "string", description: "Variable value" },
|
|
1342
|
+
secured: { type: "boolean", description: "Whether to mark as secured (encrypted)", default: false }
|
|
1343
|
+
},
|
|
1344
|
+
required: ["key", "value"]
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
},
|
|
1348
|
+
required: ["repo_slug"]
|
|
1349
|
+
}
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
name: "get_pipeline",
|
|
1353
|
+
description: "Get status of a pipeline run.",
|
|
1354
|
+
inputSchema: {
|
|
1355
|
+
type: "object",
|
|
1356
|
+
properties: {
|
|
1357
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1358
|
+
pipeline_uuid: { type: "string", description: "Pipeline UUID" }
|
|
1359
|
+
},
|
|
1360
|
+
required: ["repo_slug", "pipeline_uuid"]
|
|
1361
|
+
}
|
|
1362
|
+
},
|
|
1363
|
+
{
|
|
1364
|
+
name: "list_pipelines",
|
|
1365
|
+
description: "List recent pipeline runs for a repository.",
|
|
1366
|
+
inputSchema: {
|
|
1367
|
+
type: "object",
|
|
1368
|
+
properties: {
|
|
1369
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1370
|
+
limit: { type: "number", description: "Maximum results (default: 10)", default: 10 }
|
|
1371
|
+
},
|
|
1372
|
+
required: ["repo_slug"]
|
|
1373
|
+
}
|
|
1374
|
+
},
|
|
1375
|
+
{
|
|
1376
|
+
name: "get_pipeline_logs",
|
|
1377
|
+
description: "Get logs for a pipeline run. If step_uuid is not provided, returns list of steps.",
|
|
1378
|
+
inputSchema: {
|
|
1379
|
+
type: "object",
|
|
1380
|
+
properties: {
|
|
1381
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1382
|
+
pipeline_uuid: { type: "string", description: "Pipeline UUID" },
|
|
1383
|
+
step_uuid: { type: "string", description: "Step UUID (optional, get from steps list first)" }
|
|
1384
|
+
},
|
|
1385
|
+
required: ["repo_slug", "pipeline_uuid"]
|
|
1386
|
+
}
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
name: "stop_pipeline",
|
|
1390
|
+
description: "Stop a running pipeline.",
|
|
1391
|
+
inputSchema: {
|
|
1392
|
+
type: "object",
|
|
1393
|
+
properties: {
|
|
1394
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1395
|
+
pipeline_uuid: { type: "string", description: "Pipeline UUID" }
|
|
1396
|
+
},
|
|
1397
|
+
required: ["repo_slug", "pipeline_uuid"]
|
|
1398
|
+
}
|
|
1399
|
+
},
|
|
1400
|
+
{
|
|
1401
|
+
name: "list_pipeline_variables",
|
|
1402
|
+
description: "List pipeline variables for a repository.",
|
|
1403
|
+
inputSchema: {
|
|
1404
|
+
type: "object",
|
|
1405
|
+
properties: {
|
|
1406
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1407
|
+
limit: { type: "number", description: "Maximum results (default: 50)", default: 50 }
|
|
1408
|
+
},
|
|
1409
|
+
required: ["repo_slug"]
|
|
1410
|
+
}
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
name: "get_pipeline_variable",
|
|
1414
|
+
description: "Get details about a specific pipeline variable.",
|
|
1415
|
+
inputSchema: {
|
|
1416
|
+
type: "object",
|
|
1417
|
+
properties: {
|
|
1418
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1419
|
+
variable_uuid: { type: "string", description: "Variable UUID" }
|
|
1420
|
+
},
|
|
1421
|
+
required: ["repo_slug", "variable_uuid"]
|
|
1422
|
+
}
|
|
1423
|
+
},
|
|
1424
|
+
{
|
|
1425
|
+
name: "create_pipeline_variable",
|
|
1426
|
+
description: "Create a pipeline variable.",
|
|
1427
|
+
inputSchema: {
|
|
1428
|
+
type: "object",
|
|
1429
|
+
properties: {
|
|
1430
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1431
|
+
key: { type: "string", description: "Variable name" },
|
|
1432
|
+
value: { type: "string", description: "Variable value" },
|
|
1433
|
+
secured: { type: "boolean", description: "Encrypt the value (secured variables cannot be read back)", default: false }
|
|
1434
|
+
},
|
|
1435
|
+
required: ["repo_slug", "key", "value"]
|
|
1436
|
+
}
|
|
1437
|
+
},
|
|
1438
|
+
{
|
|
1439
|
+
name: "update_pipeline_variable",
|
|
1440
|
+
description: "Update a pipeline variable's value.",
|
|
1441
|
+
inputSchema: {
|
|
1442
|
+
type: "object",
|
|
1443
|
+
properties: {
|
|
1444
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1445
|
+
variable_uuid: { type: "string", description: "Variable UUID" },
|
|
1446
|
+
value: { type: "string", description: "New variable value" }
|
|
1447
|
+
},
|
|
1448
|
+
required: ["repo_slug", "variable_uuid", "value"]
|
|
1449
|
+
}
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
name: "delete_pipeline_variable",
|
|
1453
|
+
description: "Delete a pipeline variable.",
|
|
1454
|
+
inputSchema: {
|
|
1455
|
+
type: "object",
|
|
1456
|
+
properties: {
|
|
1457
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1458
|
+
variable_uuid: { type: "string", description: "Variable UUID" }
|
|
1459
|
+
},
|
|
1460
|
+
required: ["repo_slug", "variable_uuid"]
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
];
|
|
1464
|
+
var handlers3 = {
|
|
1465
|
+
trigger_pipeline: async (args) => {
|
|
1466
|
+
const client = getClient();
|
|
1467
|
+
const result = await client.triggerPipeline(args.repo_slug, {
|
|
1468
|
+
branch: args.branch,
|
|
1469
|
+
commit: args.commit,
|
|
1470
|
+
customPipeline: args.custom_pipeline,
|
|
1471
|
+
variables: args.variables
|
|
1472
|
+
});
|
|
1473
|
+
return {
|
|
1474
|
+
uuid: result.uuid,
|
|
1475
|
+
build_number: result.build_number,
|
|
1476
|
+
state: result.state?.name
|
|
1477
|
+
};
|
|
1478
|
+
},
|
|
1479
|
+
get_pipeline: async (args) => {
|
|
1480
|
+
const client = getClient();
|
|
1481
|
+
const result = await client.getPipeline(args.repo_slug, args.pipeline_uuid);
|
|
1482
|
+
if (!result) {
|
|
1483
|
+
return notFoundResponse("Pipeline", args.pipeline_uuid);
|
|
1484
|
+
}
|
|
1485
|
+
return {
|
|
1486
|
+
uuid: result.uuid,
|
|
1487
|
+
build_number: result.build_number,
|
|
1488
|
+
state: result.state?.name,
|
|
1489
|
+
result: result.state?.result?.name,
|
|
1490
|
+
branch: result.target?.ref_name,
|
|
1491
|
+
created: result.created_on,
|
|
1492
|
+
completed: result.completed_on,
|
|
1493
|
+
duration: result.duration_in_seconds
|
|
1494
|
+
};
|
|
1495
|
+
},
|
|
1496
|
+
list_pipelines: async (args) => {
|
|
1497
|
+
const client = getClient();
|
|
1498
|
+
const pipelines = await client.listPipelines(args.repo_slug, {
|
|
1499
|
+
limit: validateLimit(args.limit || 10)
|
|
1500
|
+
});
|
|
1501
|
+
return {
|
|
1502
|
+
pipelines: pipelines.map((p) => ({
|
|
1503
|
+
uuid: p.uuid,
|
|
1504
|
+
build_number: p.build_number,
|
|
1505
|
+
state: p.state?.name,
|
|
1506
|
+
result: p.state?.result?.name,
|
|
1507
|
+
branch: p.target?.ref_name,
|
|
1508
|
+
created: p.created_on
|
|
1509
|
+
}))
|
|
1510
|
+
};
|
|
1511
|
+
},
|
|
1512
|
+
get_pipeline_logs: async (args) => {
|
|
1513
|
+
const client = getClient();
|
|
1514
|
+
const pipelineUuid = args.pipeline_uuid;
|
|
1515
|
+
const stepUuid = args.step_uuid;
|
|
1516
|
+
if (!stepUuid) {
|
|
1517
|
+
const steps = await client.getPipelineSteps(args.repo_slug, pipelineUuid);
|
|
1518
|
+
return {
|
|
1519
|
+
message: "Provide step_uuid to get logs for a specific step",
|
|
1520
|
+
steps: steps.map((s) => ({
|
|
1521
|
+
uuid: s.uuid,
|
|
1522
|
+
name: s.name,
|
|
1523
|
+
state: s.state?.name,
|
|
1524
|
+
result: s.state?.result?.name,
|
|
1525
|
+
duration: s.duration_in_seconds
|
|
1526
|
+
}))
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
const logs = await client.getPipelineLogs(args.repo_slug, pipelineUuid, stepUuid);
|
|
1530
|
+
return {
|
|
1531
|
+
step_uuid: stepUuid,
|
|
1532
|
+
logs: logs || "(no logs available)"
|
|
1533
|
+
};
|
|
1534
|
+
},
|
|
1535
|
+
stop_pipeline: async (args) => {
|
|
1536
|
+
const client = getClient();
|
|
1537
|
+
const result = await client.stopPipeline(args.repo_slug, args.pipeline_uuid);
|
|
1538
|
+
return {
|
|
1539
|
+
uuid: result.uuid,
|
|
1540
|
+
state: result.state?.name
|
|
1541
|
+
};
|
|
1542
|
+
},
|
|
1543
|
+
list_pipeline_variables: async (args) => {
|
|
1544
|
+
const client = getClient();
|
|
1545
|
+
const variables = await client.listPipelineVariables(args.repo_slug, {
|
|
1546
|
+
limit: validateLimit(args.limit || 50)
|
|
1547
|
+
});
|
|
1548
|
+
return {
|
|
1549
|
+
variables: variables.map((v) => ({
|
|
1550
|
+
uuid: v.uuid,
|
|
1551
|
+
key: v.key,
|
|
1552
|
+
secured: v.secured,
|
|
1553
|
+
value: v.secured ? void 0 : v.value
|
|
1554
|
+
}))
|
|
1555
|
+
};
|
|
1556
|
+
},
|
|
1557
|
+
get_pipeline_variable: async (args) => {
|
|
1558
|
+
const client = getClient();
|
|
1559
|
+
const result = await client.getPipelineVariable(args.repo_slug, args.variable_uuid);
|
|
1560
|
+
if (!result) {
|
|
1561
|
+
return notFoundResponse("Pipeline variable", args.variable_uuid);
|
|
1562
|
+
}
|
|
1563
|
+
return {
|
|
1564
|
+
uuid: result.uuid,
|
|
1565
|
+
key: result.key,
|
|
1566
|
+
secured: result.secured,
|
|
1567
|
+
value: result.secured ? void 0 : result.value
|
|
1568
|
+
};
|
|
1569
|
+
},
|
|
1570
|
+
create_pipeline_variable: async (args) => {
|
|
1571
|
+
const client = getClient();
|
|
1572
|
+
const result = await client.createPipelineVariable(
|
|
1573
|
+
args.repo_slug,
|
|
1574
|
+
args.key,
|
|
1575
|
+
args.value,
|
|
1576
|
+
args.secured ?? false
|
|
1577
|
+
);
|
|
1578
|
+
return {
|
|
1579
|
+
uuid: result.uuid,
|
|
1580
|
+
key: result.key,
|
|
1581
|
+
secured: result.secured
|
|
1582
|
+
};
|
|
1583
|
+
},
|
|
1584
|
+
update_pipeline_variable: async (args) => {
|
|
1585
|
+
const client = getClient();
|
|
1586
|
+
const result = await client.updatePipelineVariable(
|
|
1587
|
+
args.repo_slug,
|
|
1588
|
+
args.variable_uuid,
|
|
1589
|
+
args.value
|
|
1590
|
+
);
|
|
1591
|
+
return {
|
|
1592
|
+
uuid: result.uuid,
|
|
1593
|
+
key: result.key,
|
|
1594
|
+
secured: result.secured
|
|
1595
|
+
};
|
|
1596
|
+
},
|
|
1597
|
+
delete_pipeline_variable: async (args) => {
|
|
1598
|
+
const client = getClient();
|
|
1599
|
+
await client.deletePipelineVariable(args.repo_slug, args.variable_uuid);
|
|
1600
|
+
return {};
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
|
|
1604
|
+
// src/tools/branches.ts
|
|
1605
|
+
var definitions4 = [
|
|
1606
|
+
{
|
|
1607
|
+
name: "list_branches",
|
|
1608
|
+
description: "List branches in a repository.",
|
|
1609
|
+
inputSchema: {
|
|
1610
|
+
type: "object",
|
|
1611
|
+
properties: {
|
|
1612
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1613
|
+
limit: { type: "number", description: "Maximum results (default: 50)", default: 50 }
|
|
1614
|
+
},
|
|
1615
|
+
required: ["repo_slug"]
|
|
1616
|
+
}
|
|
1617
|
+
},
|
|
1618
|
+
{
|
|
1619
|
+
name: "get_branch",
|
|
1620
|
+
description: "Get information about a specific branch.",
|
|
1621
|
+
inputSchema: {
|
|
1622
|
+
type: "object",
|
|
1623
|
+
properties: {
|
|
1624
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1625
|
+
branch_name: { type: "string", description: "Branch name" }
|
|
1626
|
+
},
|
|
1627
|
+
required: ["repo_slug", "branch_name"]
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
];
|
|
1631
|
+
var handlers4 = {
|
|
1632
|
+
list_branches: async (args) => {
|
|
1633
|
+
const client = getClient();
|
|
1634
|
+
const branches = await client.listBranches(args.repo_slug, {
|
|
1635
|
+
limit: validateLimit(args.limit || 50)
|
|
1636
|
+
});
|
|
1637
|
+
return {
|
|
1638
|
+
branches: branches.map((b) => ({
|
|
1639
|
+
name: b.name,
|
|
1640
|
+
commit: b.target?.hash?.substring(0, 7),
|
|
1641
|
+
message: b.target?.message,
|
|
1642
|
+
date: b.target?.date
|
|
1643
|
+
}))
|
|
1644
|
+
};
|
|
1645
|
+
},
|
|
1646
|
+
get_branch: async (args) => {
|
|
1647
|
+
const client = getClient();
|
|
1648
|
+
const result = await client.getBranch(args.repo_slug, args.branch_name);
|
|
1649
|
+
if (!result) {
|
|
1650
|
+
return notFoundResponse("Branch", args.branch_name);
|
|
1651
|
+
}
|
|
1652
|
+
return {
|
|
1653
|
+
name: result.name,
|
|
1654
|
+
latest_commit: {
|
|
1655
|
+
hash: result.target?.hash,
|
|
1656
|
+
message: result.target?.message || "",
|
|
1657
|
+
author: result.target?.author?.raw,
|
|
1658
|
+
date: result.target?.date
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
// src/tools/commits.ts
|
|
1665
|
+
var definitions5 = [
|
|
1666
|
+
{
|
|
1667
|
+
name: "list_commits",
|
|
1668
|
+
description: "List commits in a repository.",
|
|
1669
|
+
inputSchema: {
|
|
1670
|
+
type: "object",
|
|
1671
|
+
properties: {
|
|
1672
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1673
|
+
branch: { type: "string", description: "Filter by branch name (optional)" },
|
|
1674
|
+
path: { type: "string", description: "Filter by file path - only commits that modified this path (optional)" },
|
|
1675
|
+
limit: { type: "number", description: "Maximum results (default: 20)", default: 20 }
|
|
1676
|
+
},
|
|
1677
|
+
required: ["repo_slug"]
|
|
1678
|
+
}
|
|
1679
|
+
},
|
|
1680
|
+
{
|
|
1681
|
+
name: "get_commit",
|
|
1682
|
+
description: "Get detailed information about a specific commit.",
|
|
1683
|
+
inputSchema: {
|
|
1684
|
+
type: "object",
|
|
1685
|
+
properties: {
|
|
1686
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1687
|
+
commit: { type: "string", description: "Commit hash (full or short)" }
|
|
1688
|
+
},
|
|
1689
|
+
required: ["repo_slug", "commit"]
|
|
1690
|
+
}
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
name: "compare_commits",
|
|
1694
|
+
description: "Compare two commits or branches and see files changed.",
|
|
1695
|
+
inputSchema: {
|
|
1696
|
+
type: "object",
|
|
1697
|
+
properties: {
|
|
1698
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1699
|
+
base: { type: "string", description: "Base commit hash or branch name" },
|
|
1700
|
+
head: { type: "string", description: "Head commit hash or branch name" }
|
|
1701
|
+
},
|
|
1702
|
+
required: ["repo_slug", "base", "head"]
|
|
1703
|
+
}
|
|
1704
|
+
},
|
|
1705
|
+
{
|
|
1706
|
+
name: "get_commit_statuses",
|
|
1707
|
+
description: "Get build/CI statuses for a commit.",
|
|
1708
|
+
inputSchema: {
|
|
1709
|
+
type: "object",
|
|
1710
|
+
properties: {
|
|
1711
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1712
|
+
commit: { type: "string", description: "Commit hash" },
|
|
1713
|
+
limit: { type: "number", description: "Maximum results (default: 20)", default: 20 }
|
|
1714
|
+
},
|
|
1715
|
+
required: ["repo_slug", "commit"]
|
|
1716
|
+
}
|
|
1717
|
+
},
|
|
1718
|
+
{
|
|
1719
|
+
name: "create_commit_status",
|
|
1720
|
+
description: "Create a build status for a commit. Use this to report CI/CD status from external systems.",
|
|
1721
|
+
inputSchema: {
|
|
1722
|
+
type: "object",
|
|
1723
|
+
properties: {
|
|
1724
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1725
|
+
commit: { type: "string", description: "Commit hash" },
|
|
1726
|
+
state: { type: "string", description: "Status state: SUCCESSFUL, FAILED, INPROGRESS, STOPPED" },
|
|
1727
|
+
key: { type: "string", description: "Unique identifier for this status" },
|
|
1728
|
+
url: { type: "string", description: "URL to the build details (optional)" },
|
|
1729
|
+
name: { type: "string", description: "Display name for the status (optional)" },
|
|
1730
|
+
description: { type: "string", description: "Status description (optional)" }
|
|
1731
|
+
},
|
|
1732
|
+
required: ["repo_slug", "commit", "state", "key"]
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
];
|
|
1736
|
+
var handlers5 = {
|
|
1737
|
+
list_commits: async (args) => {
|
|
1738
|
+
const client = getClient();
|
|
1739
|
+
const commits = await client.listCommits(args.repo_slug, {
|
|
1740
|
+
branch: args.branch,
|
|
1741
|
+
path: args.path,
|
|
1742
|
+
limit: validateLimit(args.limit || 20)
|
|
1743
|
+
});
|
|
1744
|
+
return {
|
|
1745
|
+
commits: commits.map((c) => ({
|
|
1746
|
+
hash: truncateHash(c.hash),
|
|
1747
|
+
message: c.message,
|
|
1748
|
+
author: c.author?.raw,
|
|
1749
|
+
date: c.date
|
|
1750
|
+
}))
|
|
1751
|
+
};
|
|
1752
|
+
},
|
|
1753
|
+
get_commit: async (args) => {
|
|
1754
|
+
const client = getClient();
|
|
1755
|
+
const result = await client.getCommit(args.repo_slug, args.commit);
|
|
1756
|
+
if (!result) {
|
|
1757
|
+
return notFoundResponse("Commit", args.commit);
|
|
1758
|
+
}
|
|
1759
|
+
return {
|
|
1760
|
+
hash: result.hash,
|
|
1761
|
+
message: result.message,
|
|
1762
|
+
author: result.author?.raw,
|
|
1763
|
+
date: result.date,
|
|
1764
|
+
parents: result.parents?.map((p) => truncateHash(p.hash))
|
|
1765
|
+
};
|
|
1766
|
+
},
|
|
1767
|
+
compare_commits: async (args) => {
|
|
1768
|
+
const client = getClient();
|
|
1769
|
+
const result = await client.compareCommits(
|
|
1770
|
+
args.repo_slug,
|
|
1771
|
+
args.base,
|
|
1772
|
+
args.head
|
|
1773
|
+
);
|
|
1774
|
+
if (!result) {
|
|
1775
|
+
return { error: `Could not compare ${args.base}..${args.head}` };
|
|
1776
|
+
}
|
|
1777
|
+
const files = result.values || [];
|
|
1778
|
+
return {
|
|
1779
|
+
files: files.slice(0, 50).map((f) => ({
|
|
1780
|
+
path: f.new?.path || f.old?.path,
|
|
1781
|
+
status: f.status,
|
|
1782
|
+
"+": f.lines_added || 0,
|
|
1783
|
+
"-": f.lines_removed || 0
|
|
1784
|
+
}))
|
|
1785
|
+
};
|
|
1786
|
+
},
|
|
1787
|
+
get_commit_statuses: async (args) => {
|
|
1788
|
+
const client = getClient();
|
|
1789
|
+
const statuses = await client.getCommitStatuses(args.repo_slug, args.commit, {
|
|
1790
|
+
limit: validateLimit(args.limit || 20)
|
|
1791
|
+
});
|
|
1792
|
+
return {
|
|
1793
|
+
commit: truncateHash(args.commit),
|
|
1794
|
+
statuses: statuses.map((s) => ({
|
|
1795
|
+
key: s.key,
|
|
1796
|
+
state: s.state,
|
|
1797
|
+
name: s.name,
|
|
1798
|
+
description: s.description,
|
|
1799
|
+
url: s.url,
|
|
1800
|
+
created: s.created_on
|
|
1801
|
+
}))
|
|
1802
|
+
};
|
|
1803
|
+
},
|
|
1804
|
+
create_commit_status: async (args) => {
|
|
1805
|
+
const state = args.state.toUpperCase();
|
|
1806
|
+
const validStates = Object.values(CommitStatusState);
|
|
1807
|
+
if (!validStates.includes(state)) {
|
|
1808
|
+
return {
|
|
1809
|
+
success: false,
|
|
1810
|
+
error: `Invalid state '${args.state}'. Must be one of: ${validStates.join(", ")}`
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
const client = getClient();
|
|
1814
|
+
const result = await client.createCommitStatus(args.repo_slug, args.commit, {
|
|
1815
|
+
state,
|
|
1816
|
+
key: args.key,
|
|
1817
|
+
url: args.url,
|
|
1818
|
+
name: args.name,
|
|
1819
|
+
description: args.description
|
|
1820
|
+
});
|
|
1821
|
+
return {
|
|
1822
|
+
key: result.key,
|
|
1823
|
+
state: result.state,
|
|
1824
|
+
name: result.name,
|
|
1825
|
+
url: result.url
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
|
|
1830
|
+
// src/tools/deployments.ts
|
|
1831
|
+
var definitions6 = [
|
|
1832
|
+
{
|
|
1833
|
+
name: "list_environments",
|
|
1834
|
+
description: "List deployment environments for a repository.",
|
|
1835
|
+
inputSchema: {
|
|
1836
|
+
type: "object",
|
|
1837
|
+
properties: {
|
|
1838
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1839
|
+
limit: { type: "number", description: "Maximum results (default: 20)", default: 20 }
|
|
1840
|
+
},
|
|
1841
|
+
required: ["repo_slug"]
|
|
1842
|
+
}
|
|
1843
|
+
},
|
|
1844
|
+
{
|
|
1845
|
+
name: "get_environment",
|
|
1846
|
+
description: "Get details about a specific deployment environment.",
|
|
1847
|
+
inputSchema: {
|
|
1848
|
+
type: "object",
|
|
1849
|
+
properties: {
|
|
1850
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1851
|
+
environment_uuid: { type: "string", description: "Environment UUID" }
|
|
1852
|
+
},
|
|
1853
|
+
required: ["repo_slug", "environment_uuid"]
|
|
1854
|
+
}
|
|
1855
|
+
},
|
|
1856
|
+
{
|
|
1857
|
+
name: "list_deployment_history",
|
|
1858
|
+
description: "Get deployment history for a specific environment.",
|
|
1859
|
+
inputSchema: {
|
|
1860
|
+
type: "object",
|
|
1861
|
+
properties: {
|
|
1862
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1863
|
+
environment_uuid: { type: "string", description: "Environment UUID" },
|
|
1864
|
+
limit: { type: "number", description: "Maximum results (default: 20)", default: 20 }
|
|
1865
|
+
},
|
|
1866
|
+
required: ["repo_slug", "environment_uuid"]
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
];
|
|
1870
|
+
var handlers6 = {
|
|
1871
|
+
list_environments: async (args) => {
|
|
1872
|
+
const client = getClient();
|
|
1873
|
+
const environments = await client.listEnvironments(args.repo_slug, {
|
|
1874
|
+
limit: validateLimit(args.limit || 20)
|
|
1875
|
+
});
|
|
1876
|
+
return {
|
|
1877
|
+
environments: environments.map((e) => ({
|
|
1878
|
+
uuid: e.uuid,
|
|
1879
|
+
name: e.name,
|
|
1880
|
+
type: e.environment_type?.name,
|
|
1881
|
+
rank: e.rank
|
|
1882
|
+
}))
|
|
1883
|
+
};
|
|
1884
|
+
},
|
|
1885
|
+
get_environment: async (args) => {
|
|
1886
|
+
const client = getClient();
|
|
1887
|
+
const result = await client.getEnvironment(args.repo_slug, args.environment_uuid);
|
|
1888
|
+
if (!result) {
|
|
1889
|
+
return notFoundResponse("Environment", args.environment_uuid);
|
|
1890
|
+
}
|
|
1891
|
+
return {
|
|
1892
|
+
uuid: result.uuid,
|
|
1893
|
+
name: result.name,
|
|
1894
|
+
environment_type: result.environment_type?.name,
|
|
1895
|
+
rank: result.rank,
|
|
1896
|
+
restrictions: result.restrictions,
|
|
1897
|
+
lock: result.lock
|
|
1898
|
+
};
|
|
1899
|
+
},
|
|
1900
|
+
list_deployment_history: async (args) => {
|
|
1901
|
+
const client = getClient();
|
|
1902
|
+
const deployments = await client.listDeploymentHistory(
|
|
1903
|
+
args.repo_slug,
|
|
1904
|
+
args.environment_uuid,
|
|
1905
|
+
{ limit: validateLimit(args.limit || 20) }
|
|
1906
|
+
);
|
|
1907
|
+
return {
|
|
1908
|
+
deployments: deployments.map((d) => ({
|
|
1909
|
+
uuid: d.uuid,
|
|
1910
|
+
state: d.state?.name,
|
|
1911
|
+
started: d.state?.started_on,
|
|
1912
|
+
completed: d.state?.completed_on,
|
|
1913
|
+
commit: d.release?.commit?.hash?.substring(0, 7),
|
|
1914
|
+
pipeline_uuid: d.release?.pipeline?.uuid
|
|
1915
|
+
}))
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
};
|
|
1919
|
+
|
|
1920
|
+
// src/tools/webhooks.ts
|
|
1921
|
+
var definitions7 = [
|
|
1922
|
+
{
|
|
1923
|
+
name: "list_webhooks",
|
|
1924
|
+
description: "List webhooks configured for a repository.",
|
|
1925
|
+
inputSchema: {
|
|
1926
|
+
type: "object",
|
|
1927
|
+
properties: {
|
|
1928
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1929
|
+
limit: { type: "number", description: "Maximum results (default: 50)", default: 50 }
|
|
1930
|
+
},
|
|
1931
|
+
required: ["repo_slug"]
|
|
1932
|
+
}
|
|
1933
|
+
},
|
|
1934
|
+
{
|
|
1935
|
+
name: "create_webhook",
|
|
1936
|
+
description: "Create a webhook for a repository.",
|
|
1937
|
+
inputSchema: {
|
|
1938
|
+
type: "object",
|
|
1939
|
+
properties: {
|
|
1940
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1941
|
+
url: { type: "string", description: "URL to call when events occur" },
|
|
1942
|
+
events: {
|
|
1943
|
+
type: "array",
|
|
1944
|
+
items: { type: "string" },
|
|
1945
|
+
description: "List of events (e.g., repo:push, pullrequest:created, pullrequest:merged)"
|
|
1946
|
+
},
|
|
1947
|
+
description: { type: "string", description: "Webhook description (optional)", default: "" },
|
|
1948
|
+
active: { type: "boolean", description: "Whether webhook is active (default: true)", default: true }
|
|
1949
|
+
},
|
|
1950
|
+
required: ["repo_slug", "url", "events"]
|
|
1951
|
+
}
|
|
1952
|
+
},
|
|
1953
|
+
{
|
|
1954
|
+
name: "get_webhook",
|
|
1955
|
+
description: "Get details about a specific webhook.",
|
|
1956
|
+
inputSchema: {
|
|
1957
|
+
type: "object",
|
|
1958
|
+
properties: {
|
|
1959
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1960
|
+
webhook_uuid: { type: "string", description: "Webhook UUID" }
|
|
1961
|
+
},
|
|
1962
|
+
required: ["repo_slug", "webhook_uuid"]
|
|
1963
|
+
}
|
|
1964
|
+
},
|
|
1965
|
+
{
|
|
1966
|
+
name: "delete_webhook",
|
|
1967
|
+
description: "Delete a webhook.",
|
|
1968
|
+
inputSchema: {
|
|
1969
|
+
type: "object",
|
|
1970
|
+
properties: {
|
|
1971
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
1972
|
+
webhook_uuid: { type: "string", description: "Webhook UUID" }
|
|
1973
|
+
},
|
|
1974
|
+
required: ["repo_slug", "webhook_uuid"]
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
];
|
|
1978
|
+
var handlers7 = {
|
|
1979
|
+
list_webhooks: async (args) => {
|
|
1980
|
+
const client = getClient();
|
|
1981
|
+
const webhooks = await client.listWebhooks(args.repo_slug, {
|
|
1982
|
+
limit: validateLimit(args.limit || 50)
|
|
1983
|
+
});
|
|
1984
|
+
return {
|
|
1985
|
+
webhooks: webhooks.map((w) => ({
|
|
1986
|
+
uuid: w.uuid,
|
|
1987
|
+
url: w.url,
|
|
1988
|
+
description: w.description,
|
|
1989
|
+
events: w.events,
|
|
1990
|
+
active: w.active
|
|
1991
|
+
}))
|
|
1992
|
+
};
|
|
1993
|
+
},
|
|
1994
|
+
create_webhook: async (args) => {
|
|
1995
|
+
const client = getClient();
|
|
1996
|
+
const result = await client.createWebhook(args.repo_slug, {
|
|
1997
|
+
url: args.url,
|
|
1998
|
+
events: args.events,
|
|
1999
|
+
description: args.description,
|
|
2000
|
+
active: args.active ?? true
|
|
2001
|
+
});
|
|
2002
|
+
return {
|
|
2003
|
+
uuid: result.uuid,
|
|
2004
|
+
url: result.url,
|
|
2005
|
+
events: result.events,
|
|
2006
|
+
active: result.active
|
|
2007
|
+
};
|
|
2008
|
+
},
|
|
2009
|
+
get_webhook: async (args) => {
|
|
2010
|
+
const client = getClient();
|
|
2011
|
+
const result = await client.getWebhook(args.repo_slug, args.webhook_uuid);
|
|
2012
|
+
if (!result) {
|
|
2013
|
+
return notFoundResponse("Webhook", args.webhook_uuid);
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
uuid: result.uuid,
|
|
2017
|
+
url: result.url,
|
|
2018
|
+
description: result.description,
|
|
2019
|
+
events: result.events,
|
|
2020
|
+
active: result.active
|
|
2021
|
+
};
|
|
2022
|
+
},
|
|
2023
|
+
delete_webhook: async (args) => {
|
|
2024
|
+
const client = getClient();
|
|
2025
|
+
await client.deleteWebhook(args.repo_slug, args.webhook_uuid);
|
|
2026
|
+
return {};
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
|
|
2030
|
+
// src/tools/tags.ts
|
|
2031
|
+
var definitions8 = [
|
|
2032
|
+
{
|
|
2033
|
+
name: "list_tags",
|
|
2034
|
+
description: "List tags in a repository.",
|
|
2035
|
+
inputSchema: {
|
|
2036
|
+
type: "object",
|
|
2037
|
+
properties: {
|
|
2038
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2039
|
+
limit: { type: "number", description: "Maximum results (default: 50)", default: 50 }
|
|
2040
|
+
},
|
|
2041
|
+
required: ["repo_slug"]
|
|
2042
|
+
}
|
|
2043
|
+
},
|
|
2044
|
+
{
|
|
2045
|
+
name: "create_tag",
|
|
2046
|
+
description: "Create a new tag in a repository.",
|
|
2047
|
+
inputSchema: {
|
|
2048
|
+
type: "object",
|
|
2049
|
+
properties: {
|
|
2050
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2051
|
+
name: { type: "string", description: 'Tag name (e.g., "v1.0.0")' },
|
|
2052
|
+
target: { type: "string", description: "Commit hash or branch name to tag" },
|
|
2053
|
+
message: { type: "string", description: "Optional tag message (for annotated tags)", default: "" }
|
|
2054
|
+
},
|
|
2055
|
+
required: ["repo_slug", "name", "target"]
|
|
2056
|
+
}
|
|
2057
|
+
},
|
|
2058
|
+
{
|
|
2059
|
+
name: "delete_tag",
|
|
2060
|
+
description: "Delete a tag from a repository.",
|
|
2061
|
+
inputSchema: {
|
|
2062
|
+
type: "object",
|
|
2063
|
+
properties: {
|
|
2064
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2065
|
+
tag_name: { type: "string", description: "Tag name to delete" }
|
|
2066
|
+
},
|
|
2067
|
+
required: ["repo_slug", "tag_name"]
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
];
|
|
2071
|
+
var handlers8 = {
|
|
2072
|
+
list_tags: async (args) => {
|
|
2073
|
+
const client = getClient();
|
|
2074
|
+
const tags = await client.listTags(args.repo_slug, {
|
|
2075
|
+
limit: validateLimit(args.limit || 50)
|
|
2076
|
+
});
|
|
2077
|
+
return {
|
|
2078
|
+
tags: tags.map((t) => ({
|
|
2079
|
+
name: t.name,
|
|
2080
|
+
target: truncateHash(t.target?.hash),
|
|
2081
|
+
message: t.message,
|
|
2082
|
+
date: t.target?.date,
|
|
2083
|
+
tagger: t.tagger?.raw
|
|
2084
|
+
}))
|
|
2085
|
+
};
|
|
2086
|
+
},
|
|
2087
|
+
create_tag: async (args) => {
|
|
2088
|
+
const client = getClient();
|
|
2089
|
+
const result = await client.createTag(
|
|
2090
|
+
args.repo_slug,
|
|
2091
|
+
args.name,
|
|
2092
|
+
args.target,
|
|
2093
|
+
args.message || void 0
|
|
2094
|
+
);
|
|
2095
|
+
return {
|
|
2096
|
+
name: result.name,
|
|
2097
|
+
target: truncateHash(result.target?.hash),
|
|
2098
|
+
message: result.message || ""
|
|
2099
|
+
};
|
|
2100
|
+
},
|
|
2101
|
+
delete_tag: async (args) => {
|
|
2102
|
+
const client = getClient();
|
|
2103
|
+
await client.deleteTag(args.repo_slug, args.tag_name);
|
|
2104
|
+
return {};
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
2107
|
+
|
|
2108
|
+
// src/tools/restrictions.ts
|
|
2109
|
+
var definitions9 = [
|
|
2110
|
+
{
|
|
2111
|
+
name: "list_branch_restrictions",
|
|
2112
|
+
description: "List branch restrictions (protection rules) in a repository.",
|
|
2113
|
+
inputSchema: {
|
|
2114
|
+
type: "object",
|
|
2115
|
+
properties: {
|
|
2116
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2117
|
+
limit: { type: "number", description: "Maximum results (default: 50)", default: 50 }
|
|
2118
|
+
},
|
|
2119
|
+
required: ["repo_slug"]
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
{
|
|
2123
|
+
name: "create_branch_restriction",
|
|
2124
|
+
description: "Create a branch restriction (protection rule).",
|
|
2125
|
+
inputSchema: {
|
|
2126
|
+
type: "object",
|
|
2127
|
+
properties: {
|
|
2128
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2129
|
+
kind: {
|
|
2130
|
+
type: "string",
|
|
2131
|
+
description: "Type of restriction: push, force, delete, restrict_merges, require_passing_builds_to_merge, require_approvals_to_merge, etc."
|
|
2132
|
+
},
|
|
2133
|
+
pattern: { type: "string", description: 'Branch pattern (e.g., "main", "release/*"). Required for glob match.', default: "" },
|
|
2134
|
+
branch_match_kind: { type: "string", description: 'How to match branches: "glob" or "branching_model"', default: "glob" },
|
|
2135
|
+
branch_type: { type: "string", description: "Branch type when using branching_model: development, production, feature, etc.", default: "" },
|
|
2136
|
+
value: { type: "number", description: "Numeric value (e.g., number of required approvals)", default: 0 }
|
|
2137
|
+
},
|
|
2138
|
+
required: ["repo_slug", "kind"]
|
|
2139
|
+
}
|
|
2140
|
+
},
|
|
2141
|
+
{
|
|
2142
|
+
name: "delete_branch_restriction",
|
|
2143
|
+
description: "Delete a branch restriction.",
|
|
2144
|
+
inputSchema: {
|
|
2145
|
+
type: "object",
|
|
2146
|
+
properties: {
|
|
2147
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2148
|
+
restriction_id: { type: "number", description: "Restriction ID" }
|
|
2149
|
+
},
|
|
2150
|
+
required: ["repo_slug", "restriction_id"]
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
];
|
|
2154
|
+
var handlers9 = {
|
|
2155
|
+
list_branch_restrictions: async (args) => {
|
|
2156
|
+
const client = getClient();
|
|
2157
|
+
const restrictions = await client.listBranchRestrictions(args.repo_slug, {
|
|
2158
|
+
limit: validateLimit(args.limit || 50)
|
|
2159
|
+
});
|
|
2160
|
+
return {
|
|
2161
|
+
restrictions: restrictions.map((r) => ({
|
|
2162
|
+
id: r.id,
|
|
2163
|
+
kind: r.kind,
|
|
2164
|
+
pattern: r.pattern,
|
|
2165
|
+
branch_match_kind: r.branch_match_kind,
|
|
2166
|
+
branch_type: r.branch_type,
|
|
2167
|
+
value: r.value
|
|
2168
|
+
}))
|
|
2169
|
+
};
|
|
2170
|
+
},
|
|
2171
|
+
create_branch_restriction: async (args) => {
|
|
2172
|
+
const client = getClient();
|
|
2173
|
+
const result = await client.createBranchRestriction(args.repo_slug, {
|
|
2174
|
+
kind: args.kind,
|
|
2175
|
+
pattern: args.pattern,
|
|
2176
|
+
branchMatchKind: args.branch_match_kind || "glob",
|
|
2177
|
+
branchType: args.branch_type || void 0,
|
|
2178
|
+
value: args.value || void 0
|
|
2179
|
+
});
|
|
2180
|
+
return {
|
|
2181
|
+
id: result.id,
|
|
2182
|
+
kind: result.kind
|
|
2183
|
+
};
|
|
2184
|
+
},
|
|
2185
|
+
delete_branch_restriction: async (args) => {
|
|
2186
|
+
const client = getClient();
|
|
2187
|
+
await client.deleteBranchRestriction(args.repo_slug, args.restriction_id);
|
|
2188
|
+
return {};
|
|
2189
|
+
}
|
|
2190
|
+
};
|
|
2191
|
+
|
|
2192
|
+
// src/tools/source.ts
|
|
2193
|
+
var definitions10 = [
|
|
2194
|
+
{
|
|
2195
|
+
name: "get_file_content",
|
|
2196
|
+
description: "Get the content of a file from a repository. Read file contents without cloning.",
|
|
2197
|
+
inputSchema: {
|
|
2198
|
+
type: "object",
|
|
2199
|
+
properties: {
|
|
2200
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2201
|
+
path: { type: "string", description: 'File path (e.g., "src/main.py", "README.md")' },
|
|
2202
|
+
ref: { type: "string", description: 'Branch, tag, or commit hash (default: "main")', default: "main" }
|
|
2203
|
+
},
|
|
2204
|
+
required: ["repo_slug", "path"]
|
|
2205
|
+
}
|
|
2206
|
+
},
|
|
2207
|
+
{
|
|
2208
|
+
name: "list_directory",
|
|
2209
|
+
description: "List contents of a directory in a repository. Browse repository structure without cloning.",
|
|
2210
|
+
inputSchema: {
|
|
2211
|
+
type: "object",
|
|
2212
|
+
properties: {
|
|
2213
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2214
|
+
path: { type: "string", description: "Directory path (empty string for root)", default: "" },
|
|
2215
|
+
ref: { type: "string", description: 'Branch, tag, or commit hash (default: "main")', default: "main" },
|
|
2216
|
+
limit: { type: "number", description: "Maximum entries (default: 100)", default: 100 }
|
|
2217
|
+
},
|
|
2218
|
+
required: ["repo_slug"]
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
];
|
|
2222
|
+
var handlers10 = {
|
|
2223
|
+
get_file_content: async (args) => {
|
|
2224
|
+
const client = getClient();
|
|
2225
|
+
const ref = args.ref || "main";
|
|
2226
|
+
const content = await client.getFileContent(args.repo_slug, args.path, ref);
|
|
2227
|
+
if (content === null) {
|
|
2228
|
+
return { error: `File '${args.path}' not found at ref '${ref}'` };
|
|
2229
|
+
}
|
|
2230
|
+
return {
|
|
2231
|
+
path: args.path,
|
|
2232
|
+
ref,
|
|
2233
|
+
content,
|
|
2234
|
+
size: content.length
|
|
2235
|
+
};
|
|
2236
|
+
},
|
|
2237
|
+
list_directory: async (args) => {
|
|
2238
|
+
const client = getClient();
|
|
2239
|
+
const ref = args.ref || "main";
|
|
2240
|
+
const path = args.path || "";
|
|
2241
|
+
const entries = await client.listDirectory(args.repo_slug, path, {
|
|
2242
|
+
ref,
|
|
2243
|
+
limit: validateLimit(args.limit || 100)
|
|
2244
|
+
});
|
|
2245
|
+
return {
|
|
2246
|
+
path: path || "/",
|
|
2247
|
+
ref,
|
|
2248
|
+
entries: entries.map((e) => ({
|
|
2249
|
+
path: e.path,
|
|
2250
|
+
type: e.type === "commit_directory" ? "directory" : "file",
|
|
2251
|
+
size: e.size
|
|
2252
|
+
}))
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
|
|
2257
|
+
// src/tools/permissions.ts
|
|
2258
|
+
var definitions11 = [
|
|
2259
|
+
// User permissions
|
|
2260
|
+
{
|
|
2261
|
+
name: "list_user_permissions",
|
|
2262
|
+
description: "List user permissions for a repository.",
|
|
2263
|
+
inputSchema: {
|
|
2264
|
+
type: "object",
|
|
2265
|
+
properties: {
|
|
2266
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2267
|
+
limit: { type: "number", description: "Maximum results (default: 50)", default: 50 }
|
|
2268
|
+
},
|
|
2269
|
+
required: ["repo_slug"]
|
|
2270
|
+
}
|
|
2271
|
+
},
|
|
2272
|
+
{
|
|
2273
|
+
name: "get_user_permission",
|
|
2274
|
+
description: "Get a specific user's permission for a repository.",
|
|
2275
|
+
inputSchema: {
|
|
2276
|
+
type: "object",
|
|
2277
|
+
properties: {
|
|
2278
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2279
|
+
selected_user: { type: "string", description: "User's account_id or UUID" }
|
|
2280
|
+
},
|
|
2281
|
+
required: ["repo_slug", "selected_user"]
|
|
2282
|
+
}
|
|
2283
|
+
},
|
|
2284
|
+
{
|
|
2285
|
+
name: "update_user_permission",
|
|
2286
|
+
description: "Update or add a user's permission for a repository.",
|
|
2287
|
+
inputSchema: {
|
|
2288
|
+
type: "object",
|
|
2289
|
+
properties: {
|
|
2290
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2291
|
+
selected_user: { type: "string", description: "User's account_id or UUID" },
|
|
2292
|
+
permission: { type: "string", description: 'Permission level: "read", "write", or "admin"' }
|
|
2293
|
+
},
|
|
2294
|
+
required: ["repo_slug", "selected_user", "permission"]
|
|
2295
|
+
}
|
|
2296
|
+
},
|
|
2297
|
+
{
|
|
2298
|
+
name: "delete_user_permission",
|
|
2299
|
+
description: "Remove a user's explicit permission from a repository.",
|
|
2300
|
+
inputSchema: {
|
|
2301
|
+
type: "object",
|
|
2302
|
+
properties: {
|
|
2303
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2304
|
+
selected_user: { type: "string", description: "User's account_id or UUID" }
|
|
2305
|
+
},
|
|
2306
|
+
required: ["repo_slug", "selected_user"]
|
|
2307
|
+
}
|
|
2308
|
+
},
|
|
2309
|
+
// Group permissions
|
|
2310
|
+
{
|
|
2311
|
+
name: "list_group_permissions",
|
|
2312
|
+
description: "List group permissions for a repository.",
|
|
2313
|
+
inputSchema: {
|
|
2314
|
+
type: "object",
|
|
2315
|
+
properties: {
|
|
2316
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2317
|
+
limit: { type: "number", description: "Maximum results (default: 50)", default: 50 }
|
|
2318
|
+
},
|
|
2319
|
+
required: ["repo_slug"]
|
|
2320
|
+
}
|
|
2321
|
+
},
|
|
2322
|
+
{
|
|
2323
|
+
name: "get_group_permission",
|
|
2324
|
+
description: "Get a specific group's permission for a repository.",
|
|
2325
|
+
inputSchema: {
|
|
2326
|
+
type: "object",
|
|
2327
|
+
properties: {
|
|
2328
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2329
|
+
group_slug: { type: "string", description: "Group slug" }
|
|
2330
|
+
},
|
|
2331
|
+
required: ["repo_slug", "group_slug"]
|
|
2332
|
+
}
|
|
2333
|
+
},
|
|
2334
|
+
{
|
|
2335
|
+
name: "update_group_permission",
|
|
2336
|
+
description: "Update or add a group's permission for a repository.",
|
|
2337
|
+
inputSchema: {
|
|
2338
|
+
type: "object",
|
|
2339
|
+
properties: {
|
|
2340
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2341
|
+
group_slug: { type: "string", description: "Group slug" },
|
|
2342
|
+
permission: { type: "string", description: 'Permission level: "read", "write", or "admin"' }
|
|
2343
|
+
},
|
|
2344
|
+
required: ["repo_slug", "group_slug", "permission"]
|
|
2345
|
+
}
|
|
2346
|
+
},
|
|
2347
|
+
{
|
|
2348
|
+
name: "delete_group_permission",
|
|
2349
|
+
description: "Remove a group's explicit permission from a repository.",
|
|
2350
|
+
inputSchema: {
|
|
2351
|
+
type: "object",
|
|
2352
|
+
properties: {
|
|
2353
|
+
repo_slug: { type: "string", description: "Repository slug" },
|
|
2354
|
+
group_slug: { type: "string", description: "Group slug" }
|
|
2355
|
+
},
|
|
2356
|
+
required: ["repo_slug", "group_slug"]
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
];
|
|
2360
|
+
var handlers11 = {
|
|
2361
|
+
list_user_permissions: async (args) => {
|
|
2362
|
+
const client = getClient();
|
|
2363
|
+
const permissions = await client.listUserPermissions(args.repo_slug, {
|
|
2364
|
+
limit: validateLimit(args.limit || 50)
|
|
2365
|
+
});
|
|
2366
|
+
return {
|
|
2367
|
+
users: permissions.map((p) => ({
|
|
2368
|
+
user: p.user?.display_name,
|
|
2369
|
+
account_id: p.user?.account_id,
|
|
2370
|
+
permission: p.permission
|
|
2371
|
+
}))
|
|
2372
|
+
};
|
|
2373
|
+
},
|
|
2374
|
+
get_user_permission: async (args) => {
|
|
2375
|
+
const client = getClient();
|
|
2376
|
+
const result = await client.getUserPermission(args.repo_slug, args.selected_user);
|
|
2377
|
+
if (!result) {
|
|
2378
|
+
return notFoundResponse("User permission", args.selected_user);
|
|
2379
|
+
}
|
|
2380
|
+
return {
|
|
2381
|
+
user: result.user?.display_name,
|
|
2382
|
+
account_id: result.user?.account_id,
|
|
2383
|
+
permission: result.permission
|
|
2384
|
+
};
|
|
2385
|
+
},
|
|
2386
|
+
update_user_permission: async (args) => {
|
|
2387
|
+
const client = getClient();
|
|
2388
|
+
const result = await client.updateUserPermission(
|
|
2389
|
+
args.repo_slug,
|
|
2390
|
+
args.selected_user,
|
|
2391
|
+
args.permission
|
|
2392
|
+
);
|
|
2393
|
+
return {
|
|
2394
|
+
user: result.user?.display_name,
|
|
2395
|
+
permission: result.permission
|
|
2396
|
+
};
|
|
2397
|
+
},
|
|
2398
|
+
delete_user_permission: async (args) => {
|
|
2399
|
+
const client = getClient();
|
|
2400
|
+
await client.deleteUserPermission(args.repo_slug, args.selected_user);
|
|
2401
|
+
return {};
|
|
2402
|
+
},
|
|
2403
|
+
list_group_permissions: async (args) => {
|
|
2404
|
+
const client = getClient();
|
|
2405
|
+
const permissions = await client.listGroupPermissions(args.repo_slug, {
|
|
2406
|
+
limit: validateLimit(args.limit || 50)
|
|
2407
|
+
});
|
|
2408
|
+
return {
|
|
2409
|
+
groups: permissions.map((p) => ({
|
|
2410
|
+
group: p.group?.name,
|
|
2411
|
+
slug: p.group?.slug,
|
|
2412
|
+
permission: p.permission
|
|
2413
|
+
}))
|
|
2414
|
+
};
|
|
2415
|
+
},
|
|
2416
|
+
get_group_permission: async (args) => {
|
|
2417
|
+
const client = getClient();
|
|
2418
|
+
const result = await client.getGroupPermission(args.repo_slug, args.group_slug);
|
|
2419
|
+
if (!result) {
|
|
2420
|
+
return notFoundResponse("Group permission", args.group_slug);
|
|
2421
|
+
}
|
|
2422
|
+
return {
|
|
2423
|
+
group: result.group?.name,
|
|
2424
|
+
slug: result.group?.slug,
|
|
2425
|
+
permission: result.permission
|
|
2426
|
+
};
|
|
2427
|
+
},
|
|
2428
|
+
update_group_permission: async (args) => {
|
|
2429
|
+
const client = getClient();
|
|
2430
|
+
const result = await client.updateGroupPermission(
|
|
2431
|
+
args.repo_slug,
|
|
2432
|
+
args.group_slug,
|
|
2433
|
+
args.permission
|
|
2434
|
+
);
|
|
2435
|
+
return {
|
|
2436
|
+
group: result.group?.name,
|
|
2437
|
+
permission: result.permission
|
|
2438
|
+
};
|
|
2439
|
+
},
|
|
2440
|
+
delete_group_permission: async (args) => {
|
|
2441
|
+
const client = getClient();
|
|
2442
|
+
await client.deleteGroupPermission(args.repo_slug, args.group_slug);
|
|
2443
|
+
return {};
|
|
2444
|
+
}
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2447
|
+
// src/tools/projects.ts
|
|
2448
|
+
var definitions12 = [
|
|
2449
|
+
{
|
|
2450
|
+
name: "list_projects",
|
|
2451
|
+
description: "List projects in the workspace.",
|
|
2452
|
+
inputSchema: {
|
|
2453
|
+
type: "object",
|
|
2454
|
+
properties: {
|
|
2455
|
+
limit: { type: "number", description: "Maximum results (default: 50)", default: 50 }
|
|
2456
|
+
},
|
|
2457
|
+
required: []
|
|
2458
|
+
}
|
|
2459
|
+
},
|
|
2460
|
+
{
|
|
2461
|
+
name: "get_project",
|
|
2462
|
+
description: "Get information about a specific project.",
|
|
2463
|
+
inputSchema: {
|
|
2464
|
+
type: "object",
|
|
2465
|
+
properties: {
|
|
2466
|
+
project_key: { type: "string", description: 'Project key (e.g., "DS", "PROJ")' }
|
|
2467
|
+
},
|
|
2468
|
+
required: ["project_key"]
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
];
|
|
2472
|
+
var handlers12 = {
|
|
2473
|
+
list_projects: async (args) => {
|
|
2474
|
+
const client = getClient();
|
|
2475
|
+
const projects = await client.listProjects({
|
|
2476
|
+
limit: validateLimit(args.limit || 50)
|
|
2477
|
+
});
|
|
2478
|
+
return {
|
|
2479
|
+
projects: projects.map((p) => ({
|
|
2480
|
+
key: p.key,
|
|
2481
|
+
name: p.name,
|
|
2482
|
+
description: p.description
|
|
2483
|
+
}))
|
|
2484
|
+
};
|
|
2485
|
+
},
|
|
2486
|
+
get_project: async (args) => {
|
|
2487
|
+
const client = getClient();
|
|
2488
|
+
const result = await client.getProject(args.project_key);
|
|
2489
|
+
if (!result) {
|
|
2490
|
+
return notFoundResponse("Project", args.project_key);
|
|
2491
|
+
}
|
|
2492
|
+
return {
|
|
2493
|
+
key: result.key,
|
|
2494
|
+
name: result.name,
|
|
2495
|
+
description: result.description,
|
|
2496
|
+
uuid: result.uuid
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
};
|
|
2500
|
+
|
|
2501
|
+
// src/tools/index.ts
|
|
2502
|
+
var toolDefinitions = [
|
|
2503
|
+
// Repository tools
|
|
2504
|
+
...definitions,
|
|
2505
|
+
// Pull request tools
|
|
2506
|
+
...definitions2,
|
|
2507
|
+
// Pipeline tools
|
|
2508
|
+
...definitions3,
|
|
2509
|
+
// Branch tools
|
|
2510
|
+
...definitions4,
|
|
2511
|
+
// Commit tools
|
|
2512
|
+
...definitions5,
|
|
2513
|
+
// Deployment tools
|
|
2514
|
+
...definitions6,
|
|
2515
|
+
// Webhook tools
|
|
2516
|
+
...definitions7,
|
|
2517
|
+
// Tag tools
|
|
2518
|
+
...definitions8,
|
|
2519
|
+
// Branch restriction tools
|
|
2520
|
+
...definitions9,
|
|
2521
|
+
// Source browsing tools
|
|
2522
|
+
...definitions10,
|
|
2523
|
+
// Permission tools
|
|
2524
|
+
...definitions11,
|
|
2525
|
+
// Project tools
|
|
2526
|
+
...definitions12
|
|
2527
|
+
];
|
|
2528
|
+
async function handleToolCall(name, args) {
|
|
2529
|
+
if (name in handlers) {
|
|
2530
|
+
return await handlers[name](args);
|
|
2531
|
+
}
|
|
2532
|
+
if (name in handlers2) {
|
|
2533
|
+
return await handlers2[name](args);
|
|
2534
|
+
}
|
|
2535
|
+
if (name in handlers3) {
|
|
2536
|
+
return await handlers3[name](args);
|
|
2537
|
+
}
|
|
2538
|
+
if (name in handlers4) {
|
|
2539
|
+
return await handlers4[name](args);
|
|
2540
|
+
}
|
|
2541
|
+
if (name in handlers5) {
|
|
2542
|
+
return await handlers5[name](args);
|
|
2543
|
+
}
|
|
2544
|
+
if (name in handlers6) {
|
|
2545
|
+
return await handlers6[name](args);
|
|
2546
|
+
}
|
|
2547
|
+
if (name in handlers7) {
|
|
2548
|
+
return await handlers7[name](args);
|
|
2549
|
+
}
|
|
2550
|
+
if (name in handlers8) {
|
|
2551
|
+
return await handlers8[name](args);
|
|
2552
|
+
}
|
|
2553
|
+
if (name in handlers9) {
|
|
2554
|
+
return await handlers9[name](args);
|
|
2555
|
+
}
|
|
2556
|
+
if (name in handlers10) {
|
|
2557
|
+
return await handlers10[name](args);
|
|
2558
|
+
}
|
|
2559
|
+
if (name in handlers11) {
|
|
2560
|
+
return await handlers11[name](args);
|
|
2561
|
+
}
|
|
2562
|
+
if (name in handlers12) {
|
|
2563
|
+
return await handlers12[name](args);
|
|
2564
|
+
}
|
|
2565
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// src/resources.ts
|
|
2569
|
+
var resourceDefinitions = [
|
|
2570
|
+
{
|
|
2571
|
+
uri: "bitbucket://repositories",
|
|
2572
|
+
name: "Repositories",
|
|
2573
|
+
description: "List all repositories in the workspace",
|
|
2574
|
+
mimeType: "text/markdown"
|
|
2575
|
+
},
|
|
2576
|
+
{
|
|
2577
|
+
uri: "bitbucket://repositories/{repo_slug}",
|
|
2578
|
+
name: "Repository Details",
|
|
2579
|
+
description: "Get detailed information about a specific repository",
|
|
2580
|
+
mimeType: "text/markdown"
|
|
2581
|
+
},
|
|
2582
|
+
{
|
|
2583
|
+
uri: "bitbucket://repositories/{repo_slug}/branches",
|
|
2584
|
+
name: "Repository Branches",
|
|
2585
|
+
description: "List branches in a repository",
|
|
2586
|
+
mimeType: "text/markdown"
|
|
2587
|
+
},
|
|
2588
|
+
{
|
|
2589
|
+
uri: "bitbucket://repositories/{repo_slug}/pull-requests",
|
|
2590
|
+
name: "Pull Requests",
|
|
2591
|
+
description: "List open pull requests in a repository",
|
|
2592
|
+
mimeType: "text/markdown"
|
|
2593
|
+
},
|
|
2594
|
+
{
|
|
2595
|
+
uri: "bitbucket://projects",
|
|
2596
|
+
name: "Projects",
|
|
2597
|
+
description: "List all projects in the workspace",
|
|
2598
|
+
mimeType: "text/markdown"
|
|
2599
|
+
}
|
|
2600
|
+
];
|
|
2601
|
+
async function handleResourceRead(uri) {
|
|
2602
|
+
const client = getClient();
|
|
2603
|
+
if (uri === "bitbucket://repositories") {
|
|
2604
|
+
return await resourceRepositories(client);
|
|
2605
|
+
}
|
|
2606
|
+
if (uri === "bitbucket://projects") {
|
|
2607
|
+
return await resourceProjects(client);
|
|
2608
|
+
}
|
|
2609
|
+
const repoMatch = uri.match(/^bitbucket:\/\/repositories\/([^/]+)$/);
|
|
2610
|
+
if (repoMatch) {
|
|
2611
|
+
return await resourceRepository(client, repoMatch[1]);
|
|
2612
|
+
}
|
|
2613
|
+
const branchesMatch = uri.match(/^bitbucket:\/\/repositories\/([^/]+)\/branches$/);
|
|
2614
|
+
if (branchesMatch) {
|
|
2615
|
+
return await resourceBranches(client, branchesMatch[1]);
|
|
2616
|
+
}
|
|
2617
|
+
const prsMatch = uri.match(/^bitbucket:\/\/repositories\/([^/]+)\/pull-requests$/);
|
|
2618
|
+
if (prsMatch) {
|
|
2619
|
+
return await resourcePullRequests(client, prsMatch[1]);
|
|
2620
|
+
}
|
|
2621
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
2622
|
+
}
|
|
2623
|
+
async function resourceRepositories(client) {
|
|
2624
|
+
const repos = await client.listRepositories({ limit: 50 });
|
|
2625
|
+
const lines = [`# Repositories in ${client.workspace}`, ""];
|
|
2626
|
+
for (const r of repos) {
|
|
2627
|
+
const name = r.name || "unknown";
|
|
2628
|
+
const desc = (r.description || "").substring(0, 50) || "No description";
|
|
2629
|
+
const icon = r.is_private ? "\u{1F512}" : "\u{1F310}";
|
|
2630
|
+
lines.push(`- ${icon} **${name}**: ${desc}`);
|
|
2631
|
+
}
|
|
2632
|
+
return lines.join("\n");
|
|
2633
|
+
}
|
|
2634
|
+
async function resourceRepository(client, repoSlug) {
|
|
2635
|
+
const repo = await client.getRepository(repoSlug);
|
|
2636
|
+
if (!repo) {
|
|
2637
|
+
return `Repository '${repoSlug}' not found`;
|
|
2638
|
+
}
|
|
2639
|
+
const lines = [
|
|
2640
|
+
`# ${repo.name || repoSlug}`,
|
|
2641
|
+
"",
|
|
2642
|
+
`**Description**: ${repo.description || "No description"}`,
|
|
2643
|
+
`**Private**: ${repo.is_private ? "Yes" : "No"}`,
|
|
2644
|
+
`**Project**: ${repo.project?.name || "None"}`,
|
|
2645
|
+
`**Main branch**: ${repo.mainbranch?.name || "main"}`,
|
|
2646
|
+
"",
|
|
2647
|
+
"## Clone URLs"
|
|
2648
|
+
];
|
|
2649
|
+
for (const clone of repo.links?.clone || []) {
|
|
2650
|
+
lines.push(`- ${clone.name}: \`${clone.href}\``);
|
|
2651
|
+
}
|
|
2652
|
+
return lines.join("\n");
|
|
2653
|
+
}
|
|
2654
|
+
async function resourceBranches(client, repoSlug) {
|
|
2655
|
+
const branches = await client.listBranches(repoSlug, { limit: 30 });
|
|
2656
|
+
const lines = [`# Branches in ${repoSlug}`, ""];
|
|
2657
|
+
for (const b of branches) {
|
|
2658
|
+
const name = b.name || "unknown";
|
|
2659
|
+
const commit = (b.target?.hash || "").substring(0, 7);
|
|
2660
|
+
lines.push(`- **${name}** (${commit})`);
|
|
2661
|
+
}
|
|
2662
|
+
return lines.join("\n");
|
|
2663
|
+
}
|
|
2664
|
+
async function resourcePullRequests(client, repoSlug) {
|
|
2665
|
+
const prs = await client.listPullRequests(repoSlug, { state: "OPEN", limit: 20 });
|
|
2666
|
+
const lines = [`# Open Pull Requests in ${repoSlug}`, ""];
|
|
2667
|
+
if (prs.length === 0) {
|
|
2668
|
+
lines.push("No open pull requests");
|
|
2669
|
+
}
|
|
2670
|
+
for (const pr of prs) {
|
|
2671
|
+
const prId = pr.id;
|
|
2672
|
+
const title = pr.title || "Untitled";
|
|
2673
|
+
const author = pr.author?.display_name || "Unknown";
|
|
2674
|
+
lines.push(`- **#${prId}**: ${title} (by ${author})`);
|
|
2675
|
+
}
|
|
2676
|
+
return lines.join("\n");
|
|
2677
|
+
}
|
|
2678
|
+
async function resourceProjects(client) {
|
|
2679
|
+
const projects = await client.listProjects({ limit: 50 });
|
|
2680
|
+
const lines = [`# Projects in ${client.workspace}`, ""];
|
|
2681
|
+
for (const p of projects) {
|
|
2682
|
+
const key = p.key || "?";
|
|
2683
|
+
const name = p.name || "Unknown";
|
|
2684
|
+
const desc = (p.description || "").substring(0, 40) || "No description";
|
|
2685
|
+
lines.push(`- **${key}** - ${name}: ${desc}`);
|
|
2686
|
+
}
|
|
2687
|
+
return lines.join("\n");
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
// src/prompts.ts
|
|
2691
|
+
var promptDefinitions = [
|
|
2692
|
+
{
|
|
2693
|
+
name: "code_review",
|
|
2694
|
+
description: "Generate a code review prompt for a pull request",
|
|
2695
|
+
arguments: [
|
|
2696
|
+
{
|
|
2697
|
+
name: "repo_slug",
|
|
2698
|
+
description: "Repository slug",
|
|
2699
|
+
required: true
|
|
2700
|
+
},
|
|
2701
|
+
{
|
|
2702
|
+
name: "pr_id",
|
|
2703
|
+
description: "Pull request ID",
|
|
2704
|
+
required: true
|
|
2705
|
+
}
|
|
2706
|
+
]
|
|
2707
|
+
},
|
|
2708
|
+
{
|
|
2709
|
+
name: "release_notes",
|
|
2710
|
+
description: "Generate release notes from commits between two refs",
|
|
2711
|
+
arguments: [
|
|
2712
|
+
{
|
|
2713
|
+
name: "repo_slug",
|
|
2714
|
+
description: "Repository slug",
|
|
2715
|
+
required: true
|
|
2716
|
+
},
|
|
2717
|
+
{
|
|
2718
|
+
name: "base_tag",
|
|
2719
|
+
description: 'Base tag or commit (e.g., "v1.0.0")',
|
|
2720
|
+
required: true
|
|
2721
|
+
},
|
|
2722
|
+
{
|
|
2723
|
+
name: "head",
|
|
2724
|
+
description: 'Head ref (default: "main")',
|
|
2725
|
+
required: false
|
|
2726
|
+
}
|
|
2727
|
+
]
|
|
2728
|
+
},
|
|
2729
|
+
{
|
|
2730
|
+
name: "pipeline_debug",
|
|
2731
|
+
description: "Debug a failed pipeline",
|
|
2732
|
+
arguments: [
|
|
2733
|
+
{
|
|
2734
|
+
name: "repo_slug",
|
|
2735
|
+
description: "Repository slug",
|
|
2736
|
+
required: true
|
|
2737
|
+
}
|
|
2738
|
+
]
|
|
2739
|
+
},
|
|
2740
|
+
{
|
|
2741
|
+
name: "repo_summary",
|
|
2742
|
+
description: "Get a comprehensive summary of a repository",
|
|
2743
|
+
arguments: [
|
|
2744
|
+
{
|
|
2745
|
+
name: "repo_slug",
|
|
2746
|
+
description: "Repository slug",
|
|
2747
|
+
required: true
|
|
2748
|
+
}
|
|
2749
|
+
]
|
|
2750
|
+
}
|
|
2751
|
+
];
|
|
2752
|
+
function handlePromptGet(name, args) {
|
|
2753
|
+
switch (name) {
|
|
2754
|
+
case "code_review":
|
|
2755
|
+
return promptCodeReview(args.repo_slug, args.pr_id);
|
|
2756
|
+
case "release_notes":
|
|
2757
|
+
return promptReleaseNotes(args.repo_slug, args.base_tag, args.head || "main");
|
|
2758
|
+
case "pipeline_debug":
|
|
2759
|
+
return promptPipelineDebug(args.repo_slug);
|
|
2760
|
+
case "repo_summary":
|
|
2761
|
+
return promptRepoSummary(args.repo_slug);
|
|
2762
|
+
default:
|
|
2763
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
function promptCodeReview(repoSlug, prId) {
|
|
2767
|
+
const content = `Please review pull request #${prId} in repository '${repoSlug}'.
|
|
2768
|
+
|
|
2769
|
+
Use the following tools to gather information:
|
|
2770
|
+
1. get_pull_request(repo_slug="${repoSlug}", pr_id=${prId}) - Get PR details
|
|
2771
|
+
2. get_pr_diff(repo_slug="${repoSlug}", pr_id=${prId}) - Get the code changes
|
|
2772
|
+
3. list_pr_comments(repo_slug="${repoSlug}", pr_id=${prId}) - See existing comments
|
|
2773
|
+
|
|
2774
|
+
Then provide a thorough code review covering:
|
|
2775
|
+
- Code quality and readability
|
|
2776
|
+
- Potential bugs or edge cases
|
|
2777
|
+
- Security concerns
|
|
2778
|
+
- Performance considerations
|
|
2779
|
+
- Suggestions for improvement
|
|
2780
|
+
|
|
2781
|
+
If you find issues, use add_pr_comment() to leave feedback on specific lines.`;
|
|
2782
|
+
return {
|
|
2783
|
+
messages: [
|
|
2784
|
+
{
|
|
2785
|
+
role: "user",
|
|
2786
|
+
content: {
|
|
2787
|
+
type: "text",
|
|
2788
|
+
text: content
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
]
|
|
2792
|
+
};
|
|
2793
|
+
}
|
|
2794
|
+
function promptReleaseNotes(repoSlug, baseTag, head) {
|
|
2795
|
+
const content = `Generate release notes for repository '${repoSlug}' comparing ${baseTag} to ${head}.
|
|
2796
|
+
|
|
2797
|
+
Use these tools:
|
|
2798
|
+
1. compare_commits(repo_slug="${repoSlug}", base="${baseTag}", head="${head}") - See changed files
|
|
2799
|
+
2. list_commits(repo_slug="${repoSlug}", branch="${head}", limit=50) - Get recent commits
|
|
2800
|
+
|
|
2801
|
+
Organize the release notes into sections:
|
|
2802
|
+
- **New Features**: New functionality added
|
|
2803
|
+
- **Bug Fixes**: Issues that were resolved
|
|
2804
|
+
- **Improvements**: Enhancements to existing features
|
|
2805
|
+
- **Breaking Changes**: Changes that require user action
|
|
2806
|
+
|
|
2807
|
+
Format as markdown suitable for a GitHub/Bitbucket release.`;
|
|
2808
|
+
return {
|
|
2809
|
+
messages: [
|
|
2810
|
+
{
|
|
2811
|
+
role: "user",
|
|
2812
|
+
content: {
|
|
2813
|
+
type: "text",
|
|
2814
|
+
text: content
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
]
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
function promptPipelineDebug(repoSlug) {
|
|
2821
|
+
const content = `Help debug pipeline failures in repository '${repoSlug}'.
|
|
2822
|
+
|
|
2823
|
+
Use these tools:
|
|
2824
|
+
1. list_pipelines(repo_slug="${repoSlug}", limit=5) - Get recent pipeline runs
|
|
2825
|
+
2. get_pipeline(repo_slug="${repoSlug}", pipeline_uuid="<uuid>") - Get pipeline details
|
|
2826
|
+
3. get_pipeline_logs(repo_slug="${repoSlug}", pipeline_uuid="<uuid>") - Get step list
|
|
2827
|
+
4. get_pipeline_logs(repo_slug="${repoSlug}", pipeline_uuid="<uuid>", step_uuid="<step>") - Get logs
|
|
2828
|
+
|
|
2829
|
+
Analyze the failures and provide:
|
|
2830
|
+
- Root cause of the failure
|
|
2831
|
+
- Specific error messages
|
|
2832
|
+
- Recommended fixes
|
|
2833
|
+
- Commands to re-run the pipeline if appropriate`;
|
|
2834
|
+
return {
|
|
2835
|
+
messages: [
|
|
2836
|
+
{
|
|
2837
|
+
role: "user",
|
|
2838
|
+
content: {
|
|
2839
|
+
type: "text",
|
|
2840
|
+
text: content
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
]
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
function promptRepoSummary(repoSlug) {
|
|
2847
|
+
const content = `Provide a comprehensive summary of repository '${repoSlug}'.
|
|
2848
|
+
|
|
2849
|
+
Gather information using:
|
|
2850
|
+
1. get_repository(repo_slug="${repoSlug}") - Basic repo info
|
|
2851
|
+
2. list_branches(repo_slug="${repoSlug}", limit=10) - Active branches
|
|
2852
|
+
3. list_pull_requests(repo_slug="${repoSlug}", state="OPEN") - Open PRs
|
|
2853
|
+
4. list_pipelines(repo_slug="${repoSlug}", limit=5) - Recent CI/CD status
|
|
2854
|
+
5. list_commits(repo_slug="${repoSlug}", limit=10) - Recent activity
|
|
2855
|
+
|
|
2856
|
+
Summarize:
|
|
2857
|
+
- Repository description and purpose
|
|
2858
|
+
- Current development activity
|
|
2859
|
+
- Open pull requests needing attention
|
|
2860
|
+
- CI/CD health
|
|
2861
|
+
- Recent contributors`;
|
|
2862
|
+
return {
|
|
2863
|
+
messages: [
|
|
2864
|
+
{
|
|
2865
|
+
role: "user",
|
|
2866
|
+
content: {
|
|
2867
|
+
type: "text",
|
|
2868
|
+
text: content
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
]
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
// src/index.ts
|
|
2876
|
+
var VERSION = "0.10.0";
|
|
2877
|
+
function createServer() {
|
|
2878
|
+
const server = new Server(
|
|
2879
|
+
{
|
|
2880
|
+
name: "bitbucket",
|
|
2881
|
+
version: VERSION
|
|
2882
|
+
},
|
|
2883
|
+
{
|
|
2884
|
+
capabilities: {
|
|
2885
|
+
tools: {},
|
|
2886
|
+
resources: {},
|
|
2887
|
+
prompts: {}
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
);
|
|
2891
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
2892
|
+
return {
|
|
2893
|
+
tools: toolDefinitions
|
|
2894
|
+
};
|
|
2895
|
+
});
|
|
2896
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
2897
|
+
const { name, arguments: args } = request.params;
|
|
2898
|
+
try {
|
|
2899
|
+
const result = await handleToolCall(name, args || {});
|
|
2900
|
+
return {
|
|
2901
|
+
content: [
|
|
2902
|
+
{
|
|
2903
|
+
type: "text",
|
|
2904
|
+
text: JSON.stringify(result, null, 2)
|
|
2905
|
+
}
|
|
2906
|
+
]
|
|
2907
|
+
};
|
|
2908
|
+
} catch (error) {
|
|
2909
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2910
|
+
return {
|
|
2911
|
+
content: [
|
|
2912
|
+
{
|
|
2913
|
+
type: "text",
|
|
2914
|
+
text: JSON.stringify({ error: message }, null, 2)
|
|
2915
|
+
}
|
|
2916
|
+
],
|
|
2917
|
+
isError: true
|
|
2918
|
+
};
|
|
2919
|
+
}
|
|
2920
|
+
});
|
|
2921
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
2922
|
+
return {
|
|
2923
|
+
resources: resourceDefinitions
|
|
2924
|
+
};
|
|
2925
|
+
});
|
|
2926
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
2927
|
+
const { uri } = request.params;
|
|
2928
|
+
try {
|
|
2929
|
+
const content = await handleResourceRead(uri);
|
|
2930
|
+
return {
|
|
2931
|
+
contents: [
|
|
2932
|
+
{
|
|
2933
|
+
uri,
|
|
2934
|
+
mimeType: "text/markdown",
|
|
2935
|
+
text: content
|
|
2936
|
+
}
|
|
2937
|
+
]
|
|
2938
|
+
};
|
|
2939
|
+
} catch (error) {
|
|
2940
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2941
|
+
throw new Error(`Failed to read resource: ${message}`);
|
|
2942
|
+
}
|
|
2943
|
+
});
|
|
2944
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
2945
|
+
return {
|
|
2946
|
+
prompts: promptDefinitions
|
|
2947
|
+
};
|
|
2948
|
+
});
|
|
2949
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
2950
|
+
const { name, arguments: args } = request.params;
|
|
2951
|
+
try {
|
|
2952
|
+
const result = handlePromptGet(name, args || {});
|
|
2953
|
+
return result;
|
|
2954
|
+
} catch (error) {
|
|
2955
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2956
|
+
throw new Error(`Failed to get prompt: ${message}`);
|
|
2957
|
+
}
|
|
2958
|
+
});
|
|
2959
|
+
return server;
|
|
2960
|
+
}
|
|
2961
|
+
async function main() {
|
|
2962
|
+
try {
|
|
2963
|
+
getSettings();
|
|
2964
|
+
} catch (error) {
|
|
2965
|
+
console.error("Configuration error:", error instanceof Error ? error.message : error);
|
|
2966
|
+
process.exit(1);
|
|
2967
|
+
}
|
|
2968
|
+
const server = createServer();
|
|
2969
|
+
const transport = new StdioServerTransport();
|
|
2970
|
+
await server.connect(transport);
|
|
2971
|
+
console.error(`Bitbucket MCP Server v${VERSION} started`);
|
|
2972
|
+
}
|
|
2973
|
+
main().catch((error) => {
|
|
2974
|
+
console.error("Fatal error:", error);
|
|
2975
|
+
process.exit(1);
|
|
2976
|
+
});
|