@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.
Files changed (2) hide show
  1. package/build/index.js +65 -35
  2. 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, '../package.json');
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, 'utf8'));
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 === 'true';
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('/') ? url.slice(0, -1) : url;
247
+ let normalizedUrl = url.endsWith("/") ? url.slice(0, -1) : url;
248
248
  // Check if URL already has /api/v4
249
- if (!normalizedUrl.endsWith('/api/v4') && !normalizedUrl.endsWith('/api/v4/')) {
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 === 'label_name' && Array.isArray(value)) {
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 = 'relates_to') {
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('include_subgroups', 'true');
1211
+ url.searchParams.append("include_subgroups", "true");
1211
1212
  if (options.search)
1212
- url.searchParams.append('search', options.search);
1213
+ url.searchParams.append("search", options.search);
1213
1214
  if (options.order_by)
1214
- url.searchParams.append('order_by', options.order_by);
1215
+ url.searchParams.append("order_by", options.order_by);
1215
1216
  if (options.sort)
1216
- url.searchParams.append('sort', options.sort);
1217
+ url.searchParams.append("sort", options.sort);
1217
1218
  if (options.page)
1218
- url.searchParams.append('page', options.page.toString());
1219
+ url.searchParams.append("page", options.page.toString());
1219
1220
  if (options.per_page)
1220
- url.searchParams.append('per_page', options.per_page.toString());
1221
+ url.searchParams.append("per_page", options.per_page.toString());
1221
1222
  if (options.archived !== undefined)
1222
- url.searchParams.append('archived', options.archived.toString());
1223
+ url.searchParams.append("archived", options.archived.toString());
1223
1224
  if (options.visibility)
1224
- url.searchParams.append('visibility', options.visibility);
1225
+ url.searchParams.append("visibility", options.visibility);
1225
1226
  if (options.with_issues_enabled !== undefined)
1226
- url.searchParams.append('with_issues_enabled', options.with_issues_enabled.toString());
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('with_merge_requests_enabled', options.with_merge_requests_enabled.toString());
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('min_access_level', options.min_access_level.toString());
1231
+ url.searchParams.append("min_access_level", options.min_access_level.toString());
1231
1232
  if (options.with_programming_language)
1232
- url.searchParams.append('with_programming_language', options.with_programming_language);
1233
+ url.searchParams.append("with_programming_language", options.with_programming_language);
1233
1234
  if (options.starred !== undefined)
1234
- url.searchParams.append('starred', options.starred.toString());
1235
+ url.searchParams.append("starred", options.starred.toString());
1235
1236
  if (options.statistics !== undefined)
1236
- url.searchParams.append('statistics', options.statistics.toString());
1237
+ url.searchParams.append("statistics", options.statistics.toString());
1237
1238
  if (options.with_custom_attributes !== undefined)
1238
- url.searchParams.append('with_custom_attributes', options.with_custom_attributes.toString());
1239
+ url.searchParams.append("with_custom_attributes", options.with_custom_attributes.toString());
1239
1240
  if (options.with_security_reports !== undefined)
1240
- url.searchParams.append('with_security_reports', options.with_security_reports.toString());
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: [{ type: "text", text: JSON.stringify(forkedProject, null, 2) }],
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: [{ type: "text", text: JSON.stringify({ error: forkErrorMessage }, null, 2) }],
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: [{ type: "text", text: JSON.stringify(namespaces, null, 2) }],
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: [{ type: "text", text: JSON.stringify(namespaceExists, null, 2) }],
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: [{ type: "text", text: JSON.stringify({ status: "success", message: "Issue deleted successfully" }, null, 2) }],
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: [{ type: "text", text: JSON.stringify({ status: "success", message: "Issue link deleted successfully" }, null, 2) }],
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: [{ type: "text", text: JSON.stringify({ status: "success", message: "Label deleted successfully" }, null, 2) }],
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.25",
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 run build && npm publish"
22
+ "deploy": "npm publish --access public"
23
23
  },
24
24
  "dependencies": {
25
25
  "@modelcontextprotocol/sdk": "1.8.0",