@zereight/mcp-gitlab 1.0.32 â 1.0.33
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 +44 -237
- package/build/index.js +85 -35
- package/build/schemas.js +266 -80
- package/build/scripts/generate-tools-readme.js +41 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -23,7 +23,8 @@ When using with the Claude App, you need to set up your API key and URLs directl
|
|
|
23
23
|
"env": {
|
|
24
24
|
"GITLAB_PERSONAL_ACCESS_TOKEN": "your_gitlab_token",
|
|
25
25
|
"GITLAB_API_URL": "your_gitlab_api_url",
|
|
26
|
-
"GITLAB_READ_ONLY_MODE": "
|
|
26
|
+
"GITLAB_READ_ONLY_MODE": "false",
|
|
27
|
+
"USE_GITLAB_WIKI":"true"
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
}
|
|
@@ -36,242 +37,48 @@ When using with the Claude App, you need to set up your API key and URLs directl
|
|
|
36
37
|
- `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token.
|
|
37
38
|
- `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`)
|
|
38
39
|
- `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.
|
|
40
|
+
- `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.
|
|
39
41
|
|
|
40
42
|
## Tools đ ī¸
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
5. `get_file_contents`
|
|
84
|
-
|
|
85
|
-
- Get the contents of a file or directory. đ
|
|
86
|
-
- Inputs:
|
|
87
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
88
|
-
- `file_path` (string): Path to the file/directory
|
|
89
|
-
- `ref` (optional string): Branch, tag, or commit SHA (default: default branch)
|
|
90
|
-
- Returns: File/directory content
|
|
91
|
-
|
|
92
|
-
6. `create_issue`
|
|
93
|
-
|
|
94
|
-
- Create a new issue. đ
|
|
95
|
-
- Inputs:
|
|
96
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
97
|
-
- `title` (string): Issue title
|
|
98
|
-
- `description` (string): Issue description
|
|
99
|
-
- `assignee_ids` (optional number[]): Array of assignee IDs
|
|
100
|
-
- `milestone_id` (optional number): Milestone ID
|
|
101
|
-
- `labels` (optional string[]): Array of labels
|
|
102
|
-
- Returns: Details of the created issue
|
|
103
|
-
|
|
104
|
-
7. `create_merge_request`
|
|
105
|
-
|
|
106
|
-
- Create a new merge request. đ
|
|
107
|
-
- Inputs:
|
|
108
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
109
|
-
- `title` (string): Merge request title
|
|
110
|
-
- `description` (string): Merge request description
|
|
111
|
-
- `source_branch` (string): Branch with changes
|
|
112
|
-
- `target_branch` (string): Branch to merge into
|
|
113
|
-
- `allow_collaboration` (optional boolean): Allow collaborators to push commits to the source branch
|
|
114
|
-
- `draft` (optional boolean): Create as a draft merge request
|
|
115
|
-
- Returns: Details of the created merge request
|
|
116
|
-
|
|
117
|
-
8. `fork_repository`
|
|
118
|
-
|
|
119
|
-
- Fork a project. đ´
|
|
120
|
-
- Inputs:
|
|
121
|
-
- `project_id` (string): Project ID or namespace/project_path to fork
|
|
122
|
-
- `namespace` (optional string): Namespace to fork into (default: user namespace)
|
|
123
|
-
- Returns: Details of the forked project
|
|
124
|
-
|
|
125
|
-
9. `create_branch`
|
|
126
|
-
|
|
127
|
-
- Create a new branch. đŋ
|
|
128
|
-
- Inputs:
|
|
129
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
130
|
-
- `name` (string): New branch name
|
|
131
|
-
- `ref` (optional string): Ref to create the branch from (branch, tag, commit SHA, default: default branch)
|
|
132
|
-
- Returns: Created branch reference
|
|
133
|
-
|
|
134
|
-
10. `get_merge_request`
|
|
135
|
-
|
|
136
|
-
- Get details of a merge request. âšī¸
|
|
137
|
-
- Inputs:
|
|
138
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
139
|
-
- `merge_request_iid` (number): Merge request IID
|
|
140
|
-
- Returns: Merge request details
|
|
141
|
-
|
|
142
|
-
11. `get_merge_request_diffs`
|
|
143
|
-
|
|
144
|
-
- Get changes (diffs) of a merge request. diff
|
|
145
|
-
- Inputs:
|
|
146
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
147
|
-
- `merge_request_iid` (number): Merge request IID
|
|
148
|
-
- `view` (optional string): Diff view type ('inline' or 'parallel')
|
|
149
|
-
- Returns: Array of merge request diff information
|
|
150
|
-
|
|
151
|
-
12. `update_merge_request`
|
|
152
|
-
|
|
153
|
-
- Update a merge request. đ
|
|
154
|
-
- Inputs:
|
|
155
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
156
|
-
- `merge_request_iid` (number): Merge request IID
|
|
157
|
-
- `title` (optional string): New title
|
|
158
|
-
- `description` (string): New description
|
|
159
|
-
- `target_branch` (optional string): New target branch
|
|
160
|
-
- `state_event` (optional string): Merge request state change event ('close', 'reopen')
|
|
161
|
-
- `remove_source_branch` (optional boolean): Remove source branch after merge
|
|
162
|
-
- `allow_collaboration` (optional boolean): Allow collaborators to push commits to the source branch
|
|
163
|
-
- Returns: Updated merge request details
|
|
164
|
-
|
|
165
|
-
13. `create_note`
|
|
166
|
-
- Create a new note (comment) to an issue or merge request. đŦ
|
|
167
|
-
- Inputs:
|
|
168
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
169
|
-
- `noteable_type` (string): Type of noteable ("issue" or "merge_request")
|
|
170
|
-
- `noteable_iid` (number): IID of the issue or merge request
|
|
171
|
-
- `body` (string): Note content
|
|
172
|
-
- Returns: Details of the created note
|
|
173
|
-
|
|
174
|
-
14. `list_projects`
|
|
175
|
-
- List accessible projects with rich filtering options đ
|
|
176
|
-
- Inputs:
|
|
177
|
-
- Search/filtering:
|
|
178
|
-
- `search`
|
|
179
|
-
- `owned`
|
|
180
|
-
- `membership`
|
|
181
|
-
- `archived`
|
|
182
|
-
- `visibility`
|
|
183
|
-
- Features filtering:
|
|
184
|
-
- `with_issues_enabled`
|
|
185
|
-
- `with_merge_requests_enabled`
|
|
186
|
-
- Sorting:
|
|
187
|
-
- `order_by`
|
|
188
|
-
- `sort`
|
|
189
|
-
- Access control:
|
|
190
|
-
- `min_access_level`
|
|
191
|
-
- Pagination:
|
|
192
|
-
- `page`
|
|
193
|
-
- `per_page`
|
|
194
|
-
- `simple`
|
|
195
|
-
- Returns: Array of projects
|
|
196
|
-
15. `list_labels`
|
|
197
|
-
- List all labels for a project with filtering options đˇī¸
|
|
198
|
-
- Inputs:
|
|
199
|
-
- `project_id` (string): Project ID or path
|
|
200
|
-
- `with_counts` (optional): Include issue and merge request counts
|
|
201
|
-
- `include_ancestor_groups` (optional): Include ancestor groups
|
|
202
|
-
- `search` (optional): Filter labels by keyword
|
|
203
|
-
- Returns: Array of labels
|
|
204
|
-
16. `get_label`
|
|
205
|
-
- Get a single label from a project
|
|
206
|
-
- Inputs:
|
|
207
|
-
- `project_id` (string): Project ID or path
|
|
208
|
-
- `label_id` (number/string): Label ID or name
|
|
209
|
-
- `include_ancestor_groups` (optional): Include ancestor groups
|
|
210
|
-
- Returns: label details
|
|
211
|
-
17. `create_label`
|
|
212
|
-
- Create a new label in an object đˇī¸â
|
|
213
|
-
- Inputs:
|
|
214
|
-
- `project_id` (string): Project ID or path
|
|
215
|
-
- `name` (string): Label name
|
|
216
|
-
- `color` (string): Color in hex format (e.g., "#FF0000")
|
|
217
|
-
- `description` (optional): Label description
|
|
218
|
-
- `priority` (optional): Label priority
|
|
219
|
-
- Returns: Created label details
|
|
220
|
-
18. `update_label`
|
|
221
|
-
- Update an existing label in a project đˇī¸âī¸
|
|
222
|
-
- Inputs:
|
|
223
|
-
- `project_id` (string): Project ID or path
|
|
224
|
-
- `label_id` (number/string): Label ID or name
|
|
225
|
-
- `new_name` (optional): New label name
|
|
226
|
-
- `color` (optional): New color in hex format
|
|
227
|
-
- `description` (optional): New description
|
|
228
|
-
- `priority` (optional): New priority
|
|
229
|
-
- Returns: Updated label details
|
|
230
|
-
19. `delete_label`
|
|
231
|
-
- Delete a label from a project đˇī¸â
|
|
232
|
-
- Inputs:
|
|
233
|
-
- `project_id` (string): Project ID or path
|
|
234
|
-
- `label_id` (number/string): Label ID or name
|
|
235
|
-
- Returns: Success message
|
|
236
|
-
|
|
237
|
-
14. `list_group_projects`
|
|
238
|
-
|
|
239
|
-
- List all projects in a GitLab group. đ
|
|
240
|
-
- Inputs:
|
|
241
|
-
- `group_id` (string): Project ID or namespace/project_path
|
|
242
|
-
- Filtering options:
|
|
243
|
-
- `include_subgroups` (optional boolean): Include projects from subgroups
|
|
244
|
-
- `search` (optional string): Search term to filter projects
|
|
245
|
-
- `archived` (optional boolean): Filter for archived projects
|
|
246
|
-
- `visibility` (optional string): Filter by project visibility (public/internal/private)
|
|
247
|
-
- `with_programming_language` (optional string): Filter by programming language
|
|
248
|
-
- `starred` (optional boolean): Filter by starred projects
|
|
249
|
-
- Feature filtering:
|
|
250
|
-
- `with_issues_enabled` (optional boolean): Filter projects with issues feature enabled
|
|
251
|
-
- `with_merge_requests_enabled` (optional boolean): Filter projects with merge requests feature enabled
|
|
252
|
-
- `min_access_level` (optional number): Filter by minimum access level
|
|
253
|
-
- Pagination:
|
|
254
|
-
- `page` (optional number): Page number
|
|
255
|
-
- `per_page` (optional number): Results per page
|
|
256
|
-
- Sorting:
|
|
257
|
-
- `order_by` (optional string): Field to sort by
|
|
258
|
-
- `sort` (optional string): Sort direction (asc/desc)
|
|
259
|
-
- Additional data:
|
|
260
|
-
- `statistics` (optional boolean): Include project statistics
|
|
261
|
-
- `with_custom_attributes` (optional boolean): Include custom attributes
|
|
262
|
-
- `with_security_reports` (optional boolean): Include security reports
|
|
263
|
-
- Returns: List of projects
|
|
264
|
-
|
|
265
|
-
## Environment Variable Configuration
|
|
266
|
-
|
|
267
|
-
Before running the server, you need to set the following environment variables:
|
|
268
|
-
|
|
269
|
-
```
|
|
270
|
-
GITLAB_PERSONAL_ACCESS_TOKEN=your_gitlab_token
|
|
271
|
-
GITLAB_API_URL=your_gitlab_api_url # Default: https://gitlab.com/api/v4
|
|
272
|
-
GITLAB_READ_ONLY_MODE=true # Optional: Enable read-only mode
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
## License
|
|
276
|
-
|
|
277
|
-
MIT License
|
|
44
|
+
+<!-- TOOLS-START -->
|
|
45
|
+
1. `create_or_update_file` - Create or update a single file in a GitLab project
|
|
46
|
+
2. `search_repositories` - Search for GitLab projects
|
|
47
|
+
3. `create_repository` - Create a new GitLab project
|
|
48
|
+
4. `get_file_contents` - Get the contents of a file or directory from a GitLab project
|
|
49
|
+
5. `push_files` - Push multiple files to a GitLab project in a single commit
|
|
50
|
+
6. `create_issue` - Create a new issue in a GitLab project
|
|
51
|
+
7. `create_merge_request` - Create a new merge request in a GitLab project
|
|
52
|
+
8. `fork_repository` - Fork a GitLab project to your account or specified namespace
|
|
53
|
+
9. `create_branch` - Create a new branch in a GitLab project
|
|
54
|
+
10. `get_merge_request` - Get details of a merge request
|
|
55
|
+
11. `get_merge_request_diffs` - Get the changes/diffs of a merge request
|
|
56
|
+
12. `update_merge_request` - Update a merge request
|
|
57
|
+
13. `create_note` - Create a new note (comment) to an issue or merge request
|
|
58
|
+
14. `mr_discussions` - List discussion items for a merge request
|
|
59
|
+
15. `update_merge_request_note` - Modify an existing merge request thread note
|
|
60
|
+
16. `list_issues` - List issues in a GitLab project with filtering options
|
|
61
|
+
17. `get_issue` - Get details of a specific issue in a GitLab project
|
|
62
|
+
18. `update_issue` - Update an issue in a GitLab project
|
|
63
|
+
19. `delete_issue` - Delete an issue from a GitLab project
|
|
64
|
+
20. `list_issue_links` - List all issue links for a specific issue
|
|
65
|
+
21. `get_issue_link` - Get a specific issue link
|
|
66
|
+
22. `create_issue_link` - Create an issue link between two issues
|
|
67
|
+
23. `delete_issue_link` - Delete an issue link
|
|
68
|
+
24. `list_namespaces` - List all namespaces available to the current user
|
|
69
|
+
25. `get_namespace` - Get details of a namespace by ID or path
|
|
70
|
+
26. `verify_namespace` - Verify if a namespace path exists
|
|
71
|
+
27. `get_project` - Get details of a specific project
|
|
72
|
+
28. `list_projects` - List projects accessible by the current user
|
|
73
|
+
29. `list_labels` - List labels for a project
|
|
74
|
+
30. `get_label` - Get a single label from a project
|
|
75
|
+
31. `create_label` - Create a new label in a project
|
|
76
|
+
32. `update_label` - Update an existing label in a project
|
|
77
|
+
33. `delete_label` - Delete a label from a project
|
|
78
|
+
34. `list_group_projects` - List projects in a GitLab group with filtering options
|
|
79
|
+
35. `list_wiki_pages` - List wiki pages in a GitLab project
|
|
80
|
+
36. `get_wiki_page` - Get details of a specific wiki page
|
|
81
|
+
37. `create_wiki_page` - Create a new wiki page in a GitLab project
|
|
82
|
+
38. `update_wiki_page` - Update an existing wiki page in a GitLab project
|
|
83
|
+
39. `delete_wiki_page` - Delete a wiki page from a GitLab project
|
|
84
|
+
<!-- TOOLS-END -->
|
package/build/index.js
CHANGED
|
@@ -3,16 +3,16 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import fetch from "node-fetch";
|
|
6
|
-
import { SocksProxyAgent } from
|
|
7
|
-
import { HttpsProxyAgent } from
|
|
8
|
-
import { HttpProxyAgent } from
|
|
6
|
+
import { SocksProxyAgent } from "socks-proxy-agent";
|
|
7
|
+
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
8
|
+
import { HttpProxyAgent } from "http-proxy-agent";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
import { dirname } from "path";
|
|
13
13
|
import fs from "fs";
|
|
14
14
|
import path from "path";
|
|
15
|
-
import { URL } from
|
|
15
|
+
import { URL } from "url";
|
|
16
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, GetIssueLinkSchema, CreateIssueLinkSchema, DeleteIssueLinkSchema, ListNamespacesSchema, GetNamespaceSchema, VerifyNamespaceSchema, GetProjectSchema, ListProjectsSchema, ListLabelsSchema, GetLabelSchema, CreateLabelSchema, UpdateLabelSchema, DeleteLabelSchema, CreateNoteSchema, ListGroupProjectsSchema, ListWikiPagesSchema, GetWikiPageSchema, CreateWikiPageSchema, UpdateWikiPageSchema, DeleteWikiPageSchema, GitLabWikiPageSchema,
|
|
17
17
|
// Discussion Schemas
|
|
18
18
|
GitLabDiscussionNoteSchema, // Added
|
|
@@ -52,7 +52,7 @@ const HTTPS_PROXY = process.env.HTTPS_PROXY;
|
|
|
52
52
|
let httpAgent = undefined;
|
|
53
53
|
let httpsAgent = undefined;
|
|
54
54
|
if (HTTP_PROXY) {
|
|
55
|
-
if (HTTP_PROXY.startsWith(
|
|
55
|
+
if (HTTP_PROXY.startsWith("socks")) {
|
|
56
56
|
httpAgent = new SocksProxyAgent(HTTP_PROXY);
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
@@ -60,7 +60,7 @@ if (HTTP_PROXY) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
if (HTTPS_PROXY) {
|
|
63
|
-
if (HTTPS_PROXY.startsWith(
|
|
63
|
+
if (HTTPS_PROXY.startsWith("socks")) {
|
|
64
64
|
httpsAgent = new SocksProxyAgent(HTTPS_PROXY);
|
|
65
65
|
}
|
|
66
66
|
else {
|
|
@@ -77,11 +77,11 @@ const DEFAULT_HEADERS = {
|
|
|
77
77
|
const DEFAULT_FETCH_CONFIG = {
|
|
78
78
|
headers: DEFAULT_HEADERS,
|
|
79
79
|
agent: (parsedUrl) => {
|
|
80
|
-
if (parsedUrl.protocol ===
|
|
80
|
+
if (parsedUrl.protocol === "https:") {
|
|
81
81
|
return httpsAgent;
|
|
82
82
|
}
|
|
83
83
|
return httpAgent;
|
|
84
|
-
}
|
|
84
|
+
},
|
|
85
85
|
};
|
|
86
86
|
// Define all available tools
|
|
87
87
|
const allTools = [
|
|
@@ -132,17 +132,17 @@ const allTools = [
|
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
name: "get_merge_request",
|
|
135
|
-
description: "Get details of a merge request",
|
|
135
|
+
description: "Get details of a merge request (Either mergeRequestIid or branchName must be provided)",
|
|
136
136
|
inputSchema: zodToJsonSchema(GetMergeRequestSchema),
|
|
137
137
|
},
|
|
138
138
|
{
|
|
139
139
|
name: "get_merge_request_diffs",
|
|
140
|
-
description: "Get the changes/diffs of a merge request",
|
|
140
|
+
description: "Get the changes/diffs of a merge request (Either mergeRequestIid or branchName must be provided)",
|
|
141
141
|
inputSchema: zodToJsonSchema(GetMergeRequestDiffsSchema),
|
|
142
142
|
},
|
|
143
143
|
{
|
|
144
144
|
name: "update_merge_request",
|
|
145
|
-
description: "Update a merge request",
|
|
145
|
+
description: "Update a merge request (Either mergeRequestIid or branchName must be provided)",
|
|
146
146
|
inputSchema: zodToJsonSchema(UpdateMergeRequestSchema),
|
|
147
147
|
},
|
|
148
148
|
{
|
|
@@ -279,7 +279,7 @@ const allTools = [
|
|
|
279
279
|
name: "delete_wiki_page",
|
|
280
280
|
description: "Delete a wiki page from a GitLab project",
|
|
281
281
|
inputSchema: zodToJsonSchema(DeleteWikiPageSchema),
|
|
282
|
-
}
|
|
282
|
+
},
|
|
283
283
|
];
|
|
284
284
|
// Define which tools are read-only
|
|
285
285
|
const readOnlyTools = [
|
|
@@ -347,7 +347,8 @@ async function handleGitLabError(response) {
|
|
|
347
347
|
if (!response.ok) {
|
|
348
348
|
const errorBody = await response.text();
|
|
349
349
|
// Check specifically for Rate Limit error
|
|
350
|
-
if (response.status === 403 &&
|
|
350
|
+
if (response.status === 403 &&
|
|
351
|
+
errorBody.includes("User API Key Rate limit exceeded")) {
|
|
351
352
|
console.error("GitLab API Rate Limit Exceeded:", errorBody);
|
|
352
353
|
console.log("User API Key Rate limit exceeded. Please try again later.");
|
|
353
354
|
throw new Error(`GitLab API Rate Limit Exceeded: ${errorBody}`);
|
|
@@ -937,27 +938,50 @@ async function createRepository(options) {
|
|
|
937
938
|
* MR ėĄ°í í¨ė (Function to retrieve merge request)
|
|
938
939
|
*
|
|
939
940
|
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
940
|
-
* @param {number} mergeRequestIid - The internal ID of the merge request
|
|
941
|
+
* @param {number} mergeRequestIid - The internal ID of the merge request (Optional)
|
|
942
|
+
* @param {string} [branchName] - The name of the branch to search for merge request by branch name (Optional)
|
|
941
943
|
* @returns {Promise<GitLabMergeRequest>} The merge request details
|
|
942
944
|
*/
|
|
943
|
-
async function getMergeRequest(projectId, mergeRequestIid) {
|
|
944
|
-
|
|
945
|
+
async function getMergeRequest(projectId, mergeRequestIid, branchName) {
|
|
946
|
+
let url;
|
|
947
|
+
if (mergeRequestIid) {
|
|
948
|
+
url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`);
|
|
949
|
+
}
|
|
950
|
+
else if (branchName) {
|
|
951
|
+
url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests?source_branch=${encodeURIComponent(branchName)}`);
|
|
952
|
+
}
|
|
953
|
+
else {
|
|
954
|
+
throw new Error("Either mergeRequestIid or branchName must be provided");
|
|
955
|
+
}
|
|
945
956
|
const response = await fetch(url.toString(), {
|
|
946
957
|
...DEFAULT_FETCH_CONFIG,
|
|
947
958
|
});
|
|
948
959
|
await handleGitLabError(response);
|
|
949
|
-
|
|
960
|
+
const data = await response.json();
|
|
961
|
+
// If response is an array (Comes from branchName search), return the first item if exist
|
|
962
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
963
|
+
return GitLabMergeRequestSchema.parse(data[0]);
|
|
964
|
+
}
|
|
965
|
+
return GitLabMergeRequestSchema.parse(data);
|
|
950
966
|
}
|
|
951
967
|
/**
|
|
952
968
|
* Get merge request changes/diffs
|
|
953
969
|
* MR ëŗę˛ŊėŦí ėĄ°í í¨ė (Function to retrieve merge request changes)
|
|
954
970
|
*
|
|
955
971
|
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
956
|
-
* @param {number} mergeRequestIid - The internal ID of the merge request
|
|
972
|
+
* @param {number} mergeRequestIid - The internal ID of the merge request (Either mergeRequestIid or branchName must be provided)
|
|
973
|
+
* @param {string} [branchName] - The name of the branch to search for merge request by branch name (Either mergeRequestIid or branchName must be provided)
|
|
957
974
|
* @param {string} [view] - The view type for the diff (inline or parallel)
|
|
958
975
|
* @returns {Promise<GitLabMergeRequestDiff[]>} The merge request diffs
|
|
959
976
|
*/
|
|
960
|
-
async function getMergeRequestDiffs(projectId, mergeRequestIid, view) {
|
|
977
|
+
async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view) {
|
|
978
|
+
if (!mergeRequestIid && !branchName) {
|
|
979
|
+
throw new Error("Either mergeRequestIid or branchName must be provided");
|
|
980
|
+
}
|
|
981
|
+
if (branchName && !mergeRequestIid) {
|
|
982
|
+
const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
|
|
983
|
+
mergeRequestIid = mergeRequest.iid;
|
|
984
|
+
}
|
|
961
985
|
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/changes`);
|
|
962
986
|
if (view) {
|
|
963
987
|
url.searchParams.append("view", view);
|
|
@@ -974,11 +998,19 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, view) {
|
|
|
974
998
|
* MR ė
ë°ė´í¸ í¨ė (Function to update merge request)
|
|
975
999
|
*
|
|
976
1000
|
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
977
|
-
* @param {number} mergeRequestIid - The internal ID of the merge request
|
|
1001
|
+
* @param {number} mergeRequestIid - The internal ID of the merge request (Optional)
|
|
1002
|
+
* @param {string} branchName - The name of the branch to search for merge request by branch name (Optional)
|
|
978
1003
|
* @param {Object} options - The update options
|
|
979
1004
|
* @returns {Promise<GitLabMergeRequest>} The updated merge request
|
|
980
1005
|
*/
|
|
981
|
-
async function updateMergeRequest(projectId, mergeRequestIid,
|
|
1006
|
+
async function updateMergeRequest(projectId, options, mergeRequestIid, branchName) {
|
|
1007
|
+
if (!mergeRequestIid && !branchName) {
|
|
1008
|
+
throw new Error("Either mergeRequestIid or branchName must be provided");
|
|
1009
|
+
}
|
|
1010
|
+
if (branchName && !mergeRequestIid) {
|
|
1011
|
+
const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
|
|
1012
|
+
mergeRequestIid = mergeRequest.iid;
|
|
1013
|
+
}
|
|
982
1014
|
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`);
|
|
983
1015
|
const response = await fetch(url.toString(), {
|
|
984
1016
|
...DEFAULT_FETCH_CONFIG,
|
|
@@ -1304,9 +1336,9 @@ async function listGroupProjects(options) {
|
|
|
1304
1336
|
async function listWikiPages(projectId, options = {}) {
|
|
1305
1337
|
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis`);
|
|
1306
1338
|
if (options.page)
|
|
1307
|
-
url.searchParams.append(
|
|
1339
|
+
url.searchParams.append("page", options.page.toString());
|
|
1308
1340
|
if (options.per_page)
|
|
1309
|
-
url.searchParams.append(
|
|
1341
|
+
url.searchParams.append("per_page", options.per_page.toString());
|
|
1310
1342
|
const response = await fetch(url.toString(), {
|
|
1311
1343
|
...DEFAULT_FETCH_CONFIG,
|
|
1312
1344
|
});
|
|
@@ -1332,7 +1364,7 @@ async function createWikiPage(projectId, title, content, format) {
|
|
|
1332
1364
|
body.format = format;
|
|
1333
1365
|
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis`, {
|
|
1334
1366
|
...DEFAULT_FETCH_CONFIG,
|
|
1335
|
-
method:
|
|
1367
|
+
method: "POST",
|
|
1336
1368
|
body: JSON.stringify(body),
|
|
1337
1369
|
});
|
|
1338
1370
|
await handleGitLabError(response);
|
|
@@ -1352,7 +1384,7 @@ async function updateWikiPage(projectId, slug, title, content, format) {
|
|
|
1352
1384
|
body.format = format;
|
|
1353
1385
|
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, {
|
|
1354
1386
|
...DEFAULT_FETCH_CONFIG,
|
|
1355
|
-
method:
|
|
1387
|
+
method: "PUT",
|
|
1356
1388
|
body: JSON.stringify(body),
|
|
1357
1389
|
});
|
|
1358
1390
|
await handleGitLabError(response);
|
|
@@ -1365,7 +1397,7 @@ async function updateWikiPage(projectId, slug, title, content, format) {
|
|
|
1365
1397
|
async function deleteWikiPage(projectId, slug) {
|
|
1366
1398
|
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, {
|
|
1367
1399
|
...DEFAULT_FETCH_CONFIG,
|
|
1368
|
-
method:
|
|
1400
|
+
method: "DELETE",
|
|
1369
1401
|
});
|
|
1370
1402
|
await handleGitLabError(response);
|
|
1371
1403
|
}
|
|
@@ -1493,7 +1525,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1493
1525
|
}
|
|
1494
1526
|
case "get_merge_request": {
|
|
1495
1527
|
const args = GetMergeRequestSchema.parse(request.params.arguments);
|
|
1496
|
-
const mergeRequest = await getMergeRequest(args.project_id, args.merge_request_iid);
|
|
1528
|
+
const mergeRequest = await getMergeRequest(args.project_id, args.merge_request_iid, args.source_branch);
|
|
1497
1529
|
return {
|
|
1498
1530
|
content: [
|
|
1499
1531
|
{ type: "text", text: JSON.stringify(mergeRequest, null, 2) },
|
|
@@ -1502,15 +1534,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1502
1534
|
}
|
|
1503
1535
|
case "get_merge_request_diffs": {
|
|
1504
1536
|
const args = GetMergeRequestDiffsSchema.parse(request.params.arguments);
|
|
1505
|
-
const diffs = await getMergeRequestDiffs(args.project_id, args.merge_request_iid, args.view);
|
|
1537
|
+
const diffs = await getMergeRequestDiffs(args.project_id, args.merge_request_iid, args.source_branch, args.view);
|
|
1506
1538
|
return {
|
|
1507
1539
|
content: [{ type: "text", text: JSON.stringify(diffs, null, 2) }],
|
|
1508
1540
|
};
|
|
1509
1541
|
}
|
|
1510
1542
|
case "update_merge_request": {
|
|
1511
1543
|
const args = UpdateMergeRequestSchema.parse(request.params.arguments);
|
|
1512
|
-
const { project_id, merge_request_iid, ...options } = args;
|
|
1513
|
-
const mergeRequest = await updateMergeRequest(project_id, merge_request_iid,
|
|
1544
|
+
const { project_id, merge_request_iid, source_branch, ...options } = args;
|
|
1545
|
+
const mergeRequest = await updateMergeRequest(project_id, options, merge_request_iid, source_branch);
|
|
1514
1546
|
return {
|
|
1515
1547
|
content: [
|
|
1516
1548
|
{ type: "text", text: JSON.stringify(mergeRequest, null, 2) },
|
|
@@ -1731,27 +1763,45 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1731
1763
|
case "list_wiki_pages": {
|
|
1732
1764
|
const { project_id, page, per_page } = ListWikiPagesSchema.parse(request.params.arguments);
|
|
1733
1765
|
const wikiPages = await listWikiPages(project_id, { page, per_page });
|
|
1734
|
-
return {
|
|
1766
|
+
return {
|
|
1767
|
+
content: [{ type: "text", text: JSON.stringify(wikiPages, null, 2) }],
|
|
1768
|
+
};
|
|
1735
1769
|
}
|
|
1736
1770
|
case "get_wiki_page": {
|
|
1737
1771
|
const { project_id, slug } = GetWikiPageSchema.parse(request.params.arguments);
|
|
1738
1772
|
const wikiPage = await getWikiPage(project_id, slug);
|
|
1739
|
-
return {
|
|
1773
|
+
return {
|
|
1774
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage, null, 2) }],
|
|
1775
|
+
};
|
|
1740
1776
|
}
|
|
1741
1777
|
case "create_wiki_page": {
|
|
1742
1778
|
const { project_id, title, content, format } = CreateWikiPageSchema.parse(request.params.arguments);
|
|
1743
1779
|
const wikiPage = await createWikiPage(project_id, title, content, format);
|
|
1744
|
-
return {
|
|
1780
|
+
return {
|
|
1781
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage, null, 2) }],
|
|
1782
|
+
};
|
|
1745
1783
|
}
|
|
1746
1784
|
case "update_wiki_page": {
|
|
1747
1785
|
const { project_id, slug, title, content, format } = UpdateWikiPageSchema.parse(request.params.arguments);
|
|
1748
1786
|
const wikiPage = await updateWikiPage(project_id, slug, title, content, format);
|
|
1749
|
-
return {
|
|
1787
|
+
return {
|
|
1788
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage, null, 2) }],
|
|
1789
|
+
};
|
|
1750
1790
|
}
|
|
1751
1791
|
case "delete_wiki_page": {
|
|
1752
1792
|
const { project_id, slug } = DeleteWikiPageSchema.parse(request.params.arguments);
|
|
1753
1793
|
await deleteWikiPage(project_id, slug);
|
|
1754
|
-
return {
|
|
1794
|
+
return {
|
|
1795
|
+
content: [
|
|
1796
|
+
{
|
|
1797
|
+
type: "text",
|
|
1798
|
+
text: JSON.stringify({
|
|
1799
|
+
status: "success",
|
|
1800
|
+
message: "Wiki page deleted successfully",
|
|
1801
|
+
}, null, 2),
|
|
1802
|
+
},
|
|
1803
|
+
],
|
|
1804
|
+
};
|
|
1755
1805
|
}
|
|
1756
1806
|
default:
|
|
1757
1807
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
package/build/schemas.js
CHANGED
|
@@ -57,7 +57,8 @@ export const GitLabRepositorySchema = z.object({
|
|
|
57
57
|
created_at: z.string().optional(),
|
|
58
58
|
last_activity_at: z.string().optional(),
|
|
59
59
|
default_branch: z.string().optional(),
|
|
60
|
-
namespace: z
|
|
60
|
+
namespace: z
|
|
61
|
+
.object({
|
|
61
62
|
id: z.number(),
|
|
62
63
|
name: z.string(),
|
|
63
64
|
path: z.string(),
|
|
@@ -65,7 +66,8 @@ export const GitLabRepositorySchema = z.object({
|
|
|
65
66
|
full_path: z.string(),
|
|
66
67
|
avatar_url: z.string().nullable().optional(),
|
|
67
68
|
web_url: z.string().optional(),
|
|
68
|
-
})
|
|
69
|
+
})
|
|
70
|
+
.optional(),
|
|
69
71
|
readme_url: z.string().optional().nullable(),
|
|
70
72
|
topics: z.array(z.string()).optional(),
|
|
71
73
|
tag_list: z.array(z.string()).optional(), // deprecated but still present
|
|
@@ -73,16 +75,24 @@ export const GitLabRepositorySchema = z.object({
|
|
|
73
75
|
archived: z.boolean().optional(),
|
|
74
76
|
forks_count: z.number().optional(),
|
|
75
77
|
star_count: z.number().optional(),
|
|
76
|
-
permissions: z
|
|
77
|
-
|
|
78
|
+
permissions: z
|
|
79
|
+
.object({
|
|
80
|
+
project_access: z
|
|
81
|
+
.object({
|
|
78
82
|
access_level: z.number(),
|
|
79
83
|
notification_level: z.number().optional(),
|
|
80
|
-
})
|
|
81
|
-
|
|
84
|
+
})
|
|
85
|
+
.optional()
|
|
86
|
+
.nullable(),
|
|
87
|
+
group_access: z
|
|
88
|
+
.object({
|
|
82
89
|
access_level: z.number(),
|
|
83
90
|
notification_level: z.number().optional(),
|
|
84
|
-
})
|
|
85
|
-
|
|
91
|
+
})
|
|
92
|
+
.optional()
|
|
93
|
+
.nullable(),
|
|
94
|
+
})
|
|
95
|
+
.optional(),
|
|
86
96
|
container_registry_enabled: z.boolean().optional(),
|
|
87
97
|
container_registry_access_level: z.string().optional(),
|
|
88
98
|
issues_enabled: z.boolean().optional(),
|
|
@@ -93,12 +103,14 @@ export const GitLabRepositorySchema = z.object({
|
|
|
93
103
|
can_create_merge_request_in: z.boolean().optional(),
|
|
94
104
|
resolve_outdated_diff_discussions: z.boolean().optional(),
|
|
95
105
|
shared_runners_enabled: z.boolean().optional(),
|
|
96
|
-
shared_with_groups: z
|
|
106
|
+
shared_with_groups: z
|
|
107
|
+
.array(z.object({
|
|
97
108
|
group_id: z.number(),
|
|
98
109
|
group_name: z.string(),
|
|
99
110
|
group_full_path: z.string(),
|
|
100
111
|
group_access_level: z.number(),
|
|
101
|
-
}))
|
|
112
|
+
}))
|
|
113
|
+
.optional(),
|
|
102
114
|
});
|
|
103
115
|
// Project schema (extended from repository schema)
|
|
104
116
|
export const GitLabProjectSchema = GitLabRepositorySchema;
|
|
@@ -251,17 +263,21 @@ export const GitLabIssueSchema = z.object({
|
|
|
251
263
|
updated_at: z.string(),
|
|
252
264
|
closed_at: z.string().nullable(),
|
|
253
265
|
web_url: z.string(), // Changed from html_url to match GitLab API
|
|
254
|
-
references: z
|
|
266
|
+
references: z
|
|
267
|
+
.object({
|
|
255
268
|
short: z.string(),
|
|
256
269
|
relative: z.string(),
|
|
257
270
|
full: z.string(),
|
|
258
|
-
})
|
|
259
|
-
|
|
271
|
+
})
|
|
272
|
+
.optional(),
|
|
273
|
+
time_stats: z
|
|
274
|
+
.object({
|
|
260
275
|
time_estimate: z.number(),
|
|
261
276
|
total_time_spent: z.number(),
|
|
262
277
|
human_time_estimate: z.string().nullable(),
|
|
263
278
|
human_total_time_spent: z.string().nullable(),
|
|
264
|
-
})
|
|
279
|
+
})
|
|
280
|
+
.optional(),
|
|
265
281
|
confidential: z.boolean().optional(),
|
|
266
282
|
due_date: z.string().nullable().optional(),
|
|
267
283
|
discussion_locked: z.boolean().nullable().optional(),
|
|
@@ -270,7 +286,7 @@ export const GitLabIssueSchema = z.object({
|
|
|
270
286
|
// NEW SCHEMA: For issue with link details (used in listing issue links)
|
|
271
287
|
export const GitLabIssueWithLinkDetailsSchema = GitLabIssueSchema.extend({
|
|
272
288
|
issue_link_id: z.number(),
|
|
273
|
-
link_type: z.enum([
|
|
289
|
+
link_type: z.enum(["relates_to", "blocks", "is_blocked_by"]),
|
|
274
290
|
link_created_at: z.string(),
|
|
275
291
|
link_updated_at: z.string(),
|
|
276
292
|
});
|
|
@@ -278,11 +294,13 @@ export const GitLabIssueWithLinkDetailsSchema = GitLabIssueSchema.extend({
|
|
|
278
294
|
export const GitLabForkParentSchema = z.object({
|
|
279
295
|
name: z.string(),
|
|
280
296
|
path_with_namespace: z.string(), // Changed from full_name to match GitLab API
|
|
281
|
-
owner: z
|
|
297
|
+
owner: z
|
|
298
|
+
.object({
|
|
282
299
|
username: z.string(), // Changed from login to match GitLab API
|
|
283
300
|
id: z.number(),
|
|
284
301
|
avatar_url: z.string(),
|
|
285
|
-
})
|
|
302
|
+
})
|
|
303
|
+
.optional(), // Made optional to handle cases where GitLab API doesn't include it
|
|
286
304
|
web_url: z.string(), // Changed from html_url to match GitLab API
|
|
287
305
|
});
|
|
288
306
|
export const GitLabForkSchema = GitLabRepositorySchema.extend({
|
|
@@ -346,7 +364,9 @@ export const GitLabDiscussionNoteSchema = z.object({
|
|
|
346
364
|
resolved: z.boolean().optional(),
|
|
347
365
|
resolved_by: GitLabUserSchema.nullable().optional(),
|
|
348
366
|
resolved_at: z.string().nullable().optional(),
|
|
349
|
-
position: z
|
|
367
|
+
position: z
|
|
368
|
+
.object({
|
|
369
|
+
// Only present for DiffNote
|
|
350
370
|
base_sha: z.string(),
|
|
351
371
|
start_sha: z.string(),
|
|
352
372
|
head_sha: z.string(),
|
|
@@ -355,7 +375,8 @@ export const GitLabDiscussionNoteSchema = z.object({
|
|
|
355
375
|
position_type: z.enum(["text", "image", "file"]),
|
|
356
376
|
old_line: z.number().nullable(),
|
|
357
377
|
new_line: z.number().nullable(),
|
|
358
|
-
line_range: z
|
|
378
|
+
line_range: z
|
|
379
|
+
.object({
|
|
359
380
|
start: z.object({
|
|
360
381
|
line_code: z.string(),
|
|
361
382
|
type: z.enum(["new", "old"]),
|
|
@@ -368,12 +389,15 @@ export const GitLabDiscussionNoteSchema = z.object({
|
|
|
368
389
|
old_line: z.number().nullable(),
|
|
369
390
|
new_line: z.number().nullable(),
|
|
370
391
|
}),
|
|
371
|
-
})
|
|
392
|
+
})
|
|
393
|
+
.nullable()
|
|
394
|
+
.optional(), // For multi-line diff notes
|
|
372
395
|
width: z.number().optional(), // For image diff notes
|
|
373
396
|
height: z.number().optional(), // For image diff notes
|
|
374
397
|
x: z.number().optional(), // For image diff notes
|
|
375
398
|
y: z.number().optional(), // For image diff notes
|
|
376
|
-
})
|
|
399
|
+
})
|
|
400
|
+
.optional(),
|
|
377
401
|
});
|
|
378
402
|
export const GitLabDiscussionSchema = z.object({
|
|
379
403
|
id: z.string(),
|
|
@@ -402,10 +426,7 @@ export const CreateOrUpdateFileSchema = ProjectParamsSchema.extend({
|
|
|
402
426
|
.string()
|
|
403
427
|
.optional()
|
|
404
428
|
.describe("Path of the file to move/rename"),
|
|
405
|
-
last_commit_id: z
|
|
406
|
-
.string()
|
|
407
|
-
.optional()
|
|
408
|
-
.describe("Last known file commit ID"),
|
|
429
|
+
last_commit_id: z.string().optional().describe("Last known file commit ID"),
|
|
409
430
|
commit_id: z
|
|
410
431
|
.string()
|
|
411
432
|
.optional()
|
|
@@ -489,7 +510,9 @@ export const GitLabMergeRequestDiffSchema = z.object({
|
|
|
489
510
|
export const GetMergeRequestSchema = ProjectParamsSchema.extend({
|
|
490
511
|
merge_request_iid: z
|
|
491
512
|
.number()
|
|
492
|
-
.
|
|
513
|
+
.optional()
|
|
514
|
+
.describe("The IID of a merge request"),
|
|
515
|
+
source_branch: z.string().optional().describe("Source branch name"),
|
|
493
516
|
});
|
|
494
517
|
export const UpdateMergeRequestSchema = GetMergeRequestSchema.extend({
|
|
495
518
|
title: z.string().optional().describe("The title of the merge request"),
|
|
@@ -531,22 +554,61 @@ export const CreateNoteSchema = z.object({
|
|
|
531
554
|
// Issues API operation schemas
|
|
532
555
|
export const ListIssuesSchema = z.object({
|
|
533
556
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
534
|
-
assignee_id: z
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
557
|
+
assignee_id: z
|
|
558
|
+
.number()
|
|
559
|
+
.optional()
|
|
560
|
+
.describe("Return issues assigned to the given user ID"),
|
|
561
|
+
assignee_username: z
|
|
562
|
+
.string()
|
|
563
|
+
.optional()
|
|
564
|
+
.describe("Return issues assigned to the given username"),
|
|
565
|
+
author_id: z
|
|
566
|
+
.number()
|
|
567
|
+
.optional()
|
|
568
|
+
.describe("Return issues created by the given user ID"),
|
|
569
|
+
author_username: z
|
|
570
|
+
.string()
|
|
571
|
+
.optional()
|
|
572
|
+
.describe("Return issues created by the given username"),
|
|
573
|
+
confidential: z
|
|
574
|
+
.boolean()
|
|
575
|
+
.optional()
|
|
576
|
+
.describe("Filter confidential or public issues"),
|
|
577
|
+
created_after: z
|
|
578
|
+
.string()
|
|
579
|
+
.optional()
|
|
580
|
+
.describe("Return issues created after the given time"),
|
|
581
|
+
created_before: z
|
|
582
|
+
.string()
|
|
583
|
+
.optional()
|
|
584
|
+
.describe("Return issues created before the given time"),
|
|
585
|
+
due_date: z
|
|
586
|
+
.string()
|
|
587
|
+
.optional()
|
|
588
|
+
.describe("Return issues that have the due date"),
|
|
542
589
|
label_name: z.array(z.string()).optional().describe("Array of label names"),
|
|
543
590
|
milestone: z.string().optional().describe("Milestone title"),
|
|
544
|
-
scope: z
|
|
591
|
+
scope: z
|
|
592
|
+
.enum(["created-by-me", "assigned-to-me", "all"])
|
|
593
|
+
.optional()
|
|
594
|
+
.describe("Return issues from a specific scope"),
|
|
545
595
|
search: z.string().optional().describe("Search for specific terms"),
|
|
546
|
-
state: z
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
596
|
+
state: z
|
|
597
|
+
.enum(["opened", "closed", "all"])
|
|
598
|
+
.optional()
|
|
599
|
+
.describe("Return issues with a specific state"),
|
|
600
|
+
updated_after: z
|
|
601
|
+
.string()
|
|
602
|
+
.optional()
|
|
603
|
+
.describe("Return issues updated after the given time"),
|
|
604
|
+
updated_before: z
|
|
605
|
+
.string()
|
|
606
|
+
.optional()
|
|
607
|
+
.describe("Return issues updated before the given time"),
|
|
608
|
+
with_labels_details: z
|
|
609
|
+
.boolean()
|
|
610
|
+
.optional()
|
|
611
|
+
.describe("Return more details for each label"),
|
|
550
612
|
page: z.number().optional().describe("Page number for pagination"),
|
|
551
613
|
per_page: z.number().optional().describe("Number of items per page"),
|
|
552
614
|
});
|
|
@@ -559,13 +621,28 @@ export const UpdateIssueSchema = z.object({
|
|
|
559
621
|
issue_iid: z.number().describe("The internal ID of the project issue"),
|
|
560
622
|
title: z.string().optional().describe("The title of the issue"),
|
|
561
623
|
description: z.string().optional().describe("The description of the issue"),
|
|
562
|
-
assignee_ids: z
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
624
|
+
assignee_ids: z
|
|
625
|
+
.array(z.number())
|
|
626
|
+
.optional()
|
|
627
|
+
.describe("Array of user IDs to assign issue to"),
|
|
628
|
+
confidential: z
|
|
629
|
+
.boolean()
|
|
630
|
+
.optional()
|
|
631
|
+
.describe("Set the issue to be confidential"),
|
|
632
|
+
discussion_locked: z
|
|
633
|
+
.boolean()
|
|
634
|
+
.optional()
|
|
635
|
+
.describe("Flag to lock discussions"),
|
|
636
|
+
due_date: z
|
|
637
|
+
.string()
|
|
638
|
+
.optional()
|
|
639
|
+
.describe("Date the issue is due (YYYY-MM-DD)"),
|
|
566
640
|
labels: z.array(z.string()).optional().describe("Array of label names"),
|
|
567
641
|
milestone_id: z.number().optional().describe("Milestone ID to assign"),
|
|
568
|
-
state_event: z
|
|
642
|
+
state_event: z
|
|
643
|
+
.enum(["close", "reopen"])
|
|
644
|
+
.optional()
|
|
645
|
+
.describe("Update issue state (close/reopen)"),
|
|
569
646
|
weight: z.number().optional().describe("Weight of the issue (0-9)"),
|
|
570
647
|
});
|
|
571
648
|
export const DeleteIssueSchema = z.object({
|
|
@@ -576,7 +653,7 @@ export const DeleteIssueSchema = z.object({
|
|
|
576
653
|
export const GitLabIssueLinkSchema = z.object({
|
|
577
654
|
source_issue: GitLabIssueSchema,
|
|
578
655
|
target_issue: GitLabIssueSchema,
|
|
579
|
-
link_type: z.enum([
|
|
656
|
+
link_type: z.enum(["relates_to", "blocks", "is_blocked_by"]),
|
|
580
657
|
});
|
|
581
658
|
export const ListIssueLinksSchema = z.object({
|
|
582
659
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
@@ -590,9 +667,16 @@ export const GetIssueLinkSchema = z.object({
|
|
|
590
667
|
export const CreateIssueLinkSchema = z.object({
|
|
591
668
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
592
669
|
issue_iid: z.number().describe("The internal ID of a project's issue"),
|
|
593
|
-
target_project_id: z
|
|
594
|
-
|
|
595
|
-
|
|
670
|
+
target_project_id: z
|
|
671
|
+
.string()
|
|
672
|
+
.describe("The ID or URL-encoded path of a target project"),
|
|
673
|
+
target_issue_iid: z
|
|
674
|
+
.number()
|
|
675
|
+
.describe("The internal ID of a target project's issue"),
|
|
676
|
+
link_type: z
|
|
677
|
+
.enum(["relates_to", "blocks", "is_blocked_by"])
|
|
678
|
+
.optional()
|
|
679
|
+
.describe("The type of the relation, defaults to relates_to"),
|
|
596
680
|
});
|
|
597
681
|
export const DeleteIssueLinkSchema = z.object({
|
|
598
682
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
@@ -604,7 +688,10 @@ export const ListNamespacesSchema = z.object({
|
|
|
604
688
|
search: z.string().optional().describe("Search term for namespaces"),
|
|
605
689
|
page: z.number().optional().describe("Page number for pagination"),
|
|
606
690
|
per_page: z.number().optional().describe("Number of items per page"),
|
|
607
|
-
owned: z
|
|
691
|
+
owned: z
|
|
692
|
+
.boolean()
|
|
693
|
+
.optional()
|
|
694
|
+
.describe("Filter for namespaces owned by current user"),
|
|
608
695
|
});
|
|
609
696
|
export const GetNamespaceSchema = z.object({
|
|
610
697
|
namespace_id: z.string().describe("Namespace ID or full path"),
|
|
@@ -620,67 +707,160 @@ export const ListProjectsSchema = z.object({
|
|
|
620
707
|
search: z.string().optional().describe("Search term for projects"),
|
|
621
708
|
page: z.number().optional().describe("Page number for pagination"),
|
|
622
709
|
per_page: z.number().optional().describe("Number of items per page"),
|
|
623
|
-
|
|
624
|
-
|
|
710
|
+
search_namespaces: z
|
|
711
|
+
.boolean()
|
|
712
|
+
.optional()
|
|
713
|
+
.describe("Needs to be true if search is full path"),
|
|
714
|
+
owned: z
|
|
715
|
+
.boolean()
|
|
716
|
+
.optional()
|
|
717
|
+
.describe("Filter for projects owned by current user"),
|
|
718
|
+
membership: z
|
|
719
|
+
.boolean()
|
|
720
|
+
.optional()
|
|
721
|
+
.describe("Filter for projects where current user is a member"),
|
|
625
722
|
simple: z.boolean().optional().describe("Return only limited fields"),
|
|
626
723
|
archived: z.boolean().optional().describe("Filter for archived projects"),
|
|
627
|
-
visibility: z
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
724
|
+
visibility: z
|
|
725
|
+
.enum(["public", "internal", "private"])
|
|
726
|
+
.optional()
|
|
727
|
+
.describe("Filter by project visibility"),
|
|
728
|
+
order_by: z
|
|
729
|
+
.enum([
|
|
730
|
+
"id",
|
|
731
|
+
"name",
|
|
732
|
+
"path",
|
|
733
|
+
"created_at",
|
|
734
|
+
"updated_at",
|
|
735
|
+
"last_activity_at",
|
|
736
|
+
])
|
|
737
|
+
.optional()
|
|
738
|
+
.describe("Return projects ordered by field"),
|
|
739
|
+
sort: z
|
|
740
|
+
.enum(["asc", "desc"])
|
|
741
|
+
.optional()
|
|
742
|
+
.describe("Return projects sorted in ascending or descending order"),
|
|
743
|
+
with_issues_enabled: z
|
|
744
|
+
.boolean()
|
|
745
|
+
.optional()
|
|
746
|
+
.describe("Filter projects with issues feature enabled"),
|
|
747
|
+
with_merge_requests_enabled: z
|
|
748
|
+
.boolean()
|
|
749
|
+
.optional()
|
|
750
|
+
.describe("Filter projects with merge requests feature enabled"),
|
|
751
|
+
min_access_level: z
|
|
752
|
+
.number()
|
|
753
|
+
.optional()
|
|
754
|
+
.describe("Filter by minimum access level"),
|
|
633
755
|
});
|
|
634
756
|
// Label operation schemas
|
|
635
757
|
export const ListLabelsSchema = z.object({
|
|
636
758
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
637
|
-
with_counts: z
|
|
638
|
-
|
|
759
|
+
with_counts: z
|
|
760
|
+
.boolean()
|
|
761
|
+
.optional()
|
|
762
|
+
.describe("Whether or not to include issue and merge request counts"),
|
|
763
|
+
include_ancestor_groups: z
|
|
764
|
+
.boolean()
|
|
765
|
+
.optional()
|
|
766
|
+
.describe("Include ancestor groups"),
|
|
639
767
|
search: z.string().optional().describe("Keyword to filter labels by"),
|
|
640
768
|
});
|
|
641
769
|
export const GetLabelSchema = z.object({
|
|
642
770
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
643
|
-
label_id: z
|
|
644
|
-
|
|
771
|
+
label_id: z
|
|
772
|
+
.union([z.number(), z.string()])
|
|
773
|
+
.describe("The ID or title of a project's label"),
|
|
774
|
+
include_ancestor_groups: z
|
|
775
|
+
.boolean()
|
|
776
|
+
.optional()
|
|
777
|
+
.describe("Include ancestor groups"),
|
|
645
778
|
});
|
|
646
779
|
export const CreateLabelSchema = z.object({
|
|
647
780
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
648
781
|
name: z.string().describe("The name of the label"),
|
|
649
|
-
color: z
|
|
782
|
+
color: z
|
|
783
|
+
.string()
|
|
784
|
+
.describe("The color of the label given in 6-digit hex notation with leading '#' sign"),
|
|
650
785
|
description: z.string().optional().describe("The description of the label"),
|
|
651
|
-
priority: z
|
|
786
|
+
priority: z
|
|
787
|
+
.number()
|
|
788
|
+
.nullable()
|
|
789
|
+
.optional()
|
|
790
|
+
.describe("The priority of the label"),
|
|
652
791
|
});
|
|
653
792
|
export const UpdateLabelSchema = z.object({
|
|
654
793
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
655
|
-
label_id: z
|
|
794
|
+
label_id: z
|
|
795
|
+
.union([z.number(), z.string()])
|
|
796
|
+
.describe("The ID or title of a project's label"),
|
|
656
797
|
new_name: z.string().optional().describe("The new name of the label"),
|
|
657
|
-
color: z
|
|
658
|
-
|
|
659
|
-
|
|
798
|
+
color: z
|
|
799
|
+
.string()
|
|
800
|
+
.optional()
|
|
801
|
+
.describe("The color of the label given in 6-digit hex notation with leading '#' sign"),
|
|
802
|
+
description: z
|
|
803
|
+
.string()
|
|
804
|
+
.optional()
|
|
805
|
+
.describe("The new description of the label"),
|
|
806
|
+
priority: z
|
|
807
|
+
.number()
|
|
808
|
+
.nullable()
|
|
809
|
+
.optional()
|
|
810
|
+
.describe("The new priority of the label"),
|
|
660
811
|
});
|
|
661
812
|
export const DeleteLabelSchema = z.object({
|
|
662
813
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
663
|
-
label_id: z
|
|
814
|
+
label_id: z
|
|
815
|
+
.union([z.number(), z.string()])
|
|
816
|
+
.describe("The ID or title of a project's label"),
|
|
664
817
|
});
|
|
665
818
|
// Group projects schema
|
|
666
819
|
export const ListGroupProjectsSchema = z.object({
|
|
667
820
|
group_id: z.string().describe("Group ID or path"),
|
|
668
|
-
include_subgroups: z
|
|
821
|
+
include_subgroups: z
|
|
822
|
+
.boolean()
|
|
823
|
+
.optional()
|
|
824
|
+
.describe("Include projects from subgroups"),
|
|
669
825
|
search: z.string().optional().describe("Search term to filter projects"),
|
|
670
|
-
order_by: z
|
|
671
|
-
|
|
826
|
+
order_by: z
|
|
827
|
+
.enum(["name", "path", "created_at", "updated_at", "last_activity_at"])
|
|
828
|
+
.optional()
|
|
829
|
+
.describe("Field to sort by"),
|
|
830
|
+
sort: z.enum(["asc", "desc"]).optional().describe("Sort direction"),
|
|
672
831
|
page: z.number().optional().describe("Page number"),
|
|
673
832
|
per_page: z.number().optional().describe("Number of results per page"),
|
|
674
833
|
archived: z.boolean().optional().describe("Filter for archived projects"),
|
|
675
|
-
visibility: z
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
834
|
+
visibility: z
|
|
835
|
+
.enum(["public", "internal", "private"])
|
|
836
|
+
.optional()
|
|
837
|
+
.describe("Filter by project visibility"),
|
|
838
|
+
with_issues_enabled: z
|
|
839
|
+
.boolean()
|
|
840
|
+
.optional()
|
|
841
|
+
.describe("Filter projects with issues feature enabled"),
|
|
842
|
+
with_merge_requests_enabled: z
|
|
843
|
+
.boolean()
|
|
844
|
+
.optional()
|
|
845
|
+
.describe("Filter projects with merge requests feature enabled"),
|
|
846
|
+
min_access_level: z
|
|
847
|
+
.number()
|
|
848
|
+
.optional()
|
|
849
|
+
.describe("Filter by minimum access level"),
|
|
850
|
+
with_programming_language: z
|
|
851
|
+
.string()
|
|
852
|
+
.optional()
|
|
853
|
+
.describe("Filter by programming language"),
|
|
680
854
|
starred: z.boolean().optional().describe("Filter by starred projects"),
|
|
681
855
|
statistics: z.boolean().optional().describe("Include project statistics"),
|
|
682
|
-
with_custom_attributes: z
|
|
683
|
-
|
|
856
|
+
with_custom_attributes: z
|
|
857
|
+
.boolean()
|
|
858
|
+
.optional()
|
|
859
|
+
.describe("Include custom attributes"),
|
|
860
|
+
with_security_reports: z
|
|
861
|
+
.boolean()
|
|
862
|
+
.optional()
|
|
863
|
+
.describe("Include security reports"),
|
|
684
864
|
});
|
|
685
865
|
// Add wiki operation schemas
|
|
686
866
|
export const ListWikiPagesSchema = z.object({
|
|
@@ -696,14 +876,20 @@ export const CreateWikiPageSchema = z.object({
|
|
|
696
876
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
697
877
|
title: z.string().describe("Title of the wiki page"),
|
|
698
878
|
content: z.string().describe("Content of the wiki page"),
|
|
699
|
-
format: z
|
|
879
|
+
format: z
|
|
880
|
+
.string()
|
|
881
|
+
.optional()
|
|
882
|
+
.describe("Content format, e.g., markdown, rdoc"),
|
|
700
883
|
});
|
|
701
884
|
export const UpdateWikiPageSchema = z.object({
|
|
702
885
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
703
886
|
slug: z.string().describe("URL-encoded slug of the wiki page"),
|
|
704
887
|
title: z.string().optional().describe("New title of the wiki page"),
|
|
705
888
|
content: z.string().optional().describe("New content of the wiki page"),
|
|
706
|
-
format: z
|
|
889
|
+
format: z
|
|
890
|
+
.string()
|
|
891
|
+
.optional()
|
|
892
|
+
.describe("Content format, e.g., markdown, rdoc"),
|
|
707
893
|
});
|
|
708
894
|
export const DeleteWikiPageSchema = z.object({
|
|
709
895
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = path.dirname(__filename);
|
|
6
|
+
async function main() {
|
|
7
|
+
const repoRoot = path.resolve(__dirname, '..');
|
|
8
|
+
const indexPath = path.join(repoRoot, 'index.ts');
|
|
9
|
+
const readmePath = path.join(repoRoot, 'README.md');
|
|
10
|
+
// 1. Read index.ts
|
|
11
|
+
const code = fs.readFileSync(indexPath, 'utf-8');
|
|
12
|
+
// 2. Extract allTools array block
|
|
13
|
+
const match = code.match(/const allTools = \[([\s\S]*?)\];/);
|
|
14
|
+
if (!match) {
|
|
15
|
+
console.error('Unable to locate allTools array in index.ts');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const toolsBlock = match[1];
|
|
19
|
+
// 3. Parse tool entries
|
|
20
|
+
const toolRegex = /name:\s*"([^"]+)",[\s\S]*?description:\s*"([^"]+)"/g;
|
|
21
|
+
const tools = [];
|
|
22
|
+
let m;
|
|
23
|
+
while ((m = toolRegex.exec(toolsBlock)) !== null) {
|
|
24
|
+
tools.push({ name: m[1], description: m[2] });
|
|
25
|
+
}
|
|
26
|
+
// 4. Generate markdown
|
|
27
|
+
const lines = tools.map((tool, index) => {
|
|
28
|
+
return `${index + 1}. \`${tool.name}\` - ${tool.description}`;
|
|
29
|
+
});
|
|
30
|
+
const markdown = lines.join('\n');
|
|
31
|
+
// 5. Read README.md and replace between markers
|
|
32
|
+
const readme = fs.readFileSync(readmePath, 'utf-8');
|
|
33
|
+
const updated = readme.replace(/<!-- TOOLS-START -->([\s\S]*?)<!-- TOOLS-END -->/, `<!-- TOOLS-START -->\n${markdown}\n<!-- TOOLS-END -->`);
|
|
34
|
+
// 6. Write back
|
|
35
|
+
fs.writeFileSync(readmePath, updated, 'utf-8');
|
|
36
|
+
console.log('README.md tools section updated.');
|
|
37
|
+
}
|
|
38
|
+
main().catch(err => {
|
|
39
|
+
console.error(err);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zereight/mcp-gitlab",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.33",
|
|
4
4
|
"description": "MCP server for using the GitLab API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "zereight",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
20
20
|
"prepare": "npm run build",
|
|
21
21
|
"watch": "tsc --watch",
|
|
22
|
-
"deploy": "npm publish --access public"
|
|
22
|
+
"deploy": "npm publish --access public",
|
|
23
|
+
"generate-tools": "npx ts-node scripts/generate-tools-readme.ts"
|
|
23
24
|
},
|
|
24
25
|
"dependencies": {
|
|
25
26
|
"@modelcontextprotocol/sdk": "1.8.0",
|