@zereight/mcp-gitlab 1.0.54 → 1.0.56

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
@@ -36,8 +36,8 @@ When using with the Claude App, you need to set up your API key and URLs directl
36
36
  ```
37
37
 
38
38
  #### Docker
39
-
40
- ```json
39
+ - stdio
40
+ ```mcp.json
41
41
  {
42
42
  "mcpServers": {
43
43
  "GitLab communication server": {
@@ -73,6 +73,30 @@ When using with the Claude App, you need to set up your API key and URLs directl
73
73
  }
74
74
  ```
75
75
 
76
+ - sse
77
+ ```shell
78
+ docker run -i --rm \
79
+ -e GITLAB_PERSONAL_ACCESS_TOKEN=your_gitlab_token \
80
+ -e GITLAB_API_URL= "https://gitlab.com/api/v4"\
81
+ -e GITLAB_READ_ONLY_MODE=true \
82
+ -e USE_GITLAB_WIKI=true \
83
+ -e USE_MILESTONE=true \
84
+ -e USE_PIPELINE=true \
85
+ -e SSE=true \
86
+ -p 3333:3002 \
87
+ gitlab-mcp
88
+ ```
89
+
90
+ ```json
91
+ {
92
+ "mcpServers": {
93
+ "GitLab communication server": {
94
+ "url": "http://localhost:3333/sse"
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
76
100
  #### Docker Image Push
77
101
 
78
102
  ```shell
@@ -91,7 +115,6 @@ $ sh scripts/image_push.sh docker_user_name
91
115
  ## Tools 🛠️
92
116
 
93
117
  +<!-- TOOLS-START -->
94
-
95
118
  1. `create_or_update_file` - Create or update a single file in a GitLab project
96
119
  2. `search_repositories` - Search for GitLab projects
97
120
  3. `create_repository` - Create a new GitLab project
@@ -103,56 +126,58 @@ $ sh scripts/image_push.sh docker_user_name
103
126
  9. `create_branch` - Create a new branch in a GitLab project
104
127
  10. `get_merge_request` - Get details of a merge request (Either mergeRequestIid or branchName must be provided)
105
128
  11. `get_merge_request_diffs` - Get the changes/diffs of a merge request (Either mergeRequestIid or branchName must be provided)
