@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 +80 -55
- package/build/index.js +160 -8
- package/build/schemas.js +77 -23
- package/package.json +7 -5
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. `
|
|
107
|
-
13. `
|
|
108
|
-
14. `
|
|
109
|
-
15. `
|
|
110
|
-
16. `
|
|
111
|
-
17. `
|
|
112
|
-
18. `
|
|
113
|
-
19. `
|
|
114
|
-
20. `
|
|
115
|
-
21. `
|
|
116
|
-
22. `
|
|
117
|
-
23. `
|
|
118
|
-
24. `
|
|
119
|
-
25. `
|
|
120
|
-
26. `
|
|
121
|
-
27. `
|
|
122
|
-
28. `
|
|
123
|
-
29. `
|
|
124
|
-
30. `
|
|
125
|
-
31. `
|
|
126
|
-
32. `
|
|
127
|
-
33. `
|
|
128
|
-
34. `
|
|
129
|
-
35. `
|
|
130
|
-
36. `
|
|
131
|
-
37. `
|
|
132
|
-
38. `
|
|
133
|
-
39. `
|
|
134
|
-
40. `
|
|
135
|
-
41. `
|
|
136
|
-
42. `
|
|
137
|
-
43. `
|
|
138
|
-
44. `
|
|
139
|
-
45. `
|
|
140
|
-
46. `
|
|
141
|
-
47. `
|
|
142
|
-
48. `
|
|
143
|
-
49. `
|
|
144
|
-
50. `
|
|
145
|
-
51. `
|
|
146
|
-
52. `
|
|
147
|
-
53. `
|
|
148
|
-
54. `
|
|
149
|
-
55. `
|
|
150
|
-
56. `
|
|
151
|
-
57. `
|
|
152
|
-
58. `
|
|
153
|
-
59. `
|
|
154
|
-
60. `
|
|
155
|
-
61. `
|
|
156
|
-
62. `
|
|
157
|
-
63. `
|
|
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,
|
|
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 === "
|
|
698
|
+
if (key === "labels" && Array.isArray(value)) {
|
|
684
699
|
// Handle array of labels
|
|
685
|
-
|
|
700
|
+
value.forEach(label => {
|
|
701
|
+
url.searchParams.append("labels[]", label.toString());
|
|
702
|
+
});
|
|
686
703
|
}
|
|
687
704
|
else {
|
|
688
|
-
url.searchParams.append(
|
|
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(
|
|
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
|
-
|
|
2851
|
-
|
|
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
|
|
404
|
-
|
|
405
|
-
|
|
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
|
|
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
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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
|
-
|
|
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(["
|
|
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.
|
|
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
|
}
|