@zereight/mcp-gitlab 1.0.76 → 2.0.0-beta.0

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.
@@ -0,0 +1,1678 @@
1
+ import { z } from "zod";
2
+ import { flexibleBoolean, flexibleBooleanNullable } from "./customSchemas.js";
3
+ // Base schemas for common types
4
+ export const GitLabAuthorSchema = z.object({
5
+ name: z.string(),
6
+ email: z.string(),
7
+ date: z.string(),
8
+ });
9
+ // Pipeline related schemas
10
+ export const GitLabPipelineSchema = z.object({
11
+ id: z.coerce.string(),
12
+ project_id: z.coerce.string(),
13
+ sha: z.string(),
14
+ ref: z.string(),
15
+ status: z.string(),
16
+ source: z.string().optional(),
17
+ created_at: z.string(),
18
+ updated_at: z.string(),
19
+ web_url: z.string(),
20
+ duration: z.number().nullable().optional(),
21
+ started_at: z.string().nullable().optional(),
22
+ finished_at: z.string().nullable().optional(),
23
+ coverage: z.number().nullable().optional(),
24
+ user: z
25
+ .object({
26
+ id: z.coerce.string(),
27
+ name: z.string(),
28
+ username: z.string(),
29
+ avatar_url: z.string().nullable().optional(),
30
+ })
31
+ .optional(),
32
+ detailed_status: z
33
+ .object({
34
+ icon: z.string().optional(),
35
+ text: z.string().optional(),
36
+ label: z.string().optional(),
37
+ group: z.string().optional(),
38
+ tooltip: z.string().optional(),
39
+ has_details: flexibleBoolean.optional(),
40
+ details_path: z.string().optional(),
41
+ illustration: z
42
+ .object({
43
+ image: z.string().optional(),
44
+ size: z.string().optional(),
45
+ title: z.string().optional(),
46
+ })
47
+ .nullable()
48
+ .optional(),
49
+ favicon: z.string().optional(),
50
+ })
51
+ .optional(),
52
+ });
53
+ // Pipeline job related schemas
54
+ export const GitLabPipelineJobSchema = z.object({
55
+ id: z.coerce.string(),
56
+ status: z.string(),
57
+ stage: z.string(),
58
+ name: z.string(),
59
+ ref: z.string(),
60
+ tag: flexibleBoolean,
61
+ coverage: z.number().nullable().optional(),
62
+ created_at: z.string(),
63
+ started_at: z.string().nullable().optional(),
64
+ finished_at: z.string().nullable().optional(),
65
+ duration: z.number().nullable().optional(),
66
+ user: z
67
+ .object({
68
+ id: z.coerce.string(),
69
+ name: z.string(),
70
+ username: z.string(),
71
+ avatar_url: z.string().nullable().optional(),
72
+ })
73
+ .optional(),
74
+ commit: z
75
+ .object({
76
+ id: z.string(),
77
+ short_id: z.string(),
78
+ title: z.string(),
79
+ author_name: z.string(),
80
+ author_email: z.string(),
81
+ })
82
+ .optional(),
83
+ pipeline: z
84
+ .object({
85
+ id: z.coerce.string(),
86
+ project_id: z.coerce.string(),
87
+ status: z.string(),
88
+ ref: z.string(),
89
+ sha: z.string(),
90
+ })
91
+ .optional(),
92
+ web_url: z.string().optional(),
93
+ });
94
+ // Pipeline trigger job (bridge) schema
95
+ export const GitLabPipelineTriggerJobSchema = z.object({
96
+ id: z.coerce.string(),
97
+ status: z.string(),
98
+ stage: z.string(),
99
+ name: z.string(),
100
+ ref: z.string(),
101
+ tag: flexibleBoolean,
102
+ coverage: z.number().nullable().optional(),
103
+ created_at: z.string(),
104
+ started_at: z.string().nullable().optional(),
105
+ finished_at: z.string().nullable().optional(),
106
+ duration: z.number().nullable().optional(),
107
+ queued_duration: z.number().nullable().optional(),
108
+ user: z
109
+ .object({
110
+ id: z.coerce.string(),
111
+ name: z.string(),
112
+ username: z.string(),
113
+ avatar_url: z.string().nullable().optional(),
114
+ })
115
+ .optional(),
116
+ commit: z
117
+ .object({
118
+ id: z.string(),
119
+ short_id: z.string(),
120
+ title: z.string(),
121
+ author_name: z.string(),
122
+ author_email: z.string(),
123
+ })
124
+ .optional(),
125
+ pipeline: z
126
+ .object({
127
+ id: z.coerce.string(),
128
+ project_id: z.coerce.string(),
129
+ status: z.string(),
130
+ ref: z.string(),
131
+ sha: z.string(),
132
+ created_at: z.string().optional(),
133
+ updated_at: z.string().optional(),
134
+ web_url: z.string().optional(),
135
+ })
136
+ .optional(),
137
+ web_url: z.string().optional(),
138
+ allow_failure: flexibleBoolean.optional(),
139
+ archived: flexibleBoolean.optional(),
140
+ source: z.string().optional(),
141
+ erased_at: z.string().nullable().optional(),
142
+ project: z
143
+ .object({
144
+ ci_job_token_scope_enabled: flexibleBoolean.optional(),
145
+ })
146
+ .optional(),
147
+ downstream_pipeline: z
148
+ .object({
149
+ id: z.coerce.string(),
150
+ sha: z.string(),
151
+ ref: z.string(),
152
+ status: z.string(),
153
+ created_at: z.string(),
154
+ updated_at: z.string(),
155
+ web_url: z.string(),
156
+ })
157
+ .nullable()
158
+ .optional(),
159
+ });
160
+ // Shared base schema for various pagination options
161
+ // See https://docs.gitlab.com/api/rest/#pagination
162
+ export const PaginationOptionsSchema = z.object({
163
+ page: z.number().optional().describe("Page number for pagination (default: 1)"),
164
+ per_page: z.number().optional().describe("Number of items per page (max: 100, default: 20)"),
165
+ });
166
+ // Schema for listing pipelines
167
+ export const ListPipelinesSchema = z
168
+ .object({
169
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
170
+ scope: z
171
+ .enum(["running", "pending", "finished", "branches", "tags"])
172
+ .optional()
173
+ .describe("The scope of pipelines"),
174
+ status: z
175
+ .enum([
176
+ "created",
177
+ "waiting_for_resource",
178
+ "preparing",
179
+ "pending",
180
+ "running",
181
+ "success",
182
+ "failed",
183
+ "canceled",
184
+ "skipped",
185
+ "manual",
186
+ "scheduled",
187
+ ])
188
+ .optional()
189
+ .describe("The status of pipelines"),
190
+ ref: z.string().optional().describe("The ref of pipelines"),
191
+ sha: z.string().optional().describe("The SHA of pipelines"),
192
+ yaml_errors: flexibleBoolean
193
+ .optional()
194
+ .describe("Returns pipelines with invalid configurations"),
195
+ username: z.string().optional().describe("The username of the user who triggered pipelines"),
196
+ updated_after: z
197
+ .string()
198
+ .optional()
199
+ .describe("Return pipelines updated after the specified date"),
200
+ updated_before: z
201
+ .string()
202
+ .optional()
203
+ .describe("Return pipelines updated before the specified date"),
204
+ order_by: z
205
+ .enum(["id", "status", "ref", "updated_at", "user_id"])
206
+ .optional()
207
+ .describe("Order pipelines by"),
208
+ sort: z.enum(["asc", "desc"]).optional().describe("Sort pipelines"),
209
+ })
210
+ .merge(PaginationOptionsSchema);
211
+ // Schema for getting a specific pipeline
212
+ export const GetPipelineSchema = z.object({
213
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
214
+ pipeline_id: z.coerce.string().describe("The ID of the pipeline"),
215
+ });
216
+ // Schema for listing jobs in a pipeline
217
+ export const ListPipelineJobsSchema = z
218
+ .object({
219
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
220
+ pipeline_id: z.coerce.string().describe("The ID of the pipeline"),
221
+ scope: z
222
+ .enum(["created", "pending", "running", "failed", "success", "canceled", "skipped", "manual"])
223
+ .optional()
224
+ .describe("The scope of jobs to show"),
225
+ include_retried: flexibleBoolean.optional().describe("Whether to include retried jobs"),
226
+ })
227
+ .merge(PaginationOptionsSchema);
228
+ // Schema for listing trigger jobs (bridges) in a pipeline
229
+ export const ListPipelineTriggerJobsSchema = z
230
+ .object({
231
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
232
+ pipeline_id: z.coerce.string().describe("The ID of the pipeline"),
233
+ scope: z
234
+ // https://docs.gitlab.com/api/jobs/#job-status-values
235
+ .enum([
236
+ "canceled",
237
+ "canceling",
238
+ "created",
239
+ "failed",
240
+ "manual",
241
+ "pending",
242
+ "preparing",
243
+ "running",
244
+ "scheduled",
245
+ "skipped",
246
+ "success",
247
+ "waiting_for_resource",
248
+ ])
249
+ .optional()
250
+ .describe("The scope of trigger jobs to show"),
251
+ })
252
+ .merge(PaginationOptionsSchema);
253
+ // Schema for creating a new pipeline
254
+ export const CreatePipelineSchema = z.object({
255
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
256
+ ref: z.string().describe("The branch or tag to run the pipeline on"),
257
+ variables: z
258
+ .array(z.object({
259
+ key: z.string().describe("The key of the variable"),
260
+ value: z.string().describe("The value of the variable"),
261
+ }))
262
+ .optional()
263
+ .describe("An array of variables to use for the pipeline"),
264
+ });
265
+ // Schema for retrying a pipeline
266
+ export const RetryPipelineSchema = z.object({
267
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
268
+ pipeline_id: z.coerce.string().describe("The ID of the pipeline to retry"),
269
+ });
270
+ // Schema for canceling a pipeline
271
+ export const CancelPipelineSchema = RetryPipelineSchema;
272
+ // Schema for the input parameters for pipeline job operations
273
+ export const GetPipelineJobOutputSchema = z.object({
274
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
275
+ job_id: z.coerce.string().describe("The ID of the job"),
276
+ limit: z
277
+ .number()
278
+ .optional()
279
+ .describe("Maximum number of lines to return from the end of the log (default: 1000)"),
280
+ offset: z
281
+ .number()
282
+ .optional()
283
+ .describe("Number of lines to skip from the end of the log (default: 0)"),
284
+ });
285
+ // User schemas
286
+ export const GitLabUserSchema = z.object({
287
+ username: z.string().optional(), // Changed from login to match GitLab API
288
+ id: z.coerce.string(),
289
+ name: z.string().optional(),
290
+ avatar_url: z.string().nullable().optional(),
291
+ web_url: z.string().optional(), // Changed from html_url to match GitLab API
292
+ });
293
+ export const GetUsersSchema = z.object({
294
+ usernames: z.array(z.string()).describe("Array of usernames to search for"),
295
+ });
296
+ export const GitLabUsersResponseSchema = z.record(z.string(), z
297
+ .object({
298
+ id: z.coerce.string(),
299
+ username: z.string(),
300
+ name: z.string(),
301
+ avatar_url: z.string().nullable(),
302
+ web_url: z.string(),
303
+ })
304
+ .nullable());
305
+ // Namespace related schemas
306
+ // Base schema for project-related operations
307
+ const ProjectParamsSchema = z.object({
308
+ project_id: z.coerce.string().describe("Project ID or complete URL-encoded path to project"), // Changed from owner/repo to match GitLab API
309
+ });
310
+ export const GitLabNamespaceSchema = z.object({
311
+ id: z.coerce.string(),
312
+ name: z.string(),
313
+ path: z.string(),
314
+ kind: z.enum(["user", "group"]),
315
+ full_path: z.string(),
316
+ parent_id: z.coerce.string().nullable(),
317
+ avatar_url: z.string().nullable(),
318
+ web_url: z.string(),
319
+ members_count_with_descendants: z.number().optional(),
320
+ billable_members_count: z.number().optional(),
321
+ max_seats_used: z.number().optional(),
322
+ seats_in_use: z.number().optional(),
323
+ plan: z.string().optional(),
324
+ end_date: z.string().nullable().optional(),
325
+ trial_ends_on: z.string().nullable().optional(),
326
+ trial: flexibleBoolean.optional(),
327
+ root_repository_size: z.number().optional(),
328
+ projects_count: z.number().optional(),
329
+ });
330
+ export const GitLabNamespaceExistsResponseSchema = z.object({
331
+ exists: flexibleBoolean,
332
+ suggests: z.array(z.string()).optional(),
333
+ });
334
+ // Repository related schemas
335
+ export const GitLabOwnerSchema = z.object({
336
+ username: z.string(), // Changed from login to match GitLab API
337
+ id: z.coerce.string(),
338
+ avatar_url: z.string().nullable(),
339
+ web_url: z.string(), // Changed from html_url to match GitLab API
340
+ name: z.string(), // Added as GitLab includes full name
341
+ state: z.string(), // Added as GitLab includes user state
342
+ });
343
+ export const GitLabRepositorySchema = z.object({
344
+ id: z.coerce.string(),
345
+ name: z.string(),
346
+ path_with_namespace: z.string(),
347
+ visibility: z.string().optional(),
348
+ owner: GitLabOwnerSchema.optional(),
349
+ web_url: z.string().optional(),
350
+ description: z.string().nullable(),
351
+ fork: flexibleBoolean.optional(),
352
+ ssh_url_to_repo: z.string().optional(),
353
+ http_url_to_repo: z.string().optional(),
354
+ created_at: z.string().optional(),
355
+ last_activity_at: z.string().optional(),
356
+ default_branch: z.string().optional(),
357
+ namespace: z
358
+ .object({
359
+ id: z.coerce.string(),
360
+ name: z.string(),
361
+ path: z.string(),
362
+ kind: z.string(),
363
+ full_path: z.string(),
364
+ avatar_url: z.string().nullable().optional(),
365
+ web_url: z.string().optional(),
366
+ })
367
+ .optional(),
368
+ readme_url: z.string().optional().nullable(),
369
+ topics: z.array(z.string()).optional(),
370
+ tag_list: z.array(z.string()).optional(), // deprecated but still present
371
+ open_issues_count: z.number().optional(),
372
+ archived: flexibleBoolean.optional(),
373
+ forks_count: z.number().optional(),
374
+ star_count: z.number().optional(),
375
+ permissions: z
376
+ .object({
377
+ project_access: z
378
+ .object({
379
+ access_level: z.number(),
380
+ notification_level: z.number().nullable().optional(),
381
+ })
382
+ .optional()
383
+ .nullable(),
384
+ group_access: z
385
+ .object({
386
+ access_level: z.number(),
387
+ notification_level: z.number().nullable().optional(),
388
+ })
389
+ .optional()
390
+ .nullable(),
391
+ })
392
+ .optional(),
393
+ container_registry_enabled: flexibleBoolean.optional(),
394
+ container_registry_access_level: z.string().optional(),
395
+ issues_enabled: flexibleBoolean.optional(),
396
+ merge_requests_enabled: flexibleBoolean.optional(),
397
+ merge_requests_template: z.string().nullable().optional(),
398
+ wiki_enabled: flexibleBoolean.optional(),
399
+ jobs_enabled: flexibleBoolean.optional(),
400
+ snippets_enabled: flexibleBoolean.optional(),
401
+ can_create_merge_request_in: flexibleBoolean.optional(),
402
+ resolve_outdated_diff_discussions: flexibleBooleanNullable.optional(),
403
+ shared_runners_enabled: flexibleBoolean.optional(),
404
+ shared_with_groups: z
405
+ .array(z.object({
406
+ group_id: z.coerce.string(),
407
+ group_name: z.string(),
408
+ group_full_path: z.string(),
409
+ group_access_level: z.number(),
410
+ }))
411
+ .optional(),
412
+ });
413
+ // Project schema (extended from repository schema)
414
+ export const GitLabProjectSchema = GitLabRepositorySchema;
415
+ // File content schemas
416
+ export const GitLabFileContentSchema = z.object({
417
+ file_name: z.string(), // Changed from name to match GitLab API
418
+ file_path: z.string(), // Changed from path to match GitLab API
419
+ size: z.number(),
420
+ encoding: z.string(),
421
+ content: z.string(),
422
+ content_sha256: z.string(), // Changed from sha to match GitLab API
423
+ ref: z.string(), // Added as GitLab requires branch reference
424
+ blob_id: z.string(), // Added to match GitLab API
425
+ commit_id: z.string(), // ID of the current file version
426
+ last_commit_id: z.string(), // Added to match GitLab API
427
+ execute_filemode: flexibleBoolean.optional(), // Added to match GitLab API
428
+ });
429
+ export const GitLabDirectoryContentSchema = z.object({
430
+ name: z.string(),
431
+ path: z.string(),
432
+ type: z.string(),
433
+ mode: z.string(),
434
+ id: z.string(), // Changed from sha to match GitLab API
435
+ web_url: z.string(), // Changed from html_url to match GitLab API
436
+ });
437
+ export const GitLabContentSchema = z.union([
438
+ GitLabFileContentSchema,
439
+ z.array(GitLabDirectoryContentSchema),
440
+ ]);
441
+ // Operation schemas
442
+ export const FileOperationSchema = z.object({
443
+ path: z.string(),
444
+ content: z.string(),
445
+ });
446
+ // Tree and commit schemas
447
+ export const GitLabTreeItemSchema = z.object({
448
+ id: z.string(),
449
+ name: z.string(),
450
+ type: z.enum(["tree", "blob"]),
451
+ path: z.string(),
452
+ mode: z.string(),
453
+ });
454
+ export const GetRepositoryTreeSchema = z.object({
455
+ project_id: z.coerce.string().describe("The ID or URL-encoded path of the project"),
456
+ path: z.string().optional().describe("The path inside the repository"),
457
+ ref: z
458
+ .string()
459
+ .optional()
460
+ .describe("The name of a repository branch or tag. Defaults to the default branch."),
461
+ recursive: flexibleBoolean.optional().describe("Boolean value to get a recursive tree"),
462
+ per_page: z.number().optional().describe("Number of results to show per page"),
463
+ page_token: z.string().optional().describe("The tree record ID for pagination"),
464
+ pagination: z.string().optional().describe("Pagination method (keyset)"),
465
+ });
466
+ export const GitLabTreeSchema = z.object({
467
+ id: z.string(), // Changed from sha to match GitLab API
468
+ tree: z.array(GitLabTreeItemSchema),
469
+ });
470
+ export const GitLabCommitSchema = z.object({
471
+ id: z.string(), // Changed from sha to match GitLab API
472
+ short_id: z.string(), // Added to match GitLab API
473
+ title: z.string(), // Changed from message to match GitLab API
474
+ author_name: z.string(),
475
+ author_email: z.string(),
476
+ authored_date: z.string(),
477
+ committer_name: z.string(),
478
+ committer_email: z.string(),
479
+ committed_date: z.string(),
480
+ created_at: z.string().optional(), // Add created_at field
481
+ message: z.string().optional(), // Add full message field
482
+ web_url: z.string(), // Changed from html_url to match GitLab API
483
+ parent_ids: z.array(z.string()), // Changed from parents to match GitLab API
484
+ stats: z
485
+ .object({
486
+ additions: z.number().optional().nullable(),
487
+ deletions: z.number().optional().nullable(),
488
+ total: z.number().optional().nullable(),
489
+ })
490
+ .optional(), // Only present when with_stats=true
491
+ trailers: z.record(z.string()).optional().default({}), // Git trailers, may be empty object
492
+ extended_trailers: z.record(z.array(z.string())).optional().default({}), // Extended trailers, may be empty object
493
+ });
494
+ // Reference schema
495
+ export const GitLabReferenceSchema = z.object({
496
+ name: z.string(), // Changed from ref to match GitLab API
497
+ commit: z.object({
498
+ id: z.string(), // Changed from sha to match GitLab API
499
+ web_url: z.string(), // Changed from url to match GitLab API
500
+ }),
501
+ });
502
+ // Milestones rest api output schemas
503
+ export const GitLabMilestonesSchema = z.object({
504
+ id: z.coerce.string(),
505
+ iid: z.coerce.string(),
506
+ project_id: z.coerce.string(),
507
+ title: z.string(),
508
+ description: z.string().nullable(),
509
+ due_date: z.string().nullable(),
510
+ start_date: z.string().nullable(),
511
+ state: z.string(),
512
+ updated_at: z.string(),
513
+ created_at: z.string(),
514
+ expired: flexibleBoolean,
515
+ web_url: z.string().optional(),
516
+ });
517
+ // Input schemas for operations
518
+ export const CreateRepositoryOptionsSchema = z.object({
519
+ name: z.string(),
520
+ description: z.string().optional(),
521
+ visibility: z.enum(["private", "internal", "public"]).optional(), // Changed from private to match GitLab API
522
+ initialize_with_readme: flexibleBoolean.optional(), // Changed from auto_init to match GitLab API
523
+ });
524
+ export const CreateIssueOptionsSchema = z.object({
525
+ title: z.string(),
526
+ description: z.string().optional(), // Changed from body to match GitLab API
527
+ assignee_ids: z.array(z.number()).optional(), // Changed from assignees to match GitLab API
528
+ milestone_id: z.coerce.string().optional(), // Changed from milestone to match GitLab API
529
+ labels: z.array(z.string()).optional(),
530
+ });
531
+ export const GitLabDiffSchema = z.object({
532
+ old_path: z.string(),
533
+ new_path: z.string(),
534
+ a_mode: z.string(),
535
+ b_mode: z.string(),
536
+ diff: z.string(),
537
+ new_file: flexibleBoolean,
538
+ renamed_file: flexibleBoolean,
539
+ deleted_file: flexibleBoolean,
540
+ });
541
+ // Response schemas for operations
542
+ export const GitLabCreateUpdateFileResponseSchema = z.object({
543
+ file_path: z.string(),
544
+ branch: z.string(),
545
+ commit_id: z.string().optional(), // Optional since it's not always returned by the API
546
+ content: GitLabFileContentSchema.optional(),
547
+ });
548
+ export const GitLabSearchResponseSchema = z.object({
549
+ count: z.number().optional(),
550
+ total_pages: z.number().optional(),
551
+ current_page: z.number().optional(),
552
+ items: z.array(GitLabRepositorySchema),
553
+ });
554
+ // create branch schemas
555
+ export const CreateBranchOptionsSchema = z.object({
556
+ name: z.string(), // Changed from ref to match GitLab API
557
+ ref: z.string(), // The source branch/commit for the new branch
558
+ });
559
+ export const GitLabCompareResultSchema = z.object({
560
+ commit: z
561
+ .object({
562
+ id: z.string().optional(),
563
+ short_id: z.string().optional(),
564
+ title: z.string().optional(),
565
+ author_name: z.string().optional(),
566
+ author_email: z.string().optional(),
567
+ created_at: z.string().optional(),
568
+ })
569
+ .optional(),
570
+ commits: z.array(GitLabCommitSchema),
571
+ diffs: z.array(GitLabDiffSchema),
572
+ compare_timeout: flexibleBoolean.optional(),
573
+ compare_same_ref: flexibleBoolean.optional(),
574
+ });
575
+ // Issue related schemas
576
+ export const GitLabLabelSchema = z.object({
577
+ id: z.coerce.string(),
578
+ name: z.string(),
579
+ color: z.string(),
580
+ text_color: z.string(),
581
+ description: z.string().nullable(),
582
+ description_html: z.string().nullable(),
583
+ open_issues_count: z.number().optional(),
584
+ closed_issues_count: z.number().optional(),
585
+ open_merge_requests_count: z.number().optional(),
586
+ subscribed: flexibleBoolean.optional(),
587
+ priority: z.number().nullable().optional(),
588
+ is_project_label: flexibleBoolean.optional(),
589
+ });
590
+ export const GitLabMilestoneSchema = z.object({
591
+ id: z.coerce.string(),
592
+ iid: z.coerce.string(), // Added to match GitLab API
593
+ title: z.string(),
594
+ description: z.string().nullable().default(""),
595
+ state: z.string(),
596
+ web_url: z.string(), // Changed from html_url to match GitLab API
597
+ });
598
+ export const GitLabIssueSchema = z.object({
599
+ id: z.coerce.string(),
600
+ iid: z.coerce.string(), // Added to match GitLab API
601
+ project_id: z.coerce.string(), // Added to match GitLab API
602
+ title: z.string(),
603
+ description: z.string().nullable().default(""), // Changed from body to match GitLab API
604
+ state: z.string(),
605
+ author: GitLabUserSchema,
606
+ assignees: z.array(GitLabUserSchema),
607
+ labels: z.array(GitLabLabelSchema).or(z.array(z.string())), // Support both label objects and strings
608
+ milestone: GitLabMilestoneSchema.nullable(),
609
+ created_at: z.string(),
610
+ updated_at: z.string(),
611
+ closed_at: z.string().nullable(),
612
+ web_url: z.string(), // Changed from html_url to match GitLab API
613
+ references: z
614
+ .object({
615
+ short: z.string(),
616
+ relative: z.string(),
617
+ full: z.string(),
618
+ })
619
+ .optional(),
620
+ time_stats: z
621
+ .object({
622
+ time_estimate: z.number(),
623
+ total_time_spent: z.number(),
624
+ human_time_estimate: z.string().nullable(),
625
+ human_total_time_spent: z.string().nullable(),
626
+ })
627
+ .optional(),
628
+ confidential: flexibleBoolean.optional(),
629
+ due_date: z.string().nullable().optional(),
630
+ discussion_locked: flexibleBooleanNullable.optional(),
631
+ weight: z.number().nullable().optional(),
632
+ issue_type: z.string().describe("the type of issue.").nullish(),
633
+ });
634
+ // NEW SCHEMA: For issue with link details (used in listing issue links)
635
+ export const GitLabIssueWithLinkDetailsSchema = GitLabIssueSchema.extend({
636
+ issue_link_id: z.coerce.string(),
637
+ link_type: z.enum(["relates_to", "blocks", "is_blocked_by"]),
638
+ link_created_at: z.string(),
639
+ link_updated_at: z.string(),
640
+ });
641
+ // Fork related schemas
642
+ export const GitLabForkParentSchema = z.object({
643
+ name: z.string(),
644
+ path_with_namespace: z.string(), // Changed from full_name to match GitLab API
645
+ owner: z
646
+ .object({
647
+ username: z.string(), // Changed from login to match GitLab API
648
+ id: z.coerce.string(),
649
+ avatar_url: z.string().nullable(),
650
+ })
651
+ .optional(), // Made optional to handle cases where GitLab API doesn't include it
652
+ web_url: z.string(), // Changed from html_url to match GitLab API
653
+ });
654
+ export const GitLabForkSchema = GitLabRepositorySchema.extend({
655
+ forked_from_project: GitLabForkParentSchema.optional(), // Made optional to handle cases where GitLab API doesn't include it
656
+ });
657
+ // Merge Request related schemas (equivalent to Pull Request)
658
+ export const GitLabMergeRequestDiffRefSchema = z.object({
659
+ base_sha: z.string(),
660
+ head_sha: z.string(),
661
+ start_sha: z.string(),
662
+ });
663
+ export const GitLabMergeRequestSchema = z.object({
664
+ id: z.coerce.string(),
665
+ iid: z.coerce.string(),
666
+ project_id: z.coerce.string(),
667
+ title: z.string(),
668
+ description: z.string().nullable(),
669
+ state: z.string(),
670
+ merged: flexibleBoolean.optional(),
671
+ draft: flexibleBoolean.optional(),
672
+ author: GitLabUserSchema,
673
+ assignees: z.array(GitLabUserSchema).optional(),
674
+ reviewers: z.array(GitLabUserSchema).optional(),
675
+ source_branch: z.string(),
676
+ target_branch: z.string(),
677
+ diff_refs: GitLabMergeRequestDiffRefSchema.nullable().optional(),
678
+ web_url: z.string(),
679
+ created_at: z.string(),
680
+ updated_at: z.string(),
681
+ merged_at: z.string().nullable(),
682
+ closed_at: z.string().nullable(),
683
+ merge_commit_sha: z.string().nullable(),
684
+ detailed_merge_status: z.string().optional(),
685
+ merge_status: z.string().optional(),
686
+ merge_error: z.string().nullable().optional(),
687
+ work_in_progress: flexibleBoolean.optional(),
688
+ blocking_discussions_resolved: flexibleBoolean.optional(),
689
+ should_remove_source_branch: flexibleBooleanNullable.optional(),
690
+ force_remove_source_branch: flexibleBooleanNullable.optional(),
691
+ allow_collaboration: flexibleBoolean.optional(),
692
+ allow_maintainer_to_push: flexibleBoolean.optional(),
693
+ changes_count: z.string().nullable().optional(),
694
+ merge_when_pipeline_succeeds: flexibleBoolean.optional(),
695
+ squash: flexibleBoolean.optional(),
696
+ labels: z.array(z.string()).optional(),
697
+ });
698
+ export const LineRangeSchema = z
699
+ .object({
700
+ start: z
701
+ .object({
702
+ line_code: z
703
+ .string()
704
+ .nullable()
705
+ .optional()
706
+ .describe("CRITICAL: Line identifier in format '{file_path_sha1_hash}_{old_line_number}_{new_line_number}'. USUALLY REQUIRED for GitLab diff comments despite being optional in schema. Example: 'a1b2c3d4e5f6_10_15'. Get this from GitLab diff API response, never fabricate."),
707
+ type: z
708
+ .enum(["new", "old", "expanded"])
709
+ .nullable()
710
+ .optional()
711
+ .describe("Line type: 'old' = deleted/original line, 'new' = added/modified line, null = unchanged context. MUST match the line_code format and old_line/new_line values."),
712
+ old_line: z
713
+ .number()
714
+ .nullable()
715
+ .optional()
716
+ .describe("Line number in original file (before changes). REQUIRED when type='old', NULL when type='new' (for purely added lines), can be present for context lines."),
717
+ new_line: z
718
+ .number()
719
+ .nullable()
720
+ .optional()
721
+ .describe("Line number in modified file (after changes). REQUIRED when type='new', NULL when type='old' (for purely deleted lines), can be present for context lines."),
722
+ })
723
+ .describe("Start line position for multiline comment range. MUST specify either old_line OR new_line (or both for context), never neither."),
724
+ end: z
725
+ .object({
726
+ line_code: z
727
+ .string()
728
+ .nullable()
729
+ .optional()
730
+ .describe("CRITICAL: Line identifier in format '{file_path_sha1_hash}_{old_line_number}_{new_line_number}'. USUALLY REQUIRED for GitLab diff comments despite being optional in schema. Example: 'a1b2c3d4e5f6_12_17'. Must be from same file as start.line_code."),
731
+ type: z
732
+ .enum(["new", "old", "expanded"])
733
+ .nullable()
734
+ .optional()
735
+ .describe("Line type: 'old' = deleted/original line, 'new' = added/modified line, null = unchanged context. SHOULD MATCH start.type for consistent ranges (don't mix old/new types)."),
736
+ old_line: z
737
+ .number()
738
+ .nullable()
739
+ .optional()
740
+ .describe("Line number in original file (before changes). REQUIRED when type='old', NULL when type='new' (for purely added lines), can be present for context lines. MUST be >= start.old_line if both specified."),
741
+ new_line: z
742
+ .number()
743
+ .nullable()
744
+ .optional()
745
+ .describe("Line number in modified file (after changes). REQUIRED when type='new', NULL when type='old' (for purely deleted lines), can be present for context lines. MUST be >= start.new_line if both specified."),
746
+ })
747
+ .describe("End line position for multiline comment range. MUST specify either old_line OR new_line (or both for context), never neither. Range must be valid (end >= start)."),
748
+ })
749
+ .describe("Line range for multiline comments on GitLab merge request diffs. VALIDATION RULES: 1) line_code is critical for GitLab API success, 2) start/end must have consistent types, 3) line numbers must form valid range, 4) get line_code from GitLab diff API, never generate manually.");
750
+ // Discussion related schemas
751
+ export const GitLabDiscussionNoteSchema = z.object({
752
+ id: z.coerce.string(),
753
+ type: z.enum(["DiscussionNote", "DiffNote", "Note"]).nullable().optional(), // Allow null type for regular notes
754
+ body: z.string().optional(),
755
+ attachment: z.any().nullable().optional(), // Can be string or object, handle appropriately
756
+ author: GitLabUserSchema.optional(),
757
+ created_at: z.string().optional(),
758
+ updated_at: z.string().optional(),
759
+ system: flexibleBoolean.optional(),
760
+ noteable_id: z.coerce.string().optional(),
761
+ noteable_type: z.enum(["Issue", "MergeRequest", "Snippet", "Commit", "Epic"]).optional(),
762
+ project_id: z.coerce.string().optional(),
763
+ noteable_iid: z.coerce.string().nullable().optional(),
764
+ resolvable: flexibleBoolean.optional(),
765
+ resolved: flexibleBoolean.optional(),
766
+ resolved_by: GitLabUserSchema.nullable().optional(),
767
+ resolved_at: z.string().nullable().optional(),
768
+ position: z
769
+ .object({
770
+ // Only present for DiffNote
771
+ base_sha: z.string().optional(),
772
+ start_sha: z.string().optional(),
773
+ head_sha: z.string().optional(),
774
+ old_path: z.string().nullable().optional().describe("File path before change"),
775
+ new_path: z.string().nullable().optional().describe("File path after change"),
776
+ position_type: z.enum(["text", "image", "file"]).optional(),
777
+ new_line: z
778
+ .number()
779
+ .nullable()
780
+ .optional()
781
+ .describe("Line number in the modified file (after changes). Used for added lines and context lines. Null for deleted lines."),
782
+ old_line: z
783
+ .number()
784
+ .nullable()
785
+ .optional()
786
+ .describe("Line number in the original file (before changes). Used for deleted lines and context lines. Null for newly added lines."),
787
+ line_range: LineRangeSchema.nullable().optional(), // For multi-line diff notes
788
+ width: z.number().optional(), // For image diff notes
789
+ height: z.number().optional(), // For image diff notes
790
+ x: z.number().optional(), // For image diff notes
791
+ y: z.number().optional(), // For image diff notes
792
+ })
793
+ .passthrough() // Allow additional fields
794
+ .optional(),
795
+ }).passthrough(); // Allow additional fields that GitLab might return
796
+ // Reusable pagination schema for GitLab API responses.
797
+ // See https://docs.gitlab.com/api/rest/#pagination
798
+ export const GitLabPaginationSchema = z.object({
799
+ x_next_page: z.number().nullable().optional(),
800
+ x_page: z.number().optional(),
801
+ x_per_page: z.number().optional(),
802
+ x_prev_page: z.number().nullable().optional(),
803
+ x_total: z.number().nullable().optional(),
804
+ x_total_pages: z.number().nullable().optional(),
805
+ });
806
+ // Base paginated response schema that can be extended.
807
+ // See https://docs.gitlab.com/api/rest/#pagination
808
+ export const PaginatedResponseSchema = z.object({
809
+ pagination: GitLabPaginationSchema.optional(),
810
+ });
811
+ export const GitLabDiscussionSchema = z.object({
812
+ id: z.coerce.string(),
813
+ individual_note: flexibleBoolean,
814
+ notes: z.array(GitLabDiscussionNoteSchema),
815
+ });
816
+ // Create a schema for paginated discussions response
817
+ export const PaginatedDiscussionsResponseSchema = z.object({
818
+ items: z.array(GitLabDiscussionSchema),
819
+ pagination: GitLabPaginationSchema,
820
+ });
821
+ export const ListIssueDiscussionsSchema = z
822
+ .object({
823
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
824
+ issue_iid: z.coerce.string().describe("The internal ID of the project issue"),
825
+ })
826
+ .merge(PaginationOptionsSchema);
827
+ // Input schema for listing merge request discussions
828
+ export const ListMergeRequestDiscussionsSchema = ProjectParamsSchema.extend({
829
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
830
+ }).merge(PaginationOptionsSchema);
831
+ // Input schema for updating a merge request discussion note
832
+ export const UpdateMergeRequestNoteSchema = ProjectParamsSchema.extend({
833
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
834
+ discussion_id: z.coerce.string().describe("The ID of a thread"),
835
+ note_id: z.coerce.string().describe("The ID of a thread note"),
836
+ body: z.string().optional().describe("The content of the note or reply"),
837
+ resolved: flexibleBoolean.optional().describe("Resolve or unresolve the note"),
838
+ })
839
+ .refine(data => data.body !== undefined || data.resolved !== undefined, {
840
+ message: "At least one of 'body' or 'resolved' must be provided",
841
+ })
842
+ .refine(data => !(data.body !== undefined && data.resolved !== undefined), {
843
+ message: "Only one of 'body' or 'resolved' can be provided, not both",
844
+ });
845
+ // Input schema for adding a note to an existing merge request discussion
846
+ export const CreateMergeRequestNoteSchema = ProjectParamsSchema.extend({
847
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
848
+ discussion_id: z.coerce.string().describe("The ID of a thread"),
849
+ body: z.string().describe("The content of the note or reply"),
850
+ created_at: z.string().optional().describe("Date the note was created at (ISO 8601 format)"),
851
+ });
852
+ // Input schema for updating an issue discussion note
853
+ export const UpdateIssueNoteSchema = ProjectParamsSchema.extend({
854
+ issue_iid: z.coerce.string().describe("The IID of an issue"),
855
+ discussion_id: z.coerce.string().describe("The ID of a thread"),
856
+ note_id: z.coerce.string().describe("The ID of a thread note"),
857
+ body: z.string().describe("The content of the note or reply"),
858
+ });
859
+ // Input schema for adding a note to an existing issue discussion
860
+ export const CreateIssueNoteSchema = ProjectParamsSchema.extend({
861
+ issue_iid: z.coerce.string().describe("The IID of an issue"),
862
+ discussion_id: z.coerce.string().describe("The ID of a thread"),
863
+ body: z.string().describe("The content of the note or reply"),
864
+ created_at: z.string().optional().describe("Date the note was created at (ISO 8601 format)"),
865
+ });
866
+ // API Operation Parameter Schemas
867
+ export const CreateOrUpdateFileSchema = ProjectParamsSchema.extend({
868
+ file_path: z.string().describe("Path where to create/update the file"),
869
+ content: z.string().describe("Content of the file"),
870
+ commit_message: z.string().describe("Commit message"),
871
+ branch: z.string().describe("Branch to create/update the file in"),
872
+ previous_path: z.string().optional().describe("Path of the file to move/rename"),
873
+ last_commit_id: z.string().optional().describe("Last known file commit ID"),
874
+ commit_id: z.string().optional().describe("Current file commit ID (for update operations)"),
875
+ });
876
+ export const SearchRepositoriesSchema = z
877
+ .object({
878
+ search: z.string().describe("Search query"), // Changed from query to match GitLab API
879
+ })
880
+ .merge(PaginationOptionsSchema);
881
+ export const CreateRepositorySchema = z.object({
882
+ name: z.string().describe("Repository name"),
883
+ description: z.string().optional().describe("Repository description"),
884
+ visibility: z
885
+ .enum(["private", "internal", "public"])
886
+ .optional()
887
+ .describe("Repository visibility level"),
888
+ initialize_with_readme: flexibleBoolean.optional().describe("Initialize with README.md"),
889
+ });
890
+ export const GetFileContentsSchema = ProjectParamsSchema.extend({
891
+ file_path: z.string().describe("Path to the file or directory"),
892
+ ref: z.string().optional().describe("Branch/tag/commit to get contents from"),
893
+ });
894
+ export const PushFilesSchema = ProjectParamsSchema.extend({
895
+ branch: z.string().describe("Branch to push to"),
896
+ files: z
897
+ .array(z.object({
898
+ file_path: z.string().describe("Path where to create the file"),
899
+ content: z.string().describe("Content of the file"),
900
+ }))
901
+ .describe("Array of files to push"),
902
+ commit_message: z.string().describe("Commit message"),
903
+ });
904
+ export const CreateIssueSchema = ProjectParamsSchema.extend({
905
+ title: z.string().describe("Issue title"),
906
+ description: z.string().optional().describe("Issue description"),
907
+ assignee_ids: z.array(z.number()).optional().describe("Array of user IDs to assign"),
908
+ labels: z.array(z.string()).optional().describe("Array of label names"),
909
+ milestone_id: z.coerce.string().optional().describe("Milestone ID to assign"),
910
+ issue_type: z.enum(["issue", "incident", "test_case", "task"]).describe("the type of issue. One of issue, incident, test_case or task.").nullish().default("issue"),
911
+ });
912
+ const MergeRequestOptionsSchema = {
913
+ title: z.string().describe("Merge request title"),
914
+ description: z.string().optional().describe("Merge request description"),
915
+ source_branch: z.string().describe("Branch containing changes"),
916
+ target_branch: z.string().describe("Branch to merge into"),
917
+ target_project_id: z.coerce.string().optional().describe("Numeric ID of the target project."),
918
+ assignee_ids: z.array(z.number()).optional().describe("The ID of the users to assign the MR to"),
919
+ reviewer_ids: z
920
+ .array(z.number())
921
+ .optional()
922
+ .describe("The ID of the users to assign as reviewers of the MR"),
923
+ labels: z.array(z.string()).optional().describe("Labels for the MR"),
924
+ draft: flexibleBoolean.optional().describe("Create as draft merge request"),
925
+ allow_collaboration: z.boolean().optional().describe("Allow commits from upstream members"),
926
+ remove_source_branch: flexibleBooleanNullable
927
+ .optional()
928
+ .describe("Flag indicating if a merge request should remove the source branch when merging."),
929
+ squash: flexibleBooleanNullable
930
+ .optional()
931
+ .describe("If true, squash all commits into a single commit on merge."),
932
+ };
933
+ export const CreateMergeRequestOptionsSchema = z.object(MergeRequestOptionsSchema);
934
+ export const CreateMergeRequestSchema = ProjectParamsSchema.extend(MergeRequestOptionsSchema);
935
+ export const ForkRepositorySchema = ProjectParamsSchema.extend({
936
+ namespace: z.string().optional().describe("Namespace to fork to (full path)"),
937
+ });
938
+ // Branch related schemas
939
+ export const CreateBranchSchema = ProjectParamsSchema.extend({
940
+ branch: z.string().describe("Name for the new branch"),
941
+ ref: z.string().optional().describe("Source branch/commit for new branch"),
942
+ });
943
+ export const GetBranchDiffsSchema = ProjectParamsSchema.extend({
944
+ from: z.string().describe("The base branch or commit SHA to compare from"),
945
+ to: z.string().describe("The target branch or commit SHA to compare to"),
946
+ straight: flexibleBoolean
947
+ .optional()
948
+ .describe("Comparison method: false for '...' (default), true for '--'"),
949
+ excluded_file_patterns: z
950
+ .array(z.string())
951
+ .optional()
952
+ .describe('Array of regex patterns to exclude files from the diff results. Each pattern is a JavaScript-compatible regular expression that matches file paths to ignore. Examples: ["^test/mocks/", "\\.spec\\.ts$", "package-lock\\.json"]'),
953
+ });
954
+ export const GetMergeRequestSchema = ProjectParamsSchema.extend({
955
+ merge_request_iid: z.coerce.string().optional().describe("The IID of a merge request"),
956
+ source_branch: z.string().optional().describe("Source branch name"),
957
+ });
958
+ export const UpdateMergeRequestSchema = GetMergeRequestSchema.extend({
959
+ title: z.string().optional().describe("The title of the merge request"),
960
+ description: z.string().optional().describe("The description of the merge request"),
961
+ target_branch: z.string().optional().describe("The target branch"),
962
+ assignee_ids: z.array(z.number()).optional().describe("The ID of the users to assign the MR to"),
963
+ reviewer_ids: z
964
+ .array(z.number())
965
+ .optional()
966
+ .describe("The ID of the users to assign as reviewers of the MR"),
967
+ labels: z.array(z.string()).optional().describe("Labels for the MR"),
968
+ state_event: z
969
+ .enum(["close", "reopen"])
970
+ .optional()
971
+ .describe("New state (close/reopen) for the MR"),
972
+ remove_source_branch: z
973
+ .boolean()
974
+ .optional()
975
+ .describe("Flag indicating if the source branch should be removed"),
976
+ squash: flexibleBoolean.optional().describe("Squash commits into a single commit when merging"),
977
+ draft: flexibleBoolean.optional().describe("Work in progress merge request"),
978
+ });
979
+ export const MergeMergeRequestSchema = ProjectParamsSchema.extend({
980
+ merge_request_iid: z.coerce.string().optional().describe("The IID of a merge request"),
981
+ auto_merge: flexibleBoolean.optional().default(false).describe("If true, the merge request merges when the pipeline succeeds."),
982
+ merge_commit_message: z.string().optional().describe("Custom merge commit message"),
983
+ merge_when_pipeline_succeeds: flexibleBoolean.optional().default(false).describe("If true, the merge request merges when the pipeline succeeds.in GitLab 17.11. Use"),
984
+ should_remove_source_branch: flexibleBoolean.optional().default(false).describe("Remove source branch after merge"),
985
+ squash_commit_message: z.string().optional().describe("Custom squash commit message"),
986
+ squash: flexibleBoolean.optional().default(false).describe("Squash commits into a single commit when merging"),
987
+ });
988
+ export const GetMergeRequestDiffsSchema = GetMergeRequestSchema.extend({
989
+ view: z.enum(["inline", "parallel"]).optional().describe("Diff view type"),
990
+ });
991
+ export const ListMergeRequestDiffsSchema = GetMergeRequestSchema.extend({
992
+ page: z.number().optional().describe("Page number for pagination (default: 1)"),
993
+ per_page: z.number().optional().describe("Number of items per page (max: 100, default: 20)"),
994
+ unidiff: flexibleBoolean
995
+ .optional()
996
+ .describe("Present diffs in the unified diff format. Default is false. Introduced in GitLab 16.5."),
997
+ });
998
+ export const CreateNoteSchema = z.object({
999
+ project_id: z.coerce.string().describe("Project ID or namespace/project_path"),
1000
+ noteable_type: z
1001
+ .enum(["issue", "merge_request"])
1002
+ .describe("Type of noteable (issue or merge_request)"),
1003
+ noteable_iid: z.coerce.string().describe("IID of the issue or merge request"),
1004
+ body: z.string().describe("Note content"),
1005
+ });
1006
+ // Issues API operation schemas
1007
+ export const ListIssuesSchema = z
1008
+ .object({
1009
+ project_id: z.coerce
1010
+ .string()
1011
+ .optional()
1012
+ .describe("Project ID or URL-encoded path (optional - if not provided, lists issues across all accessible projects)"),
1013
+ assignee_id: z.coerce
1014
+ .string()
1015
+ .optional()
1016
+ .describe("Return issues assigned to the given user ID. user id or none or any"),
1017
+ assignee_username: z
1018
+ .array(z.string())
1019
+ .optional()
1020
+ .describe("Return issues assigned to the given username"),
1021
+ author_id: z.coerce.string().optional().describe("Return issues created by the given user ID"),
1022
+ author_username: z.string().optional().describe("Return issues created by the given username"),
1023
+ confidential: flexibleBoolean.optional().describe("Filter confidential or public issues"),
1024
+ created_after: z.string().optional().describe("Return issues created after the given time"),
1025
+ created_before: z.string().optional().describe("Return issues created before the given time"),
1026
+ due_date: z.string().optional().describe("Return issues that have the due date"),
1027
+ labels: z.array(z.string()).optional().describe("Array of label names"),
1028
+ milestone: z.string().optional().describe("Milestone title"),
1029
+ issue_type: z
1030
+ .string()
1031
+ .optional()
1032
+ .nullable()
1033
+ .describe("Filter to a given type of issue. One of issue, incident, test_case or task"),
1034
+ iteration_id: z.coerce
1035
+ .string()
1036
+ .optional()
1037
+ .nullable()
1038
+ .describe("Return issues assigned to the given iteration ID. None returns issues that do not belong to an iteration. Any returns issues that belong to an iteration. "),
1039
+ scope: z
1040
+ .enum(["created_by_me", "assigned_to_me", "all"])
1041
+ .optional()
1042
+ .describe("Return issues from a specific scope"),
1043
+ search: z.string().optional().describe("Search for specific terms"),
1044
+ state: z
1045
+ .enum(["opened", "closed", "all"])
1046
+ .optional()
1047
+ .describe("Return issues with a specific state"),
1048
+ updated_after: z.string().optional().describe("Return issues updated after the given time"),
1049
+ updated_before: z.string().optional().describe("Return issues updated before the given time"),
1050
+ with_labels_details: flexibleBoolean.optional().describe("Return more details for each label"),
1051
+ })
1052
+ .merge(PaginationOptionsSchema);
1053
+ // Merge Requests API operation schemas
1054
+ export const ListMergeRequestsSchema = z
1055
+ .object({
1056
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1057
+ assignee_id: z.coerce
1058
+ .string()
1059
+ .optional()
1060
+ .describe("Return issues assigned to the given user ID. user id or none or any"),
1061
+ assignee_username: z
1062
+ .string()
1063
+ .optional()
1064
+ .describe("Returns merge requests assigned to the given username"),
1065
+ author_id: z.coerce
1066
+ .string()
1067
+ .optional()
1068
+ .describe("Returns merge requests created by the given user ID"),
1069
+ author_username: z
1070
+ .string()
1071
+ .optional()
1072
+ .describe("Returns merge requests created by the given username"),
1073
+ reviewer_id: z.coerce
1074
+ .string()
1075
+ .optional()
1076
+ .describe("Returns merge requests which have the user as a reviewer. user id or none or any"),
1077
+ reviewer_username: z
1078
+ .string()
1079
+ .optional()
1080
+ .describe("Returns merge requests which have the user as a reviewer"),
1081
+ created_after: z
1082
+ .string()
1083
+ .optional()
1084
+ .describe("Return merge requests created after the given time"),
1085
+ created_before: z
1086
+ .string()
1087
+ .optional()
1088
+ .describe("Return merge requests created before the given time"),
1089
+ updated_after: z
1090
+ .string()
1091
+ .optional()
1092
+ .describe("Return merge requests updated after the given time"),
1093
+ updated_before: z
1094
+ .string()
1095
+ .optional()
1096
+ .describe("Return merge requests updated before the given time"),
1097
+ labels: z.array(z.string()).optional().describe("Array of label names"),
1098
+ milestone: z.string().optional().describe("Milestone title"),
1099
+ scope: z
1100
+ .enum(["created_by_me", "assigned_to_me", "all"])
1101
+ .optional()
1102
+ .describe("Return merge requests from a specific scope"),
1103
+ search: z.string().optional().describe("Search for specific terms"),
1104
+ state: z
1105
+ .enum(["opened", "closed", "locked", "merged", "all"])
1106
+ .optional()
1107
+ .describe("Return merge requests with a specific state"),
1108
+ order_by: z
1109
+ .enum([
1110
+ "created_at",
1111
+ "updated_at",
1112
+ "priority",
1113
+ "label_priority",
1114
+ "milestone_due",
1115
+ "popularity",
1116
+ ])
1117
+ .optional()
1118
+ .describe("Return merge requests ordered by the given field"),
1119
+ sort: z
1120
+ .enum(["asc", "desc"])
1121
+ .optional()
1122
+ .describe("Return merge requests sorted in ascending or descending order"),
1123
+ target_branch: z
1124
+ .string()
1125
+ .optional()
1126
+ .describe("Return merge requests targeting a specific branch"),
1127
+ source_branch: z
1128
+ .string()
1129
+ .optional()
1130
+ .describe("Return merge requests from a specific source branch"),
1131
+ wip: z
1132
+ .enum(["yes", "no"])
1133
+ .optional()
1134
+ .describe("Filter merge requests against their wip status"),
1135
+ with_labels_details: flexibleBoolean.optional().describe("Return more details for each label"),
1136
+ })
1137
+ .merge(PaginationOptionsSchema);
1138
+ export const GetIssueSchema = z.object({
1139
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1140
+ issue_iid: z.coerce.string().describe("The internal ID of the project issue"),
1141
+ });
1142
+ export const UpdateIssueSchema = z.object({
1143
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1144
+ issue_iid: z.coerce.string().describe("The internal ID of the project issue"),
1145
+ title: z.string().optional().describe("The title of the issue"),
1146
+ description: z.string().optional().describe("The description of the issue"),
1147
+ assignee_ids: z.array(z.number()).optional().describe("Array of user IDs to assign issue to"),
1148
+ confidential: flexibleBoolean.optional().describe("Set the issue to be confidential"),
1149
+ discussion_locked: flexibleBoolean.optional().describe("Flag to lock discussions"),
1150
+ due_date: z.string().optional().describe("Date the issue is due (YYYY-MM-DD)"),
1151
+ labels: z.array(z.string()).optional().describe("Array of label names"),
1152
+ milestone_id: z.coerce.string().optional().describe("Milestone ID to assign"),
1153
+ state_event: z.enum(["close", "reopen"]).optional().describe("Update issue state (close/reopen)"),
1154
+ weight: z.number().optional().describe("Weight of the issue (0-9)"),
1155
+ issue_type: z.enum(["issue", "incident", "test_case", "task"]).describe("the type of issue. One of issue, incident, test_case or task."),
1156
+ });
1157
+ export const DeleteIssueSchema = z.object({
1158
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1159
+ issue_iid: z.coerce.string().describe("The internal ID of the project issue"),
1160
+ });
1161
+ // Issue links related schemas
1162
+ export const GitLabIssueLinkSchema = z.object({
1163
+ source_issue: GitLabIssueSchema,
1164
+ target_issue: GitLabIssueSchema,
1165
+ link_type: z.enum(["relates_to", "blocks", "is_blocked_by"]),
1166
+ });
1167
+ export const ListIssueLinksSchema = z.object({
1168
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1169
+ issue_iid: z.coerce.string().describe("The internal ID of a project's issue"),
1170
+ });
1171
+ export const GetIssueLinkSchema = z.object({
1172
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1173
+ issue_iid: z.coerce.string().describe("The internal ID of a project's issue"),
1174
+ issue_link_id: z.coerce.string().describe("ID of an issue relationship"),
1175
+ });
1176
+ export const CreateIssueLinkSchema = z.object({
1177
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1178
+ issue_iid: z.coerce.string().describe("The internal ID of a project's issue"),
1179
+ target_project_id: z.coerce.string().describe("The ID or URL-encoded path of a target project"),
1180
+ target_issue_iid: z.coerce.string().describe("The internal ID of a target project's issue"),
1181
+ link_type: z
1182
+ .enum(["relates_to", "blocks", "is_blocked_by"])
1183
+ .optional()
1184
+ .describe("The type of the relation, defaults to relates_to"),
1185
+ });
1186
+ export const DeleteIssueLinkSchema = z.object({
1187
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1188
+ issue_iid: z.coerce.string().describe("The internal ID of a project's issue"),
1189
+ issue_link_id: z.coerce.string().describe("The ID of an issue relationship"),
1190
+ });
1191
+ // Namespace API operation schemas
1192
+ export const ListNamespacesSchema = z
1193
+ .object({
1194
+ search: z.string().optional().describe("Search term for namespaces"),
1195
+ owned: flexibleBoolean.optional().describe("Filter for namespaces owned by current user"),
1196
+ })
1197
+ .merge(PaginationOptionsSchema);
1198
+ export const GetNamespaceSchema = z.object({
1199
+ namespace_id: z.coerce.string().describe("Namespace ID or full path"),
1200
+ });
1201
+ export const VerifyNamespaceSchema = z.object({
1202
+ path: z.string().describe("Namespace path to verify"),
1203
+ });
1204
+ // Project API operation schemas
1205
+ export const GetProjectSchema = z.object({
1206
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1207
+ });
1208
+ export const ListProjectsSchema = z
1209
+ .object({
1210
+ search: z.string().optional().describe("Search term for projects"),
1211
+ search_namespaces: flexibleBoolean
1212
+ .optional()
1213
+ .describe("Needs to be true if search is full path"),
1214
+ owned: flexibleBoolean.optional().describe("Filter for projects owned by current user"),
1215
+ membership: flexibleBoolean
1216
+ .optional()
1217
+ .describe("Filter for projects where current user is a member"),
1218
+ simple: flexibleBoolean.optional().describe("Return only limited fields"),
1219
+ archived: flexibleBoolean.optional().describe("Filter for archived projects"),
1220
+ visibility: z
1221
+ .enum(["public", "internal", "private"])
1222
+ .optional()
1223
+ .describe("Filter by project visibility"),
1224
+ order_by: z
1225
+ .enum(["id", "name", "path", "created_at", "updated_at", "last_activity_at"])
1226
+ .optional()
1227
+ .describe("Return projects ordered by field"),
1228
+ sort: z
1229
+ .enum(["asc", "desc"])
1230
+ .optional()
1231
+ .describe("Return projects sorted in ascending or descending order"),
1232
+ with_issues_enabled: z
1233
+ .boolean()
1234
+ .optional()
1235
+ .describe("Filter projects with issues feature enabled"),
1236
+ with_merge_requests_enabled: z
1237
+ .boolean()
1238
+ .optional()
1239
+ .describe("Filter projects with merge requests feature enabled"),
1240
+ min_access_level: z.number().optional().describe("Filter by minimum access level"),
1241
+ })
1242
+ .merge(PaginationOptionsSchema);
1243
+ // Label operation schemas
1244
+ export const ListLabelsSchema = z.object({
1245
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1246
+ with_counts: z
1247
+ .boolean()
1248
+ .optional()
1249
+ .describe("Whether or not to include issue and merge request counts"),
1250
+ include_ancestor_groups: flexibleBoolean.optional().describe("Include ancestor groups"),
1251
+ search: z.string().optional().describe("Keyword to filter labels by"),
1252
+ });
1253
+ export const GetLabelSchema = z.object({
1254
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1255
+ label_id: z.coerce.string().describe("The ID or title of a project's label"),
1256
+ include_ancestor_groups: flexibleBoolean.optional().describe("Include ancestor groups"),
1257
+ });
1258
+ export const CreateLabelSchema = z.object({
1259
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1260
+ name: z.string().describe("The name of the label"),
1261
+ color: z
1262
+ .string()
1263
+ .describe("The color of the label given in 6-digit hex notation with leading '#' sign"),
1264
+ description: z.string().optional().describe("The description of the label"),
1265
+ priority: z.number().nullable().optional().describe("The priority of the label"),
1266
+ });
1267
+ export const UpdateLabelSchema = z.object({
1268
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1269
+ label_id: z.coerce.string().describe("The ID or title of a project's label"),
1270
+ new_name: z.string().optional().describe("The new name of the label"),
1271
+ color: z
1272
+ .string()
1273
+ .optional()
1274
+ .describe("The color of the label given in 6-digit hex notation with leading '#' sign"),
1275
+ description: z.string().optional().describe("The new description of the label"),
1276
+ priority: z.number().nullable().optional().describe("The new priority of the label"),
1277
+ });
1278
+ export const DeleteLabelSchema = z.object({
1279
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1280
+ label_id: z.coerce.string().describe("The ID or title of a project's label"),
1281
+ });
1282
+ // Group projects schema
1283
+ export const ListGroupProjectsSchema = z
1284
+ .object({
1285
+ group_id: z.coerce.string().describe("Group ID or path"),
1286
+ include_subgroups: flexibleBoolean.optional().describe("Include projects from subgroups"),
1287
+ search: z.string().optional().describe("Search term to filter projects"),
1288
+ order_by: z
1289
+ .enum(["name", "path", "created_at", "updated_at", "last_activity_at"])
1290
+ .optional()
1291
+ .describe("Field to sort by"),
1292
+ sort: z.enum(["asc", "desc"]).optional().describe("Sort direction"),
1293
+ archived: flexibleBoolean.optional().describe("Filter for archived projects"),
1294
+ visibility: z
1295
+ .enum(["public", "internal", "private"])
1296
+ .optional()
1297
+ .describe("Filter by project visibility"),
1298
+ with_issues_enabled: z
1299
+ .boolean()
1300
+ .optional()
1301
+ .describe("Filter projects with issues feature enabled"),
1302
+ with_merge_requests_enabled: z
1303
+ .boolean()
1304
+ .optional()
1305
+ .describe("Filter projects with merge requests feature enabled"),
1306
+ min_access_level: z.number().optional().describe("Filter by minimum access level"),
1307
+ with_programming_language: z.string().optional().describe("Filter by programming language"),
1308
+ starred: flexibleBoolean.optional().describe("Filter by starred projects"),
1309
+ statistics: flexibleBoolean.optional().describe("Include project statistics"),
1310
+ with_custom_attributes: flexibleBoolean.optional().describe("Include custom attributes"),
1311
+ with_security_reports: flexibleBoolean.optional().describe("Include security reports"),
1312
+ })
1313
+ .merge(PaginationOptionsSchema);
1314
+ // Add wiki operation schemas
1315
+ export const ListWikiPagesSchema = z
1316
+ .object({
1317
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1318
+ with_content: flexibleBoolean.optional().describe("Include content of the wiki pages"),
1319
+ })
1320
+ .merge(PaginationOptionsSchema);
1321
+ export const GetWikiPageSchema = z.object({
1322
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1323
+ slug: z.string().describe("URL-encoded slug of the wiki page"),
1324
+ });
1325
+ export const CreateWikiPageSchema = z.object({
1326
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1327
+ title: z.string().describe("Title of the wiki page"),
1328
+ content: z.string().describe("Content of the wiki page"),
1329
+ format: z.string().optional().describe("Content format, e.g., markdown, rdoc"),
1330
+ });
1331
+ export const UpdateWikiPageSchema = z.object({
1332
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1333
+ slug: z.string().describe("URL-encoded slug of the wiki page"),
1334
+ title: z.string().optional().describe("New title of the wiki page"),
1335
+ content: z.string().optional().describe("New content of the wiki page"),
1336
+ format: z.string().optional().describe("Content format, e.g., markdown, rdoc"),
1337
+ });
1338
+ export const DeleteWikiPageSchema = z.object({
1339
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
1340
+ slug: z.string().describe("URL-encoded slug of the wiki page"),
1341
+ });
1342
+ // Define wiki response schemas
1343
+ export const GitLabWikiPageSchema = z.object({
1344
+ title: z.string(),
1345
+ slug: z.string(),
1346
+ format: z.string(),
1347
+ content: z.string().optional(),
1348
+ created_at: z.string().optional(),
1349
+ updated_at: z.string().optional(),
1350
+ });
1351
+ // Merge Request Thread position schema - used for diff notes
1352
+ // Extremely flexible position schema for API responses - accepts any structure
1353
+ // Strict position schema for creating draft notes and merge request threads
1354
+ export const MergeRequestThreadPositionCreateSchema = z.object({
1355
+ base_sha: z.string().describe("REQUIRED: Base commit SHA in the source branch. Get this from merge request diff_refs.base_sha."),
1356
+ head_sha: z.string().describe("REQUIRED: SHA referencing HEAD of the source branch. Get this from merge request diff_refs.head_sha."),
1357
+ start_sha: z.string().describe("REQUIRED: SHA referencing the start commit of the source branch. Get this from merge request diff_refs.start_sha."),
1358
+ position_type: z.enum(["text", "image", "file"]).describe("REQUIRED: Position type. Use 'text' for code diffs, 'image' for image diffs, 'file' for file-level comments."),
1359
+ new_path: z.string().nullable().optional().describe("File path after changes. REQUIRED for most diff comments. Use same as old_path if file wasn't renamed."),
1360
+ old_path: z.string().nullable().optional().describe("File path before changes. REQUIRED for most diff comments. Use same as new_path if file wasn't renamed."),
1361
+ new_line: z.number().nullable().optional().describe("Line number in modified file (after changes). Use for added lines or context lines. NULL for deleted lines. For single-line comments on new lines."),
1362
+ old_line: z.number().nullable().optional().describe("Line number in original file (before changes). Use for deleted lines or context lines. NULL for added lines. For single-line comments on old lines."),
1363
+ line_range: LineRangeSchema.optional().describe("MULTILINE COMMENTS: Specify start/end line positions for commenting on multiple lines. Alternative to single old_line/new_line."),
1364
+ width: z.number().optional().describe("IMAGE DIFFS ONLY: Width of the image (for position_type='image')."),
1365
+ height: z.number().optional().describe("IMAGE DIFFS ONLY: Height of the image (for position_type='image')."),
1366
+ x: z.number().optional().describe("IMAGE DIFFS ONLY: X coordinate on the image (for position_type='image')."),
1367
+ y: z.number().optional().describe("IMAGE DIFFS ONLY: Y coordinate on the image (for position_type='image')."),
1368
+ });
1369
+ export const MergeRequestThreadPositionSchema = z.object({
1370
+ base_sha: z
1371
+ .string()
1372
+ .describe("REQUIRED: Base commit SHA in the source branch. Get this from merge request diff_refs.base_sha."),
1373
+ head_sha: z
1374
+ .string()
1375
+ .describe("REQUIRED: SHA referencing HEAD of the source branch. Get this from merge request diff_refs.head_sha."),
1376
+ start_sha: z
1377
+ .string()
1378
+ .describe("REQUIRED: SHA referencing the start commit of the source branch. Get this from merge request diff_refs.start_sha."),
1379
+ position_type: z
1380
+ .enum(["text", "image", "file"])
1381
+ .describe("REQUIRED: Position type. Use 'text' for code diffs, 'image' for image diffs, 'file' for file-level comments."),
1382
+ new_path: z
1383
+ .string()
1384
+ .nullable()
1385
+ .optional()
1386
+ .describe("File path after changes. REQUIRED for most diff comments. Use same as old_path if file wasn't renamed."),
1387
+ old_path: z
1388
+ .string()
1389
+ .nullable()
1390
+ .optional()
1391
+ .describe("File path before changes. REQUIRED for most diff comments. Use same as new_path if file wasn't renamed."),
1392
+ new_line: z
1393
+ .number()
1394
+ .nullable()
1395
+ .optional()
1396
+ .describe("Line number in modified file (after changes). Use for added lines or context lines. NULL for deleted lines. For single-line comments on new lines."),
1397
+ old_line: z
1398
+ .number()
1399
+ .nullable()
1400
+ .optional()
1401
+ .describe("Line number in original file (before changes). Use for deleted lines or context lines. NULL for added lines. For single-line comments on old lines."),
1402
+ line_range: LineRangeSchema.optional().describe("MULTILINE COMMENTS: Specify start/end line positions for commenting on multiple lines. Alternative to single old_line/new_line."),
1403
+ width: z
1404
+ .number()
1405
+ .optional()
1406
+ .describe("IMAGE DIFFS ONLY: Width of the image (for position_type='image')."),
1407
+ height: z
1408
+ .number()
1409
+ .optional()
1410
+ .describe("IMAGE DIFFS ONLY: Height of the image (for position_type='image')."),
1411
+ x: z
1412
+ .number()
1413
+ .optional()
1414
+ .describe("IMAGE DIFFS ONLY: X coordinate on the image (for position_type='image')."),
1415
+ y: z
1416
+ .number()
1417
+ .optional()
1418
+ .describe("IMAGE DIFFS ONLY: Y coordinate on the image (for position_type='image')."),
1419
+ });
1420
+ // Draft Notes API schemas
1421
+ export const GitLabDraftNoteSchema = z.object({
1422
+ id: z.coerce.string(),
1423
+ author: GitLabUserSchema.optional(),
1424
+ body: z.string().optional(),
1425
+ note: z.string().optional(), // Some APIs might use 'note' instead of 'body'
1426
+ created_at: z.string().optional(),
1427
+ updated_at: z.string().optional(),
1428
+ position: MergeRequestThreadPositionSchema.nullable().optional(),
1429
+ resolve_discussion: flexibleBoolean.optional(),
1430
+ }).transform((data) => ({
1431
+ // Normalize the response to always have consistent field names
1432
+ id: data.id,
1433
+ author: data.author,
1434
+ body: data.body || data.note || "",
1435
+ created_at: data.created_at || "",
1436
+ updated_at: data.updated_at || "",
1437
+ position: data.position,
1438
+ resolve_discussion: data.resolve_discussion,
1439
+ }));
1440
+ // Get draft note schema
1441
+ export const GetDraftNoteSchema = ProjectParamsSchema.extend({
1442
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
1443
+ draft_note_id: z.coerce.string().describe("The ID of the draft note"),
1444
+ });
1445
+ // List draft notes schema
1446
+ export const ListDraftNotesSchema = ProjectParamsSchema.extend({
1447
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
1448
+ });
1449
+ // Create draft note schema
1450
+ export const CreateDraftNoteSchema = ProjectParamsSchema.extend({
1451
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
1452
+ body: z.string().describe("The content of the draft note"),
1453
+ position: MergeRequestThreadPositionCreateSchema.optional().describe("Position when creating a diff note"),
1454
+ resolve_discussion: flexibleBoolean.optional().describe("Whether to resolve the discussion when publishing"),
1455
+ });
1456
+ // Update draft note schema
1457
+ export const UpdateDraftNoteSchema = ProjectParamsSchema.extend({
1458
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
1459
+ draft_note_id: z.coerce.string().describe("The ID of the draft note"),
1460
+ body: z.string().optional().describe("The content of the draft note"),
1461
+ position: MergeRequestThreadPositionCreateSchema.optional().describe("Position when creating a diff note"),
1462
+ resolve_discussion: flexibleBoolean.optional().describe("Whether to resolve the discussion when publishing"),
1463
+ });
1464
+ // Delete draft note schema
1465
+ export const DeleteDraftNoteSchema = ProjectParamsSchema.extend({
1466
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
1467
+ draft_note_id: z.coerce.string().describe("The ID of the draft note"),
1468
+ });
1469
+ // Publish draft note schema
1470
+ export const PublishDraftNoteSchema = ProjectParamsSchema.extend({
1471
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
1472
+ draft_note_id: z.coerce.string().describe("The ID of the draft note"),
1473
+ });
1474
+ // Bulk publish draft notes schema
1475
+ export const BulkPublishDraftNotesSchema = ProjectParamsSchema.extend({
1476
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
1477
+ });
1478
+ // Schema for creating a new merge request thread
1479
+ export const CreateMergeRequestThreadSchema = ProjectParamsSchema.extend({
1480
+ merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
1481
+ body: z.string().describe("The content of the thread"),
1482
+ position: MergeRequestThreadPositionSchema.optional().describe("Position when creating a diff note"),
1483
+ created_at: z.string().optional().describe("Date the thread was created at (ISO 8601 format)"),
1484
+ });
1485
+ // Milestone related schemas
1486
+ // Schema for listing project milestones
1487
+ export const ListProjectMilestonesSchema = ProjectParamsSchema.extend({
1488
+ iids: z.array(z.number()).optional().describe("Return only the milestones having the given iid"),
1489
+ state: z
1490
+ .enum(["active", "closed"])
1491
+ .optional()
1492
+ .describe("Return only active or closed milestones"),
1493
+ title: z
1494
+ .string()
1495
+ .optional()
1496
+ .describe("Return only milestones with a title matching the provided string"),
1497
+ search: z
1498
+ .string()
1499
+ .optional()
1500
+ .describe("Return only milestones with a title or description matching the provided string"),
1501
+ include_ancestors: flexibleBoolean.optional().describe("Include ancestor groups"),
1502
+ updated_before: z
1503
+ .string()
1504
+ .optional()
1505
+ .describe("Return milestones updated before the specified date (ISO 8601 format)"),
1506
+ updated_after: z
1507
+ .string()
1508
+ .optional()
1509
+ .describe("Return milestones updated after the specified date (ISO 8601 format)"),
1510
+ }).merge(PaginationOptionsSchema);
1511
+ // Schema for getting a single milestone
1512
+ export const GetProjectMilestoneSchema = ProjectParamsSchema.extend({
1513
+ milestone_id: z.coerce.string().describe("The ID of a project milestone"),
1514
+ });
1515
+ // Schema for creating a new milestone
1516
+ export const CreateProjectMilestoneSchema = ProjectParamsSchema.extend({
1517
+ title: z.string().describe("The title of the milestone"),
1518
+ description: z.string().optional().describe("The description of the milestone"),
1519
+ due_date: z.string().optional().describe("The due date of the milestone (YYYY-MM-DD)"),
1520
+ start_date: z.string().optional().describe("The start date of the milestone (YYYY-MM-DD)"),
1521
+ });
1522
+ // Schema for editing a milestone
1523
+ export const EditProjectMilestoneSchema = GetProjectMilestoneSchema.extend({
1524
+ title: z.string().optional().describe("The title of the milestone"),
1525
+ description: z.string().optional().describe("The description of the milestone"),
1526
+ due_date: z.string().optional().describe("The due date of the milestone (YYYY-MM-DD)"),
1527
+ start_date: z.string().optional().describe("The start date of the milestone (YYYY-MM-DD)"),
1528
+ state_event: z
1529
+ .enum(["close", "activate"])
1530
+ .optional()
1531
+ .describe("The state event of the milestone"),
1532
+ });
1533
+ // Schema for deleting a milestone
1534
+ export const DeleteProjectMilestoneSchema = GetProjectMilestoneSchema;
1535
+ // Schema for getting issues assigned to a milestone
1536
+ export const GetMilestoneIssuesSchema = GetProjectMilestoneSchema;
1537
+ // Schema for getting merge requests assigned to a milestone
1538
+ export const GetMilestoneMergeRequestsSchema = GetProjectMilestoneSchema.merge(PaginationOptionsSchema);
1539
+ // Schema for promoting a project milestone to a group milestone
1540
+ export const PromoteProjectMilestoneSchema = GetProjectMilestoneSchema;
1541
+ // Schema for getting burndown chart events for a milestone
1542
+ export const GetMilestoneBurndownEventsSchema = GetProjectMilestoneSchema.merge(PaginationOptionsSchema);
1543
+ // Add schemas for commit operations
1544
+ export const ListCommitsSchema = z.object({
1545
+ project_id: z.coerce.string().describe("Project ID or complete URL-encoded path to project"),
1546
+ ref_name: z
1547
+ .string()
1548
+ .optional()
1549
+ .describe("The name of a repository branch, tag or revision range, or if not given the default branch"),
1550
+ since: z
1551
+ .string()
1552
+ .optional()
1553
+ .describe("Only commits after or on this date are returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ"),
1554
+ until: z
1555
+ .string()
1556
+ .optional()
1557
+ .describe("Only commits before or on this date are returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ"),
1558
+ path: z.string().optional().describe("The file path"),
1559
+ author: z.string().optional().describe("Search commits by commit author"),
1560
+ all: flexibleBoolean.optional().describe("Retrieve every commit from the repository"),
1561
+ with_stats: flexibleBoolean
1562
+ .optional()
1563
+ .describe("Stats about each commit are added to the response"),
1564
+ first_parent: flexibleBoolean
1565
+ .optional()
1566
+ .describe("Follow only the first parent commit upon seeing a merge commit"),
1567
+ order: z.enum(["default", "topo"]).optional().describe("List commits in order"),
1568
+ trailers: flexibleBoolean.optional().describe("Parse and include Git trailers for every commit"),
1569
+ page: z.number().optional().describe("Page number for pagination (default: 1)"),
1570
+ per_page: z.number().optional().describe("Number of items per page (max: 100, default: 20)"),
1571
+ });
1572
+ export const GetCommitSchema = z.object({
1573
+ project_id: z.coerce.string().describe("Project ID or complete URL-encoded path to project"),
1574
+ sha: z.string().describe("The commit hash or name of a repository branch or tag"),
1575
+ stats: flexibleBoolean.optional().describe("Include commit stats"),
1576
+ });
1577
+ export const GetCommitDiffSchema = z.object({
1578
+ project_id: z.coerce.string().describe("Project ID or complete URL-encoded path to project"),
1579
+ sha: z.string().describe("The commit hash or name of a repository branch or tag"),
1580
+ });
1581
+ export const GetCurrentUserSchema = z.object({});
1582
+ // Schema for listing issues assigned to the current user
1583
+ export const MyIssuesSchema = z.object({
1584
+ project_id: z.string().optional().describe("Project ID or URL-encoded path (optional when GITLAB_PROJECT_ID is set)"),
1585
+ state: z
1586
+ .enum(["opened", "closed", "all"])
1587
+ .optional()
1588
+ .describe("Return issues with a specific state (default: opened)"),
1589
+ labels: z.array(z.string()).optional().describe("Array of label names to filter by"),
1590
+ milestone: z.string().optional().describe("Milestone title to filter by"),
1591
+ search: z.string().optional().describe("Search for specific terms in title and description"),
1592
+ created_after: z.string().optional().describe("Return issues created after the given time (ISO 8601)"),
1593
+ created_before: z.string().optional().describe("Return issues created before the given time (ISO 8601)"),
1594
+ updated_after: z.string().optional().describe("Return issues updated after the given time (ISO 8601)"),
1595
+ updated_before: z.string().optional().describe("Return issues updated before the given time (ISO 8601)"),
1596
+ per_page: z.number().optional().describe("Number of items per page (default: 20, max: 100)"),
1597
+ page: z.number().optional().describe("Page number for pagination (default: 1)"),
1598
+ });
1599
+ // Schema for listing project members
1600
+ export const ListProjectMembersSchema = z.object({
1601
+ project_id: z.string().describe("Project ID or URL-encoded path"),
1602
+ query: z.string().optional().describe("Search for members by name or username"),
1603
+ user_ids: z.array(z.number()).optional().describe("Filter by user IDs"),
1604
+ skip_users: z.array(z.number()).optional().describe("User IDs to exclude"),
1605
+ per_page: z.number().optional().describe("Number of items per page (default: 20, max: 100)"),
1606
+ page: z.number().optional().describe("Page number for pagination (default: 1)"),
1607
+ });
1608
+ // Schema for GitLab project member
1609
+ export const GitLabProjectMemberSchema = z.object({
1610
+ id: z.number(),
1611
+ username: z.string(),
1612
+ name: z.string(),
1613
+ state: z.string(),
1614
+ avatar_url: z.string().nullable(),
1615
+ web_url: z.string(),
1616
+ access_level: z.number(),
1617
+ access_level_description: z.string().optional(),
1618
+ created_at: z.string(),
1619
+ expires_at: z.string().nullable().optional(),
1620
+ email: z.string().optional(),
1621
+ });
1622
+ // Markdown upload schemas
1623
+ export const GitLabMarkdownUploadSchema = z.object({
1624
+ id: z.number(),
1625
+ alt: z.string(),
1626
+ url: z.string(),
1627
+ full_path: z.string(),
1628
+ markdown: z.string(),
1629
+ });
1630
+ export const MarkdownUploadSchema = z.object({
1631
+ project_id: z.string().describe("Project ID or URL-encoded path of the project"),
1632
+ file_path: z.string().describe("Path to the file to upload"),
1633
+ });
1634
+ export const GroupIteration = z.object({
1635
+ id: z.coerce.string(),
1636
+ iid: z.coerce.string(),
1637
+ sequence: z.number(),
1638
+ group_id: z.coerce.string(),
1639
+ title: z.string().optional().nullable(),
1640
+ description: z.string().optional().nullable(),
1641
+ state: z.number(),
1642
+ created_at: z.string(),
1643
+ updated_at: z.string(),
1644
+ due_date: z.string().optional().nullable(),
1645
+ start_date: z.string().optional().nullable(),
1646
+ web_url: z.string().optional().nullable(),
1647
+ });
1648
+ export const ListGroupIterationsSchema = z
1649
+ .object({
1650
+ group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
1651
+ state: z
1652
+ .enum(["opened", "upcoming", "current", "closed", "all"])
1653
+ .optional()
1654
+ .describe("Return opened, upcoming, current, closed, or all iterations."),
1655
+ search: z
1656
+ .string()
1657
+ .optional()
1658
+ .describe("Return only iterations with a title matching the provided string."),
1659
+ in: z
1660
+ .array(z.enum(["title", "cadence_title"]))
1661
+ .optional()
1662
+ .describe("Fields in which fuzzy search should be performed with the query given in the argument search. The available options are title and cadence_title. Default is [title]."),
1663
+ include_ancestors: flexibleBoolean
1664
+ .optional()
1665
+ .describe("Include iterations for group and its ancestors. Defaults to true."),
1666
+ include_descendants: flexibleBoolean
1667
+ .optional()
1668
+ .describe("Include iterations for group and its descendants. Defaults to false."),
1669
+ updated_before: z
1670
+ .string()
1671
+ .optional()
1672
+ .describe("Return only iterations updated before the given datetime. Expected in ISO 8601 format (2019-03-15T08:00:00Z)."),
1673
+ updated_after: z
1674
+ .string()
1675
+ .optional()
1676
+ .describe("Return only iterations updated after the given datetime. Expected in ISO 8601 format (2019-03-15T08:00:00Z)."),
1677
+ })
1678
+ .merge(PaginationOptionsSchema);