106
- 12. `update_merge_request` - Update a merge request (Either mergeRequestIid or branchName must be provided)
107
- 13. `create_note` - Create a new note (comment) to an issue or merge request
108
- 14. `create_merge_request_thread` - Create a new thread on a merge request
109
- 15. `mr_discussions` - List discussion items for a merge request
110
- 16. `update_merge_request_note` - Modify an existing merge request thread note
111
- 17. `create_merge_request_note` - Add a new note to an existing merge request thread
112
- 18. `update_issue_note` - Modify an existing issue thread note
113
- 19. `create_issue_note` - Add a new note to an existing issue thread
114
- 20. `list_issues` - List issues in a GitLab project with filtering options
115
- 21. `get_issue` - Get details of a specific issue in a GitLab project
116
- 22. `update_issue` - Update an issue in a GitLab project
117
- 23. `delete_issue` - Delete an issue from a GitLab project
118
- 24. `list_issue_links` - List all issue links for a specific issue
119
- 25. `list_issue_discussions` - List discussions for an issue in a GitLab project
120
- 26. `get_issue_link` - Get a specific issue link
121
- 27. `create_issue_link` - Create an issue link between two issues
122
- 28. `delete_issue_link` - Delete an issue link
123
- 29. `list_namespaces` - List all namespaces available to the current user
124
- 30. `get_namespace` - Get details of a namespace by ID or path
125
- 31. `verify_namespace` - Verify if a namespace path exists
126
- 32. `get_project` - Get details of a specific project
127
- 33. `list_projects` - List projects accessible by the current user
128
- 34. `list_labels` - List labels for a project
129
- 35. `get_label` - Get a single label from a project
130
- 36. `create_label` - Create a new label in a project
131
- 37. `update_label` - Update an existing label in a project
132
- 38. `delete_label` - Delete a label from a project
133
- 39. `list_group_projects` - List projects in a GitLab group with filtering options
134
- 40. `list_wiki_pages` - List wiki pages in a GitLab project
135
- 41. `get_wiki_page` - Get details of a specific wiki page
136
- 42. `create_wiki_page` - Create a new wiki page in a GitLab project
137
- 43. `update_wiki_page` - Update an existing wiki page in a GitLab project
138
- 44. `delete_wiki_page` - Delete a wiki page from a GitLab project
139
- 45. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories)
140
- 46. `list_pipelines` - List pipelines in a GitLab project with filtering options
141
- 47. `get_pipeline` - Get details of a specific pipeline in a GitLab project
142
- 48. `list_pipeline_jobs` - List all jobs in a specific pipeline
143
- 49. `get_pipeline_job` - Get details of a GitLab pipeline job number
144
- 50. `get_pipeline_job_output` - Get the output/trace of a GitLab pipeline job number
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
129
+ 12. `get_branch_diffs` - Get the changes/diffs between two branches or commits in a GitLab project
130
+ 13. `update_merge_request` - Update a merge request (Either mergeRequestIid or branchName must be provided)
131
+ 14. `create_note` - Create a new note (comment) to an issue or merge request
132
+ 15. `create_merge_request_thread` - Create a new thread on a merge request
133
+ 16. `mr_discussions` - List discussion items for a merge request
134
+ 17. `update_merge_request_note` - Modify an existing merge request thread note
135
+ 18. `create_merge_request_note` - Add a new note to an existing merge request thread
136
+ 19. `update_issue_note` - Modify an existing issue thread note
137
+ 20. `create_issue_note` - Add a new note to an existing issue thread
138
+ 21. `list_issues` - List issues in a GitLab project with filtering options
139
+ 22. `get_issue` - Get details of a specific issue in a GitLab project
140
+ 23. `update_issue` - Update an issue in a GitLab project
141
+ 24. `delete_issue` - Delete an issue from a GitLab project
142
+ 25. `list_issue_links` - List all issue links for a specific issue
143
+ 26. `list_issue_discussions` - List discussions for an issue in a GitLab project
144
+ 27. `get_issue_link` - Get a specific issue link
145
+ 28. `create_issue_link` - Create an issue link between two issues
146
+ 29. `delete_issue_link` - Delete an issue link
147
+ 30. `list_namespaces` - List all namespaces available to the current user
148
+ 31. `get_namespace` - Get details of a namespace by ID or path
149
+ 32. `verify_namespace` - Verify if a namespace path exists
150
+ 33. `get_project` - Get details of a specific project
151
+ 34. `list_projects` - List projects accessible by the current user
152
+ 35. `list_labels` - List labels for a project
153
+ 36. `get_label` - Get a single label from a project
154
+ 37. `create_label` - Create a new label in a project
155
+ 38. `update_label` - Update an existing label in a project
156
+ 39. `delete_label` - Delete a label from a project
157
+ 40. `list_group_projects` - List projects in a GitLab group with filtering options
158
+ 41. `list_wiki_pages` - List wiki pages in a GitLab project
159
+ 42. `get_wiki_page` - Get details of a specific wiki page
160
+ 43. `create_wiki_page` - Create a new wiki page in a GitLab project
161
+ 44. `update_wiki_page` - Update an existing wiki page in a GitLab project
162
+ 45. `delete_wiki_page` - Delete a wiki page from a GitLab project
163
+ 46. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories)
164
+ 47. `list_pipelines` - List pipelines in a GitLab project with filtering options
165
+ 48. `get_pipeline` - Get details of a specific pipeline in a GitLab project
166
+ 49. `list_pipeline_jobs` - List all jobs in a specific pipeline
167
+ 50. `get_pipeline_job` - Get details of a GitLab pipeline job number
168
+ 51. `get_pipeline_job_output` - Get the output/trace of a GitLab pipeline job number
169
+ 52. `create_pipeline` - Create a new pipeline for a branch or tag
170
+ 53. `retry_pipeline` - Retry a failed or canceled pipeline
171
+ 54. `cancel_pipeline` - Cancel a running pipeline
172
+ 55. `list_merge_requests` - List merge requests in a GitLab project with filtering options
173
+ 56. `list_milestones` - List milestones in a GitLab project with filtering options
174
+ 57. `get_milestone` - Get details of a specific milestone
175
+ 58. `create_milestone` - Create a new milestone in a GitLab project
176
+ 59. `edit_milestone` - Edit an existing milestone in a GitLab project
177
+ 60. `delete_milestone` - Delete a milestone from a GitLab project
178
+ 61. `get_milestone_issue` - Get issues associated with a specific milestone
179
+ 62. `get_milestone_merge_requests` - Get merge requests associated with a specific milestone
180
+ 63. `promote_milestone` - Promote a milestone to the next stage
181
+ 64. `get_milestone_burndown_events` - Get burndown events for a specific milestone
182
+ 65. `get_users` - Get GitLab user details by usernames
158
183
  <!-- TOOLS-END -->
