@zereight/mcp-gitlab 1.0.50 → 1.0.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,7 +26,9 @@ When using with the Claude App, you need to set up your API key and URLs directl
26
26
  "GITLAB_PERSONAL_ACCESS_TOKEN": "your_gitlab_token",
27
27
  "GITLAB_API_URL": "your_gitlab_api_url",
28
28
  "GITLAB_READ_ONLY_MODE": "false",
29
- "USE_GITLAB_WIKI": "true"
29
+ "USE_GITLAB_WIKI": "false", // use wiki api?
30
+ "USE_MILESTONE": "false", // use milestone api?
31
+ "USE_PIPELINE": "false" // use pipeline api?
30
32
  }
31
33
  }
32
34
  }
@@ -52,13 +54,19 @@ When using with the Claude App, you need to set up your API key and URLs directl
52
54
  "GITLAB_READ_ONLY_MODE",
53
55
  "-e",
54
56
  "USE_GITLAB_WIKI",
57
+ "-e",
58
+ "USE_MILESTONE",
59
+ "-e",
60
+ "USE_PIPELINE",
55
61
  "iwakitakuma/gitlab-mcp"
56
62
  ],
57
63
  "env": {
58
64
  "GITLAB_PERSONAL_ACCESS_TOKEN": "your_gitlab_token",
59
65
  "GITLAB_API_URL": "https://gitlab.com/api/v4", // Optional, for self-hosted GitLab
60
66
  "GITLAB_READ_ONLY_MODE": "false",
61
- "USE_GITLAB_WIKI": "true"
67
+ "USE_GITLAB_WIKI": "true",
68
+ "USE_MILESTONE": "true",
69
+ "USE_PIPELINE": "true"
62
70
  }
63
71
  }
64
72
  }
@@ -77,10 +85,13 @@ $ sh scripts/image_push.sh docker_user_name
77
85
  - `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`)
78
86
  - `GITLAB_READ_ONLY_MODE`: When set to 'true', restricts the server to only expose read-only operations. Useful for enhanced security or when write access is not needed. Also useful for using with Cursor and it's 40 tool limit.
79
87
  - `USE_GITLAB_WIKI`: When set to 'true', enables the wiki-related tools (list_wiki_pages, get_wiki_page, create_wiki_page, update_wiki_page, delete_wiki_page). By default, wiki features are disabled.
88
+ - `USE_MILESTONE`: When set to 'true', enables the milestone-related tools (list_milestones, get_milestone, create_milestone, edit_milestone, delete_milestone, get_milestone_issue, get_milestone_merge_requests, promote_milestone, get_milestone_burndown_events). By default, milestone features are disabled.
89
+ - `USE_PIPELINE`: When set to 'true', enables the pipeline-related tools (list_pipelines, get_pipeline, list_pipeline_jobs, get_pipeline_job, get_pipeline_job_output, create_pipeline, retry_pipeline, cancel_pipeline). By default, pipeline features are disabled.
80
90
 
81
91
  ## Tools 🛠️
82
92
 
83
93
  +<!-- TOOLS-START -->
94
+
84
95
  1. `create_or_update_file` - Create or update a single file in a GitLab project
85
96
  2. `search_repositories` - Search for GitLab projects
86
97
  3. `create_repository` - Create a new GitLab project
@@ -131,14 +142,17 @@ $ sh scripts/image_push.sh docker_user_name
131
142
  48. `list_pipeline_jobs` - List all jobs in a specific pipeline
132
143
  49. `get_pipeline_job` - Get details of a GitLab pipeline job number
133
144
  50. `get_pipeline_job_output` - Get the output/trace of a GitLab pipeline job number
