@zereight/mcp-gitlab 2.1.52 → 2.1.54

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/build/index.js CHANGED
@@ -62,7 +62,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
62
62
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
63
63
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
64
64
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
65
- import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
65
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
66
66
  import { AsyncLocalStorage } from "node:async_hooks";
67
67
  import express from "express";
68
68
  import fetchCookie from "fetch-cookie";
@@ -110,12 +110,59 @@ CreateMergeRequestNoteSchema, CreateMergeRequestDiscussionNoteSchema, CreateMerg
110
110
  GitLabDiscussionNoteSchema, // Added
111
111
  GitLabDiscussionSchema,
112
112
  // Draft Notes Schemas
113
- GitLabDraftNoteSchema, GitLabForkSchema, GitLabBranchSchema, GitLabProtectedBranchSchema, GitLabGroupSchema, GitLabIssueLinkSchema, GitLabIssueSchema, GitLabIssueWithLinkDetailsSchema, GitLabMarkdownUploadSchema, GitLabMergeRequestPipelineSchema, GitLabMergeRequestSchema, GitLabMilestonesSchema, GitLabGroupMilestonesSchema, GitLabNamespaceExistsResponseSchema, GitLabNamespaceSchema, GitLabPipelineJobSchema, GitLabDeploymentSchema, GitLabEnvironmentSchema, GitLabPipelineSchema, GitLabPipelineTriggerJobSchema, GitLabProjectMemberSchema, GitLabProjectSchema, GitLabTodoSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabSearchBlobResultSchema, GitLabSearchResponseSchema, GitLabTreeItemSchema, GitLabUserSchema, GitLabUsersResponseSchema, GitLabWikiPageSchema, GroupIteration, ListCommitStatusesSchema, ListBranchesSchema, ListCommitsSchema, ListDraftNotesSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, GitLabCiVariableSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, GitLabDependencyProxySchema, GitLabDependencyProxyBlobSchema, GetDependencyProxySettingsSchema, UpdateDependencyProxySettingsSchema, ListDependencyProxyBlobsSchema, PurgeDependencyProxyCacheSchema, ListProjectVulnerabilitiesSchema, GetVulnerabilitySchema, DismissVulnerabilitySchema, ConfirmVulnerabilitySchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListTodosSchema, ListLabelsSchema, ListMergeRequestDiffsSchema, // Added
114
- GetMergeRequestFileDiffSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestsSchema, ListMergeRequestVersionsSchema, GetMergeRequestVersionSchema, GitLabMergeRequestVersionSchema, GitLabMergeRequestVersionDetailSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListDeploymentsSchema, ListEnvironmentsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListCiCatalogResourcesSchema, GetCiCatalogResourceSchema, ListProjectMembersSchema, ListGroupMembersSchema, ListProjectMilestonesSchema, ListGroupMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, GetGroupWikiPageSchema, ListGroupWikiPagesSchema, UpdateGroupWikiPageSchema, MarkdownUploadSchema, MarkdownUploadRemoteSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, GetJobArtifactFileSchema, GitLabArtifactEntrySchema, ListJobArtifactsSchema, MergeMergeRequestSchema, ApproveMergeRequestSchema, UnapproveMergeRequestSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GitLabMergeRequestApprovalsResponseSchema, GitLabMergeRequestApprovalStateSchema, MyIssuesSchema, MarkAllTodosDoneSchema, MarkTodoDoneSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PlayPipelineJobSchema, PushFilesSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateIssueDescriptionPatchSchema, UpdateLabelSchema, UpdateProjectSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestSchema, UpdateWikiPageSchema, VerifyNamespaceSchema, GitLabEventSchema, ListEventsSchema, GetProjectEventsSchema, ExecuteGraphQLSchema, GitLabReleaseSchema, ListReleasesSchema, GetReleaseSchema, CreateReleaseSchema, UpdateReleaseSchema, DeleteReleaseSchema, CreateReleaseEvidenceSchema, DownloadReleaseAssetSchema, ListTagsSchema, GetTagSchema, CreateTagSchema, DeleteTagSchema, GetTagSignatureSchema, GitLabTagSchema, GitLabTagSignatureSchema, GetMergeRequestNotesSchema, GetMergeRequestNoteSchema, DeleteMergeRequestDiscussionNoteSchema, ResolveMergeRequestThreadSchema, GetWorkItemSchema, ListWorkItemsSchema, CreateWorkItemSchema, UpdateWorkItemSchema, ConvertWorkItemTypeSchema, ListWorkItemStatusesSchema, ListWorkItemNotesSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, MoveWorkItemSchema, ListCustomFieldDefinitionsSchema, GetTimelineEventsSchema, CreateTimelineEventSchema, ListWebhooksSchema, ListWebhookEventsSchema, GetWebhookEventSchema, HealthCheckSchema, } from "./schemas.js";
113
+ GitLabDraftNoteSchema, GitLabForkSchema, GitLabBranchSchema, GitLabProtectedBranchSchema, GitLabGroupSchema, GitLabIssueLinkSchema, GitLabIssueSchema, GitLabIssueWithLinkDetailsSchema, GitLabMarkdownUploadSchema, GitLabMergeRequestPipelineSchema, GitLabMergeRequestSchema, GitLabMilestonesSchema, GitLabGroupMilestonesSchema, GitLabNamespaceExistsResponseSchema, GitLabNamespaceSchema, GitLabPipelineJobSchema, GitLabDeploymentSchema, GitLabEnvironmentSchema, GitLabPipelineSchema, GitLabPipelineScheduleSchema, GitLabPipelineScheduleVariableSchema, GitLabPipelineSchedulePlayResultSchema, ListPipelineSchedulesSchema, GetPipelineScheduleSchema, ListPipelineSchedulePipelinesSchema, CreatePipelineScheduleSchema, UpdatePipelineScheduleSchema, DeletePipelineScheduleSchema, PlayPipelineScheduleSchema, TakeOwnershipPipelineScheduleSchema, GetPipelineScheduleVariableSchema, CreatePipelineScheduleVariableSchema, UpdatePipelineScheduleVariableSchema, DeletePipelineScheduleVariableSchema, GitLabPipelineTriggerJobSchema, GitLabProjectMemberSchema, GitLabProjectSchema, GitLabTodoSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabSearchBlobResultSchema, GitLabSearchResponseSchema, GitLabTreeItemSchema, GitLabUserSchema, GitLabUsersResponseSchema, GitLabWikiPageSchema, GroupIteration, ListCommitStatusesSchema, ListBranchesSchema, ListCommitsSchema, ListDraftNotesSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, GitLabCiVariableSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, GitLabDependencyProxySchema, GitLabDependencyProxyBlobSchema, GetDependencyProxySettingsSchema, UpdateDependencyProxySettingsSchema, ListDependencyProxyBlobsSchema, PurgeDependencyProxyCacheSchema, ListProjectVulnerabilitiesSchema, GetVulnerabilitySchema, DismissVulnerabilitySchema, ConfirmVulnerabilitySchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListTodosSchema, ListLabelsSchema, ListMergeRequestDiffsSchema, // Added
114
+ GetMergeRequestFileDiffSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestsSchema, ListGroupMergeRequestsSchema, ListMergeRequestVersionsSchema, GetMergeRequestVersionSchema, GitLabMergeRequestVersionSchema, GitLabMergeRequestVersionDetailSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListDeploymentsSchema, ListEnvironmentsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListCiCatalogResourcesSchema, GetCiCatalogResourceSchema, ListProjectMembersSchema, ListGroupMembersSchema, ListProjectMilestonesSchema, ListGroupMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, GetGroupWikiPageSchema, ListGroupWikiPagesSchema, UpdateGroupWikiPageSchema, MarkdownUploadSchema, MarkdownUploadRemoteSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, GetJobArtifactFileSchema, GitLabArtifactEntrySchema, ListJobArtifactsSchema, MergeMergeRequestSchema, ApproveMergeRequestSchema, UnapproveMergeRequestSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GitLabMergeRequestApprovalsResponseSchema, GitLabMergeRequestApprovalStateSchema, MyIssuesSchema, MarkAllTodosDoneSchema, MarkTodoDoneSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PlayPipelineJobSchema, PushFilesSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateIssueDescriptionPatchSchema, UpdateLabelSchema, UpdateProjectSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestSchema, UpdateWikiPageSchema, VerifyNamespaceSchema, GitLabEventSchema, ListEventsSchema, GetProjectEventsSchema, ExecuteGraphQLSchema, GitLabReleaseSchema, ListReleasesSchema, GetReleaseSchema, CreateReleaseSchema, UpdateReleaseSchema, DeleteReleaseSchema, CreateReleaseEvidenceSchema, DownloadReleaseAssetSchema, ListTagsSchema, GetTagSchema, CreateTagSchema, DeleteTagSchema, GetTagSignatureSchema, GitLabTagSchema, GitLabTagSignatureSchema, GetMergeRequestNotesSchema, GetMergeRequestNoteSchema, DeleteMergeRequestDiscussionNoteSchema, ResolveMergeRequestThreadSchema, GetWorkItemSchema, ListWorkItemsSchema, CreateWorkItemSchema, UpdateWorkItemSchema, ConvertWorkItemTypeSchema, ListWorkItemStatusesSchema, ListWorkItemNotesSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, MoveWorkItemSchema, ListCustomFieldDefinitionsSchema, GetTimelineEventsSchema, CreateTimelineEventSchema, ListWebhooksSchema, ListWebhookEventsSchema, GetWebhookEventSchema, HealthCheckSchema, } from "./schemas.js";
115
115
  import { randomUUID, timingSafeEqual, } from "node:crypto";