package/build/index.js CHANGED
@@ -1,6 +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 { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
4
5
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
6
  import fetch from "node-fetch";
6
7
  import { SocksProxyAgent } from "socks-proxy-agent";
@@ -12,18 +13,19 @@ import { fileURLToPath } from "url";
12
13
  import { dirname } from "path";
13
14
  import fs from "fs";
14
15
  import path from "path";
16
+ import express from "express";
15
17
  // Add type imports for proxy agents
16
18
  import { Agent } from "http";
17
19
  import { Agent as HttpsAgent } from 'https';
18
20
  import { URL } from "url";
19
- 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,
21
+ import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, GitLabNamespaceSchema, GitLabNamespaceExistsResponseSchema, GitLabProjectSchema, GitLabUserSchema, GitLabUsersResponseSchema, GetUsersSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabDiffSchema, 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,
20
22
  // pipeline job schemas
21
23
  GetPipelineJobOutputSchema, GitLabPipelineJobSchema,
22
24
  // Discussion Schemas
23
25
  GitLabDiscussionNoteSchema, // Added
24
26
  GitLabDiscussionSchema, UpdateMergeRequestNoteSchema, // Added
25
27
  CreateMergeRequestNoteSchema, // Added
26
- ListMergeRequestDiscussionsSchema, UpdateIssueNoteSchema, CreateIssueNoteSchema, ListMergeRequestsSchema, GitLabMilestonesSchema, ListProjectMilestonesSchema, GetProjectMilestoneSchema, CreateProjectMilestoneSchema, EditProjectMilestoneSchema, DeleteProjectMilestoneSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, PromoteProjectMilestoneSchema, GetMilestoneBurndownEventsSchema, } from "./schemas.js";
28
+ ListMergeRequestDiscussionsSchema, UpdateIssueNoteSchema, CreateIssueNoteSchema, ListMergeRequestsSchema, GitLabMilestonesSchema, ListProjectMilestonesSchema, GetProjectMilestoneSchema, CreateProjectMilestoneSchema, EditProjectMilestoneSchema, DeleteProjectMilestoneSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, PromoteProjectMilestoneSchema, GetMilestoneBurndownEventsSchema, GitLabCompareResultSchema, GetBranchDiffsSchema, } from "./schemas.js";
27
29
  /**
28
30
  * Read version from package.json
29
31
  */
@@ -53,6 +55,7 @@ const GITLAB_READ_ONLY_MODE = process.env.GITLAB_READ_ONLY_MODE === "true";
53
55
  const USE_GITLAB_WIKI = process.env.USE_GITLAB_WIKI === "true";
54
56
  const USE_MILESTONE = process.env.USE_MILESTONE === "true";
55
57
  const USE_PIPELINE = process.env.USE_PIPELINE === "true";
58
+ const SSE = process.env.SSE === "true";
56
59
  // Add proxy configuration
57
60
  const HTTP_PROXY = process.env.HTTP_PROXY;
58
61
  const HTTPS_PROXY = process.env.HTTPS_PROXY;
@@ -160,6 +163,11 @@ const allTools = [
160
163
  description: "Get the changes/diffs of a merge request (Either mergeRequestIid or branchName must be provided)",
161
164
  inputSchema: zodToJsonSchema(GetMergeRequestDiffsSchema),
162
165
  },