134
- 51. `list_merge_requests` - List merge requests in a GitLab project with filtering options
135
- 52. `list_milestones` - List milestones in a GitLab project with filtering options
136
- 53. `get_milestone` - Get details of a specific milestone
137
- 54. `create_milestone` - Create a new milestone in a GitLab project
138
- 55. `edit_milestone ` - Edit an existing milestone in a GitLab project
139
- 56. `delete_milestone` - Delete a milestone from a GitLab project
140
- 57. `get_milestone_issue` - Get issues associated with a specific milestone
141
- 58. `get_milestone_merge_requests` - Get merge requests associated with a specific milestone
142
- 59. `promote_milestone` - Promote a milestone to the next stage
143
- 60. `get_milestone_burndown_events` - Get burndown events for a specific milestone
145
+ 51. `create_pipeline` - Create a new pipeline for a branch or tag
146
+ 52. `retry_pipeline` - Retry a failed or canceled pipeline
147
+ 53. `cancel_pipeline` - Cancel a running pipeline
148
+ 54. `list_merge_requests` - List merge requests in a GitLab project with filtering options
149
+ 55. `list_milestones` - List milestones in a GitLab project with filtering options
150
+ 56. `get_milestone` - Get details of a specific milestone
151
+ 57. `create_milestone` - Create a new milestone in a GitLab project
152
+ 58. `edit_milestone ` - Edit an existing milestone in a GitLab project
153
+ 59. `delete_milestone` - Delete a milestone from a GitLab project
154
+ 60. `get_milestone_issue` - Get issues associated with a specific milestone
155
+ 61. `get_milestone_merge_requests` - Get merge requests associated with a specific milestone
156
+ 62. `promote_milestone` - Promote a milestone to the next stage
157
+ 63. `get_milestone_burndown_events` - Get burndown events for a specific milestone
144
158
  <!-- TOOLS-END -->
package/build/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
5
  import fetch from "node-fetch";
6
6
  import { SocksProxyAgent } from "socks-proxy-agent";
7
7
  import { HttpsProxyAgent } from "https-proxy-agent";
@@ -13,7 +13,7 @@ import { dirname } from "path";
13
13
  import fs from "fs";
14
14
  import path from "path";
15
15
  import { URL } from "url";
16
- import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, GitLabNamespaceSchema, GitLabNamespaceExistsResponseSchema, GitLabProjectSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabMergeRequestDiffSchema, GetMergeRequestSchema, GetMergeRequestDiffsSchema, UpdateMergeRequestSchema, ListIssuesSchema, GetIssueSchema, UpdateIssueSchema, DeleteIssueSchema, GitLabIssueLinkSchema, GitLabIssueWithLinkDetailsSchema, ListIssueLinksSchema, ListIssueDiscussionsSchema, GetIssueLinkSchema, CreateIssueLinkSchema, DeleteIssueLinkSchema, ListNamespacesSchema, GetNamespaceSchema, VerifyNamespaceSchema, GetProjectSchema, ListProjectsSchema, ListLabelsSchema, GetLabelSchema, CreateLabelSchema, UpdateLabelSchema, DeleteLabelSchema, CreateNoteSchema, CreateMergeRequestThreadSchema, ListGroupProjectsSchema, ListWikiPagesSchema, GetWikiPageSchema, CreateWikiPageSchema, UpdateWikiPageSchema, DeleteWikiPageSchema, GitLabWikiPageSchema, GetRepositoryTreeSchema, GitLabTreeItemSchema, GitLabPipelineSchema, GetPipelineSchema, ListPipelinesSchema, ListPipelineJobsSchema,
16
+ import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, GitLabNamespaceSchema, GitLabNamespaceExistsResponseSchema, GitLabProjectSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabMergeRequestDiffSchema, GetMergeRequestSchema, GetMergeRequestDiffsSchema, UpdateMergeRequestSchema, ListIssuesSchema, GetIssueSchema, UpdateIssueSchema, DeleteIssueSchema, GitLabIssueLinkSchema, GitLabIssueWithLinkDetailsSchema, ListIssueLinksSchema, ListIssueDiscussionsSchema, GetIssueLinkSchema, CreateIssueLinkSchema, DeleteIssueLinkSchema, ListNamespacesSchema, GetNamespaceSchema, VerifyNamespaceSchema, GetProjectSchema, ListProjectsSchema, ListLabelsSchema, GetLabelSchema, CreateLabelSchema, UpdateLabelSchema, DeleteLabelSchema, CreateNoteSchema, CreateMergeRequestThreadSchema, ListGroupProjectsSchema, ListWikiPagesSchema, GetWikiPageSchema, CreateWikiPageSchema, UpdateWikiPageSchema, DeleteWikiPageSchema, GitLabWikiPageSchema, GetRepositoryTreeSchema, GitLabTreeItemSchema, GitLabPipelineSchema, GetPipelineSchema, ListPipelinesSchema, ListPipelineJobsSchema, CreatePipelineSchema, RetryPipelineSchema, CancelPipelineSchema,
17
17
  // pipeline job schemas
