@zereight/mcp-gitlab 2.0.3 → 2.0.5
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 +41 -37
- package/build/index.js +197 -2
- package/build/schemas.js +73 -2
- package/package.json +1 -2
- package/build/scripts/generate-tools-readme.js +0 -41
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# GitLab MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://www.star-history.com/#zereight/gitlab-mcp&Date)
|
|
2
4
|
|
|
3
5
|
## @zereight/mcp-gitlab
|
|
4
6
|
|
|
@@ -10,7 +12,7 @@ GitLab MCP(Model Context Protocol) Server. **Includes bug fixes and improvements
|
|
|
10
12
|
|
|
11
13
|
## Usage
|
|
12
14
|
|
|
13
|
-
### Using with Claude App, Cline, Roo Code, Cursor
|
|
15
|
+
### Using with Claude App, Cline, Roo Code, Cursor, Kilo Code
|
|
14
16
|
|
|
15
17
|
When using with the Claude App, you need to set up your API key and URLs directly.
|
|
16
18
|
|
|
@@ -19,7 +21,7 @@ When using with the Claude App, you need to set up your API key and URLs directl
|
|
|
19
21
|
```json
|
|
20
22
|
{
|
|
21
23
|
"mcpServers": {
|
|
22
|
-
"
|
|
24
|
+
"gitlab": {
|
|
23
25
|
"command": "npx",
|
|
24
26
|
"args": ["-y", "@zereight/mcp-gitlab"],
|
|
25
27
|
"env": {
|
|
@@ -72,7 +74,7 @@ When using with the Claude App, you need to set up your API key and URLs directl
|
|
|
72
74
|
```json
|
|
73
75
|
{
|
|
74
76
|
"mcpServers": {
|
|
75
|
-
"
|
|
77
|
+
"gitlab": {
|
|
76
78
|
"command": "docker",
|
|
77
79
|
"args": [
|
|
78
80
|
"run",
|
|
@@ -110,7 +112,7 @@ When using with the Claude App, you need to set up your API key and URLs directl
|
|
|
110
112
|
```shell
|
|
111
113
|
docker run -i --rm \
|
|
112
114
|
-e GITLAB_PERSONAL_ACCESS_TOKEN=your_gitlab_token \
|
|
113
|
-
-e GITLAB_API_URL=
|
|
115
|
+
-e GITLAB_API_URL="https://gitlab.com/api/v4" \
|
|
114
116
|
-e GITLAB_READ_ONLY_MODE=true \
|
|
115
117
|
-e USE_GITLAB_WIKI=true \
|
|
116
118
|
-e USE_MILESTONE=true \
|
|
@@ -123,7 +125,7 @@ docker run -i --rm \
|
|
|
123
125
|
```json
|
|
124
126
|
{
|
|
125
127
|
"mcpServers": {
|
|
126
|
-
"
|
|
128
|
+
"gitlab": {
|
|
127
129
|
"type": "sse",
|
|
128
130
|
"url": "http://localhost:3333/sse"
|
|
129
131
|
}
|
|
@@ -131,10 +133,8 @@ docker run -i --rm \
|
|
|
131
133
|
}
|
|
132
134
|
```
|
|
133
135
|
|
|
134
|
-
|
|
135
136
|
- streamable-http
|
|
136
137
|
|
|
137
|
-
|
|
138
138
|
```shell
|
|
139
139
|
docker run -i --rm \
|
|
140
140
|
-e GITLAB_PERSONAL_ACCESS_TOKEN=your_gitlab_token \
|
|
@@ -151,20 +151,14 @@ docker run -i --rm \
|
|
|
151
151
|
```json
|
|
152
152
|
{
|
|
153
153
|
"mcpServers": {
|
|
154
|
-
"
|
|
154
|
+
"gitlab": {
|
|
155
|
+
"type": "streamable-http",
|
|
155
156
|
"url": "http://localhost:3333/mcp"
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
```
|
|
160
161
|
|
|
161
|
-
|
|
162
|
-
#### Docker Image Push
|
|
163
|
-
|
|
164
|
-
```shell
|
|
165
|
-
$ sh scripts/image_push.sh docker_user_name
|
|
166
|
-
```
|
|
167
|
-
|
|
168
162
|
### Environment Variables
|
|
169
163
|
|
|
170
164
|
- `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token.
|
|
@@ -174,18 +168,21 @@ $ sh scripts/image_push.sh docker_user_name
|
|
|
174
168
|
- Single value `123`: MCP server can only access project 123 and uses it as default
|
|
175
169
|
- Multiple values `123,456,789`: MCP server can access projects 123, 456, and 789 but requires explicit project ID in requests
|
|
176
170
|
- `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.
|
|
171
|
+
- `GITLAB_DENIED_TOOLS_REGEX`: When set as a regular expression, it excludes the matching tools.
|
|
177
172
|
- `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.
|
|
178
173
|
- `USE_MILESTONE`: When set to 'true', enables the milestone-related tools (list_milestones, get_milestone, create_milestone, edit_milestone, delete_milestone, get_milestone_issue, get_milestone_merge_requests, promote_milestone, get_milestone_burndown_events). By default, milestone features are disabled.
|
|
179
|
-
- `USE_PIPELINE`: When set to 'true', enables the pipeline-related tools (list_pipelines, get_pipeline, list_pipeline_jobs, get_pipeline_job, get_pipeline_job_output, create_pipeline, retry_pipeline, cancel_pipeline). By default, pipeline features are disabled.
|
|
174
|
+
- `USE_PIPELINE`: When set to 'true', enables the pipeline-related tools (list_pipelines, get_pipeline, list_pipeline_jobs, list_pipeline_trigger_jobs, get_pipeline_job, get_pipeline_job_output, create_pipeline, retry_pipeline, cancel_pipeline, play_pipeline_job, retry_pipeline_job, cancel_pipeline_job). By default, pipeline features are disabled.
|
|
180
175
|
- `GITLAB_AUTH_COOKIE_PATH`: Path to an authentication cookie file for GitLab instances that require cookie-based authentication. When provided, the cookie will be included in all GitLab API requests.
|
|
181
176
|
- `SSE`: When set to 'true', enables the Server-Sent Events transport.
|
|
182
177
|
- `STREAMABLE_HTTP`: When set to 'true', enables the Streamable HTTP transport. If both **SSE** and **STREAMABLE_HTTP** are set to 'true', the server will prioritize Streamable HTTP over SSE transport.
|
|
183
178
|
|
|
184
|
-
[](https://www.star-history.com/#zereight/gitlab-mcp&Date)
|
|
185
|
-
|
|
186
179
|
## Tools 🛠️
|
|
187
180
|
|
|
188
|
-
|
|
181
|
+
<details>
|
|
182
|
+
<summary>Click to expand</summary>
|
|
183
|
+
|
|
184
|
+
<!-- TOOLS-START -->
|
|
185
|
+
|
|
189
186
|
1. `merge_merge_request` - Merge a merge request in a GitLab project
|
|
190
187
|
2. `create_or_update_file` - Create or update a single file in a GitLab project
|
|
191
188
|
3. `search_repositories` - Search for GitLab projects
|
|
@@ -252,21 +249,28 @@ $ sh scripts/image_push.sh docker_user_name
|
|
|
252
249
|
64. `create_pipeline` - Create a new pipeline for a branch or tag
|
|
253
250
|
65. `retry_pipeline` - Retry a failed or canceled pipeline
|
|
254
251
|
66. `cancel_pipeline` - Cancel a running pipeline
|
|
255
|
-
67. `
|
|
256
|
-
68. `
|
|
257
|
-
69. `
|
|
258
|
-
70. `
|
|
259
|
-
71. `
|
|
260
|
-
72. `
|
|
261
|
-
73. `
|
|
262
|
-
74. `
|
|
263
|
-
75. `
|
|
264
|
-
76. `
|
|
265
|
-
77. `
|
|
266
|
-
78. `
|
|
267
|
-
79. `
|
|
268
|
-
80. `
|
|
269
|
-
81. `
|
|
270
|
-
82. `
|
|
271
|
-
83. `
|
|
252
|
+
67. `play_pipeline_job` - Run a manual pipeline job
|
|
253
|
+
68. `retry_pipeline_job` - Retry a failed or canceled pipeline job
|
|
254
|
+
69. `cancel_pipeline_job` - Cancel a running pipeline job
|
|
255
|
+
70. `list_merge_requests` - List merge requests in a GitLab project with filtering options
|
|
256
|
+
71. `list_milestones` - List milestones in a GitLab project with filtering options
|
|
257
|
+
72. `get_milestone` - Get details of a specific milestone
|
|
258
|
+
73. `create_milestone` - Create a new milestone in a GitLab project
|
|
259
|
+
74. `edit_milestone` - Edit an existing milestone in a GitLab project
|
|
260
|
+
75. `delete_milestone` - Delete a milestone from a GitLab project
|
|
261
|
+
76. `get_milestone_issue` - Get issues associated with a specific milestone
|
|
262
|
+
77. `get_milestone_merge_requests` - Get merge requests associated with a specific milestone
|
|
263
|
+
78. `promote_milestone` - Promote a milestone to the next stage
|
|
264
|
+
79. `get_milestone_burndown_events` - Get burndown events for a specific milestone
|
|
265
|
+
80. `get_users` - Get GitLab user details by usernames
|
|
266
|
+
81. `list_commits` - List repository commits with filtering options
|
|
267
|
+
82. `get_commit` - Get details of a specific commit
|
|
268
|
+
83. `get_commit_diff` - Get changes/diffs of a specific commit
|
|
269
|
+
84. `list_group_iterations` - List group iterations with filtering options
|
|
270
|
+
85. `upload_markdown` - Upload a file to a GitLab project for use in markdown content
|
|
271
|
+
86. `download_attachment` - Download an uploaded file from a GitLab project by secret and filename
|
|
272
|
+
87. `list_events` - List all events for the currently authenticated user
|
|
273
|
+
88. `get_project_events` - List all visible events for a specified project
|
|
272
274
|
<!-- TOOLS-END -->
|
|
275
|
+
|
|
276
|
+
</details>
|
package/build/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import { zodToJsonSchema } from "zod-to-json-schema";
|
|
|
20
20
|
import { Agent } from "http";
|
|
21
21
|
import { Agent as HttpsAgent } from "https";
|
|
22
22
|
import { URL } from "url";
|
|
23
|
-
import { BulkPublishDraftNotesSchema, CancelPipelineSchema, CreateBranchSchema, CreateDraftNoteSchema, CreateIssueLinkSchema, CreateIssueNoteSchema, CreateIssueSchema, CreateLabelSchema, // Added
|
|
23
|
+
import { BulkPublishDraftNotesSchema, CancelPipelineJobSchema, CancelPipelineSchema, CreateBranchSchema, CreateDraftNoteSchema, CreateIssueLinkSchema, CreateIssueNoteSchema, CreateIssueSchema, CreateLabelSchema, // Added
|
|
24
24
|
CreateMergeRequestNoteSchema, CreateMergeRequestSchema, CreateMergeRequestThreadSchema, CreateNoteSchema, CreateOrUpdateFileSchema, CreatePipelineSchema, CreateProjectMilestoneSchema, CreateRepositorySchema, CreateWikiPageSchema, DeleteDraftNoteSchema, DeleteIssueLinkSchema, DeleteIssueSchema, DeleteLabelSchema, DeleteProjectMilestoneSchema, DeleteWikiPageSchema, EditProjectMilestoneSchema, ForkRepositorySchema, GetBranchDiffsSchema, GetCommitDiffSchema, GetCommitSchema, GetDraftNoteSchema, GetFileContentsSchema, GetIssueLinkSchema, GetIssueSchema, GetLabelSchema, GetMergeRequestDiffsSchema, GetMergeRequestSchema, GetMilestoneBurndownEventsSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, GetNamespaceSchema,
|
|
25
25
|
// pipeline job schemas
|
|
26
26
|
GetPipelineJobOutputSchema, GetPipelineSchema, GetProjectMilestoneSchema, GetProjectSchema, GetRepositoryTreeSchema, GetUsersSchema, GetWikiPageSchema, GitLabCommitSchema, GitLabCompareResultSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabDiffSchema,
|
|
@@ -29,7 +29,7 @@ GitLabDiscussionNoteSchema, // Added
|
|
|
29
29
|
GitLabDiscussionSchema,
|
|
30
30
|
// Draft Notes Schemas
|
|
31
31
|
GitLabDraftNoteSchema, GitLabForkSchema, GitLabIssueLinkSchema, GitLabIssueSchema, GitLabIssueWithLinkDetailsSchema, GitLabMarkdownUploadSchema, GitLabMergeRequestSchema, GitLabMilestonesSchema, GitLabNamespaceExistsResponseSchema, GitLabNamespaceSchema, GitLabPipelineJobSchema, GitLabPipelineSchema, GitLabPipelineTriggerJobSchema, GitLabProjectMemberSchema, GitLabProjectSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabSearchResponseSchema, GitLabTreeItemSchema, GitLabTreeSchema, GitLabUserSchema, GitLabUsersResponseSchema, GitLabWikiPageSchema, GroupIteration, ListCommitsSchema, ListDraftNotesSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListLabelsSchema, ListMergeRequestDiffsSchema, // Added
|
|
32
|
-
ListMergeRequestDiscussionsSchema, ListMergeRequestsSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListPipelineTriggerJobsSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, MarkdownUploadSchema, DownloadAttachmentSchema, MergeMergeRequestSchema, MyIssuesSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PushFilesSchema, RetryPipelineSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateLabelSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestSchema, UpdateWikiPageSchema, VerifyNamespaceSchema } from "./schemas.js";
|
|
32
|
+
ListMergeRequestDiscussionsSchema, ListMergeRequestsSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListPipelineTriggerJobsSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, MarkdownUploadSchema, DownloadAttachmentSchema, MergeMergeRequestSchema, MyIssuesSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PlayPipelineJobSchema, PushFilesSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateLabelSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestSchema, UpdateWikiPageSchema, VerifyNamespaceSchema, GitLabEventSchema, ListEventsSchema, GetProjectEventsSchema } from "./schemas.js";
|
|
33
33
|
import { randomUUID } from "crypto";
|
|
34
34
|
import { pino } from "pino";
|
|
35
35
|
const logger = pino({
|
|
@@ -80,6 +80,7 @@ const GITLAB_PERSONAL_ACCESS_TOKEN = process.env.GITLAB_PERSONAL_ACCESS_TOKEN;
|
|
|
80
80
|
const GITLAB_AUTH_COOKIE_PATH = process.env.GITLAB_AUTH_COOKIE_PATH;
|
|
81
81
|
const IS_OLD = process.env.GITLAB_IS_OLD === "true";
|
|
82
82
|
const GITLAB_READ_ONLY_MODE = process.env.GITLAB_READ_ONLY_MODE === "true";
|
|
83
|
+
const GITLAB_DENIED_TOOLS_REGEX = process.env.GITLAB_DENIED_TOOLS_REGEX ? new RegExp(process.env.GITLAB_DENIED_TOOLS_REGEX) : undefined;
|
|
83
84
|
const USE_GITLAB_WIKI = process.env.USE_GITLAB_WIKI === "true";
|
|
84
85
|
const USE_MILESTONE = process.env.USE_MILESTONE === "true";
|
|
85
86
|
const USE_PIPELINE = process.env.USE_PIPELINE === "true";
|
|
@@ -544,6 +545,21 @@ const allTools = [
|
|
|
544
545
|
description: "Cancel a running pipeline",
|
|
545
546
|
inputSchema: zodToJsonSchema(CancelPipelineSchema),
|
|
546
547
|
},
|
|
548
|
+
{
|
|
549
|
+
name: "play_pipeline_job",
|
|
550
|
+
description: "Run a manual pipeline job",
|
|
551
|
+
inputSchema: zodToJsonSchema(PlayPipelineJobSchema),
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: "retry_pipeline_job",
|
|
555
|
+
description: "Retry a failed or canceled pipeline job",
|
|
556
|
+
inputSchema: zodToJsonSchema(RetryPipelineJobSchema),
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
name: "cancel_pipeline_job",
|
|
560
|
+
description: "Cancel a running pipeline job",
|
|
561
|
+
inputSchema: zodToJsonSchema(CancelPipelineJobSchema),
|
|
562
|
+
},
|
|
547
563
|
{
|
|
548
564
|
name: "list_merge_requests",
|
|
549
565
|
description: "List merge requests in a GitLab project with filtering options",
|
|
@@ -629,6 +645,16 @@ const allTools = [
|
|
|
629
645
|
description: "Download an uploaded file from a GitLab project by secret and filename",
|
|
630
646
|
inputSchema: zodToJsonSchema(DownloadAttachmentSchema),
|
|
631
647
|
},
|
|
648
|
+
{
|
|
649
|
+
name: "list_events",
|
|
650
|
+
description: "List all events for the currently authenticated user. Note: before/after parameters accept date format YYYY-MM-DD only",
|
|
651
|
+
inputSchema: zodToJsonSchema(ListEventsSchema),
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
name: "get_project_events",
|
|
655
|
+
description: "List all visible events for a specified project. Note: before/after parameters accept date format YYYY-MM-DD only",
|
|
656
|
+
inputSchema: zodToJsonSchema(GetProjectEventsSchema),
|
|
657
|
+
},
|
|
632
658
|
];
|
|
633
659
|
// Define which tools are read-only
|
|
634
660
|
const readOnlyTools = [
|
|
@@ -675,6 +701,8 @@ const readOnlyTools = [
|
|
|
675
701
|
"list_group_iterations",
|
|
676
702
|
"get_group_iteration",
|
|
677
703
|
"download_attachment",
|
|
704
|
+
"list_events",
|
|
705
|
+
"get_project_events",
|
|
678
706
|
];
|
|
679
707
|
// Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
|
|
680
708
|
const wikiToolNames = [
|
|
@@ -708,6 +736,9 @@ const pipelineToolNames = [
|
|
|
708
736
|
"create_pipeline",
|
|
709
737
|
"retry_pipeline",
|
|
710
738
|
"cancel_pipeline",
|
|
739
|
+
"play_pipeline_job",
|
|
740
|
+
"retry_pipeline_job",
|
|
741
|
+
"cancel_pipeline_job",
|
|
711
742
|
];
|
|
712
743
|
/**
|
|
713
744
|
* Smart URL handling for GitLab API
|
|
@@ -2591,6 +2622,70 @@ async function cancelPipeline(projectId, pipelineId) {
|
|
|
2591
2622
|
const data = await response.json();
|
|
2592
2623
|
return GitLabPipelineSchema.parse(data);
|
|
2593
2624
|
}
|
|
2625
|
+
/**
|
|
2626
|
+
* Run a manual job
|
|
2627
|
+
*
|
|
2628
|
+
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
2629
|
+
* @param {number} jobId - The ID of the job to run
|
|
2630
|
+
* @param {Object} variables - Optional job variables
|
|
2631
|
+
* @returns {Promise<GitLabPipelineJob>} The run job
|
|
2632
|
+
*/
|
|
2633
|
+
async function playPipelineJob(projectId, jobId, variables) {
|
|
2634
|
+
projectId = decodeURIComponent(projectId);
|
|
2635
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/play`);
|
|
2636
|
+
const body = {};
|
|
2637
|
+
if (variables && variables.length > 0) {
|
|
2638
|
+
body.job_variables_attributes = variables;
|
|
2639
|
+
}
|
|
2640
|
+
const response = await fetch(url.toString(), {
|
|
2641
|
+
...DEFAULT_FETCH_CONFIG,
|
|
2642
|
+
method: "POST",
|
|
2643
|
+
body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,
|
|
2644
|
+
});
|
|
2645
|
+
await handleGitLabError(response);
|
|
2646
|
+
const data = await response.json();
|
|
2647
|
+
return GitLabPipelineJobSchema.parse(data);
|
|
2648
|
+
}
|
|
2649
|
+
/**
|
|
2650
|
+
* Retry a job
|
|
2651
|
+
*
|
|
2652
|
+
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
2653
|
+
* @param {number} jobId - The ID of the job to retry
|
|
2654
|
+
* @returns {Promise<GitLabPipelineJob>} The retried job
|
|
2655
|
+
*/
|
|
2656
|
+
async function retryPipelineJob(projectId, jobId) {
|
|
2657
|
+
projectId = decodeURIComponent(projectId);
|
|
2658
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/retry`);
|
|
2659
|
+
const response = await fetch(url.toString(), {
|
|
2660
|
+
...DEFAULT_FETCH_CONFIG,
|
|
2661
|
+
method: "POST",
|
|
2662
|
+
});
|
|
2663
|
+
await handleGitLabError(response);
|
|
2664
|
+
const data = await response.json();
|
|
2665
|
+
return GitLabPipelineJobSchema.parse(data);
|
|
2666
|
+
}
|
|
2667
|
+
/**
|
|
2668
|
+
* Cancel a job
|
|
2669
|
+
*
|
|
2670
|
+
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
2671
|
+
* @param {number} jobId - The ID of the job to cancel
|
|
2672
|
+
* @param {boolean} force - Force cancellation of the job
|
|
2673
|
+
* @returns {Promise<GitLabPipelineJob>} The canceled job
|
|
2674
|
+
*/
|
|
2675
|
+
async function cancelPipelineJob(projectId, jobId, force) {
|
|
2676
|
+
projectId = decodeURIComponent(projectId);
|
|
2677
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/cancel`);
|
|
2678
|
+
if (force !== undefined) {
|
|
2679
|
+
url.searchParams.append('force', force.toString());
|
|
2680
|
+
}
|
|
2681
|
+
const response = await fetch(url.toString(), {
|
|
2682
|
+
...DEFAULT_FETCH_CONFIG,
|
|
2683
|
+
method: "POST",
|
|
2684
|
+
});
|
|
2685
|
+
await handleGitLabError(response);
|
|
2686
|
+
const data = await response.json();
|
|
2687
|
+
return GitLabPipelineJobSchema.parse(data);
|
|
2688
|
+
}
|
|
2594
2689
|
/**
|
|
2595
2690
|
* Get the repository tree for a project
|
|
2596
2691
|
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
@@ -3098,6 +3193,54 @@ async function downloadAttachment(projectId, secret, filename, localPath) {
|
|
|
3098
3193
|
fs.writeFileSync(savePath, Buffer.from(buffer));
|
|
3099
3194
|
return savePath;
|
|
3100
3195
|
}
|
|
3196
|
+
/**
|
|
3197
|
+
* List all events for the currently authenticated user
|
|
3198
|
+
* @param {Object} options - Options for listing events
|
|
3199
|
+
* @returns {Promise<GitLabEvent[]>} List of events
|
|
3200
|
+
*/
|
|
3201
|
+
async function listEvents(options = {}) {
|
|
3202
|
+
const url = new URL(`${GITLAB_API_URL}/events`);
|
|
3203
|
+
// Add all query parameters
|
|
3204
|
+
Object.entries(options).forEach(([key, value]) => {
|
|
3205
|
+
if (value !== undefined) {
|
|
3206
|
+
url.searchParams.append(key, value.toString());
|
|
3207
|
+
}
|
|
3208
|
+
});
|
|
3209
|
+
const response = await fetch(url.toString(), {
|
|
3210
|
+
method: "GET",
|
|
3211
|
+
headers: DEFAULT_HEADERS,
|
|
3212
|
+
});
|
|
3213
|
+
if (!response.ok) {
|
|
3214
|
+
await handleGitLabError(response);
|
|
3215
|
+
}
|
|
3216
|
+
const data = await response.json();
|
|
3217
|
+
return GitLabEventSchema.array().parse(data);
|
|
3218
|
+
}
|
|
3219
|
+
/**
|
|
3220
|
+
* List all visible events for a specified project
|
|
3221
|
+
* @param {string} projectId - Project ID or URL-encoded path
|
|
3222
|
+
* @param {Object} options - Options for getting project events
|
|
3223
|
+
* @returns {Promise<GitLabEvent[]>} List of project events
|
|
3224
|
+
*/
|
|
3225
|
+
async function getProjectEvents(projectId, options = {}) {
|
|
3226
|
+
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
3227
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/events`);
|
|
3228
|
+
// Add all query parameters
|
|
3229
|
+
Object.entries(options).forEach(([key, value]) => {
|
|
3230
|
+
if (value !== undefined) {
|
|
3231
|
+
url.searchParams.append(key, value.toString());
|
|
3232
|
+
}
|
|
3233
|
+
});
|
|
3234
|
+
const response = await fetch(url.toString(), {
|
|
3235
|
+
method: "GET",
|
|
3236
|
+
headers: DEFAULT_HEADERS,
|
|
3237
|
+
});
|
|
3238
|
+
if (!response.ok) {
|
|
3239
|
+
await handleGitLabError(response);
|
|
3240
|
+
}
|
|
3241
|
+
const data = await response.json();
|
|
3242
|
+
return GitLabEventSchema.array().parse(data);
|
|
3243
|
+
}
|
|
3101
3244
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
3102
3245
|
// Apply read-only filter first
|
|
3103
3246
|
const tools0 = GITLAB_READ_ONLY_MODE
|
|
@@ -3113,6 +3256,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
3113
3256
|
: tools1.filter(tool => !milestoneToolNames.includes(tool.name));
|
|
3114
3257
|
// Toggle pipeline tools by USE_PIPELINE flag
|
|
3115
3258
|
let tools = USE_PIPELINE ? tools2 : tools2.filter(tool => !pipelineToolNames.includes(tool.name));
|
|
3259
|
+
tools = GITLAB_DENIED_TOOLS_REGEX ? tools.filter(tool => !GITLAB_DENIED_TOOLS_REGEX.test(tool.name)) : tools;
|
|
3116
3260
|
// <<< START: Gemini 호환성을 위해 $schema 제거 >>>
|
|
3117
3261
|
tools = tools.map(tool => {
|
|
3118
3262
|
// inputSchema가 존재하고 객체인지 확인
|
|
@@ -3782,6 +3926,42 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3782
3926
|
],
|
|
3783
3927
|
};
|
|
3784
3928
|
}
|
|
3929
|
+
case "play_pipeline_job": {
|
|
3930
|
+
const { project_id, job_id, job_variables_attributes } = PlayPipelineJobSchema.parse(request.params.arguments);
|
|
3931
|
+
const job = await playPipelineJob(project_id, job_id, job_variables_attributes);
|
|
3932
|
+
return {
|
|
3933
|
+
content: [
|
|
3934
|
+
{
|
|
3935
|
+
type: "text",
|
|
3936
|
+
text: `Ran job #${job.id} (${job.name}). Status: ${job.status}\nWeb URL: ${job.web_url}`,
|
|
3937
|
+
},
|
|
3938
|
+
],
|
|
3939
|
+
};
|
|
3940
|
+
}
|
|
3941
|
+
case "retry_pipeline_job": {
|
|
3942
|
+
const { project_id, job_id } = RetryPipelineJobSchema.parse(request.params.arguments);
|
|
3943
|
+
const job = await retryPipelineJob(project_id, job_id);
|
|
3944
|
+
return {
|
|
3945
|
+
content: [
|
|
3946
|
+
{
|
|
3947
|
+
type: "text",
|
|
3948
|
+
text: `Retried job #${job.id} (${job.name}). Status: ${job.status}\nWeb URL: ${job.web_url}`,
|
|
3949
|
+
},
|
|
3950
|
+
],
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
case "cancel_pipeline_job": {
|
|
3954
|
+
const { project_id, job_id, force } = CancelPipelineJobSchema.parse(request.params.arguments);
|
|
3955
|
+
const job = await cancelPipelineJob(project_id, job_id, force);
|
|
3956
|
+
return {
|
|
3957
|
+
content: [
|
|
3958
|
+
{
|
|
3959
|
+
type: "text",
|
|
3960
|
+
text: `Canceled job #${job.id} (${job.name}). Status: ${job.status}\nWeb URL: ${job.web_url}`,
|
|
3961
|
+
},
|
|
3962
|
+
],
|
|
3963
|
+
};
|
|
3964
|
+
}
|
|
3785
3965
|
case "list_merge_requests": {
|
|
3786
3966
|
const args = ListMergeRequestsSchema.parse(request.params.arguments);
|
|
3787
3967
|
const mergeRequests = await listMergeRequests(args.project_id, args);
|
|
@@ -3942,6 +4122,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3942
4122
|
content: [{ type: "text", text: JSON.stringify({ success: true, file_path: filePath }, null, 2) }],
|
|
3943
4123
|
};
|
|
3944
4124
|
}
|
|
4125
|
+
case "list_events": {
|
|
4126
|
+
const args = ListEventsSchema.parse(request.params.arguments);
|
|
4127
|
+
const events = await listEvents(args);
|
|
4128
|
+
return {
|
|
4129
|
+
content: [{ type: "text", text: JSON.stringify(events, null, 2) }],
|
|
4130
|
+
};
|
|
4131
|
+
}
|
|
4132
|
+
case "get_project_events": {
|
|
4133
|
+
const args = GetProjectEventsSchema.parse(request.params.arguments);
|
|
4134
|
+
const { project_id, ...options } = args;
|
|
4135
|
+
const events = await getProjectEvents(project_id, options);
|
|
4136
|
+
return {
|
|
4137
|
+
content: [{ type: "text", text: JSON.stringify(events, null, 2) }],
|
|
4138
|
+
};
|
|
4139
|
+
}
|
|
3945
4140
|
default:
|
|
3946
4141
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
3947
4142
|
}
|
package/build/schemas.js
CHANGED
|
@@ -20,7 +20,7 @@ export const GitLabPipelineSchema = z.object({
|
|
|
20
20
|
duration: z.number().nullable().optional(),
|
|
21
21
|
started_at: z.string().nullable().optional(),
|
|
22
22
|
finished_at: z.string().nullable().optional(),
|
|
23
|
-
coverage: z.number().nullable().optional(),
|
|
23
|
+
coverage: z.coerce.number().nullable().optional(),
|
|
24
24
|
user: z
|
|
25
25
|
.object({
|
|
26
26
|
id: z.coerce.string(),
|
|
@@ -58,7 +58,7 @@ export const GitLabPipelineJobSchema = z.object({
|
|
|
58
58
|
name: z.string(),
|
|
59
59
|
ref: z.string(),
|
|
60
60
|
tag: flexibleBoolean,
|
|
61
|
-
coverage: z.number().nullable().optional(),
|
|
61
|
+
coverage: z.coerce.number().nullable().optional(),
|
|
62
62
|
created_at: z.string(),
|
|
63
63
|
started_at: z.string().nullable().optional(),
|
|
64
64
|
finished_at: z.string().nullable().optional(),
|
|
@@ -282,6 +282,31 @@ export const GetPipelineJobOutputSchema = z.object({
|
|
|
282
282
|
.optional()
|
|
283
283
|
.describe("Number of lines to skip from the end of the log (default: 0)"),
|
|
284
284
|
});
|
|
285
|
+
// Schema for pipeline job control operations
|
|
286
|
+
export const PipelineJobControlSchema = z.object({
|
|
287
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
288
|
+
job_id: z.coerce.string().describe("The ID of the job"),
|
|
289
|
+
});
|
|
290
|
+
// Schema for running a manual job
|
|
291
|
+
export const PlayPipelineJobSchema = z.object({
|
|
292
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
293
|
+
job_id: z.coerce.string().describe("The ID of the job"),
|
|
294
|
+
job_variables_attributes: z
|
|
295
|
+
.array(z.object({
|
|
296
|
+
key: z.string().describe("Variable key"),
|
|
297
|
+
value: z.string().describe("Variable value"),
|
|
298
|
+
}))
|
|
299
|
+
.optional()
|
|
300
|
+
.describe("Custom job variables to use when running the job"),
|
|
301
|
+
});
|
|
302
|
+
// Schema for retrying a job
|
|
303
|
+
export const RetryPipelineJobSchema = PipelineJobControlSchema;
|
|
304
|
+
// Schema for canceling a job
|
|
305
|
+
export const CancelPipelineJobSchema = z.object({
|
|
306
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
307
|
+
job_id: z.coerce.string().describe("The ID of the job"),
|
|
308
|
+
force: z.boolean().optional().describe("Force cancellation of the job"),
|
|
309
|
+
});
|
|
285
310
|
// User schemas
|
|
286
311
|
export const GitLabUserSchema = z.object({
|
|
287
312
|
username: z.string().optional(), // Changed from login to match GitLab API
|
|
@@ -1681,3 +1706,49 @@ export const ListGroupIterationsSchema = z
|
|
|
1681
1706
|
.describe("Return only iterations updated after the given datetime. Expected in ISO 8601 format (2019-03-15T08:00:00Z)."),
|
|
1682
1707
|
})
|
|
1683
1708
|
.merge(PaginationOptionsSchema);
|
|
1709
|
+
// Events API schemas
|
|
1710
|
+
export const GitLabEventAuthorSchema = z.object({
|
|
1711
|
+
id: z.coerce.string(),
|
|
1712
|
+
name: z.string(),
|
|
1713
|
+
username: z.string(),
|
|
1714
|
+
state: z.string(),
|
|
1715
|
+
avatar_url: z.string().nullable(),
|
|
1716
|
+
web_url: z.string(),
|
|
1717
|
+
});
|
|
1718
|
+
export const GitLabEventSchema = z.object({
|
|
1719
|
+
id: z.coerce.string(),
|
|
1720
|
+
project_id: z.coerce.string(),
|
|
1721
|
+
action_name: z.string(),
|
|
1722
|
+
target_id: z.coerce.string().nullable(),
|
|
1723
|
+
target_iid: z.coerce.string().nullable(),
|
|
1724
|
+
target_type: z.string().nullable(),
|
|
1725
|
+
author_id: z.coerce.string(),
|
|
1726
|
+
target_title: z.string().nullable(),
|
|
1727
|
+
created_at: z.string(),
|
|
1728
|
+
author: GitLabEventAuthorSchema,
|
|
1729
|
+
author_username: z.string(),
|
|
1730
|
+
imported: flexibleBoolean,
|
|
1731
|
+
imported_from: z.string(),
|
|
1732
|
+
}).passthrough(); // Allow additional fields
|
|
1733
|
+
// List events schema
|
|
1734
|
+
export const ListEventsSchema = z.object({
|
|
1735
|
+
action: z.string().optional().describe("If defined, returns events with the specified action type"),
|
|
1736
|
+
target_type: z.enum(["epic", "issue", "merge_request", "milestone", "note", "project", "snippet", "user"]).optional().describe("If defined, returns events with the specified target type"),
|
|
1737
|
+
before: z.string().optional().describe("If defined, Returns events created before the specified date (YYYY-MM-DD format). To include events on 2025-08-29, use before=2025-08-30"),
|
|
1738
|
+
after: z.string().optional().describe("If defined, Returns events created after the specified date (YYYY-MM-DD format). To include events on 2025-08-29, use after=2025-08-28"),
|
|
1739
|
+
scope: z.string().optional().describe("Include all events across a user's projects"),
|
|
1740
|
+
sort: z.enum(["asc", "desc"]).optional().describe("Direction to sort the results by creation date. Default: desc"),
|
|
1741
|
+
page: z.number().optional().describe("Returns the specified results page. Default: 1"),
|
|
1742
|
+
per_page: z.number().optional().describe("Number of results per page. Default: 20"),
|
|
1743
|
+
});
|
|
1744
|
+
// Get project events schema
|
|
1745
|
+
export const GetProjectEventsSchema = z.object({
|
|
1746
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1747
|
+
action: z.string().optional().describe("If defined, returns events with the specified action type"),
|
|
1748
|
+
target_type: z.enum(["epic", "issue", "merge_request", "milestone", "note", "project", "snippet", "user"]).optional().describe("If defined, returns events with the specified target type"),
|
|
1749
|
+
before: z.string().optional().describe("If defined, Returns events created before the specified date (YYYY-MM-DD format). To include events on 2025-08-29, use before=2025-08-30"),
|
|
1750
|
+
after: z.string().optional().describe("If defined, Returns events created after the specified date (YYYY-MM-DD format). To include events on 2025-08-29, use after=2025-08-28"),
|
|
1751
|
+
sort: z.enum(["asc", "desc"]).optional().describe("Direction to sort the results by creation date. Default: desc"),
|
|
1752
|
+
page: z.number().optional().describe("Returns the specified results page. Default: 1"),
|
|
1753
|
+
per_page: z.number().optional().describe("Number of results per page. Default: 20"),
|
|
1754
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zereight/mcp-gitlab",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "MCP server for using the GitLab API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "zereight",
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
"dev": "npm run build && node build/index.js",
|
|
22
22
|
"watch": "tsc --watch",
|
|
23
23
|
"deploy": "npm publish --access public",
|
|
24
|
-
"generate-tools": "npx ts-node scripts/generate-tools-readme.ts",
|
|
25
24
|
"changelog": "auto-changelog -p",
|
|
26
25
|
"test": "node test/validate-api.js",
|
|
27
26
|
"test:integration": "node test/validate-api.js",
|
|
@@ -1,41 +0,0 @@
|
|
|
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
|
-
});
|