166
+ {
167
+ name: "get_branch_diffs",
168
+ description: "Get the changes/diffs between two branches or commits in a GitLab project",
169
+ inputSchema: zodToJsonSchema(GetBranchDiffsSchema),
170
+ },
163
171
  {
164
172
  name: "update_merge_request",
165
173
  description: "Update a merge request (Either mergeRequestIid or branchName must be provided)",
@@ -420,6 +428,11 @@ const allTools = [
420
428
  description: "Get burndown events for a specific milestone",
421
429
  inputSchema: zodToJsonSchema(GetMilestoneBurndownEventsSchema),
422
430
  },
431
+ {
432
+ name: "get_users",
433
+ description: "Get GitLab user details by usernames",
434
+ inputSchema: zodToJsonSchema(GetUsersSchema),
435
+ },
423
436
  ];
424
437
  // Define which tools are read-only
425
438
  const readOnlyTools = [
@@ -427,6 +440,7 @@ const readOnlyTools = [
427
440
  "get_file_contents",
428
441
  "get_merge_request",
429
442
  "get_merge_request_diffs",
443
+ "get_branch_diffs",
430
444
  "mr_discussions",
431
445
  "list_issues",
432
446
  "list_merge_requests",
@@ -455,6 +469,7 @@ const readOnlyTools = [
455
469
  "get_milestone_burndown_events",
456
470
  "list_wiki_pages",
457
471
  "get_wiki_page",
472
+ "get_users",
458
473
  ];
459
474
  // Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
460
475
  const wikiToolNames = [
@@ -680,12 +695,14 @@ async function listIssues(projectId, options = {}) {
680
695
  // Add all query parameters
681
696
  Object.entries(options).forEach(([key, value]) => {
682
697
  if (value !== undefined) {
683
- if (key === "label_name" && Array.isArray(value)) {
698
+ if (key === "labels" && Array.isArray(value)) {
684
699
  // Handle array of labels
685
- url.searchParams.append(key, value.join(","));
700
+ value.forEach(label => {
701
+ url.searchParams.append("labels[]", label.toString());
702
+ });
686
703
  }
687
704
  else {
688
- url.searchParams.append(key, value.toString());
705
+ url.searchParams.append("labels[]", value.toString());
689
706
  }
690
707
  }
691
708
  });
@@ -888,6 +905,9 @@ async function createMergeRequest(projectId, options) {
888
905
  description: options.description,
889
906
  source_branch: options.source_branch,
890
907
  target_branch: options.target_branch,
908
+ assignee_ids: options.assignee_ids,
909
+ reviewer_ids: options.reviewer_ids,
910
+ labels: options.labels?.join(","),
891
911
  allow_collaboration: options.allow_collaboration,
892
912
  draft: options.draft,
893
913
  }),
@@ -1328,7 +1348,34 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view
1328
1348
  });
1329
1349
  await handleGitLabError(response);
1330
1350
  const data = (await response.json());
1331
- return z.array(GitLabMergeRequestDiffSchema).parse(data.changes);
1351
+ return z.array(GitLabDiffSchema).parse(data.changes);
1352
+ }
1353
+ /**
1354
+ * Get branch comparison diffs
1355
+ *
1356
+ * @param {string} projectId - The ID or URL-encoded path of the project
1357
+ * @param {string} from - The branch name or commit SHA to compare from
1358
+ * @param {string} to - The branch name or commit SHA to compare to
1359
+ * @param {boolean} [straight] - Comparison method: false for '...' (default), true for '--'
1360
+ * @returns {Promise<GitLabCompareResult>} Branch comparison results
1361
+ */
1362
+ async function getBranchDiffs(projectId, from, to, straight) {
1363
+ projectId = decodeURIComponent(projectId); // Decode project ID
1364
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/compare`);
1365
+ url.searchParams.append("from", from);
1366
+ url.searchParams.append("to", to);
1367
+ if (straight !== undefined) {
1368
+ url.searchParams.append("straight", straight.toString());
1369
+ }
1370
+ const response = await fetch(url.toString(), {
1371
+ ...DEFAULT_FETCH_CONFIG,
1372
+ });
1373
+ if (!response.ok) {
1374
+ const errorBody = await response.text();
1375
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}\n${errorBody}`);
1376
+ }
1377
+ const data = await response.json();
1378
+ return GitLabCompareResultSchema.parse(data);
1332
1379
  }
