@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 CHANGED
@@ -1,4 +1,6 @@
1
- # Better GitLab MCP Server
1
+ # GitLab MCP Server
2
+
3
+ [![Star History Chart](https://api.star-history.com/svg?repos=zereight/gitlab-mcp&type=Date)](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
- "GitLab communication server": {
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
- "GitLab communication server": {
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= "https://gitlab.com/api/v4"\
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
- "GitLab communication server": {
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
- "GitLab communication server": {
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
- [![Star History Chart](https://api.star-history.com/svg?repos=zereight/gitlab-mcp&type=Date)](https://www.star-history.com/#zereight/gitlab-mcp&Date)
185
-
186
179
  ## Tools 🛠️
187
180
 
188
- +<!-- TOOLS-START -->
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. `list_merge_requests` - List merge requests in a GitLab project with filtering options
256
- 68. `list_milestones` - List milestones in a GitLab project with filtering options
257
- 69. `get_milestone` - Get details of a specific milestone
258
- 70. `create_milestone` - Create a new milestone in a GitLab project
259
- 71. `edit_milestone` - Edit an existing milestone in a GitLab project
260
- 72. `delete_milestone` - Delete a milestone from a GitLab project
261
- 73. `get_milestone_issue` - Get issues associated with a specific milestone
262
- 74. `get_milestone_merge_requests` - Get merge requests associated with a specific milestone
263
- 75. `promote_milestone` - Promote a milestone to the next stage
264
- 76. `get_milestone_burndown_events` - Get burndown events for a specific milestone
265
- 77. `get_users` - Get GitLab user details by usernames
266
- 78. `list_commits` - List repository commits with filtering options
267
- 79. `get_commit` - Get details of a specific commit
268
- 80. `get_commit_diff` - Get changes/diffs of a specific commit
269
- 81. `list_group_iterations` - List group iterations with filtering options
270
- 82. `upload_markdown` - Upload a file to a GitLab project for use in markdown content
271
- 83. `download_attachment` - Download an uploaded file from a GitLab project by secret and filename
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",
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
- });