@zereight/mcp-gitlab 1.0.25 → 1.0.27
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/build/index.js +65 -35
- package/package.json +2 -2
package/build/index.js
CHANGED
|
@@ -19,11 +19,11 @@ ListMergeRequestDiscussionsSchema, } from "./schemas.js";
|
|
|
19
19
|
*/
|
|
20
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
21
|
const __dirname = dirname(__filename);
|
|
22
|
-
const packageJsonPath = path.resolve(__dirname,
|
|
22
|
+
const packageJsonPath = path.resolve(__dirname, "../package.json");
|
|
23
23
|
let SERVER_VERSION = "unknown";
|
|
24
24
|
try {
|
|
25
25
|
if (fs.existsSync(packageJsonPath)) {
|
|
26
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath,
|
|
26
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
27
27
|
SERVER_VERSION = packageJson.version || SERVER_VERSION;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -39,7 +39,7 @@ const server = new Server({
|
|
|
39
39
|
},
|
|
40
40
|
});
|
|
41
41
|
const GITLAB_PERSONAL_ACCESS_TOKEN = process.env.GITLAB_PERSONAL_ACCESS_TOKEN;
|
|
42
|
-
const GITLAB_READ_ONLY_MODE = process.env.GITLAB_READ_ONLY_MODE ===
|
|
42
|
+
const GITLAB_READ_ONLY_MODE = process.env.GITLAB_READ_ONLY_MODE === "true";
|
|
43
43
|
// Define all available tools
|
|
44
44
|
const allTools = [
|
|
45
45
|
{
|
|
@@ -231,7 +231,7 @@ const readOnlyTools = [
|
|
|
231
231
|
"list_projects",
|
|
232
232
|
"list_labels",
|
|
233
233
|
"get_label",
|
|
234
|
-
"list_group_projects"
|
|
234
|
+
"list_group_projects",
|
|
235
235
|
];
|
|
236
236
|
/**
|
|
237
237
|
* Smart URL handling for GitLab API
|
|
@@ -244,9 +244,10 @@ function normalizeGitLabApiUrl(url) {
|
|
|
244
244
|
return "https://gitlab.com/api/v4";
|
|
245
245
|
}
|
|
246
246
|
// Remove trailing slash if present
|
|
247
|
-
let normalizedUrl = url.endsWith(
|
|
247
|
+
let normalizedUrl = url.endsWith("/") ? url.slice(0, -1) : url;
|
|
248
248
|
// Check if URL already has /api/v4
|
|
249
|
-
if (!normalizedUrl.endsWith(
|
|
249
|
+
if (!normalizedUrl.endsWith("/api/v4") &&
|
|
250
|
+
!normalizedUrl.endsWith("/api/v4/")) {
|
|
250
251
|
// Append /api/v4 if not already present
|
|
251
252
|
normalizedUrl = `${normalizedUrl}/api/v4`;
|
|
252
253
|
}
|
|
@@ -420,9 +421,9 @@ async function listIssues(projectId, options = {}) {
|
|
|
420
421
|
// Add all query parameters
|
|
421
422
|
Object.entries(options).forEach(([key, value]) => {
|
|
422
423
|
if (value !== undefined) {
|
|
423
|
-
if (key ===
|
|
424
|
+
if (key === "label_name" && Array.isArray(value)) {
|
|
424
425
|
// Handle array of labels
|
|
425
|
-
url.searchParams.append(key, value.join(
|
|
426
|
+
url.searchParams.append(key, value.join(","));
|
|
426
427
|
}
|
|
427
428
|
else {
|
|
428
429
|
url.searchParams.append(key, value.toString());
|
|
@@ -467,7 +468,7 @@ async function updateIssue(projectId, issueIid, options) {
|
|
|
467
468
|
// Convert labels array to comma-separated string if present
|
|
468
469
|
const body = { ...options };
|
|
469
470
|
if (body.labels && Array.isArray(body.labels)) {
|
|
470
|
-
body.labels = body.labels.join(
|
|
471
|
+
body.labels = body.labels.join(",");
|
|
471
472
|
}
|
|
472
473
|
const response = await fetch(url.toString(), {
|
|
473
474
|
method: "PUT",
|
|
@@ -540,7 +541,7 @@ async function getIssueLink(projectId, issueIid, issueLinkId) {
|
|
|
540
541
|
* @param {string} linkType - The type of the relation (relates_to, blocks, is_blocked_by)
|
|
541
542
|
* @returns {Promise<GitLabIssueLink>} The created issue link
|
|
542
543
|
*/
|
|
543
|
-
async function createIssueLink(projectId, issueIid, targetProjectId, targetIssueIid, linkType =
|
|
544
|
+
async function createIssueLink(projectId, issueIid, targetProjectId, targetIssueIid, linkType = "relates_to") {
|
|
544
545
|
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links`);
|
|
545
546
|
const response = await fetch(url.toString(), {
|
|
546
547
|
method: "POST",
|
|
@@ -548,7 +549,7 @@ async function createIssueLink(projectId, issueIid, targetProjectId, targetIssue
|
|
|
548
549
|
body: JSON.stringify({
|
|
549
550
|
target_project_id: targetProjectId,
|
|
550
551
|
target_issue_iid: targetIssueIid,
|
|
551
|
-
link_type: linkType
|
|
552
|
+
link_type: linkType,
|
|
552
553
|
}),
|
|
553
554
|
});
|
|
554
555
|
await handleGitLabError(response);
|
|
@@ -1207,37 +1208,37 @@ async function listGroupProjects(options) {
|
|
|
1207
1208
|
const url = new URL(`${GITLAB_API_URL}/groups/${encodeURIComponent(options.group_id)}/projects`);
|
|
1208
1209
|
// Add optional parameters to URL
|
|
1209
1210
|
if (options.include_subgroups)
|
|
1210
|
-
url.searchParams.append(
|
|
1211
|
+
url.searchParams.append("include_subgroups", "true");
|
|
1211
1212
|
if (options.search)
|
|
1212
|
-
url.searchParams.append(
|
|
1213
|
+
url.searchParams.append("search", options.search);
|
|
1213
1214
|
if (options.order_by)
|
|
1214
|
-
url.searchParams.append(
|
|
1215
|
+
url.searchParams.append("order_by", options.order_by);
|
|
1215
1216
|
if (options.sort)
|
|
1216
|
-
url.searchParams.append(
|
|
1217
|
+
url.searchParams.append("sort", options.sort);
|
|
1217
1218
|
if (options.page)
|
|
1218
|
-
url.searchParams.append(
|
|
1219
|
+
url.searchParams.append("page", options.page.toString());
|
|
1219
1220
|
if (options.per_page)
|
|
1220
|
-
url.searchParams.append(
|
|
1221
|
+
url.searchParams.append("per_page", options.per_page.toString());
|
|
1221
1222
|
if (options.archived !== undefined)
|
|
1222
|
-
url.searchParams.append(
|
|
1223
|
+
url.searchParams.append("archived", options.archived.toString());
|
|
1223
1224
|
if (options.visibility)
|
|
1224
|
-
url.searchParams.append(
|
|
1225
|
+
url.searchParams.append("visibility", options.visibility);
|
|
1225
1226
|
if (options.with_issues_enabled !== undefined)
|
|
1226
|
-
url.searchParams.append(
|
|
1227
|
+
url.searchParams.append("with_issues_enabled", options.with_issues_enabled.toString());
|
|
1227
1228
|
if (options.with_merge_requests_enabled !== undefined)
|
|
1228
|
-
url.searchParams.append(
|
|
1229
|
+
url.searchParams.append("with_merge_requests_enabled", options.with_merge_requests_enabled.toString());
|
|
1229
1230
|
if (options.min_access_level !== undefined)
|
|
1230
|
-
url.searchParams.append(
|
|
1231
|
+
url.searchParams.append("min_access_level", options.min_access_level.toString());
|
|
1231
1232
|
if (options.with_programming_language)
|
|
1232
|
-
url.searchParams.append(
|
|
1233
|
+
url.searchParams.append("with_programming_language", options.with_programming_language);
|
|
1233
1234
|
if (options.starred !== undefined)
|
|
1234
|
-
url.searchParams.append(
|
|
1235
|
+
url.searchParams.append("starred", options.starred.toString());
|
|
1235
1236
|
if (options.statistics !== undefined)
|
|
1236
|
-
url.searchParams.append(
|
|
1237
|
+
url.searchParams.append("statistics", options.statistics.toString());
|
|
1237
1238
|
if (options.with_custom_attributes !== undefined)
|
|
1238
|
-
url.searchParams.append(
|
|
1239
|
+
url.searchParams.append("with_custom_attributes", options.with_custom_attributes.toString());
|
|
1239
1240
|
if (options.with_security_reports !== undefined)
|
|
1240
|
-
url.searchParams.append(
|
|
1241
|
+
url.searchParams.append("with_security_reports", options.with_security_reports.toString());
|
|
1241
1242
|
const response = await fetch(url.toString(), {
|
|
1242
1243
|
method: "GET",
|
|
1243
1244
|
headers: DEFAULT_HEADERS,
|
|
@@ -1249,7 +1250,7 @@ async function listGroupProjects(options) {
|
|
|
1249
1250
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1250
1251
|
// If read-only mode is enabled, filter out write operations
|
|
1251
1252
|
const tools = GITLAB_READ_ONLY_MODE
|
|
1252
|
-
? allTools.filter(tool => readOnlyTools.includes(tool.name))
|
|
1253
|
+
? allTools.filter((tool) => readOnlyTools.includes(tool.name))
|
|
1253
1254
|
: allTools;
|
|
1254
1255
|
return {
|
|
1255
1256
|
tools,
|
|
@@ -1266,7 +1267,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1266
1267
|
try {
|
|
1267
1268
|
const forkedProject = await forkProject(forkArgs.project_id, forkArgs.namespace);
|
|
1268
1269
|
return {
|
|
1269
|
-
content: [
|
|
1270
|
+
content: [
|
|
1271
|
+
{ type: "text", text: JSON.stringify(forkedProject, null, 2) },
|
|
1272
|
+
],
|
|
1270
1273
|
};
|
|
1271
1274
|
}
|
|
1272
1275
|
catch (forkError) {
|
|
@@ -1276,7 +1279,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1276
1279
|
forkErrorMessage = `${forkErrorMessage}: ${forkError.message}`;
|
|
1277
1280
|
}
|
|
1278
1281
|
return {
|
|
1279
|
-
content: [
|
|
1282
|
+
content: [
|
|
1283
|
+
{
|
|
1284
|
+
type: "text",
|
|
1285
|
+
text: JSON.stringify({ error: forkErrorMessage }, null, 2),
|
|
1286
|
+
},
|
|
1287
|
+
],
|
|
1280
1288
|
};
|
|
1281
1289
|
}
|
|
1282
1290
|
}
|
|
@@ -1414,7 +1422,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1414
1422
|
const data = await response.json();
|
|
1415
1423
|
const namespaces = z.array(GitLabNamespaceSchema).parse(data);
|
|
1416
1424
|
return {
|
|
1417
|
-
content: [
|
|
1425
|
+
content: [
|
|
1426
|
+
{ type: "text", text: JSON.stringify(namespaces, null, 2) },
|
|
1427
|
+
],
|
|
1418
1428
|
};
|
|
1419
1429
|
}
|
|
1420
1430
|
case "get_namespace": {
|
|
@@ -1440,7 +1450,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1440
1450
|
const data = await response.json();
|
|
1441
1451
|
const namespaceExists = GitLabNamespaceExistsResponseSchema.parse(data);
|
|
1442
1452
|
return {
|
|
1443
|
-
content: [
|
|
1453
|
+
content: [
|
|
1454
|
+
{ type: "text", text: JSON.stringify(namespaceExists, null, 2) },
|
|
1455
|
+
],
|
|
1444
1456
|
};
|
|
1445
1457
|
}
|
|
1446
1458
|
case "get_project": {
|
|
@@ -1498,7 +1510,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1498
1510
|
const args = DeleteIssueSchema.parse(request.params.arguments);
|
|
1499
1511
|
await deleteIssue(args.project_id, args.issue_iid);
|
|
1500
1512
|
return {
|
|
1501
|
-
content: [
|
|
1513
|
+
content: [
|
|
1514
|
+
{
|
|
1515
|
+
type: "text",
|
|
1516
|
+
text: JSON.stringify({ status: "success", message: "Issue deleted successfully" }, null, 2),
|
|
1517
|
+
},
|
|
1518
|
+
],
|
|
1502
1519
|
};
|
|
1503
1520
|
}
|
|
1504
1521
|
case "list_issue_links": {
|
|
@@ -1526,7 +1543,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1526
1543
|
const args = DeleteIssueLinkSchema.parse(request.params.arguments);
|
|
1527
1544
|
await deleteIssueLink(args.project_id, args.issue_iid, args.issue_link_id);
|
|
1528
1545
|
return {
|
|
1529
|
-
content: [
|
|
1546
|
+
content: [
|
|
1547
|
+
{
|
|
1548
|
+
type: "text",
|
|
1549
|
+
text: JSON.stringify({
|
|
1550
|
+
status: "success",
|
|
1551
|
+
message: "Issue link deleted successfully",
|
|
1552
|
+
}, null, 2),
|
|
1553
|
+
},
|
|
1554
|
+
],
|
|
1530
1555
|
};
|
|
1531
1556
|
}
|
|
1532
1557
|
case "list_labels": {
|
|
@@ -1562,7 +1587,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1562
1587
|
const args = DeleteLabelSchema.parse(request.params.arguments);
|
|
1563
1588
|
await deleteLabel(args.project_id, args.label_id);
|
|
1564
1589
|
return {
|
|
1565
|
-
content: [
|
|
1590
|
+
content: [
|
|
1591
|
+
{
|
|
1592
|
+
type: "text",
|
|
1593
|
+
text: JSON.stringify({ status: "success", message: "Label deleted successfully" }, null, 2),
|
|
1594
|
+
},
|
|
1595
|
+
],
|
|
1566
1596
|
};
|
|
1567
1597
|
}
|
|
1568
1598
|
case "list_group_projects": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zereight/mcp-gitlab",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.27",
|
|
4
4
|
"description": "MCP server for using the GitLab API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "zereight",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
20
20
|
"prepare": "npm run build",
|
|
21
21
|
"watch": "tsc --watch",
|
|
22
|
-
"deploy": "npm
|
|
22
|
+
"deploy": "npm publish --access public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@modelcontextprotocol/sdk": "1.8.0",
|