1333
1380
  /**
1334
1381
  * Update a merge request
@@ -2159,6 +2206,58 @@ async function getMilestoneBurndownEvents(projectId, milestoneId) {
2159
2206
  const data = await response.json();
2160
2207
  return data;
2161
2208
  }
2209
+ /**
2210
+ * Get a single user from GitLab
2211
+ *
2212
+ * @param {string} username - The username to look up
2213
+ * @returns {Promise<GitLabUser | null>} The user data or null if not found
2214
+ */
2215
+ async function getUser(username) {
2216
+ try {
2217
+ const url = new URL(`${GITLAB_API_URL}/users`);
2218
+ url.searchParams.append("username", username);
2219
+ const response = await fetch(url.toString(), {
2220
+ ...DEFAULT_FETCH_CONFIG,
2221
+ });
2222
+ await handleGitLabError(response);
2223
+ const users = await response.json();
2224
+ // GitLab returns an array of users that match the username
2225
+ if (Array.isArray(users) && users.length > 0) {
2226
+ // Find exact match for username (case-sensitive)
2227
+ const exactMatch = users.find(user => user.username === username);
2228
+ if (exactMatch) {
2229
+ return GitLabUserSchema.parse(exactMatch);
2230
+ }
2231
+ }
2232
+ // No matching user found
2233
+ return null;
2234
+ }
2235
+ catch (error) {
2236
+ console.error(`Error fetching user by username '${username}':`, error);
2237
+ return null;
2238
+ }
2239
+ }
2240
+ /**
2241
+ * Get multiple users from GitLab
2242
+ *
2243
+ * @param {string[]} usernames - Array of usernames to look up
2244
+ * @returns {Promise<GitLabUsersResponse>} Object with usernames as keys and user objects or null as values
2245
+ */
2246
+ async function getUsers(usernames) {
2247
+ const users = {};
2248
+ // Process usernames sequentially to avoid rate limiting
2249
+ for (const username of usernames) {
2250
+ try {
2251
+ const user = await getUser(username);
2252
+ users[username] = user;
2253
+ }
2254
+ catch (error) {
2255
+ console.error(`Error processing username '${username}':`, error);
2256
+ users[username] = null;
2257
+ }
2258
+ }
2259
+ return GitLabUsersResponseSchema.parse(users);
2260
+ }
2162
2261
  server.setRequestHandler(ListToolsRequestSchema, async () => {
2163
2262
  // Apply read-only filter first
2164
2263
  const tools0 = GITLAB_READ_ONLY_MODE
@@ -2240,6 +2339,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2240
2339
  content: [{ type: "text", text: JSON.stringify(branch, null, 2) }],
2241
2340
  };
2242
2341
  }
2342
+ case "get_branch_diffs": {
2343
+ const args = GetBranchDiffsSchema.parse(request.params.arguments);
2344
+ const diffResp = await getBranchDiffs(args.project_id, args.from, args.to, args.straight);
2345
+ if (args.excluded_file_patterns?.length) {
2346
+ const regexPatterns = args.excluded_file_patterns.map(pattern => new RegExp(pattern));
2347
+ // Helper function to check if a path matches any regex pattern
2348
+ const matchesAnyPattern = (path) => {
2349
+ if (!path)
2350
+ return false;
2351
+ return regexPatterns.some(regex => regex.test(path));
2352
+ };
2353
+ // Filter out files that match any of the regex patterns on new files
2354
+ diffResp.diffs = diffResp.diffs.filter(diff => !matchesAnyPattern(diff.new_path));
2355
+ }
2356
+ return {
2357
+ content: [{ type: "text", text: JSON.stringify(diffResp, null, 2) }],
2358
+ };
2359
+ }
2243
2360
  case "search_repositories": {
2244
2361
  const args = SearchRepositoriesSchema.parse(request.params.arguments);
2245
2362
  const results = await searchProjects(args.search, args.page, args.per_page);
@@ -2421,6 +2538,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2421
2538
  content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
2422
2539
  };
2423
2540
  }
