@zereight/mcp-gitlab 1.0.72 → 1.0.74
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/customSchemas.js +19 -0
- package/build/index.js +5 -3
- package/build/schemas.js +114 -126
- package/package.json +1 -1
- package/build/tests/integration.test.js +0 -151
- package/build/tests/unit.test.js +0 -122
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { pino } from 'pino';
|
|
3
|
+
const logger = pino({
|
|
4
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
5
|
+
transport: {
|
|
6
|
+
target: 'pino-pretty',
|
|
7
|
+
options: {
|
|
8
|
+
colorize: true,
|
|
9
|
+
levelFirst: true,
|
|
10
|
+
destination: 2,
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
export const flexibleBoolean = z.preprocess((val) => {
|
|
15
|
+
if (typeof val === 'string') {
|
|
16
|
+
return val.toLowerCase() === 'true';
|
|
17
|
+
}
|
|
18
|
+
return val;
|
|
19
|
+
}, z.boolean());
|
package/build/index.js
CHANGED
|
@@ -845,12 +845,12 @@ async function listIssues(projectId, options = {}) {
|
|
|
845
845
|
url.searchParams.append(`${key}[]`, label.toString());
|
|
846
846
|
});
|
|
847
847
|
}
|
|
848
|
-
else {
|
|
848
|
+
else if (value) {
|
|
849
849
|
url.searchParams.append(`${key}[]`, value.toString());
|
|
850
850
|
}
|
|
851
851
|
}
|
|
852
852
|
else {
|
|
853
|
-
url.searchParams.append(key, value
|
|
853
|
+
url.searchParams.append(key, String(value));
|
|
854
854
|
}
|
|
855
855
|
}
|
|
856
856
|
});
|
|
@@ -879,7 +879,7 @@ async function listMergeRequests(projectId, options = {}) {
|
|
|
879
879
|
url.searchParams.append(key, value.join(","));
|
|
880
880
|
}
|
|
881
881
|
else {
|
|
882
|
-
url.searchParams.append(key, value
|
|
882
|
+
url.searchParams.append(key, String(value));
|
|
883
883
|
}
|
|
884
884
|
}
|
|
885
885
|
});
|
|
@@ -2617,6 +2617,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2617
2617
|
if (GITLAB_AUTH_COOKIE_PATH) {
|
|
2618
2618
|
await ensureSessionForRequest();
|
|
2619
2619
|
}
|
|
2620
|
+
logger.info(request.params.name);
|
|
2620
2621
|
switch (request.params.name) {
|
|
2621
2622
|
case "fork_repository": {
|
|
2622
2623
|
if (GITLAB_PROJECT_ID) {
|
|
@@ -3305,6 +3306,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3305
3306
|
}
|
|
3306
3307
|
}
|
|
3307
3308
|
catch (error) {
|
|
3309
|
+
logger.debug(request.params);
|
|
3308
3310
|
if (error instanceof z.ZodError) {
|
|
3309
3311
|
throw new Error(`Invalid arguments: ${error.errors
|
|
3310
3312
|
.map(e => `${e.path.join(".")}: ${e.message}`)
|
package/build/schemas.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
if (typeof val === 'string') {
|
|
4
|
-
return val.toLowerCase() === 'true';
|
|
5
|
-
}
|
|
6
|
-
return val;
|
|
7
|
-
}, z.boolean());
|
|
2
|
+
import { flexibleBoolean } from "./customSchemas.js";
|
|
8
3
|
// Base schemas for common types
|
|
9
4
|
export const GitLabAuthorSchema = z.object({
|
|
10
5
|
name: z.string(),
|
|
@@ -13,8 +8,8 @@ export const GitLabAuthorSchema = z.object({
|
|
|
13
8
|
});
|
|
14
9
|
// Pipeline related schemas
|
|
15
10
|
export const GitLabPipelineSchema = z.object({
|
|
16
|
-
id: z.string()
|
|
17
|
-
project_id: z.string()
|
|
11
|
+
id: z.coerce.string(),
|
|
12
|
+
project_id: z.coerce.string(),
|
|
18
13
|
sha: z.string(),
|
|
19
14
|
ref: z.string(),
|
|
20
15
|
status: z.string(),
|
|
@@ -28,7 +23,7 @@ export const GitLabPipelineSchema = z.object({
|
|
|
28
23
|
coverage: z.number().nullable().optional(),
|
|
29
24
|
user: z
|
|
30
25
|
.object({
|
|
31
|
-
id: z.string()
|
|
26
|
+
id: z.coerce.string(),
|
|
32
27
|
name: z.string(),
|
|
33
28
|
username: z.string(),
|
|
34
29
|
avatar_url: z.string().nullable().optional(),
|
|
@@ -57,7 +52,7 @@ export const GitLabPipelineSchema = z.object({
|
|
|
57
52
|
});
|
|
58
53
|
// Pipeline job related schemas
|
|
59
54
|
export const GitLabPipelineJobSchema = z.object({
|
|
60
|
-
id: z.string()
|
|
55
|
+
id: z.coerce.string(),
|
|
61
56
|
status: z.string(),
|
|
62
57
|
stage: z.string(),
|
|
63
58
|
name: z.string(),
|
|
@@ -70,7 +65,7 @@ export const GitLabPipelineJobSchema = z.object({
|
|
|
70
65
|
duration: z.number().nullable().optional(),
|
|
71
66
|
user: z
|
|
72
67
|
.object({
|
|
73
|
-
id: z.string()
|
|
68
|
+
id: z.coerce.string(),
|
|
74
69
|
name: z.string(),
|
|
75
70
|
username: z.string(),
|
|
76
71
|
avatar_url: z.string().nullable().optional(),
|
|
@@ -87,8 +82,8 @@ export const GitLabPipelineJobSchema = z.object({
|
|
|
87
82
|
.optional(),
|
|
88
83
|
pipeline: z
|
|
89
84
|
.object({
|
|
90
|
-
id: z.string()
|
|
91
|
-
project_id: z.string()
|
|
85
|
+
id: z.coerce.string(),
|
|
86
|
+
project_id: z.coerce.string(),
|
|
92
87
|
status: z.string(),
|
|
93
88
|
ref: z.string(),
|
|
94
89
|
sha: z.string(),
|
|
@@ -104,7 +99,7 @@ export const PaginationOptionsSchema = z.object({
|
|
|
104
99
|
});
|
|
105
100
|
// Schema for listing pipelines
|
|
106
101
|
export const ListPipelinesSchema = z.object({
|
|
107
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
102
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
108
103
|
scope: z
|
|
109
104
|
.enum(["running", "pending", "finished", "branches", "tags"])
|
|
110
105
|
.optional()
|
|
@@ -145,13 +140,13 @@ export const ListPipelinesSchema = z.object({
|
|
|
145
140
|
}).merge(PaginationOptionsSchema);
|
|
146
141
|
// Schema for getting a specific pipeline
|
|
147
142
|
export const GetPipelineSchema = z.object({
|
|
148
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
149
|
-
pipeline_id: z.string().
|
|
143
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
144
|
+
pipeline_id: z.coerce.string().describe("The ID of the pipeline"),
|
|
150
145
|
});
|
|
151
146
|
// Schema for listing jobs in a pipeline
|
|
152
147
|
export const ListPipelineJobsSchema = z.object({
|
|
153
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
154
|
-
pipeline_id: z.string().
|
|
148
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
149
|
+
pipeline_id: z.coerce.string().describe("The ID of the pipeline"),
|
|
155
150
|
scope: z
|
|
156
151
|
.enum(["created", "pending", "running", "failed", "success", "canceled", "skipped", "manual"])
|
|
157
152
|
.optional()
|
|
@@ -160,7 +155,7 @@ export const ListPipelineJobsSchema = z.object({
|
|
|
160
155
|
}).merge(PaginationOptionsSchema);
|
|
161
156
|
// Schema for creating a new pipeline
|
|
162
157
|
export const CreatePipelineSchema = z.object({
|
|
163
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
158
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
164
159
|
ref: z.string().describe("The branch or tag to run the pipeline on"),
|
|
165
160
|
variables: z
|
|
166
161
|
.array(z.object({
|
|
@@ -172,25 +167,22 @@ export const CreatePipelineSchema = z.object({
|
|
|
172
167
|
});
|
|
173
168
|
// Schema for retrying a pipeline
|
|
174
169
|
export const RetryPipelineSchema = z.object({
|
|
175
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
176
|
-
pipeline_id: z.string().
|
|
170
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
171
|
+
pipeline_id: z.coerce.string().describe("The ID of the pipeline to retry"),
|
|
177
172
|
});
|
|
178
173
|
// Schema for canceling a pipeline
|
|
179
|
-
export const CancelPipelineSchema =
|
|
180
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
181
|
-
pipeline_id: z.string().or(z.number().describe("The ID of the pipeline to cancel")),
|
|
182
|
-
});
|
|
174
|
+
export const CancelPipelineSchema = RetryPipelineSchema;
|
|
183
175
|
// Schema for the input parameters for pipeline job operations
|
|
184
176
|
export const GetPipelineJobOutputSchema = z.object({
|
|
185
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
186
|
-
job_id: z.string().
|
|
177
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
178
|
+
job_id: z.coerce.string().describe("The ID of the job"),
|
|
187
179
|
limit: z.number().optional().describe("Maximum number of lines to return from the end of the log (default: 1000)"),
|
|
188
180
|
offset: z.number().optional().describe("Number of lines to skip from the end of the log (default: 0)"),
|
|
189
181
|
});
|
|
190
182
|
// User schemas
|
|
191
183
|
export const GitLabUserSchema = z.object({
|
|
192
184
|
username: z.string(), // Changed from login to match GitLab API
|
|
193
|
-
id: z.string()
|
|
185
|
+
id: z.coerce.string(),
|
|
194
186
|
name: z.string(),
|
|
195
187
|
avatar_url: z.string().nullable(),
|
|
196
188
|
web_url: z.string(), // Changed from html_url to match GitLab API
|
|
@@ -199,7 +191,7 @@ export const GetUsersSchema = z.object({
|
|
|
199
191
|
usernames: z.array(z.string()).describe("Array of usernames to search for"),
|
|
200
192
|
});
|
|
201
193
|
export const GitLabUsersResponseSchema = z.record(z.string(), z.object({
|
|
202
|
-
id: z.string()
|
|
194
|
+
id: z.coerce.string(),
|
|
203
195
|
username: z.string(),
|
|
204
196
|
name: z.string(),
|
|
205
197
|
avatar_url: z.string().nullable(),
|
|
@@ -208,15 +200,15 @@ export const GitLabUsersResponseSchema = z.record(z.string(), z.object({
|
|
|
208
200
|
// Namespace related schemas
|
|
209
201
|
// Base schema for project-related operations
|
|
210
202
|
const ProjectParamsSchema = z.object({
|
|
211
|
-
project_id: z.string().describe("Project ID or complete URL-encoded path to project"), // Changed from owner/repo to match GitLab API
|
|
203
|
+
project_id: z.coerce.string().describe("Project ID or complete URL-encoded path to project"), // Changed from owner/repo to match GitLab API
|
|
212
204
|
});
|
|
213
205
|
export const GitLabNamespaceSchema = z.object({
|
|
214
|
-
id: z.string()
|
|
206
|
+
id: z.coerce.string(),
|
|
215
207
|
name: z.string(),
|
|
216
208
|
path: z.string(),
|
|
217
209
|
kind: z.enum(["user", "group"]),
|
|
218
210
|
full_path: z.string(),
|
|
219
|
-
parent_id: z.string().
|
|
211
|
+
parent_id: z.coerce.string().nullable(),
|
|
220
212
|
avatar_url: z.string().nullable(),
|
|
221
213
|
web_url: z.string(),
|
|
222
214
|
members_count_with_descendants: z.number().optional(),
|
|
@@ -237,14 +229,14 @@ export const GitLabNamespaceExistsResponseSchema = z.object({
|
|
|
237
229
|
// Repository related schemas
|
|
238
230
|
export const GitLabOwnerSchema = z.object({
|
|
239
231
|
username: z.string(), // Changed from login to match GitLab API
|
|
240
|
-
id: z.string()
|
|
232
|
+
id: z.coerce.string(),
|
|
241
233
|
avatar_url: z.string().nullable(),
|
|
242
234
|
web_url: z.string(), // Changed from html_url to match GitLab API
|
|
243
235
|
name: z.string(), // Added as GitLab includes full name
|
|
244
236
|
state: z.string(), // Added as GitLab includes user state
|
|
245
237
|
});
|
|
246
238
|
export const GitLabRepositorySchema = z.object({
|
|
247
|
-
id: z.string()
|
|
239
|
+
id: z.coerce.string(),
|
|
248
240
|
name: z.string(),
|
|
249
241
|
path_with_namespace: z.string(),
|
|
250
242
|
visibility: z.string().optional(),
|
|
@@ -259,7 +251,7 @@ export const GitLabRepositorySchema = z.object({
|
|
|
259
251
|
default_branch: z.string().optional(),
|
|
260
252
|
namespace: z
|
|
261
253
|
.object({
|
|
262
|
-
id: z.string()
|
|
254
|
+
id: z.coerce.string(),
|
|
263
255
|
name: z.string(),
|
|
264
256
|
path: z.string(),
|
|
265
257
|
kind: z.string(),
|
|
@@ -306,7 +298,7 @@ export const GitLabRepositorySchema = z.object({
|
|
|
306
298
|
shared_runners_enabled: flexibleBoolean.optional(),
|
|
307
299
|
shared_with_groups: z
|
|
308
300
|
.array(z.object({
|
|
309
|
-
group_id: z.string()
|
|
301
|
+
group_id: z.coerce.string(),
|
|
310
302
|
group_name: z.string(),
|
|
311
303
|
group_full_path: z.string(),
|
|
312
304
|
group_access_level: z.number(),
|
|
@@ -355,7 +347,7 @@ export const GitLabTreeItemSchema = z.object({
|
|
|
355
347
|
mode: z.string(),
|
|
356
348
|
});
|
|
357
349
|
export const GetRepositoryTreeSchema = z.object({
|
|
358
|
-
project_id: z.string().describe("The ID or URL-encoded path of the project"),
|
|
350
|
+
project_id: z.coerce.string().describe("The ID or URL-encoded path of the project"),
|
|
359
351
|
path: z.string().optional().describe("The path inside the repository"),
|
|
360
352
|
ref: z
|
|
361
353
|
.string()
|
|
@@ -402,9 +394,9 @@ export const GitLabReferenceSchema = z.object({
|
|
|
402
394
|
});
|
|
403
395
|
// Milestones rest api output schemas
|
|
404
396
|
export const GitLabMilestonesSchema = z.object({
|
|
405
|
-
id: z.string()
|
|
406
|
-
iid: z.string()
|
|
407
|
-
project_id: z.string()
|
|
397
|
+
id: z.coerce.string(),
|
|
398
|
+
iid: z.coerce.string(),
|
|
399
|
+
project_id: z.coerce.string(),
|
|
408
400
|
title: z.string(),
|
|
409
401
|
description: z.string().nullable(),
|
|
410
402
|
due_date: z.string().nullable(),
|
|
@@ -426,7 +418,7 @@ export const CreateIssueOptionsSchema = z.object({
|
|
|
426
418
|
title: z.string(),
|
|
427
419
|
description: z.string().optional(), // Changed from body to match GitLab API
|
|
428
420
|
assignee_ids: z.array(z.number()).optional(), // Changed from assignees to match GitLab API
|
|
429
|
-
milestone_id: z.string().
|
|
421
|
+
milestone_id: z.coerce.string().optional(), // Changed from milestone to match GitLab API
|
|
430
422
|
labels: z.array(z.string()).optional(),
|
|
431
423
|
});
|
|
432
424
|
export const GitLabDiffSchema = z.object({
|
|
@@ -473,7 +465,7 @@ export const GitLabCompareResultSchema = z.object({
|
|
|
473
465
|
});
|
|
474
466
|
// Issue related schemas
|
|
475
467
|
export const GitLabLabelSchema = z.object({
|
|
476
|
-
id: z.string()
|
|
468
|
+
id: z.coerce.string(),
|
|
477
469
|
name: z.string(),
|
|
478
470
|
color: z.string(),
|
|
479
471
|
text_color: z.string(),
|
|
@@ -487,17 +479,17 @@ export const GitLabLabelSchema = z.object({
|
|
|
487
479
|
is_project_label: flexibleBoolean.optional(),
|
|
488
480
|
});
|
|
489
481
|
export const GitLabMilestoneSchema = z.object({
|
|
490
|
-
id: z.string()
|
|
491
|
-
iid: z.string()
|
|
482
|
+
id: z.coerce.string(),
|
|
483
|
+
iid: z.coerce.string(), // Added to match GitLab API
|
|
492
484
|
title: z.string(),
|
|
493
485
|
description: z.string().nullable().default(""),
|
|
494
486
|
state: z.string(),
|
|
495
487
|
web_url: z.string(), // Changed from html_url to match GitLab API
|
|
496
488
|
});
|
|
497
489
|
export const GitLabIssueSchema = z.object({
|
|
498
|
-
id: z.string()
|
|
499
|
-
iid: z.string()
|
|
500
|
-
project_id: z.string()
|
|
490
|
+
id: z.coerce.string(),
|
|
491
|
+
iid: z.coerce.string(), // Added to match GitLab API
|
|
492
|
+
project_id: z.coerce.string(), // Added to match GitLab API
|
|
501
493
|
title: z.string(),
|
|
502
494
|
description: z.string().nullable().default(""), // Changed from body to match GitLab API
|
|
503
495
|
state: z.string(),
|
|
@@ -531,7 +523,7 @@ export const GitLabIssueSchema = z.object({
|
|
|
531
523
|
});
|
|
532
524
|
// NEW SCHEMA: For issue with link details (used in listing issue links)
|
|
533
525
|
export const GitLabIssueWithLinkDetailsSchema = GitLabIssueSchema.extend({
|
|
534
|
-
issue_link_id: z.string()
|
|
526
|
+
issue_link_id: z.coerce.string(),
|
|
535
527
|
link_type: z.enum(["relates_to", "blocks", "is_blocked_by"]),
|
|
536
528
|
link_created_at: z.string(),
|
|
537
529
|
link_updated_at: z.string(),
|
|
@@ -543,7 +535,7 @@ export const GitLabForkParentSchema = z.object({
|
|
|
543
535
|
owner: z
|
|
544
536
|
.object({
|
|
545
537
|
username: z.string(), // Changed from login to match GitLab API
|
|
546
|
-
id: z.string()
|
|
538
|
+
id: z.coerce.string(),
|
|
547
539
|
avatar_url: z.string().nullable(),
|
|
548
540
|
})
|
|
549
541
|
.optional(), // Made optional to handle cases where GitLab API doesn't include it
|
|
@@ -559,9 +551,9 @@ export const GitLabMergeRequestDiffRefSchema = z.object({
|
|
|
559
551
|
start_sha: z.string(),
|
|
560
552
|
});
|
|
561
553
|
export const GitLabMergeRequestSchema = z.object({
|
|
562
|
-
id: z.string()
|
|
563
|
-
iid: z.string()
|
|
564
|
-
project_id: z.string()
|
|
554
|
+
id: z.coerce.string(),
|
|
555
|
+
iid: z.coerce.string(),
|
|
556
|
+
project_id: z.coerce.string(),
|
|
565
557
|
title: z.string(),
|
|
566
558
|
description: z.string().nullable(),
|
|
567
559
|
state: z.string(),
|
|
@@ -609,7 +601,7 @@ export const LineRangeSchema = z.object({
|
|
|
609
601
|
}).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.");
|
|
610
602
|
// Discussion related schemas
|
|
611
603
|
export const GitLabDiscussionNoteSchema = z.object({
|
|
612
|
-
id: z.string()
|
|
604
|
+
id: z.coerce.string(),
|
|
613
605
|
type: z.enum(["DiscussionNote", "DiffNote", "Note"]).nullable(), // Allow null type for regular notes
|
|
614
606
|
body: z.string(),
|
|
615
607
|
attachment: z.any().nullable(), // Can be string or object, handle appropriately
|
|
@@ -617,10 +609,10 @@ export const GitLabDiscussionNoteSchema = z.object({
|
|
|
617
609
|
created_at: z.string(),
|
|
618
610
|
updated_at: z.string(),
|
|
619
611
|
system: flexibleBoolean,
|
|
620
|
-
noteable_id: z.string()
|
|
612
|
+
noteable_id: z.coerce.string(),
|
|
621
613
|
noteable_type: z.enum(["Issue", "MergeRequest", "Snippet", "Commit", "Epic"]),
|
|
622
|
-
project_id: z.string().
|
|
623
|
-
noteable_iid: z.coerce.
|
|
614
|
+
project_id: z.coerce.string().optional(),
|
|
615
|
+
noteable_iid: z.coerce.string().nullable().optional(),
|
|
624
616
|
resolvable: flexibleBoolean.optional(),
|
|
625
617
|
resolved: flexibleBoolean.optional(),
|
|
626
618
|
resolved_by: GitLabUserSchema.nullable().optional(),
|
|
@@ -660,7 +652,7 @@ export const PaginatedResponseSchema = z.object({
|
|
|
660
652
|
pagination: GitLabPaginationSchema.optional(),
|
|
661
653
|
});
|
|
662
654
|
export const GitLabDiscussionSchema = z.object({
|
|
663
|
-
id: z.string(),
|
|
655
|
+
id: z.coerce.string(),
|
|
664
656
|
individual_note: flexibleBoolean,
|
|
665
657
|
notes: z.array(GitLabDiscussionNoteSchema),
|
|
666
658
|
});
|
|
@@ -670,18 +662,18 @@ export const PaginatedDiscussionsResponseSchema = z.object({
|
|
|
670
662
|
pagination: GitLabPaginationSchema,
|
|
671
663
|
});
|
|
672
664
|
export const ListIssueDiscussionsSchema = z.object({
|
|
673
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
674
|
-
issue_iid: z.string().
|
|
665
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
666
|
+
issue_iid: z.coerce.string().describe("The internal ID of the project issue"),
|
|
675
667
|
}).merge(PaginationOptionsSchema);
|
|
676
668
|
// Input schema for listing merge request discussions
|
|
677
669
|
export const ListMergeRequestDiscussionsSchema = ProjectParamsSchema.extend({
|
|
678
|
-
merge_request_iid: z.string().
|
|
670
|
+
merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
|
|
679
671
|
}).merge(PaginationOptionsSchema);
|
|
680
672
|
// Input schema for updating a merge request discussion note
|
|
681
673
|
export const UpdateMergeRequestNoteSchema = ProjectParamsSchema.extend({
|
|
682
|
-
merge_request_iid: z.string().
|
|
683
|
-
discussion_id: z.string().describe("The ID of a thread"),
|
|
684
|
-
note_id: z.string().
|
|
674
|
+
merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
|
|
675
|
+
discussion_id: z.coerce.string().describe("The ID of a thread"),
|
|
676
|
+
note_id: z.coerce.string().describe("The ID of a thread note"),
|
|
685
677
|
body: z.string().optional().describe("The content of the note or reply"),
|
|
686
678
|
resolved: flexibleBoolean.optional().describe("Resolve or unresolve the note"),
|
|
687
679
|
})
|
|
@@ -693,22 +685,22 @@ export const UpdateMergeRequestNoteSchema = ProjectParamsSchema.extend({
|
|
|
693
685
|
});
|
|
694
686
|
// Input schema for adding a note to an existing merge request discussion
|
|
695
687
|
export const CreateMergeRequestNoteSchema = ProjectParamsSchema.extend({
|
|
696
|
-
merge_request_iid: z.string().
|
|
697
|
-
discussion_id: z.string().describe("The ID of a thread"),
|
|
688
|
+
merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
|
|
689
|
+
discussion_id: z.coerce.string().describe("The ID of a thread"),
|
|
698
690
|
body: z.string().describe("The content of the note or reply"),
|
|
699
691
|
created_at: z.string().optional().describe("Date the note was created at (ISO 8601 format)"),
|
|
700
692
|
});
|
|
701
693
|
// Input schema for updating an issue discussion note
|
|
702
694
|
export const UpdateIssueNoteSchema = ProjectParamsSchema.extend({
|
|
703
|
-
issue_iid: z.string().
|
|
704
|
-
discussion_id: z.string().describe("The ID of a thread"),
|
|
705
|
-
note_id: z.string().
|
|
695
|
+
issue_iid: z.coerce.string().describe("The IID of an issue"),
|
|
696
|
+
discussion_id: z.coerce.string().describe("The ID of a thread"),
|
|
697
|
+
note_id: z.coerce.string().describe("The ID of a thread note"),
|
|
706
698
|
body: z.string().describe("The content of the note or reply"),
|
|
707
699
|
});
|
|
708
700
|
// Input schema for adding a note to an existing issue discussion
|
|
709
701
|
export const CreateIssueNoteSchema = ProjectParamsSchema.extend({
|
|
710
|
-
issue_iid: z.string().
|
|
711
|
-
discussion_id: z.string().describe("The ID of a thread"),
|
|
702
|
+
issue_iid: z.coerce.string().describe("The IID of an issue"),
|
|
703
|
+
discussion_id: z.coerce.string().describe("The ID of a thread"),
|
|
712
704
|
body: z.string().describe("The content of the note or reply"),
|
|
713
705
|
created_at: z.string().optional().describe("Date the note was created at (ISO 8601 format)"),
|
|
714
706
|
});
|
|
@@ -753,7 +745,7 @@ export const CreateIssueSchema = ProjectParamsSchema.extend({
|
|
|
753
745
|
description: z.string().optional().describe("Issue description"),
|
|
754
746
|
assignee_ids: z.array(z.number()).optional().describe("Array of user IDs to assign"),
|
|
755
747
|
labels: z.array(z.string()).optional().describe("Array of label names"),
|
|
756
|
-
milestone_id: z.string().
|
|
748
|
+
milestone_id: z.coerce.string().optional().describe("Milestone ID to assign"),
|
|
757
749
|
});
|
|
758
750
|
const MergeRequestOptionsSchema = {
|
|
759
751
|
title: z.string().describe("Merge request title"),
|
|
@@ -794,7 +786,7 @@ export const GetBranchDiffsSchema = ProjectParamsSchema.extend({
|
|
|
794
786
|
excluded_file_patterns: z.array(z.string()).optional().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\"]"),
|
|
795
787
|
});
|
|
796
788
|
export const GetMergeRequestSchema = ProjectParamsSchema.extend({
|
|
797
|
-
merge_request_iid: z.string().
|
|
789
|
+
merge_request_iid: z.coerce.string().optional().describe("The IID of a merge request"),
|
|
798
790
|
source_branch: z.string().optional().describe("Source branch name"),
|
|
799
791
|
});
|
|
800
792
|
export const UpdateMergeRequestSchema = GetMergeRequestSchema.extend({
|
|
@@ -830,19 +822,19 @@ export const ListMergeRequestDiffsSchema = GetMergeRequestSchema.extend({
|
|
|
830
822
|
unidiff: flexibleBoolean.optional().describe("Present diffs in the unified diff format. Default is false. Introduced in GitLab 16.5."),
|
|
831
823
|
});
|
|
832
824
|
export const CreateNoteSchema = z.object({
|
|
833
|
-
project_id: z.string().describe("Project ID or namespace/project_path"),
|
|
825
|
+
project_id: z.coerce.string().describe("Project ID or namespace/project_path"),
|
|
834
826
|
noteable_type: z
|
|
835
827
|
.enum(["issue", "merge_request"])
|
|
836
828
|
.describe("Type of noteable (issue or merge_request)"),
|
|
837
|
-
noteable_iid: z.coerce.
|
|
829
|
+
noteable_iid: z.coerce.string().describe("IID of the issue or merge request"),
|
|
838
830
|
body: z.string().describe("Note content"),
|
|
839
831
|
});
|
|
840
832
|
// Issues API operation schemas
|
|
841
833
|
export const ListIssuesSchema = z.object({
|
|
842
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
843
|
-
assignee_id: z.string().
|
|
834
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
835
|
+
assignee_id: z.coerce.string().optional().describe("Return issues assigned to the given user ID. user id or none or any"),
|
|
844
836
|
assignee_username: z.array(z.string()).optional().describe("Return issues assigned to the given username"),
|
|
845
|
-
author_id: z.string().
|
|
837
|
+
author_id: z.coerce.string().optional().describe("Return issues created by the given user ID"),
|
|
846
838
|
author_username: z.string().optional().describe("Return issues created by the given username"),
|
|
847
839
|
confidential: flexibleBoolean.optional().describe("Filter confidential or public issues"),
|
|
848
840
|
created_after: z.string().optional().describe("Return issues created after the given time"),
|
|
@@ -865,24 +857,20 @@ export const ListIssuesSchema = z.object({
|
|
|
865
857
|
}).merge(PaginationOptionsSchema);
|
|
866
858
|
// Merge Requests API operation schemas
|
|
867
859
|
export const ListMergeRequestsSchema = z.object({
|
|
868
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
869
|
-
assignee_id: z
|
|
870
|
-
.number()
|
|
871
|
-
.optional()
|
|
872
|
-
.describe("Returns merge requests assigned to the given user ID"),
|
|
860
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
861
|
+
assignee_id: z.coerce.string().optional().describe("Return issues assigned to the given user ID. user id or none or any"),
|
|
873
862
|
assignee_username: z
|
|
874
863
|
.string()
|
|
875
864
|
.optional()
|
|
876
865
|
.describe("Returns merge requests assigned to the given username"),
|
|
877
|
-
author_id: z.string().
|
|
866
|
+
author_id: z.coerce.string().optional().describe("Returns merge requests created by the given user ID"),
|
|
878
867
|
author_username: z
|
|
879
868
|
.string()
|
|
880
869
|
.optional()
|
|
881
870
|
.describe("Returns merge requests created by the given username"),
|
|
882
|
-
reviewer_id: z
|
|
883
|
-
.number()
|
|
871
|
+
reviewer_id: z.coerce.string()
|
|
884
872
|
.optional()
|
|
885
|
-
.describe("Returns merge requests which have the user as a reviewer"),
|
|
873
|
+
.describe("Returns merge requests which have the user as a reviewer. user id or none or any"),
|
|
886
874
|
reviewer_username: z
|
|
887
875
|
.string()
|
|
888
876
|
.optional()
|
|
@@ -934,12 +922,12 @@ export const ListMergeRequestsSchema = z.object({
|
|
|
934
922
|
with_labels_details: flexibleBoolean.optional().describe("Return more details for each label"),
|
|
935
923
|
}).merge(PaginationOptionsSchema);
|
|
936
924
|
export const GetIssueSchema = z.object({
|
|
937
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
938
|
-
issue_iid: z.string().
|
|
925
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
926
|
+
issue_iid: z.coerce.string().describe("The internal ID of the project issue"),
|
|
939
927
|
});
|
|
940
928
|
export const UpdateIssueSchema = z.object({
|
|
941
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
942
|
-
issue_iid: z.string().
|
|
929
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
930
|
+
issue_iid: z.coerce.string().describe("The internal ID of the project issue"),
|
|
943
931
|
title: z.string().optional().describe("The title of the issue"),
|
|
944
932
|
description: z.string().optional().describe("The description of the issue"),
|
|
945
933
|
assignee_ids: z.array(z.number()).optional().describe("Array of user IDs to assign issue to"),
|
|
@@ -947,13 +935,13 @@ export const UpdateIssueSchema = z.object({
|
|
|
947
935
|
discussion_locked: flexibleBoolean.optional().describe("Flag to lock discussions"),
|
|
948
936
|
due_date: z.string().optional().describe("Date the issue is due (YYYY-MM-DD)"),
|
|
949
937
|
labels: z.array(z.string()).optional().describe("Array of label names"),
|
|
950
|
-
milestone_id: z.string().
|
|
938
|
+
milestone_id: z.coerce.string().optional().describe("Milestone ID to assign"),
|
|
951
939
|
state_event: z.enum(["close", "reopen"]).optional().describe("Update issue state (close/reopen)"),
|
|
952
940
|
weight: z.number().optional().describe("Weight of the issue (0-9)"),
|
|
953
941
|
});
|
|
954
942
|
export const DeleteIssueSchema = z.object({
|
|
955
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
956
|
-
issue_iid: z.string().
|
|
943
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
944
|
+
issue_iid: z.coerce.string().describe("The internal ID of the project issue"),
|
|
957
945
|
});
|
|
958
946
|
// Issue links related schemas
|
|
959
947
|
export const GitLabIssueLinkSchema = z.object({
|
|
@@ -962,28 +950,28 @@ export const GitLabIssueLinkSchema = z.object({
|
|
|
962
950
|
link_type: z.enum(["relates_to", "blocks", "is_blocked_by"]),
|
|
963
951
|
});
|
|
964
952
|
export const ListIssueLinksSchema = z.object({
|
|
965
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
966
|
-
issue_iid: z.string().
|
|
953
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
954
|
+
issue_iid: z.coerce.string().describe("The internal ID of a project's issue"),
|
|
967
955
|
});
|
|
968
956
|
export const GetIssueLinkSchema = z.object({
|
|
969
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
970
|
-
issue_iid: z.string().
|
|
971
|
-
issue_link_id: z.string().
|
|
957
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
958
|
+
issue_iid: z.coerce.string().describe("The internal ID of a project's issue"),
|
|
959
|
+
issue_link_id: z.coerce.string().describe("ID of an issue relationship"),
|
|
972
960
|
});
|
|
973
961
|
export const CreateIssueLinkSchema = z.object({
|
|
974
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
975
|
-
issue_iid: z.string().
|
|
976
|
-
target_project_id: z.string().describe("The ID or URL-encoded path of a target project"),
|
|
977
|
-
target_issue_iid: z.string().
|
|
962
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
963
|
+
issue_iid: z.coerce.string().describe("The internal ID of a project's issue"),
|
|
964
|
+
target_project_id: z.coerce.string().describe("The ID or URL-encoded path of a target project"),
|
|
965
|
+
target_issue_iid: z.coerce.string().describe("The internal ID of a target project's issue"),
|
|
978
966
|
link_type: z
|
|
979
967
|
.enum(["relates_to", "blocks", "is_blocked_by"])
|
|
980
968
|
.optional()
|
|
981
969
|
.describe("The type of the relation, defaults to relates_to"),
|
|
982
970
|
});
|
|
983
971
|
export const DeleteIssueLinkSchema = z.object({
|
|
984
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
985
|
-
issue_iid: z.string().
|
|
986
|
-
issue_link_id: z.string().
|
|
972
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
973
|
+
issue_iid: z.coerce.string().describe("The internal ID of a project's issue"),
|
|
974
|
+
issue_link_id: z.coerce.string().describe("The ID of an issue relationship"),
|
|
987
975
|
});
|
|
988
976
|
// Namespace API operation schemas
|
|
989
977
|
export const ListNamespacesSchema = z.object({
|
|
@@ -991,14 +979,14 @@ export const ListNamespacesSchema = z.object({
|
|
|
991
979
|
owned: flexibleBoolean.optional().describe("Filter for namespaces owned by current user"),
|
|
992
980
|
}).merge(PaginationOptionsSchema);
|
|
993
981
|
export const GetNamespaceSchema = z.object({
|
|
994
|
-
namespace_id: z.string().describe("Namespace ID or full path"),
|
|
982
|
+
namespace_id: z.coerce.string().describe("Namespace ID or full path"),
|
|
995
983
|
});
|
|
996
984
|
export const VerifyNamespaceSchema = z.object({
|
|
997
985
|
path: z.string().describe("Namespace path to verify"),
|
|
998
986
|
});
|
|
999
987
|
// Project API operation schemas
|
|
1000
988
|
export const GetProjectSchema = z.object({
|
|
1001
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
989
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1002
990
|
});
|
|
1003
991
|
export const ListProjectsSchema = z.object({
|
|
1004
992
|
search: z.string().optional().describe("Search term for projects"),
|
|
@@ -1031,7 +1019,7 @@ export const ListProjectsSchema = z.object({
|
|
|
1031
1019
|
}).merge(PaginationOptionsSchema);
|
|
1032
1020
|
// Label operation schemas
|
|
1033
1021
|
export const ListLabelsSchema = z.object({
|
|
1034
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1022
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1035
1023
|
with_counts: z
|
|
1036
1024
|
.boolean()
|
|
1037
1025
|
.optional()
|
|
@@ -1040,12 +1028,12 @@ export const ListLabelsSchema = z.object({
|
|
|
1040
1028
|
search: z.string().optional().describe("Keyword to filter labels by"),
|
|
1041
1029
|
});
|
|
1042
1030
|
export const GetLabelSchema = z.object({
|
|
1043
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1044
|
-
label_id: z.string().describe("The ID or title of a project's label"),
|
|
1031
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1032
|
+
label_id: z.coerce.string().describe("The ID or title of a project's label"),
|
|
1045
1033
|
include_ancestor_groups: flexibleBoolean.optional().describe("Include ancestor groups"),
|
|
1046
1034
|
});
|
|
1047
1035
|
export const CreateLabelSchema = z.object({
|
|
1048
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1036
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1049
1037
|
name: z.string().describe("The name of the label"),
|
|
1050
1038
|
color: z
|
|
1051
1039
|
.string()
|
|
@@ -1054,8 +1042,8 @@ export const CreateLabelSchema = z.object({
|
|
|
1054
1042
|
priority: z.number().nullable().optional().describe("The priority of the label"),
|
|
1055
1043
|
});
|
|
1056
1044
|
export const UpdateLabelSchema = z.object({
|
|
1057
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1058
|
-
label_id: z.string().describe("The ID or title of a project's label"),
|
|
1045
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1046
|
+
label_id: z.coerce.string().describe("The ID or title of a project's label"),
|
|
1059
1047
|
new_name: z.string().optional().describe("The new name of the label"),
|
|
1060
1048
|
color: z
|
|
1061
1049
|
.string()
|
|
@@ -1065,12 +1053,12 @@ export const UpdateLabelSchema = z.object({
|
|
|
1065
1053
|
priority: z.number().nullable().optional().describe("The new priority of the label"),
|
|
1066
1054
|
});
|
|
1067
1055
|
export const DeleteLabelSchema = z.object({
|
|
1068
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1069
|
-
label_id: z.string().describe("The ID or title of a project's label"),
|
|
1056
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1057
|
+
label_id: z.coerce.string().describe("The ID or title of a project's label"),
|
|
1070
1058
|
});
|
|
1071
1059
|
// Group projects schema
|
|
1072
1060
|
export const ListGroupProjectsSchema = z.object({
|
|
1073
|
-
group_id: z.string().describe("Group ID or path"),
|
|
1061
|
+
group_id: z.coerce.string().describe("Group ID or path"),
|
|
1074
1062
|
include_subgroups: flexibleBoolean.optional().describe("Include projects from subgroups"),
|
|
1075
1063
|
search: z.string().optional().describe("Search term to filter projects"),
|
|
1076
1064
|
order_by: z
|
|
@@ -1100,28 +1088,28 @@ export const ListGroupProjectsSchema = z.object({
|
|
|
1100
1088
|
}).merge(PaginationOptionsSchema);
|
|
1101
1089
|
// Add wiki operation schemas
|
|
1102
1090
|
export const ListWikiPagesSchema = z.object({
|
|
1103
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1091
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1104
1092
|
with_content: flexibleBoolean.optional().describe("Include content of the wiki pages"),
|
|
1105
1093
|
}).merge(PaginationOptionsSchema);
|
|
1106
1094
|
export const GetWikiPageSchema = z.object({
|
|
1107
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1095
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1108
1096
|
slug: z.string().describe("URL-encoded slug of the wiki page"),
|
|
1109
1097
|
});
|
|
1110
1098
|
export const CreateWikiPageSchema = z.object({
|
|
1111
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1099
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1112
1100
|
title: z.string().describe("Title of the wiki page"),
|
|
1113
1101
|
content: z.string().describe("Content of the wiki page"),
|
|
1114
1102
|
format: z.string().optional().describe("Content format, e.g., markdown, rdoc"),
|
|
1115
1103
|
});
|
|
1116
1104
|
export const UpdateWikiPageSchema = z.object({
|
|
1117
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1105
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1118
1106
|
slug: z.string().describe("URL-encoded slug of the wiki page"),
|
|
1119
1107
|
title: z.string().optional().describe("New title of the wiki page"),
|
|
1120
1108
|
content: z.string().optional().describe("New content of the wiki page"),
|
|
1121
1109
|
format: z.string().optional().describe("Content format, e.g., markdown, rdoc"),
|
|
1122
1110
|
});
|
|
1123
1111
|
export const DeleteWikiPageSchema = z.object({
|
|
1124
|
-
project_id: z.string().describe("Project ID or URL-encoded path"),
|
|
1112
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
1125
1113
|
slug: z.string().describe("URL-encoded slug of the wiki page"),
|
|
1126
1114
|
});
|
|
1127
1115
|
// Define wiki response schemas
|
|
@@ -1151,7 +1139,7 @@ export const MergeRequestThreadPositionSchema = z.object({
|
|
|
1151
1139
|
});
|
|
1152
1140
|
// Schema for creating a new merge request thread
|
|
1153
1141
|
export const CreateMergeRequestThreadSchema = ProjectParamsSchema.extend({
|
|
1154
|
-
merge_request_iid: z.string().
|
|
1142
|
+
merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
|
|
1155
1143
|
body: z.string().describe("The content of the thread"),
|
|
1156
1144
|
position: MergeRequestThreadPositionSchema.optional().describe("Position when creating a diff note"),
|
|
1157
1145
|
created_at: z.string().optional().describe("Date the thread was created at (ISO 8601 format)"),
|
|
@@ -1184,7 +1172,7 @@ export const ListProjectMilestonesSchema = ProjectParamsSchema.extend({
|
|
|
1184
1172
|
}).merge(PaginationOptionsSchema);
|
|
1185
1173
|
// Schema for getting a single milestone
|
|
1186
1174
|
export const GetProjectMilestoneSchema = ProjectParamsSchema.extend({
|
|
1187
|
-
milestone_id: z.string().
|
|
1175
|
+
milestone_id: z.coerce.string().describe("The ID of a project milestone"),
|
|
1188
1176
|
});
|
|
1189
1177
|
// Schema for creating a new milestone
|
|
1190
1178
|
export const CreateProjectMilestoneSchema = ProjectParamsSchema.extend({
|
|
@@ -1216,7 +1204,7 @@ export const PromoteProjectMilestoneSchema = GetProjectMilestoneSchema;
|
|
|
1216
1204
|
export const GetMilestoneBurndownEventsSchema = GetProjectMilestoneSchema.merge(PaginationOptionsSchema);
|
|
1217
1205
|
// Add schemas for commit operations
|
|
1218
1206
|
export const ListCommitsSchema = z.object({
|
|
1219
|
-
project_id: z.string().describe("Project ID or complete URL-encoded path to project"),
|
|
1207
|
+
project_id: z.coerce.string().describe("Project ID or complete URL-encoded path to project"),
|
|
1220
1208
|
ref_name: z.string().optional().describe("The name of a repository branch, tag or revision range, or if not given the default branch"),
|
|
1221
1209
|
since: z.string().optional().describe("Only commits after or on this date are returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ"),
|
|
1222
1210
|
until: z.string().optional().describe("Only commits before or on this date are returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ"),
|
|
@@ -1231,11 +1219,11 @@ export const ListCommitsSchema = z.object({
|
|
|
1231
1219
|
per_page: z.number().optional().describe("Number of items per page (max: 100, default: 20)"),
|
|
1232
1220
|
});
|
|
1233
1221
|
export const GetCommitSchema = z.object({
|
|
1234
|
-
project_id: z.string().describe("Project ID or complete URL-encoded path to project"),
|
|
1222
|
+
project_id: z.coerce.string().describe("Project ID or complete URL-encoded path to project"),
|
|
1235
1223
|
sha: z.string().describe("The commit hash or name of a repository branch or tag"),
|
|
1236
1224
|
stats: flexibleBoolean.optional().describe("Include commit stats"),
|
|
1237
1225
|
});
|
|
1238
1226
|
export const GetCommitDiffSchema = z.object({
|
|
1239
|
-
project_id: z.string().describe("Project ID or complete URL-encoded path to project"),
|
|
1227
|
+
project_id: z.coerce.string().describe("Project ID or complete URL-encoded path to project"),
|
|
1240
1228
|
sha: z.string().describe("The commit hash or name of a repository branch or tag"),
|
|
1241
1229
|
});
|
package/package.json
CHANGED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
|
|
2
|
-
import fetch from 'node-fetch';
|
|
3
|
-
// Integration tests that run against real GitLab API (when credentials are provided)
|
|
4
|
-
const GITLAB_API_URL = process.env.GITLAB_API_URL || 'https://gitlab.com';
|
|
5
|
-
const GITLAB_TOKEN = process.env.GITLAB_TOKEN || '';
|
|
6
|
-
const TEST_PROJECT_ID = process.env.TEST_PROJECT_ID || '';
|
|
7
|
-
const skipIntegrationTests = !GITLAB_TOKEN || !TEST_PROJECT_ID;
|
|
8
|
-
describe('GitLab MCP Server Integration Tests', () => {
|
|
9
|
-
if (skipIntegrationTests) {
|
|
10
|
-
it('should skip integration tests when credentials are missing', () => {
|
|
11
|
-
console.log('Skipping integration tests: Missing GITLAB_TOKEN or TEST_PROJECT_ID');
|
|
12
|
-
expect(true).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
let testIssueId = null;
|
|
17
|
-
let testMRId = null;
|
|
18
|
-
beforeAll(() => {
|
|
19
|
-
console.log('Running integration tests with GitLab API');
|
|
20
|
-
});
|
|
21
|
-
afterAll(async () => {
|
|
22
|
-
// Cleanup: Delete test issue and MR if created
|
|
23
|
-
if (testIssueId) {
|
|
24
|
-
await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/issues/${testIssueId}`, {
|
|
25
|
-
method: 'DELETE',
|
|
26
|
-
headers: {
|
|
27
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
describe('Project Access', () => {
|
|
33
|
-
it('should fetch project information', async () => {
|
|
34
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}`, {
|
|
35
|
-
headers: {
|
|
36
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
37
|
-
'Accept': 'application/json'
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
expect(response.ok).toBe(true);
|
|
41
|
-
const project = await response.json();
|
|
42
|
-
expect(project).toHaveProperty('id');
|
|
43
|
-
expect(project).toHaveProperty('name');
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
describe('Issue Operations', () => {
|
|
47
|
-
it('should create an issue', async () => {
|
|
48
|
-
const issueData = {
|
|
49
|
-
title: `Test Issue ${Date.now()}`,
|
|
50
|
-
description: 'This is a test issue created by MCP integration tests'
|
|
51
|
-
};
|
|
52
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/issues`, {
|
|
53
|
-
method: 'POST',
|
|
54
|
-
headers: {
|
|
55
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
56
|
-
'Accept': 'application/json',
|
|
57
|
-
'Content-Type': 'application/json'
|
|
58
|
-
},
|
|
59
|
-
body: JSON.stringify(issueData)
|
|
60
|
-
});
|
|
61
|
-
expect(response.ok).toBe(true);
|
|
62
|
-
const issue = await response.json();
|
|
63
|
-
expect(issue).toHaveProperty('iid');
|
|
64
|
-
expect(issue.title).toBe(issueData.title);
|
|
65
|
-
testIssueId = issue.iid;
|
|
66
|
-
});
|
|
67
|
-
it('should list issues', async () => {
|
|
68
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/issues?state=opened`, {
|
|
69
|
-
headers: {
|
|
70
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
71
|
-
'Accept': 'application/json'
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
expect(response.ok).toBe(true);
|
|
75
|
-
const issues = await response.json();
|
|
76
|
-
expect(Array.isArray(issues)).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
it('should add a comment to an issue', async () => {
|
|
79
|
-
if (!testIssueId) {
|
|
80
|
-
console.log('Skipping comment test: No test issue created');
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const commentData = {
|
|
84
|
-
body: 'Test comment from MCP integration tests'
|
|
85
|
-
};
|
|
86
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/issues/${testIssueId}/notes`, {
|
|
87
|
-
method: 'POST',
|
|
88
|
-
headers: {
|
|
89
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
90
|
-
'Accept': 'application/json',
|
|
91
|
-
'Content-Type': 'application/json'
|
|
92
|
-
},
|
|
93
|
-
body: JSON.stringify(commentData)
|
|
94
|
-
});
|
|
95
|
-
expect(response.ok).toBe(true);
|
|
96
|
-
const comment = await response.json();
|
|
97
|
-
expect(comment.body).toBe(commentData.body);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
describe('Merge Request Operations', () => {
|
|
101
|
-
it('should list merge requests', async () => {
|
|
102
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/merge_requests?state=opened`, {
|
|
103
|
-
headers: {
|
|
104
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
105
|
-
'Accept': 'application/json'
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
expect(response.ok).toBe(true);
|
|
109
|
-
const mrs = await response.json();
|
|
110
|
-
expect(Array.isArray(mrs)).toBe(true);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
describe('Repository Operations', () => {
|
|
114
|
-
it('should fetch repository branches', async () => {
|
|
115
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/repository/branches`, {
|
|
116
|
-
headers: {
|
|
117
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
118
|
-
'Accept': 'application/json'
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
expect(response.ok).toBe(true);
|
|
122
|
-
const branches = await response.json();
|
|
123
|
-
expect(Array.isArray(branches)).toBe(true);
|
|
124
|
-
expect(branches.length).toBeGreaterThan(0);
|
|
125
|
-
});
|
|
126
|
-
it('should fetch repository commits', async () => {
|
|
127
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/repository/commits?per_page=5`, {
|
|
128
|
-
headers: {
|
|
129
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
130
|
-
'Accept': 'application/json'
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
expect(response.ok).toBe(true);
|
|
134
|
-
const commits = await response.json();
|
|
135
|
-
expect(Array.isArray(commits)).toBe(true);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
describe('Pipeline Operations', () => {
|
|
139
|
-
it('should list pipelines', async () => {
|
|
140
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/pipelines?per_page=5`, {
|
|
141
|
-
headers: {
|
|
142
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
143
|
-
'Accept': 'application/json'
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
expect(response.ok).toBe(true);
|
|
147
|
-
const pipelines = await response.json();
|
|
148
|
-
expect(Array.isArray(pipelines)).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
});
|
package/build/tests/unit.test.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
-
import fetch from 'node-fetch';
|
|
3
|
-
// Mock fetch for unit tests
|
|
4
|
-
jest.mock('node-fetch');
|
|
5
|
-
const mockedFetch = fetch;
|
|
6
|
-
describe('GitLab MCP Server Unit Tests', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
jest.clearAllMocks();
|
|
9
|
-
});
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
jest.restoreAllMocks();
|
|
12
|
-
});
|
|
13
|
-
describe('API URL Construction', () => {
|
|
14
|
-
it('should use plural resource names in API endpoints', () => {
|
|
15
|
-
const projectId = 'test/project';
|
|
16
|
-
const issueIid = 123;
|
|
17
|
-
// Test issue endpoint
|
|
18
|
-
const issueUrl = `/api/v4/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`;
|
|
19
|
-
expect(issueUrl).toContain('/issues/');
|
|
20
|
-
expect(issueUrl).not.toContain('/issue/');
|
|
21
|
-
// Test merge request endpoint
|
|
22
|
-
const mrUrl = `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${issueIid}`;
|
|
23
|
-
expect(mrUrl).toContain('/merge_requests/');
|
|
24
|
-
expect(mrUrl).not.toContain('/merge_request/');
|
|
25
|
-
});
|
|
26
|
-
it('should properly encode project IDs with special characters', () => {
|
|
27
|
-
const projectId = 'namespace/project-name';
|
|
28
|
-
const encoded = encodeURIComponent(projectId);
|
|
29
|
-
expect(encoded).toBe('namespace%2Fproject-name');
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
describe('API Response Handling', () => {
|
|
33
|
-
it('should handle successful responses', async () => {
|
|
34
|
-
const mockResponse = {
|
|
35
|
-
ok: true,
|
|
36
|
-
status: 200,
|
|
37
|
-
json: async () => ({ id: 1, title: 'Test Issue' })
|
|
38
|
-
};
|
|
39
|
-
mockedFetch.mockResolvedValueOnce(mockResponse);
|
|
40
|
-
const response = await fetch('https://gitlab.com/api/v4/projects/1/issues/1');
|
|
41
|
-
const data = await response.json();
|
|
42
|
-
expect(response.ok).toBe(true);
|
|
43
|
-
expect(data).toEqual({ id: 1, title: 'Test Issue' });
|
|
44
|
-
});
|
|
45
|
-
it('should handle error responses', async () => {
|
|
46
|
-
const mockResponse = {
|
|
47
|
-
ok: false,
|
|
48
|
-
status: 404,
|
|
49
|
-
statusText: 'Not Found',
|
|
50
|
-
text: async () => '{"message":"404 Project Not Found"}'
|
|
51
|
-
};
|
|
52
|
-
mockedFetch.mockResolvedValueOnce(mockResponse);
|
|
53
|
-
const response = await fetch('https://gitlab.com/api/v4/projects/999/issues/1');
|
|
54
|
-
expect(response.ok).toBe(false);
|
|
55
|
-
expect(response.status).toBe(404);
|
|
56
|
-
});
|
|
57
|
-
it('should handle rate limiting', async () => {
|
|
58
|
-
const mockResponse = {
|
|
59
|
-
ok: false,
|
|
60
|
-
status: 429,
|
|
61
|
-
statusText: 'Too Many Requests',
|
|
62
|
-
headers: {
|
|
63
|
-
get: (name) => name === 'RateLimit-Reset' ? '1234567890' : null
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
mockedFetch.mockResolvedValueOnce(mockResponse);
|
|
67
|
-
const response = await fetch('https://gitlab.com/api/v4/projects/1/issues');
|
|
68
|
-
expect(response.status).toBe(429);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe('Authentication', () => {
|
|
72
|
-
it('should include Bearer token in Authorization header', () => {
|
|
73
|
-
const token = 'test-token-123';
|
|
74
|
-
const headers = {
|
|
75
|
-
'Authorization': `Bearer ${token}`,
|
|
76
|
-
'Accept': 'application/json',
|
|
77
|
-
'Content-Type': 'application/json'
|
|
78
|
-
};
|
|
79
|
-
expect(headers.Authorization).toBe('Bearer test-token-123');
|
|
80
|
-
});
|
|
81
|
-
it('should handle missing token gracefully', () => {
|
|
82
|
-
const token = process.env.GITLAB_TOKEN || '';
|
|
83
|
-
expect(token).toBeDefined();
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
describe('Data Validation', () => {
|
|
87
|
-
it('should validate required fields for issue creation', () => {
|
|
88
|
-
const validIssue = {
|
|
89
|
-
title: 'Test Issue',
|
|
90
|
-
description: 'Test Description'
|
|
91
|
-
};
|
|
92
|
-
expect(validIssue.title).toBeTruthy();
|
|
93
|
-
expect(validIssue.title.length).toBeGreaterThan(0);
|
|
94
|
-
});
|
|
95
|
-
it('should validate merge request parameters', () => {
|
|
96
|
-
const validMR = {
|
|
97
|
-
source_branch: 'feature-branch',
|
|
98
|
-
target_branch: 'main',
|
|
99
|
-
title: 'Test MR'
|
|
100
|
-
};
|
|
101
|
-
expect(validMR.source_branch).not.toBe(validMR.target_branch);
|
|
102
|
-
expect(validMR.title).toBeTruthy();
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
describe('Error Handling', () => {
|
|
106
|
-
it('should handle network errors', async () => {
|
|
107
|
-
mockedFetch.mockRejectedValueOnce(new Error('Network error'));
|
|
108
|
-
await expect(fetch('https://gitlab.com/api/v4/projects/1/issues'))
|
|
109
|
-
.rejects.toThrow('Network error');
|
|
110
|
-
});
|
|
111
|
-
it('should handle JSON parsing errors', async () => {
|
|
112
|
-
const mockResponse = {
|
|
113
|
-
ok: true,
|
|
114
|
-
status: 200,
|
|
115
|
-
json: async () => { throw new Error('Invalid JSON'); }
|
|
116
|
-
};
|
|
117
|
-
mockedFetch.mockResolvedValueOnce(mockResponse);
|
|
118
|
-
const response = await fetch('https://gitlab.com/api/v4/projects/1/issues');
|
|
119
|
-
await expect(response.json()).rejects.toThrow('Invalid JSON');
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
});
|