18
18
  GetPipelineJobOutputSchema, GitLabPipelineJobSchema,
19
19
  // Discussion Schemas
@@ -48,6 +48,8 @@ const server = new Server({
48
48
  const GITLAB_PERSONAL_ACCESS_TOKEN = process.env.GITLAB_PERSONAL_ACCESS_TOKEN;
49
49
  const GITLAB_READ_ONLY_MODE = process.env.GITLAB_READ_ONLY_MODE === "true";
50
50
  const USE_GITLAB_WIKI = process.env.USE_GITLAB_WIKI === "true";
51
+ const USE_MILESTONE = process.env.USE_MILESTONE === "true";
52
+ const USE_PIPELINE = process.env.USE_PIPELINE === "true";
51
53
  // Add proxy configuration
52
54
  const HTTP_PROXY = process.env.HTTP_PROXY;
53
55
  const HTTPS_PROXY = process.env.HTTPS_PROXY;
@@ -338,6 +340,21 @@ const allTools = [
338
340
  description: "Get the output/trace of a GitLab pipeline job number",
339
341
  inputSchema: zodToJsonSchema(GetPipelineJobOutputSchema),
340
342
  },
343
+ {
344
+ name: "create_pipeline",
345
+ description: "Create a new pipeline for a branch or tag",
346
+ inputSchema: zodToJsonSchema(CreatePipelineSchema),
347
+ },
348
+ {
349
+ name: "retry_pipeline",
350
+ description: "Retry a failed or canceled pipeline",
351
+ inputSchema: zodToJsonSchema(RetryPipelineSchema),
352
+ },
353
+ {
354
+ name: "cancel_pipeline",
355
+ description: "Cancel a running pipeline",
356
+ inputSchema: zodToJsonSchema(CancelPipelineSchema),
357
+ },
341
358
  {
342
359
  name: "list_merge_requests",
343
360
  description: "List merge requests in a GitLab project with filtering options",
@@ -421,6 +438,8 @@ const readOnlyTools = [
421
438
  "get_milestone_issue",
422
439
  "get_milestone_merge_requests",
423
440
  "get_milestone_burndown_events",
441
+ "list_wiki_pages",
442
+ "get_wiki_page",
424
443
  ];
425
444
  // Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
426
445
  const wikiToolNames = [
@@ -431,6 +450,29 @@ const wikiToolNames = [
431
450
  "delete_wiki_page",
432
451
  "upload_wiki_attachment",
433
452
  ];
453
+ // Define which tools are related to milestones and can be toggled by USE_MILESTONE
454
+ const milestoneToolNames = [
455
+ "list_milestones",
456
+ "get_milestone",
457
+ "create_milestone",
458
+ "edit_milestone",
459
+ "delete_milestone",
460
+ "get_milestone_issue",
461
+ "get_milestone_merge_requests",
462
+ "promote_milestone",
463
+ "get_milestone_burndown_events",
464
+ ];
465
+ // Define which tools are related to pipelines and can be toggled by USE_PIPELINE
466
+ const pipelineToolNames = [
467
+ "list_pipelines",
468
+ "get_pipeline",
469
+ "list_pipeline_jobs",
470
+ "get_pipeline_job",
471
+ "get_pipeline_job_output",
472
+ "create_pipeline",
473
+ "retry_pipeline",
474
+ "cancel_pipeline",
475
+ ];
434
476
  /**
435
477
  * Smart URL handling for GitLab API
436
478
  *
@@ -444,8 +486,7 @@ function normalizeGitLabApiUrl(url) {
444
486
  // Remove trailing slash if present
445
487
  let normalizedUrl = url.endsWith("/") ? url.slice(0, -1) : url;
446
488
  // Check if URL already has /api/v4
447
- if (!normalizedUrl.endsWith("/api/v4") &&
448
- !normalizedUrl.endsWith("/api/v4/")) {
489
+ if (!normalizedUrl.endsWith("/api/v4") && !normalizedUrl.endsWith("/api/v4/")) {
449
490
  // Append /api/v4 if not already present
450
491
  normalizedUrl = `${normalizedUrl}/api/v4`;
451
492
  }
@@ -468,8 +509,7 @@ async function handleGitLabError(response) {
468
509
  if (!response.ok) {
469
510
  const errorBody = await response.text();
470
511
  // Check specifically for Rate Limit error
471
- if (response.status === 403 &&
472
- errorBody.includes("User API Key Rate limit exceeded")) {
512
+ if (response.status === 403 && errorBody.includes("User API Key Rate limit exceeded")) {
473
513
  console.error("GitLab API Rate Limit Exceeded:", errorBody);
474
514
  console.log("User API Key Rate limit exceeded. Please try again later.");
475
515
  throw new Error(`GitLab API Rate Limit Exceeded: ${errorBody}`);
@@ -1095,7 +1135,7 @@ async function createTree(projectId, files, ref) {
1095
1135
  ...DEFAULT_FETCH_CONFIG,
1096
1136
  method: "POST",
1097
1137
  body: JSON.stringify({
1098
- files: files.map((file) => ({
1138
+ files: files.map(file => ({
1099
1139
  file_path: file.path,
1100
1140
  content: file.content,
1101
1141
  encoding: "text",
@@ -1132,7 +1172,7 @@ async function createCommit(projectId, message, branch, actions) {
1132
1172
  body: JSON.stringify({
1133
1173
  branch,
1134
1174
  commit_message: message,
1135
- actions: actions.map((action) => ({
1175
+ actions: actions.map(action => ({
1136
1176
  action: "create",
1137
1177
  file_path: action.path,
1138
1178
  content: action.content,
@@ -1844,6 +1884,69 @@ async function getPipelineJobOutput(projectId, jobId) {
1844
1884
  await handleGitLabError(response);
1845
1885
  return await response.text();
1846
1886
  }
1887
+ /**
1888
+ * Create a new pipeline
1889
+ *
1890
+ * @param {string} projectId - The ID or URL-encoded path of the project
1891
+ * @param {string} ref - The branch or tag to run the pipeline on
1892
+ * @param {Array} variables - Optional variables for the pipeline
1893
+ * @returns {Promise<GitLabPipeline>} The created pipeline
1894
+ */
1895
+ async function createPipeline(projectId, ref, variables) {
1896
+ projectId = decodeURIComponent(projectId); // Decode project ID
1897
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipeline`);
1898
+ const body = { ref };
1899
+ if (variables && variables.length > 0) {
1900
+ body.variables = variables.reduce((acc, { key, value }) => {
1901
+ acc[key] = value;
1902
+ return acc;
1903
+ }, {});
1904
+ }
1905
+ const response = await fetch(url.toString(), {
1906
+ method: "POST",
1907
+ headers: DEFAULT_HEADERS,
1908
+ body: JSON.stringify(body),
1909
+ });
1910
+ await handleGitLabError(response);
1911
+ const data = await response.json();
1912
+ return GitLabPipelineSchema.parse(data);
1913
+ }
1914
+ /**
1915
+ * Retry a pipeline
1916
+ *
1917
+ * @param {string} projectId - The ID or URL-encoded path of the project
1918
+ * @param {number} pipelineId - The ID of the pipeline to retry
1919
+ * @returns {Promise<GitLabPipeline>} The retried pipeline
1920
+ */
1921
+ async function retryPipeline(projectId, pipelineId) {
1922
+ projectId = decodeURIComponent(projectId); // Decode project ID
1923
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/retry`);
1924
+ const response = await fetch(url.toString(), {
1925
+ method: "POST",
1926
+ headers: DEFAULT_HEADERS,
1927
+ });
1928
+ await handleGitLabError(response);
1929
+ const data = await response.json();
1930
+ return GitLabPipelineSchema.parse(data);
1931
+ }
1932
+ /**
1933
+ * Cancel a pipeline
1934
+ *
1935
+ * @param {string} projectId - The ID or URL-encoded path of the project
1936
+ * @param {number} pipelineId - The ID of the pipeline to cancel
1937
+ * @returns {Promise<GitLabPipeline>} The canceled pipeline
1938
+ */
1939
+ async function cancelPipeline(projectId, pipelineId) {
1940
+ projectId = decodeURIComponent(projectId); // Decode project ID
1941
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/cancel`);
1942
+ const response = await fetch(url.toString(), {
1943
+ method: "POST",
1944
+ headers: DEFAULT_HEADERS,
1945
+ });
1946
+ await handleGitLabError(response);
1947
+ const data = await response.json();
1948
+ return GitLabPipelineSchema.parse(data);
1949
+ }
1847
1950
  /**
1848
1951
  * Get the repository tree for a project
1849
1952
  * @param {string} projectId - The ID or URL-encoded path of the project
@@ -1892,7 +1995,7 @@ async function listProjectMilestones(projectId, options) {
1892
1995
  Object.entries(options).forEach(([key, value]) => {
1893
1996
  if (value !== undefined) {
1894
1997
  if (key === "iids" && Array.isArray(value) && value.length > 0) {
1895
- value.forEach((iid) => {
1998
+ value.forEach(iid => {
1896
1999
  url.searchParams.append("iids[]", iid.toString());
1897
2000
  });
1898
2001
  }
@@ -2044,18 +2147,24 @@ async function getMilestoneBurndownEvents(projectId, milestoneId) {
2044
2147
  server.setRequestHandler(ListToolsRequestSchema, async () => {
2045
2148
  // Apply read-only filter first
2046
2149
  const tools0 = GITLAB_READ_ONLY_MODE
2047
- ? allTools.filter((tool) => readOnlyTools.includes(tool.name))
2150
+ ? allTools.filter(tool => readOnlyTools.includes(tool.name))
2048
2151
  : allTools;
2049
2152
  // Toggle wiki tools by USE_GITLAB_WIKI flag
2050
- let tools = USE_GITLAB_WIKI
2153
+ const tools1 = USE_GITLAB_WIKI
2051
2154
  ? tools0
2052
- : tools0.filter((tool) => !wikiToolNames.includes(tool.name));
2155
+ : tools0.filter(tool => !wikiToolNames.includes(tool.name));
2156
+ // Toggle milestone tools by USE_MILESTONE flag
2157
+ const tools2 = USE_MILESTONE
2158
+ ? tools1
2159
+ : tools1.filter(tool => !milestoneToolNames.includes(tool.name));
2160
+ // Toggle pipeline tools by USE_PIPELINE flag
2161
+ let tools = USE_PIPELINE
2162
+ ? tools2
2163
+ : tools2.filter(tool => !pipelineToolNames.includes(tool.name));
2053
2164
  // <<< START: Gemini 호환성을 위해 $schema 제거 >>>
2054
- tools = tools.map((tool) => {
2165
+ tools = tools.map(tool => {
2055
2166
  // inputSchema가 존재하고 객체인지 확인
2056
- if (tool.inputSchema &&
2057
- typeof tool.inputSchema === "object" &&
2058
- tool.inputSchema !== null) {
2167
+ if (tool.inputSchema && typeof tool.inputSchema === "object" && tool.inputSchema !== null) {
2059
2168
  // $schema 키가 존재하면 삭제
2060
2169
  if ("$schema" in tool.inputSchema) {
2061
2170
  // 불변성을 위해 새로운 객체 생성 (선택적이지만 권장)
@@ -2083,9 +2192,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2083
2192
  try {
2084
2193
  const forkedProject = await forkProject(forkArgs.project_id, forkArgs.namespace);
2085
2194
  return {
2086
- content: [
2087
- { type: "text", text: JSON.stringify(forkedProject, null, 2) },
2088
- ],
2195
+ content: [{ type: "text", text: JSON.stringify(forkedProject, null, 2) }],
2089
2196
  };
2090
2197
  }
2091
2198
  catch (forkError) {
@@ -2129,9 +2236,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2129
2236
  const args = CreateRepositorySchema.parse(request.params.arguments);
2130
2237
  const repository = await createRepository(args);
2131
2238
  return {
2132
- content: [
2133
- { type: "text", text: JSON.stringify(repository, null, 2) },
2134
- ],
2239
+ content: [{ type: "text", text: JSON.stringify(repository, null, 2) }],
2135
2240
  };
2136
2241
  }
2137
2242
  case "get_file_contents": {
@@ -2150,7 +2255,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2150
2255
  }
2151
2256
  case "push_files": {
2152
2257
  const args = PushFilesSchema.parse(request.params.arguments);
2153
- const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map((f) => ({ path: f.file_path, content: f.content })));
2258
+ const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map(f => ({ path: f.file_path, content: f.content })));
2154
2259
  return {
2155
2260
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
2156
2261
  };
@@ -2168,9 +2273,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2168
2273
  const { project_id, ...options } = args;
2169
2274
  const mergeRequest = await createMergeRequest(project_id, options);
2170
2275
  return {
2171
- content: [
2172
- { type: "text", text: JSON.stringify(mergeRequest, null, 2) },
2173
- ],
2276
+ content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
2174
2277
  };
2175
2278
  }
2176
2279
  case "update_merge_request_note": {
@@ -2207,9 +2310,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2207
2310
  const args = GetMergeRequestSchema.parse(request.params.arguments);
2208
2311
  const mergeRequest = await getMergeRequest(args.project_id, args.merge_request_iid, args.source_branch);
2209
2312
  return {
2210
- content: [
2211
- { type: "text", text: JSON.stringify(mergeRequest, null, 2) },
2212
- ],
2313
+ content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
2213
2314
  };
2214
2315
  }
2215
2316
  case "get_merge_request_diffs": {
@@ -2224,18 +2325,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2224
2325
  const { project_id, merge_request_iid, source_branch, ...options } = args;
2225
2326
  const mergeRequest = await updateMergeRequest(project_id, options, merge_request_iid, source_branch);
2226
2327
  return {
2227
- content: [
2228
- { type: "text", text: JSON.stringify(mergeRequest, null, 2) },
2229
- ],
2328
+ content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
2230
2329
  };
2231
2330
  }
2232
2331
  case "mr_discussions": {
2233
2332
  const args = ListMergeRequestDiscussionsSchema.parse(request.params.arguments);
2234
2333
  const discussions = await listMergeRequestDiscussions(args.project_id, args.merge_request_iid);
2235
2334
  return {
2236
- content: [
2237
- { type: "text", text: JSON.stringify(discussions, null, 2) },
2238
- ],
2335
+ content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
2239
2336
  };
2240
2337
  }
2241
2338
  case "list_namespaces": {
@@ -2260,9 +2357,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2260
2357
  const data = await response.json();
2261
2358
  const namespaces = z.array(GitLabNamespaceSchema).parse(data);
2262
2359
  return {
2263
- content: [
2264
- { type: "text", text: JSON.stringify(namespaces, null, 2) },
2265
- ],
2360
+ content: [{ type: "text", text: JSON.stringify(namespaces, null, 2) }],
2266
2361
  };
2267
2362
  }
2268
2363
  case "get_namespace": {
@@ -2288,9 +2383,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2288
2383
  const data = await response.json();
2289
2384
  const namespaceExists = GitLabNamespaceExistsResponseSchema.parse(data);
2290
2385
  return {
2291
- content: [
2292
- { type: "text", text: JSON.stringify(namespaceExists, null, 2) },
2293
- ],
2386
+ content: [{ type: "text", text: JSON.stringify(namespaceExists, null, 2) }],
2294
2387
  };
2295
2388
  }
2296
2389
  case "get_project": {
@@ -2376,9 +2469,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2376
2469
  const { project_id, issue_iid, ...options } = args;
2377
2470
  const discussions = await listIssueDiscussions(project_id, issue_iid, options);
2378
2471
  return {
2379
- content: [
2380
- { type: "text", text: JSON.stringify(discussions, null, 2) },
2381
- ],
2472
+ content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
2382
2473
  };
2383
2474
  }
2384
2475
  case "get_issue_link": {
@@ -2564,13 +2655,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2564
2655
  ],
2565
2656
  };
2566
2657
  }
2658
+ case "create_pipeline": {
2659
+ const { project_id, ref, variables } = CreatePipelineSchema.parse(request.params.arguments);
2660
+ const pipeline = await createPipeline(project_id, ref, variables);
2661
+ return {
2662
+ content: [
2663
+ {
2664
+ type: "text",
2665
+ text: `Created pipeline #${pipeline.id} for ${ref}. Status: ${pipeline.status}\nWeb URL: ${pipeline.web_url}`,
2666
+ },
2667
+ ],
2668
+ };
2669
+ }
2670
+ case "retry_pipeline": {
2671
+ const { project_id, pipeline_id } = RetryPipelineSchema.parse(request.params.arguments);
2672
+ const pipeline = await retryPipeline(project_id, pipeline_id);
2673
+ return {
2674
+ content: [
2675
+ {
2676
+ type: "text",
2677
+ text: `Retried pipeline #${pipeline.id}. Status: ${pipeline.status}\nWeb URL: ${pipeline.web_url}`,
2678
+ },
2679
+ ],
2680
+ };
2681
+ }
2682
+ case "cancel_pipeline": {
2683
+ const { project_id, pipeline_id } = CancelPipelineSchema.parse(request.params.arguments);
2684
+ const pipeline = await cancelPipeline(project_id, pipeline_id);
2685
+ return {
2686
+ content: [
2687
+ {
2688
+ type: "text",
2689
+ text: `Canceled pipeline #${pipeline.id}. Status: ${pipeline.status}\nWeb URL: ${pipeline.web_url}`,
2690
+ },
2691
+ ],
2692
+ };
2693
+ }
2567
2694
  case "list_merge_requests": {
2568
2695
  const args = ListMergeRequestsSchema.parse(request.params.arguments);
2569
2696
  const mergeRequests = await listMergeRequests(args.project_id, args);
2570
2697
  return {
2571
- content: [
2572
- { type: "text", text: JSON.stringify(mergeRequests, null, 2) },
2573
- ],
2698
+ content: [{ type: "text", text: JSON.stringify(mergeRequests, null, 2) }],
2574
2699
  };
2575
2700
  }
2576
2701
  case "list_milestones": {
@@ -2691,7 +2816,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2691
2816
  catch (error) {
2692
2817
  if (error instanceof z.ZodError) {
2693
2818
  throw new Error(`Invalid arguments: ${error.errors
2694
- .map((e) => `${e.path.join(".")}: ${e.message}`)
2819
+ .map(e => `${e.path.join(".")}: ${e.message}`)
2695
2820
  .join(", ")}`);
2696
2821
  }
2697
2822
  throw error;
@@ -2716,7 +2841,7 @@ async function runServer() {
2716
2841
  process.exit(1);
2717
2842
  }
2718
2843
  }
2719
- runServer().catch((error) => {
2844
+ runServer().catch(error => {
2720
2845
  console.error("Fatal error in main():", error);
2721
2846
  process.exit(1);
2722
2847
  });