2541
+ case "get_users": {
2542
+ const args = GetUsersSchema.parse(request.params.arguments);
2543
+ const usersMap = await getUsers(args.usernames);
2544
+ return {
2545
+ content: [{ type: "text", text: JSON.stringify(usersMap, null, 2) }],
2546
+ };
2547
+ }
2424
2548
  case "create_note": {
2425
2549
  const args = CreateNoteSchema.parse(request.params.arguments);
2426
2550
  const { project_id, noteable_type, noteable_iid, body } = args;
@@ -2847,8 +2971,36 @@ async function runServer() {
2847
2971
  console.error(`GitLab MCP Server v${SERVER_VERSION}`);
2848
2972
  console.error(`API URL: ${GITLAB_API_URL}`);
2849
2973
  console.error("========================");
2850
- const transport = new StdioServerTransport();
2851
- await server.connect(transport);
2974
+ if (!SSE) {
2975
+ const transport = new StdioServerTransport();
2976
+ await server.connect(transport);
2977
+ }
2978
+ else {
2979
+ const app = express();
2980
+ const transports = {};
2981
+ app.get("/sse", async (_, res) => {
2982
+ const transport = new SSEServerTransport("/messages", res);
2983
+ transports[transport.sessionId] = transport;
2984
+ res.on("close", () => {
2985
+ delete transports[transport.sessionId];
2986
+ });
2987
+ await server.connect(transport);
2988
+ });
2989
+ app.post("/messages", async (req, res) => {
2990
+ const sessionId = req.query.sessionId;
2991
+ const transport = transports[sessionId];
2992
+ if (transport) {
2993
+ await transport.handlePostMessage(req, res);
2994
+ }
2995
+ else {
2996
+ res.status(400).send("No transport found for sessionId");
2997
+ }
2998
+ });
2999
+ const PORT = process.env.PORT || 3002;
3000
+ app.listen(PORT, () => {
3001
+ console.log(`Server is running on port ${PORT}`);
3002
+ });
3003
+ }
2852
3004
  console.error("GitLab MCP Server running on stdio");
2853
3005
  }
2854
3006
  catch (error) {
package/build/schemas.js CHANGED
@@ -177,10 +177,28 @@ export const GetPipelineJobOutputSchema = z.object({
177
177
  project_id: z.string().describe("Project ID or URL-encoded path"),
178
178
  job_id: z.number().describe("The ID of the job"),
179
179
  });
180
+ // User schemas
181
+ export const GitLabUserSchema = z.object({
182
+ username: z.string(), // Changed from login to match GitLab API
183
+ id: z.number(),
184
+ name: z.string(),
185
+ avatar_url: z.string().nullable(),
186
+ web_url: z.string(), // Changed from html_url to match GitLab API
187
+ });
188
+ export const GetUsersSchema = z.object({
189
+ usernames: z.array(z.string()).describe("Array of usernames to search for"),
190
+ });
191
+ export const GitLabUsersResponseSchema = z.record(z.string(), z.object({
192
+ id: z.number(),
193
+ username: z.string(),
194
+ name: z.string(),
195
+ avatar_url: z.string(),
196
+ web_url: z.string(),
197
+ }).nullable());
180
198
  // Namespace related schemas
181
199
  // Base schema for project-related operations
182
200
  const ProjectParamsSchema = z.object({
183
- project_id: z.string().describe("Project ID or URL-encoded path"), // Changed from owner/repo to match GitLab API
201
+ project_id: z.string().describe("Project ID or complete URL-encoded path to project"), // Changed from owner/repo to match GitLab API
184
202
  });
185
203
  export const GitLabNamespaceSchema = z.object({
186
204
  id: z.number(),
@@ -269,6 +287,7 @@ export const GitLabRepositorySchema = z.object({
269
287
  container_registry_access_level: z.string().optional(),
270
288
  issues_enabled: z.boolean().optional(),
271
289
  merge_requests_enabled: z.boolean().optional(),
290
+ merge_requests_template: z.string().optional(),
272
291
  wiki_enabled: z.boolean().optional(),
273
292
  jobs_enabled: z.boolean().optional(),
274
293
  snippets_enabled: z.boolean().optional(),
@@ -397,12 +416,25 @@ export const CreateMergeRequestOptionsSchema = z.object({
397
416
  description: z.string().optional(), // Changed from body to match GitLab API
398
417
  source_branch: z.string(), // Changed from head to match GitLab API
399
418
  target_branch: z.string(), // Changed from base to match GitLab API
419
+ assignee_ids: z
420
+ .array(z.number())
421
+ .optional(),
422
+ reviewer_ids: z
423
+ .array(z.number())
424
+ .optional(),
425
+ labels: z.array(z.string()).optional(),
400
426
  allow_collaboration: z.boolean().optional(), // Changed from maintainer_can_modify to match GitLab API
401
427
  draft: z.boolean().optional(),
402
428
  });
403
- export const CreateBranchOptionsSchema = z.object({
404
- name: z.string(), // Changed from ref to match GitLab API
405
- ref: z.string(), // The source branch/commit for the new branch
429
+ export const GitLabDiffSchema = z.object({
430
+ old_path: z.string(),
431
+ new_path: z.string(),
432
+ a_mode: z.string(),
433
+ b_mode: z.string(),
434
+ diff: z.string(),
435
+ new_file: z.boolean(),
436
+ renamed_file: z.boolean(),
437
+ deleted_file: z.boolean(),
406
438
  });
407
439
  // Response schemas for operations
408
440
  export const GitLabCreateUpdateFileResponseSchema = z.object({
@@ -417,6 +449,25 @@ export const GitLabSearchResponseSchema = z.object({
417
449
  current_page: z.number().optional(),
418
450
  items: z.array(GitLabRepositorySchema),
419
451
  });
452
+ // create branch schemas
453
+ export const CreateBranchOptionsSchema = z.object({
454
+ name: z.string(), // Changed from ref to match GitLab API
455
+ ref: z.string(), // The source branch/commit for the new branch
456
+ });
457
+ export const GitLabCompareResultSchema = z.object({
458
+ commit: z.object({
459
+ id: z.string().optional(),
460
+ short_id: z.string().optional(),
461
+ title: z.string().optional(),
462
+ author_name: z.string().optional(),
463
+ author_email: z.string().optional(),
464
+ created_at: z.string().optional(),
465
+ }).optional(),
466
+ commits: z.array(GitLabCommitSchema),
467
+ diffs: z.array(GitLabDiffSchema),
468
+ compare_timeout: z.boolean().optional(),
469
+ compare_same_ref: z.boolean().optional(),
470
+ });
420
471
  // Issue related schemas
421
472
  export const GitLabLabelSchema = z.object({
422
473
  id: z.number(),
@@ -432,13 +483,6 @@ export const GitLabLabelSchema = z.object({
432
483
  priority: z.number().nullable().optional(),
433
484
  is_project_label: z.boolean().optional(),
434
485
  });
435
- export const GitLabUserSchema = z.object({
436
- username: z.string(), // Changed from login to match GitLab API
437
- id: z.number(),
438
- name: z.string(),
439
- avatar_url: z.string().nullable(),
440
- web_url: z.string(), // Changed from html_url to match GitLab API
441
- });
442
486
  export const GitLabMilestoneSchema = z.object({
443
487
  id: z.number(),
444
488
  iid: z.number(), // Added to match GitLab API
@@ -522,6 +566,7 @@ export const GitLabMergeRequestSchema = z.object({
522
566
  draft: z.boolean().optional(),
523
567
  author: GitLabUserSchema,
524
568
  assignees: z.array(GitLabUserSchema).optional(),
569
+ reviewers: z.array(GitLabUserSchema).optional(),
525
570
  source_branch: z.string(),
526
571
  target_branch: z.string(),
527
572
  diff_refs: GitLabMergeRequestDiffRefSchema.nullable().optional(),
@@ -692,25 +737,34 @@ export const CreateMergeRequestSchema = ProjectParamsSchema.extend({
692
737
  description: z.string().optional().describe("Merge request description"),
693
738
  source_branch: z.string().describe("Branch containing changes"),
694
739
  target_branch: z.string().describe("Branch to merge into"),
740
+ assignee_ids: z
741
+ .array(z.number())
742
+ .optional()
743
+ .describe("The ID of the users to assign the MR to"),
744
+ reviewer_ids: z
745
+ .array(z.number())
746
+ .optional()
747
+ .describe("The ID of the users to assign as reviewers of the MR"),
748
+ labels: z.array(z.string()).optional().describe("Labels for the MR"),
695
749
  draft: z.boolean().optional().describe("Create as draft merge request"),
696
- allow_collaboration: z.boolean().optional().describe("Allow commits from upstream members"),
750
+ allow_collaboration: z
751
+ .boolean()
752
+ .optional()
753
+ .describe("Allow commits from upstream members"),
697
754
  });
698
755
  export const ForkRepositorySchema = ProjectParamsSchema.extend({
699
756
  namespace: z.string().optional().describe("Namespace to fork to (full path)"),
700
757
  });
758
+ // Branch related schemas
701
759
  export const CreateBranchSchema = ProjectParamsSchema.extend({
702
760
  branch: z.string().describe("Name for the new branch"),
703
761
  ref: z.string().optional().describe("Source branch/commit for new branch"),
704
762
  });
705
- export const GitLabMergeRequestDiffSchema = z.object({
706
- old_path: z.string(),
707
- new_path: z.string(),
708
- a_mode: z.string(),
709
- b_mode: z.string(),
710
- diff: z.string(),
711
- new_file: z.boolean(),
712
- renamed_file: z.boolean(),
713
- deleted_file: z.boolean(),
763
+ export const GetBranchDiffsSchema = ProjectParamsSchema.extend({
764
+ from: z.string().describe("The base branch or commit SHA to compare from"),
765
+ to: z.string().describe("The target branch or commit SHA to compare to"),
766
+ straight: z.boolean().optional().describe("Comparison method: false for '...' (default), true for '--'"),
767
+ excluded_file_patterns: z.array(z.string()).optional().describe("Array of regex patterns to exclude files from the diff results. Each pattern is a JavaScript-compatible regular expression that matches file paths to ignore. Examples: [\"^test/mocks/\", \"\\.spec\\.ts$\", \"package-lock\\.json\"]"),
714
768
  });
715
769
  export const GetMergeRequestSchema = ProjectParamsSchema.extend({
716
770
  merge_request_iid: z.number().optional().describe("The IID of a merge request"),
@@ -755,10 +809,10 @@ export const ListIssuesSchema = z.object({
755
809
  created_after: z.string().optional().describe("Return issues created after the given time"),
756
810
  created_before: z.string().optional().describe("Return issues created before the given time"),
757
811
  due_date: z.string().optional().describe("Return issues that have the due date"),
758
- label_name: z.array(z.string()).optional().describe("Array of label names"),
812
+ labels: z.array(z.string()).optional().describe("Array of label names"),
759
813
  milestone: z.string().optional().describe("Milestone title"),
760
814
  scope: z
761
- .enum(["created-by-me", "assigned-to-me", "all"])
815
+ .enum(["created_by_me", "assigned_to_me", "all"])
762
816
  .optional()
763
817
  .describe("Return issues from a specific scope"),
764
818
  search: z.string().optional().describe("Search for specific terms"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "1.0.54",
3
+ "version": "1.0.56",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",
@@ -30,8 +30,9 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@modelcontextprotocol/sdk": "1.8.0",
33
- "form-data": "^4.0.0",
34
33
  "@types/node-fetch": "^2.6.12",
34
+ "express": "^5.1.0",
35
+ "form-data": "^4.0.0",
35
36
  "http-proxy-agent": "^7.0.2",
36
37
  "https-proxy-agent": "^7.0.6",
37
38
  "node-fetch": "^3.3.2",
@@ -39,13 +40,14 @@
39
40
  "zod-to-json-schema": "^3.23.5"
40
41
  },
41
42
  "devDependencies": {
43
+ "@types/express": "^5.0.2",
42
44
  "@types/node": "^22.13.10",
43
- "typescript": "^5.8.2",
44
- "zod": "^3.24.2",
45
45
  "@typescript-eslint/eslint-plugin": "^8.21.0",
46
46
  "@typescript-eslint/parser": "^8.21.0",
47
47
  "eslint": "^9.18.0",
48
48
  "prettier": "^3.4.2",
49
- "ts-node": "^10.9.2"
49
+ "ts-node": "^10.9.2",
50
+ "typescript": "^5.8.2",
51
+ "zod": "^3.24.2"
50
52
  }
51
53
  }