116
116
  import { createLogger } from "./utils/logger.js";
117
117
  const logger = createLogger();
118
118
  const SERVER_NAME = process.env.MCP_SERVER_NAME?.trim() || "zereight-gitlab-mcp-server";
119
+ /**
120
+ * Static MCP prompt catalog. These wrap the multi-step tool workflows documented in
121
+ * skills/gitlab-mcp/ so MCP clients that surface prompts (e.g. slash commands) can
122
+ * trigger them without the agent needing to read the Skill first. Kept inline (not
123
+ * loaded from skills/gitlab-mcp/*.md) since only "build" ships in the npm package.
124
+ */
125
+ const MCP_PROMPTS = [
126
+ {
127
+ name: "review_merge_request",
128
+ description: "Review a merge request's diff in batches and leave threaded comments.",
129
+ arguments: [
130
+ { name: "project_id", description: "Numeric project ID or URL-encoded path", required: true },
131
+ { name: "merge_request_iid", description: "Merge request IID", required: true },
132
+ ],
133
+ render: (args) => `Review merge request !${args.merge_request_iid ?? "<iid>"} in project ${args.project_id ?? "<project_id>"}:\n` +
134
+ `1. Call list_merge_request_changed_files to get the changed file list (use excluded_file_patterns for lockfiles/dist/generated files).\n` +
135
+ `2. Call get_merge_request_file_diff in batches of 3-5 files, prioritizing source over config/tests.\n` +
136
+ `3. Leave findings with create_merge_request_thread (inline, position: new_path/new_line) or create_merge_request_note (general).\n` +
137
+ `4. Summarize the review verdict in a final top-level note.`,
138
+ },
139
+ {
140
+ name: "triage_pipeline_failure",
141
+ description: "Find the failing job in a pipeline and pull its log tail for diagnosis.",
142
+ arguments: [
143
+ { name: "project_id", description: "Numeric project ID or URL-encoded path", required: true },
144
+ { name: "pipeline_id", description: "Pipeline ID", required: true },
145
+ ],
146
+ render: (args) => `Diagnose pipeline ${args.pipeline_id ?? "<pipeline_id>"} in project ${args.project_id ?? "<project_id>"}:\n` +
147
+ `1. Call list_pipeline_jobs to find jobs with status "failed".\n` +
148
+ `2. Call get_pipeline_job_output (or the job trace tool) on each failed job, focusing on the tail of the log.\n` +
149
+ `3. Identify the root cause (compile error, test failure, timeout, runner issue) and quote the relevant log lines.\n` +
150
+ `4. Suggest the fix or the next diagnostic step.`,
151
+ },
152
+ {
153
+ name: "triage_vulnerabilities",
154
+ description: "List a project's open vulnerabilities and propose a remediation priority order.",
155
+ arguments: [
156
+ { name: "project_id", description: "Numeric project ID or URL-encoded path", required: true },
157
+ ],
158
+ render: (args) => `Triage vulnerabilities for project ${args.project_id ?? "<project_id>"}:\n` +
159
+ `1. Call list_project_vulnerabilities once with state=detected and once with state=confirmed ` +
160
+ `(the tool only accepts one state per call). Follow pageInfo.endCursor with the "after" ` +
161
+ `param while pageInfo.hasNextPage is true, and aggregate both states' results.\n` +
162
+ `2. Group the aggregated results by severity (critical > high > medium > low) and by whether a fix is already available.\n` +
163
+ `3. Propose a remediation order, calling out any critical/high items with no available fix.`,
164
+ },
165
+ ];
119
166
  /**
120
167
  * Create a new MCP Server instance with request handlers registered.
121
168
  * Each transport connection gets its own Server instance to prevent
@@ -168,8 +215,35 @@ function createServer() {
168
215
  tools: {
169
216
  listChanged: true,
170
217
  },
218
+ prompts: {},
171
219
  },
172
220
  });
221
+ mcpServer.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
222
+ prompts: MCP_PROMPTS.map(({ name, description, arguments: args }) => ({
223
+ name,
224
+ description,
225
+ arguments: args,
226
+ })),
227
+ }));
228
+ mcpServer.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
229
+ const prompt = MCP_PROMPTS.find(p => p.name === request.params.name);
230
+ if (!prompt) {
231
+ throw new Error(`Unknown prompt: ${request.params.name}`);
232
+ }
233
+ const args = (request.params.arguments ?? {});
234
+ return {
235
+ description: prompt.description,
236
+ messages: [
237
+ {
238
+ role: "user",
239
+ content: {
240
+ type: "text",
241
+ text: prompt.render(args),
242
+ },
243
+ },
244
+ ],
245
+ };
246
+ });
173
247
  mcpServer.server.setRequestHandler(ListToolsRequestSchema, async () => {
174
248
  // Step 6: Gemini $schema cleanup + annotations (only dynamic step per request)
175
249
  // <<< START: Remove $schema for Gemini compatibility >>>
@@ -1381,6 +1455,28 @@ async function markAllTodosDone() {
1381
1455
  });
1382
1456
  await handleGitLabError(response);
1383
1457
  }
1458
+ /**
1459
+ * Serialize merge request list filters onto a URL, honouring GitLab's per-parameter array forms
1460
+ */
1461
+ function appendMergeRequestFilters(url, options) {
1462
+ Object.entries(options).forEach(([key, value]) => {
1463
+ if (value === undefined) {
1464
+ return;
1465
+ }
1466
+ if (key === "labels" && Array.isArray(value)) {
1467
+ url.searchParams.append(key, value.join(","));
1468
+ }
1469
+ else if (key === "approved_by_usernames" && Array.isArray(value)) {
1470
+ // GitLab expects array-bracket form: approved_by_usernames[]=alice&approved_by_usernames[]=bob
1471
+ for (const v of value) {
1472
+ url.searchParams.append(`${key}[]`, String(v));
1473
+ }
1474
+ }
1475
+ else {
1476
+ url.searchParams.append(key, String(value));
1477
+ }
1478
+ });
1479
+ }
1384
1480
  /**
1385
1481
  * List merge requests globally or for a specific GitLab project
1386
1482
  *
@@ -1395,24 +1491,25 @@ async function listMergeRequests(projectId, options = {}) {
1395
1491
  ? `${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(decodedProjectId))}/merge_requests`
1396
1492
  : `${getEffectiveApiUrl()}/merge_requests`;
1397
1493
  const url = new URL(endpoint);
1398
- // Add all query parameters
1399
- Object.entries(options).forEach(([key, value]) => {
1400
- if (value !== undefined) {
1401
- if (key === "labels" && Array.isArray(value)) {
1402
- // Handle array of labels
1403
- url.searchParams.append(key, value.join(","));
1404
- }
1405
- else if (key === "approved_by_usernames" && Array.isArray(value)) {
1406
- // GitLab expects array-bracket form: approved_by_usernames[]=alice&approved_by_usernames[]=bob
1407
- for (const v of value) {
1408
- url.searchParams.append(`${key}[]`, String(v));
1409
- }
1410
- }
1411
- else {
1412
- url.searchParams.append(key, String(value));
1413
- }
1414
- }
1494
+ appendMergeRequestFilters(url, options);
1495
+ const response = await fetch(url.toString(), {
1496
+ ...getFetchConfig(),
1415
1497
  });
1498
+ await handleGitLabError(response);
1499
+ const data = await response.json();
1500
+ return z.array(GitLabMergeRequestSchema).parse(data);
1501
+ }
1502
+ /**
1503
+ * List merge requests across every project of a GitLab group and its subgroups
1504
+ *
1505
+ * @param {string} groupId - The ID or URL-encoded path of the group
1506
+ * @param {Object} options - Optional filtering parameters
1507
+ * @returns {Promise<GitLabMergeRequest[]>} List of merge requests
1508
+ */
1509
+ async function listGroupMergeRequests(groupId, options = {}) {
1510
+ const decodedGroupId = decodeURIComponent(groupId);
1511
+ const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeURIComponent(decodedGroupId)}/merge_requests`);
1512
+ appendMergeRequestFilters(url, options);
1416
1513
  const response = await fetch(url.toString(), {
1417
1514
  ...getFetchConfig(),
1418
1515
  });
@@ -5374,6 +5471,235 @@ async function cancelPipeline(projectId, pipelineId) {
5374
5471
  const data = await response.json();
5375
5472
  return GitLabPipelineSchema.parse(data);
5376
5473
  }
5474
+ /**
5475
+ * List pipeline schedules in a GitLab project
5476
+ *
5477
+ * @param {string} projectId - The ID or URL-encoded path of the project
5478
+ * @param {ListPipelineSchedulesOptions} options - Options for filtering pipeline schedules
5479
+ * @returns {Promise<GitLabPipelineSchedule[]>} List of pipeline schedules
5480
+ */
5481
+ async function listPipelineSchedules(projectId, options = {}) {
5482
+ projectId = decodeURIComponent(projectId); // Decode project ID
5483
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules`);
5484
+ Object.entries(options).forEach(([key, value]) => {
5485
+ if (value !== undefined) {
5486
+ url.searchParams.append(key, value.toString());
5487
+ }
5488
+ });
5489
+ const response = await fetch(url.toString(), {
5490
+ ...getFetchConfig(),
5491
+ });
5492
+ await handleGitLabError(response);
5493
+ const data = await response.json();
5494
+ return z.array(GitLabPipelineScheduleSchema).parse(data);
5495
+ }
5496
+ /**
5497
+ * Get details of a specific pipeline schedule
5498
+ *
5499
+ * @param {string} projectId - The ID or URL-encoded path of the project
5500
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule
5501
+ * @returns {Promise<GitLabPipelineSchedule>} Pipeline schedule details
5502
+ */
5503
+ async function getPipelineSchedule(projectId, pipelineScheduleId) {
5504
+ projectId = decodeURIComponent(projectId); // Decode project ID
5505
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}`);
5506
+ const response = await fetch(url.toString(), {
5507
+ ...getFetchConfig(),
5508
+ });
5509
+ await handleGitLabError(response);
5510
+ const data = await response.json();
5511
+ return GitLabPipelineScheduleSchema.parse(data);
5512
+ }
5513
+ /**
5514
+ * List the pipelines triggered by a pipeline schedule
5515
+ *
5516
+ * @param {string} projectId - The ID or URL-encoded path of the project
5517
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule
5518
+ * @param {ListPipelineSchedulePipelinesOptions} options - Pagination options
5519
+ * @returns {Promise<GitLabPipeline[]>} List of pipelines the schedule triggered
5520
+ */
5521
+ async function listPipelineSchedulePipelines(projectId, pipelineScheduleId, options = {}) {
5522
+ projectId = decodeURIComponent(projectId); // Decode project ID
5523
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/pipelines`);
5524
+ Object.entries(options).forEach(([key, value]) => {
5525
+ if (value !== undefined) {
5526
+ url.searchParams.append(key, value.toString());
5527
+ }
5528
+ });
5529
+ const response = await fetch(url.toString(), {
5530
+ ...getFetchConfig(),
5531
+ });
5532
+ await handleGitLabError(response);
5533
+ const data = await response.json();
5534
+ return z.array(GitLabPipelineSchema).parse(data);
5535
+ }
5536
+ /**
5537
+ * Create a new pipeline schedule
5538
+ *
5539
+ * @param {string} projectId - The ID or URL-encoded path of the project
5540
+ * @param {CreatePipelineScheduleOptions} options - The schedule's cron, description, ref and options
5541
+ * @returns {Promise<GitLabPipelineSchedule>} The created pipeline schedule
5542
+ */
5543
+ async function createPipelineSchedule(projectId, options) {
5544
+ projectId = decodeURIComponent(projectId); // Decode project ID
5545
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules`);
5546
+ const response = await fetch(url.toString(), {
5547
+ ...getFetchConfig(),
5548
+ method: "POST",
5549
+ body: JSON.stringify(options),
5550
+ });
5551
+ await handleGitLabError(response);
5552
+ const data = await response.json();
5553
+ return GitLabPipelineScheduleSchema.parse(data);
5554
+ }
5555
+ /**
5556
+ * Update an existing pipeline schedule
5557
+ *
5558
+ * @param {string} projectId - The ID or URL-encoded path of the project
5559
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule to update
5560
+ * @param {UpdatePipelineScheduleOptions} options - The fields to change
5561
+ * @returns {Promise<GitLabPipelineSchedule>} The updated pipeline schedule
5562
+ */
5563
+ async function updatePipelineSchedule(projectId, pipelineScheduleId, options) {
5564
+ projectId = decodeURIComponent(projectId); // Decode project ID
5565
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}`);
5566
+ const response = await fetch(url.toString(), {
5567
+ ...getFetchConfig(),
5568
+ method: "PUT",
5569
+ body: JSON.stringify(options),
5570
+ });
5571
+ await handleGitLabError(response);
5572
+ const data = await response.json();
5573
+ return GitLabPipelineScheduleSchema.parse(data);
5574
+ }
5575
+ /**
5576
+ * Delete a pipeline schedule
5577
+ *
5578
+ * @param {string} projectId - The ID or URL-encoded path of the project
5579
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule to delete
5580
+ * @returns {Promise<void>}
5581
+ */
5582
+ async function deletePipelineSchedule(projectId, pipelineScheduleId) {
5583
+ projectId = decodeURIComponent(projectId); // Decode project ID
5584
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}`);
5585
+ const response = await fetch(url.toString(), {
5586
+ ...getFetchConfig(),
5587
+ method: "DELETE",
5588
+ });
5589
+ await handleGitLabError(response);
5590
+ }
5591
+ /**
5592
+ * Run a pipeline schedule immediately, without changing its next scheduled run
5593
+ *
5594
+ * @param {string} projectId - The ID or URL-encoded path of the project
5595
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule to run
5596
+ * @returns {Promise<GitLabPipelineSchedulePlayResult>} GitLab's acknowledgement message
5597
+ */
5598
+ async function playPipelineSchedule(projectId, pipelineScheduleId) {
5599
+ projectId = decodeURIComponent(projectId); // Decode project ID
5600
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/play`);
5601
+ const response = await fetch(url.toString(), {
5602
+ ...getFetchConfig(),
5603
+ method: "POST",
5604
+ });
5605
+ await handleGitLabError(response);
5606
+ const data = await response.json();
5607
+ return GitLabPipelineSchedulePlayResultSchema.parse(data);
5608
+ }
5609
+ /**
5610
+ * Take ownership of a pipeline schedule
5611
+ *
5612
+ * @param {string} projectId - The ID or URL-encoded path of the project
5613
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule
5614
+ * @returns {Promise<GitLabPipelineSchedule>} The pipeline schedule with its new owner
5615
+ */
5616
+ async function takeOwnershipPipelineSchedule(projectId, pipelineScheduleId) {
5617
+ projectId = decodeURIComponent(projectId); // Decode project ID
5618
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/take_ownership`);
5619
+ const response = await fetch(url.toString(), {
5620
+ ...getFetchConfig(),
5621
+ method: "POST",
5622
+ });
5623
+ await handleGitLabError(response);
5624
+ const data = await response.json();
5625
+ return GitLabPipelineScheduleSchema.parse(data);
5626
+ }
5627
+ /**
5628
+ * Get a single variable of a pipeline schedule
5629
+ *
5630
+ * @param {string} projectId - The ID or URL-encoded path of the project
5631
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule
5632
+ * @param {string} key - The key of the variable
5633
+ * @returns {Promise<GitLabPipelineScheduleVariable>} The pipeline schedule variable
5634
+ */
5635
+ async function getPipelineScheduleVariable(projectId, pipelineScheduleId, key) {
5636
+ projectId = decodeURIComponent(projectId); // Decode project ID
5637
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/variables/${encodeGitLabPathSegment(key)}`);
5638
+ const response = await fetch(url.toString(), {
5639
+ ...getFetchConfig(),
5640
+ });
5641
+ await handleGitLabError(response);
5642
+ const data = await response.json();
5643
+ return GitLabPipelineScheduleVariableSchema.parse(data);
5644
+ }
5645
+ /**
5646
+ * Create a variable for a pipeline schedule
5647
+ *
5648
+ * @param {string} projectId - The ID or URL-encoded path of the project
5649
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule
5650
+ * @param {CreatePipelineScheduleVariableOptions} options - The variable's key, value and type
5651
+ * @returns {Promise<GitLabPipelineScheduleVariable>} The created pipeline schedule variable
5652
+ */
5653
+ async function createPipelineScheduleVariable(projectId, pipelineScheduleId, options) {
5654
+ projectId = decodeURIComponent(projectId); // Decode project ID
5655
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/variables`);
5656
+ const response = await fetch(url.toString(), {
5657
+ ...getFetchConfig(),
5658
+ method: "POST",
5659
+ body: JSON.stringify(options),
5660
+ });
5661
+ await handleGitLabError(response);
5662
+ const data = await response.json();
5663
+ return GitLabPipelineScheduleVariableSchema.parse(data);
5664
+ }
5665
+ /**
5666
+ * Update a variable of a pipeline schedule
5667
+ *
5668
+ * @param {string} projectId - The ID or URL-encoded path of the project
5669
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule
5670
+ * @param {string} key - The key of the variable to update
5671
+ * @param {UpdatePipelineScheduleVariableOptions} options - The new value and type
5672
+ * @returns {Promise<GitLabPipelineScheduleVariable>} The updated pipeline schedule variable
5673
+ */
5674
+ async function updatePipelineScheduleVariable(projectId, pipelineScheduleId, key, options) {
5675
+ projectId = decodeURIComponent(projectId); // Decode project ID
5676
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/variables/${encodeGitLabPathSegment(key)}`);
5677
+ const response = await fetch(url.toString(), {
5678
+ ...getFetchConfig(),
5679
+ method: "PUT",
5680
+ body: JSON.stringify(options),
5681
+ });
5682
+ await handleGitLabError(response);
5683
+ const data = await response.json();
5684
+ return GitLabPipelineScheduleVariableSchema.parse(data);
5685
+ }
5686
+ /**
5687
+ * Delete a variable from a pipeline schedule
5688
+ *
5689
+ * @param {string} projectId - The ID or URL-encoded path of the project
5690
+ * @param {number|string} pipelineScheduleId - The ID of the pipeline schedule
5691
+ * @param {string} key - The key of the variable to delete
5692
+ * @returns {Promise<void>}
5693
+ */
5694
+ async function deletePipelineScheduleVariable(projectId, pipelineScheduleId, key) {
5695
+ projectId = decodeURIComponent(projectId); // Decode project ID
5696
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/variables/${encodeGitLabPathSegment(key)}`);
5697
+ const response = await fetch(url.toString(), {
5698
+ ...getFetchConfig(),
5699
+ method: "DELETE",
5700
+ });
5701
+ await handleGitLabError(response);
5702
+ }
5377
5703
  /**
5378
5704
  * Run a manual job
5379
5705
  *
@@ -8602,6 +8928,136 @@ async function handleToolCall(params) {
8602
8928
  ],
8603
8929
  };
8604
8930
  }
8931
+ case "list_pipeline_schedules": {
8932
+ const args = ListPipelineSchedulesSchema.parse(params.arguments);
8933
+ const { project_id, ...options } = args;
8934
+ const schedules = await listPipelineSchedules(project_id, options);
8935
+ return {
8936
+ content: [{ type: "text", text: JSON.stringify(schedules, null, 2) }],
8937
+ };
8938
+ }
8939
+ case "get_pipeline_schedule": {
8940
+ const { project_id, pipeline_schedule_id } = GetPipelineScheduleSchema.parse(params.arguments);
8941
+ const schedule = await getPipelineSchedule(project_id, pipeline_schedule_id);
8942
+ return {
8943
+ content: [{ type: "text", text: JSON.stringify(schedule, null, 2) }],
8944
+ };
8945
+ }
8946
+ case "list_pipeline_schedule_pipelines": {
8947
+ const args = ListPipelineSchedulePipelinesSchema.parse(params.arguments);
8948
+ const { project_id, pipeline_schedule_id, ...options } = args;
8949
+ const pipelines = await listPipelineSchedulePipelines(project_id, pipeline_schedule_id, options);
8950
+ return {
8951
+ content: [{ type: "text", text: JSON.stringify(pipelines, null, 2) }],
8952
+ };
8953
+ }
8954
+ case "create_pipeline_schedule": {
8955
+ const args = CreatePipelineScheduleSchema.parse(params.arguments);
8956
+ const { project_id, ...options } = args;
8957
+ const schedule = await createPipelineSchedule(project_id, options);
8958
+ return {
8959
+ content: [
8960
+ {
8961
+ type: "text",
8962
+ text: `Created pipeline schedule #${schedule.id} "${schedule.description}" on ${schedule.ref} (cron: ${schedule.cron} ${schedule.cron_timezone ?? "UTC"}). Active: ${schedule.active}\nNext run: ${schedule.next_run_at ?? "not scheduled"}`,
8963
+ },
8964
+ ],
8965
+ };
8966
+ }
8967
+ case "update_pipeline_schedule": {
8968
+ const args = UpdatePipelineScheduleSchema.parse(params.arguments);
8969
+ const { project_id, pipeline_schedule_id, ...options } = args;
8970
+ const schedule = await updatePipelineSchedule(project_id, pipeline_schedule_id, options);
8971
+ return {
8972
+ content: [
8973
+ {
8974
+ type: "text",
8975
+ text: `Updated pipeline schedule #${schedule.id} "${schedule.description}" on ${schedule.ref} (cron: ${schedule.cron} ${schedule.cron_timezone ?? "UTC"}). Active: ${schedule.active}\nNext run: ${schedule.next_run_at ?? "not scheduled"}`,
8976
+ },
8977
+ ],
8978
+ };
8979
+ }
8980
+ case "delete_pipeline_schedule": {
8981
+ const { project_id, pipeline_schedule_id } = DeletePipelineScheduleSchema.parse(params.arguments);
8982
+ await deletePipelineSchedule(project_id, pipeline_schedule_id);
8983
+ return {
8984
+ content: [
8985
+ {
8986
+ type: "text",
8987
+ text: `Deleted pipeline schedule #${pipeline_schedule_id}`,
8988
+ },
8989
+ ],
8990
+ };
8991
+ }
8992
+ case "play_pipeline_schedule": {
8993
+ const { project_id, pipeline_schedule_id } = PlayPipelineScheduleSchema.parse(params.arguments);
8994
+ const result = await playPipelineSchedule(project_id, pipeline_schedule_id);
8995
+ return {
8996
+ content: [
8997
+ {
8998
+ type: "text",
8999
+ text: `Ran pipeline schedule #${pipeline_schedule_id} immediately; the next scheduled run is unchanged. GitLab responded: ${result.message ?? "accepted"}`,
9000
+ },
9001
+ ],
9002
+ };
9003
+ }
9004
+ case "take_ownership_pipeline_schedule": {
9005
+ const { project_id, pipeline_schedule_id } = TakeOwnershipPipelineScheduleSchema.parse(params.arguments);
9006
+ const schedule = await takeOwnershipPipelineSchedule(project_id, pipeline_schedule_id);
9007
+ return {
9008
+ content: [
9009
+ {
9010
+ type: "text",
9011
+ text: `Took ownership of pipeline schedule #${schedule.id} "${schedule.description}". Owner: ${schedule.owner?.username ?? "unknown"}`,
9012
+ },
9013
+ ],
9014
+ };
9015
+ }
9016
+ case "get_pipeline_schedule_variable": {
9017
+ const { project_id, pipeline_schedule_id, key } = GetPipelineScheduleVariableSchema.parse(params.arguments);
9018
+ const variable = await getPipelineScheduleVariable(project_id, pipeline_schedule_id, key);
9019
+ return {
9020
+ content: [{ type: "text", text: JSON.stringify(variable, null, 2) }],
9021
+ };
9022
+ }
9023
+ case "create_pipeline_schedule_variable": {
9024
+ const args = CreatePipelineScheduleVariableSchema.parse(params.arguments);
9025
+ const { project_id, pipeline_schedule_id, ...options } = args;
9026
+ const variable = await createPipelineScheduleVariable(project_id, pipeline_schedule_id, options);
9027
+ return {
9028
+ content: [
9029
+ {
9030
+ type: "text",
9031
+ text: `Created variable '${variable.key}' on pipeline schedule #${pipeline_schedule_id}`,
9032
+ },
9033
+ ],
9034
+ };
9035
+ }
9036
+ case "update_pipeline_schedule_variable": {
9037
+ const args = UpdatePipelineScheduleVariableSchema.parse(params.arguments);
9038
+ const { project_id, pipeline_schedule_id, key, ...options } = args;
9039
+ const variable = await updatePipelineScheduleVariable(project_id, pipeline_schedule_id, key, options);
9040
+ return {
9041
+ content: [
9042
+ {
9043
+ type: "text",
9044
+ text: `Updated variable '${variable.key}' on pipeline schedule #${pipeline_schedule_id}`,
9045
+ },
9046
+ ],
9047
+ };
9048
+ }
9049
+ case "delete_pipeline_schedule_variable": {
9050
+ const { project_id, pipeline_schedule_id, key } = DeletePipelineScheduleVariableSchema.parse(params.arguments);
9051
+ await deletePipelineScheduleVariable(project_id, pipeline_schedule_id, key);
9052
+ return {
9053
+ content: [
9054
+ {
9055
+ type: "text",
9056
+ text: `Deleted variable '${key}' from pipeline schedule #${pipeline_schedule_id}`,
9057
+ },
9058
+ ],
9059
+ };
9060
+ }
8605
9061
  case "play_pipeline_job": {
8606
9062
  const { project_id, job_id, job_variables_attributes } = PlayPipelineJobSchema.parse(params.arguments);
8607
9063
  const job = await playPipelineJob(project_id, job_id, job_variables_attributes);
@@ -8691,6 +9147,14 @@ async function handleToolCall(params) {
8691
9147
  content: [{ type: "text", text: JSON.stringify(mergeRequests) }],
8692
9148
  };
8693
9149
  }
9150
+ case "list_group_merge_requests": {
9151
+ const { group_id, ...options } = ListGroupMergeRequestsSchema.parse(params.arguments);
9152
+ const cleanedOptions = cleanMutuallyExclusiveIdUsernameOptions(options, LIST_MERGE_REQUESTS_ID_USERNAME_PAIRS);
9153
+ const mergeRequests = await listGroupMergeRequests(group_id, cleanedOptions);
9154
+ return {
9155
+ content: [{ type: "text", text: JSON.stringify(mergeRequests) }],
9156
+ };
9157
+ }
8694
9158
  case "list_milestones": {
8695
9159
  const { project_id, ...options } = ListProjectMilestonesSchema.parse(params.arguments);
8696
9160
  const milestones = await listProjectMilestones(project_id, options);