@zereight/mcp-gitlab 2.0.30 → 2.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/build/index.js +777 -28
- package/build/oauth.js +16 -4
- package/build/schemas.js +310 -12
- package/build/test/schema-tests.js +311 -0
- package/build/test/test-deployment-tools.js +366 -0
- package/build/test/test-job-artifacts.js +194 -0
- package/build/test/test-merge-request-approval-state-tools.js +171 -0
- package/build/test/test-toolset-filtering.js +7 -6
- package/package.json +3 -2
package/build/oauth.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
|
+
import * as os from "os";
|
|
2
3
|
import * as path from "path";
|
|
3
4
|
import * as http from "http";
|
|
4
5
|
import * as net from "net";
|
|
@@ -82,7 +83,7 @@ export class GitLabOAuth {
|
|
|
82
83
|
constructor(config) {
|
|
83
84
|
this.config = config;
|
|
84
85
|
this.tokenStoragePath =
|
|
85
|
-
config.tokenStoragePath || path.join(
|
|
86
|
+
config.tokenStoragePath || path.join(os.homedir(), ".gitlab-mcp-token.json");
|
|
86
87
|
}
|
|
87
88
|
/**
|
|
88
89
|
* Get the authorization URL for OAuth flow
|
|
@@ -505,9 +506,11 @@ export class GitLabOAuth {
|
|
|
505
506
|
}
|
|
506
507
|
}
|
|
507
508
|
/**
|
|
508
|
-
*
|
|
509
|
+
* Create and initialize a GitLabOAuth client.
|
|
510
|
+
* Performs initial authentication (triggers browser flow if needed).
|
|
511
|
+
* Returns the client instance and the initial access token.
|
|
509
512
|
*/
|
|
510
|
-
export async function
|
|
513
|
+
export async function initializeOAuthClient(gitlabUrl = "https://gitlab.com") {
|
|
511
514
|
const clientId = process.env.GITLAB_OAUTH_CLIENT_ID;
|
|
512
515
|
const clientSecret = process.env.GITLAB_OAUTH_CLIENT_SECRET;
|
|
513
516
|
const redirectUri = process.env.GITLAB_OAUTH_REDIRECT_URI || "http://127.0.0.1:8888/callback";
|
|
@@ -523,5 +526,14 @@ export async function initializeOAuth(gitlabUrl = "https://gitlab.com") {
|
|
|
523
526
|
scopes: ["api"],
|
|
524
527
|
tokenStoragePath,
|
|
525
528
|
});
|
|
526
|
-
|
|
529
|
+
// Single call: triggers browser flow if needed, or reads cached token
|
|
530
|
+
const accessToken = await oauth.getAccessToken();
|
|
531
|
+
return { client: oauth, accessToken };
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Initialize OAuth authentication for GitLab MCP server
|
|
535
|
+
*/
|
|
536
|
+
export async function initializeOAuth(gitlabUrl = "https://gitlab.com") {
|
|
537
|
+
const { accessToken } = await initializeOAuthClient(gitlabUrl);
|
|
538
|
+
return accessToken;
|
|
527
539
|
}
|
package/build/schemas.js
CHANGED
|
@@ -62,6 +62,8 @@ export const GitLabPipelineJobSchema = z.object({
|
|
|
62
62
|
started_at: z.string().nullable().optional(),
|
|
63
63
|
finished_at: z.string().nullable().optional(),
|
|
64
64
|
duration: z.number().nullable().optional(),
|
|
65
|
+
queued_duration: z.number().nullable().optional(),
|
|
66
|
+
failure_reason: z.string().nullable().optional(),
|
|
65
67
|
user: z
|
|
66
68
|
.object({
|
|
67
69
|
id: z.coerce.string(),
|
|
@@ -89,6 +91,19 @@ export const GitLabPipelineJobSchema = z.object({
|
|
|
89
91
|
})
|
|
90
92
|
.optional(),
|
|
91
93
|
web_url: z.string().optional(),
|
|
94
|
+
allow_failure: z.boolean().optional(),
|
|
95
|
+
retried: z.boolean().optional(),
|
|
96
|
+
tag_list: z.array(z.string()).optional(),
|
|
97
|
+
runner: z
|
|
98
|
+
.object({
|
|
99
|
+
id: z.coerce.string().optional(),
|
|
100
|
+
description: z.string().nullable().optional(),
|
|
101
|
+
active: z.boolean().optional(),
|
|
102
|
+
is_shared: z.boolean().optional(),
|
|
103
|
+
runner_type: z.string().optional(),
|
|
104
|
+
})
|
|
105
|
+
.nullable()
|
|
106
|
+
.optional(),
|
|
92
107
|
});
|
|
93
108
|
// Pipeline trigger job (bridge) schema
|
|
94
109
|
export const GitLabPipelineTriggerJobSchema = z.object({
|
|
@@ -247,6 +262,126 @@ export const ListPipelineTriggerJobsSchema = z
|
|
|
247
262
|
.describe("The scope of trigger jobs to show"),
|
|
248
263
|
})
|
|
249
264
|
.merge(PaginationOptionsSchema);
|
|
265
|
+
// Deployment related schemas
|
|
266
|
+
export const GitLabDeploymentSchema = z.object({
|
|
267
|
+
id: z.coerce.string(),
|
|
268
|
+
iid: z.coerce.string().optional(),
|
|
269
|
+
status: z.string(),
|
|
270
|
+
ref: z.string().optional(),
|
|
271
|
+
sha: z.string(),
|
|
272
|
+
created_at: z.string(),
|
|
273
|
+
updated_at: z.string().optional(),
|
|
274
|
+
finished_at: z.string().nullable().optional(),
|
|
275
|
+
environment: z
|
|
276
|
+
.object({
|
|
277
|
+
id: z.coerce.string().optional(),
|
|
278
|
+
name: z.string(),
|
|
279
|
+
slug: z.string().optional(),
|
|
280
|
+
external_url: z.string().nullable().optional(),
|
|
281
|
+
state: z.string().optional(),
|
|
282
|
+
tier: z.string().optional(),
|
|
283
|
+
})
|
|
284
|
+
.optional(),
|
|
285
|
+
deployable: z
|
|
286
|
+
.object({
|
|
287
|
+
id: z.coerce.string().optional(),
|
|
288
|
+
name: z.string().optional(),
|
|
289
|
+
status: z.string().optional(),
|
|
290
|
+
stage: z.string().optional(),
|
|
291
|
+
web_url: z.string().optional(),
|
|
292
|
+
pipeline: z
|
|
293
|
+
.object({
|
|
294
|
+
id: z.coerce.string().optional(),
|
|
295
|
+
status: z.string().optional(),
|
|
296
|
+
ref: z.string().optional(),
|
|
297
|
+
sha: z.string().optional(),
|
|
298
|
+
web_url: z.string().optional(),
|
|
299
|
+
})
|
|
300
|
+
.optional(),
|
|
301
|
+
})
|
|
302
|
+
.nullable()
|
|
303
|
+
.optional(),
|
|
304
|
+
user: z
|
|
305
|
+
.object({
|
|
306
|
+
id: z.coerce.string().optional(),
|
|
307
|
+
username: z.string().optional(),
|
|
308
|
+
name: z.string().optional(),
|
|
309
|
+
avatar_url: z.string().nullable().optional(),
|
|
310
|
+
})
|
|
311
|
+
.optional(),
|
|
312
|
+
web_url: z.string().optional(),
|
|
313
|
+
});
|
|
314
|
+
export const ListDeploymentsSchema = z
|
|
315
|
+
.object({
|
|
316
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
317
|
+
environment: z.string().optional().describe("Filter by environment name"),
|
|
318
|
+
ref: z.string().optional().describe("Filter by ref"),
|
|
319
|
+
sha: z
|
|
320
|
+
.string()
|
|
321
|
+
.optional()
|
|
322
|
+
.describe("Filter by commit SHA (if supported by your GitLab version)"),
|
|
323
|
+
status: z.string().optional().describe("Filter by deployment status"),
|
|
324
|
+
updated_after: z
|
|
325
|
+
.string()
|
|
326
|
+
.optional()
|
|
327
|
+
.describe("Return deployments updated after the specified date"),
|
|
328
|
+
updated_before: z
|
|
329
|
+
.string()
|
|
330
|
+
.optional()
|
|
331
|
+
.describe("Return deployments updated before the specified date"),
|
|
332
|
+
order_by: z
|
|
333
|
+
.enum(["id", "iid", "created_at", "updated_at", "ref", "status", "environment"])
|
|
334
|
+
.optional()
|
|
335
|
+
.describe("Order deployments by"),
|
|
336
|
+
sort: z.enum(["asc", "desc"]).optional().describe("Sort deployments"),
|
|
337
|
+
})
|
|
338
|
+
.merge(PaginationOptionsSchema);
|
|
339
|
+
export const GetDeploymentSchema = z.object({
|
|
340
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
341
|
+
deployment_id: z.coerce.string().describe("The ID of the deployment"),
|
|
342
|
+
});
|
|
343
|
+
// Environment related schemas
|
|
344
|
+
const GitLabEnvironmentLastDeploymentSchema = z.object({
|
|
345
|
+
id: z.coerce.string().optional(),
|
|
346
|
+
iid: z.coerce.string().optional(),
|
|
347
|
+
status: z.string().optional(),
|
|
348
|
+
ref: z.string().optional(),
|
|
349
|
+
sha: z.string().optional(),
|
|
350
|
+
created_at: z.string().optional(),
|
|
351
|
+
updated_at: z.string().optional(),
|
|
352
|
+
web_url: z.string().optional(),
|
|
353
|
+
});
|
|
354
|
+
export const GitLabEnvironmentSchema = z.object({
|
|
355
|
+
id: z.coerce.string(),
|
|
356
|
+
name: z.string(),
|
|
357
|
+
slug: z.string().optional(),
|
|
358
|
+
external_url: z.string().nullable().optional(),
|
|
359
|
+
state: z.string().optional(),
|
|
360
|
+
tier: z.string().optional(),
|
|
361
|
+
environment_type: z.string().optional(),
|
|
362
|
+
created_at: z.string().optional(),
|
|
363
|
+
updated_at: z.string().optional(),
|
|
364
|
+
auto_stop_at: z.string().nullable().optional(),
|
|
365
|
+
enable_advanced_logs_querying: z.boolean().optional(),
|
|
366
|
+
logs_api_path: z.string().optional(),
|
|
367
|
+
web_url: z.string().optional(),
|
|
368
|
+
last_deployment: GitLabEnvironmentLastDeploymentSchema.nullable().optional(),
|
|
369
|
+
});
|
|
370
|
+
export const ListEnvironmentsSchema = z
|
|
371
|
+
.object({
|
|
372
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
373
|
+
name: z.string().optional().describe("Return environments with this exact name"),
|
|
374
|
+
search: z.string().optional().describe("Search environments by name"),
|
|
375
|
+
states: z
|
|
376
|
+
.enum(["available", "stopped"])
|
|
377
|
+
.optional()
|
|
378
|
+
.describe("Filter environments by state"),
|
|
379
|
+
})
|
|
380
|
+
.merge(PaginationOptionsSchema);
|
|
381
|
+
export const GetEnvironmentSchema = z.object({
|
|
382
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
383
|
+
environment_id: z.coerce.string().describe("The ID of the environment"),
|
|
384
|
+
});
|
|
250
385
|
// Schema for creating a new pipeline
|
|
251
386
|
export const CreatePipelineSchema = z.object({
|
|
252
387
|
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
@@ -258,6 +393,10 @@ export const CreatePipelineSchema = z.object({
|
|
|
258
393
|
}))
|
|
259
394
|
.optional()
|
|
260
395
|
.describe("An array of variables to use for the pipeline"),
|
|
396
|
+
inputs: z
|
|
397
|
+
.record(z.string(), z.string())
|
|
398
|
+
.optional()
|
|
399
|
+
.describe("Input parameters for the pipeline (key-value pairs for spec:inputs)"),
|
|
261
400
|
});
|
|
262
401
|
// Schema for retrying a pipeline
|
|
263
402
|
export const RetryPipelineSchema = z.object({
|
|
@@ -436,17 +575,17 @@ export const GitLabRepositorySchema = z.object({
|
|
|
436
575
|
export const GitLabProjectSchema = GitLabRepositorySchema;
|
|
437
576
|
// File content schemas
|
|
438
577
|
export const GitLabFileContentSchema = z.object({
|
|
439
|
-
file_name: z.string(),
|
|
440
|
-
file_path: z.string(),
|
|
441
|
-
size: z.number(),
|
|
578
|
+
file_name: z.string().optional(),
|
|
579
|
+
file_path: z.string(),
|
|
580
|
+
size: z.coerce.number().optional(),
|
|
442
581
|
encoding: z.string(),
|
|
443
582
|
content: z.string(),
|
|
444
|
-
content_sha256: z.string(),
|
|
445
|
-
ref: z.string(),
|
|
446
|
-
blob_id: z.string(),
|
|
447
|
-
commit_id: z.string(),
|
|
448
|
-
last_commit_id: z.string(),
|
|
449
|
-
execute_filemode: z.boolean().optional(),
|
|
583
|
+
content_sha256: z.string().optional(),
|
|
584
|
+
ref: z.string().optional(),
|
|
585
|
+
blob_id: z.string().optional(),
|
|
586
|
+
commit_id: z.string().optional(),
|
|
587
|
+
last_commit_id: z.string().optional(),
|
|
588
|
+
execute_filemode: z.boolean().optional(),
|
|
450
589
|
});
|
|
451
590
|
export const GitLabDirectoryContentSchema = z.object({
|
|
452
591
|
name: z.string(),
|
|
@@ -713,6 +852,15 @@ export const GitLabMergeRequestSchema = z.object({
|
|
|
713
852
|
allow_collaboration: z.boolean().optional(),
|
|
714
853
|
allow_maintainer_to_push: z.boolean().optional(),
|
|
715
854
|
changes_count: z.string().nullable().optional(),
|
|
855
|
+
diverged_commits_count: z.coerce
|
|
856
|
+
.number()
|
|
857
|
+
.nullable()
|
|
858
|
+
.optional()
|
|
859
|
+
.describe("Number of commits the source branch is behind the target branch"),
|
|
860
|
+
rebase_in_progress: z
|
|
861
|
+
.boolean()
|
|
862
|
+
.optional()
|
|
863
|
+
.describe("Whether rebase is currently in progress for this merge request"),
|
|
716
864
|
merge_when_pipeline_succeeds: z.boolean().optional(),
|
|
717
865
|
squash: z.boolean().optional(),
|
|
718
866
|
labels: z.array(z.string()).optional(),
|
|
@@ -953,10 +1101,43 @@ export const CreateRepositorySchema = z.object({
|
|
|
953
1101
|
.describe("Repository visibility level"),
|
|
954
1102
|
initialize_with_readme: z.boolean().optional().describe("Initialize with README.md"),
|
|
955
1103
|
});
|
|
956
|
-
export const GetFileContentsSchema =
|
|
957
|
-
|
|
1104
|
+
export const GetFileContentsSchema = z
|
|
1105
|
+
.object({
|
|
1106
|
+
project_id: z.coerce
|
|
1107
|
+
.string()
|
|
1108
|
+
.optional()
|
|
1109
|
+
.describe("Project ID or URL-encoded path (optional; falls back to env)"),
|
|
1110
|
+
file_path: z
|
|
1111
|
+
.string()
|
|
1112
|
+
.optional()
|
|
1113
|
+
.describe("Path to the file or directory. Takes precedence over 'path' when both are provided"),
|
|
1114
|
+
path: z.string().optional().describe("Alias of file_path"),
|
|
958
1115
|
ref: z.string().optional().describe("Branch/tag/commit to get contents from"),
|
|
959
|
-
})
|
|
1116
|
+
})
|
|
1117
|
+
.superRefine((data, ctx) => {
|
|
1118
|
+
const fp = data.file_path?.trim();
|
|
1119
|
+
const p = data.path?.trim();
|
|
1120
|
+
if (!fp && !p) {
|
|
1121
|
+
ctx.addIssue({
|
|
1122
|
+
code: z.ZodIssueCode.custom,
|
|
1123
|
+
message: "Either 'file_path' or 'path' must be provided",
|
|
1124
|
+
path: ["file_path"],
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
const finalPath = fp && fp.length > 0 ? fp : p ?? "";
|
|
1128
|
+
if (finalPath.trim().length === 0) {
|
|
1129
|
+
ctx.addIssue({
|
|
1130
|
+
code: z.ZodIssueCode.custom,
|
|
1131
|
+
message: "file_path cannot be empty or whitespace",
|
|
1132
|
+
path: ["file_path"],
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
})
|
|
1136
|
+
.transform(data => ({
|
|
1137
|
+
project_id: (data.project_id ?? "").trim() || undefined,
|
|
1138
|
+
file_path: ((data.file_path ?? "").trim() || (data.path ?? "").trim()).trim(),
|
|
1139
|
+
ref: (data.ref ?? "").trim() || undefined,
|
|
1140
|
+
}));
|
|
960
1141
|
export const PushFilesSchema = ProjectParamsSchema.extend({
|
|
961
1142
|
branch: z.string().describe("Branch to push to"),
|
|
962
1143
|
files: z
|
|
@@ -1129,9 +1310,25 @@ export const GitLabApprovalRuleSchema = z.object({
|
|
|
1129
1310
|
.optional(),
|
|
1130
1311
|
approved: z.boolean().optional(),
|
|
1131
1312
|
});
|
|
1313
|
+
export const GitLabMergeRequestApprovalsResponseSchema = z.object({
|
|
1314
|
+
approved: z.boolean().optional(),
|
|
1315
|
+
user_has_approved: z.boolean().optional(),
|
|
1316
|
+
user_can_approve: z.boolean().optional(),
|
|
1317
|
+
approved_by: z
|
|
1318
|
+
.array(z.object({
|
|
1319
|
+
user: GitLabApprovalUserSchema,
|
|
1320
|
+
}))
|
|
1321
|
+
.optional(),
|
|
1322
|
+
});
|
|
1132
1323
|
export const GitLabMergeRequestApprovalStateSchema = z.object({
|
|
1133
1324
|
approval_rules_overwritten: z.boolean().optional(),
|
|
1134
1325
|
rules: z.array(GitLabApprovalRuleSchema).optional(),
|
|
1326
|
+
approved: z.boolean().optional(),
|
|
1327
|
+
user_has_approved: z.boolean().optional(),
|
|
1328
|
+
user_can_approve: z.boolean().optional(),
|
|
1329
|
+
approved_by: z.array(GitLabApprovalUserSchema).optional(),
|
|
1330
|
+
approved_by_usernames: z.array(z.string()).optional(),
|
|
1331
|
+
source_endpoint: z.enum(["approval_state", "approvals"]).optional(),
|
|
1135
1332
|
});
|
|
1136
1333
|
export const GetMergeRequestApprovalStateSchema = ProjectParamsSchema.extend({
|
|
1137
1334
|
merge_request_iid: z.coerce.string().describe("The IID of the merge request"),
|
|
@@ -2210,6 +2407,41 @@ export const CreateReleaseEvidenceSchema = z.object({
|
|
|
2210
2407
|
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
2211
2408
|
tag_name: z.string().describe("The Git tag the release is associated with"),
|
|
2212
2409
|
});
|
|
2410
|
+
// Job Artifacts schemas
|
|
2411
|
+
export const ListJobArtifactsSchema = z.object({
|
|
2412
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
2413
|
+
job_id: z.coerce.string().describe("The ID of the job"),
|
|
2414
|
+
path: z
|
|
2415
|
+
.string()
|
|
2416
|
+
.optional()
|
|
2417
|
+
.describe("Directory path within the artifacts archive (defaults to root)"),
|
|
2418
|
+
recursive: z
|
|
2419
|
+
.boolean()
|
|
2420
|
+
.optional()
|
|
2421
|
+
.describe("Whether to list artifacts recursively"),
|
|
2422
|
+
});
|
|
2423
|
+
export const GitLabArtifactEntrySchema = z.object({
|
|
2424
|
+
name: z.string(),
|
|
2425
|
+
path: z.string(),
|
|
2426
|
+
type: z.enum(["file", "directory"]),
|
|
2427
|
+
size: z.number().optional(),
|
|
2428
|
+
mode: z.string().optional(),
|
|
2429
|
+
});
|
|
2430
|
+
export const DownloadJobArtifactsSchema = z.object({
|
|
2431
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
2432
|
+
job_id: z.coerce.string().describe("The ID of the job"),
|
|
2433
|
+
local_path: z
|
|
2434
|
+
.string()
|
|
2435
|
+
.optional()
|
|
2436
|
+
.describe("Local directory to save the artifact archive (defaults to current directory)"),
|
|
2437
|
+
});
|
|
2438
|
+
export const GetJobArtifactFileSchema = z.object({
|
|
2439
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
2440
|
+
job_id: z.coerce.string().describe("The ID of the job"),
|
|
2441
|
+
artifact_path: z
|
|
2442
|
+
.string()
|
|
2443
|
+
.describe("Path to the file within the artifacts archive"),
|
|
2444
|
+
});
|
|
2213
2445
|
export const DownloadReleaseAssetSchema = z.object({
|
|
2214
2446
|
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
2215
2447
|
tag_name: z.string().describe("The Git tag the release is associated with"),
|
|
@@ -2217,3 +2449,69 @@ export const DownloadReleaseAssetSchema = z.object({
|
|
|
2217
2449
|
.string()
|
|
2218
2450
|
.describe("Path to the release asset file as specified when creating or updating its link"),
|
|
2219
2451
|
});
|
|
2452
|
+
// --- Webhook schemas ---
|
|
2453
|
+
export const ListWebhooksSchema = z
|
|
2454
|
+
.object({
|
|
2455
|
+
project_id: z.coerce
|
|
2456
|
+
.string()
|
|
2457
|
+
.optional()
|
|
2458
|
+
.describe("Project ID or URL-encoded path. Provide either project_id or group_id, not both."),
|
|
2459
|
+
group_id: z.coerce
|
|
2460
|
+
.string()
|
|
2461
|
+
.optional()
|
|
2462
|
+
.describe("Group ID or URL-encoded path. Provide either project_id or group_id, not both."),
|
|
2463
|
+
})
|
|
2464
|
+
.merge(PaginationOptionsSchema)
|
|
2465
|
+
.refine(data => (data.project_id || data.group_id) && !(data.project_id && data.group_id), {
|
|
2466
|
+
message: "Provide exactly one of project_id or group_id",
|
|
2467
|
+
});
|
|
2468
|
+
export const ListWebhookEventsSchema = z
|
|
2469
|
+
.object({
|
|
2470
|
+
project_id: z.coerce
|
|
2471
|
+
.string()
|
|
2472
|
+
.optional()
|
|
2473
|
+
.describe("Project ID or URL-encoded path. Provide either project_id or group_id, not both."),
|
|
2474
|
+
group_id: z.coerce
|
|
2475
|
+
.string()
|
|
2476
|
+
.optional()
|
|
2477
|
+
.describe("Group ID or URL-encoded path. Provide either project_id or group_id, not both."),
|
|
2478
|
+
hook_id: z.coerce.number().describe("ID of the webhook"),
|
|
2479
|
+
status: z
|
|
2480
|
+
.union([z.number(), z.string()])
|
|
2481
|
+
.optional()
|
|
2482
|
+
.describe("Filter by response status code (e.g. 200, 500) or category: successful, client_failure, server_failure"),
|
|
2483
|
+
summary: z
|
|
2484
|
+
.boolean()
|
|
2485
|
+
.optional()
|
|
2486
|
+
.describe("If true, return only summary fields (id, url, trigger, response_status, execution_duration) without full request/response payloads. Recommended for overview queries to avoid huge responses."),
|
|
2487
|
+
per_page: z
|
|
2488
|
+
.number()
|
|
2489
|
+
.max(20)
|
|
2490
|
+
.optional()
|
|
2491
|
+
.default(20)
|
|
2492
|
+
.describe("Number of events per page"),
|
|
2493
|
+
page: z.number().optional().describe("Page number for pagination"),
|
|
2494
|
+
})
|
|
2495
|
+
.refine(data => (data.project_id || data.group_id) && !(data.project_id && data.group_id), {
|
|
2496
|
+
message: "Provide exactly one of project_id or group_id",
|
|
2497
|
+
});
|
|
2498
|
+
export const GetWebhookEventSchema = z
|
|
2499
|
+
.object({
|
|
2500
|
+
project_id: z.coerce
|
|
2501
|
+
.string()
|
|
2502
|
+
.optional()
|
|
2503
|
+
.describe("Project ID or URL-encoded path. Provide either project_id or group_id, not both."),
|
|
2504
|
+
group_id: z.coerce
|
|
2505
|
+
.string()
|
|
2506
|
+
.optional()
|
|
2507
|
+
.describe("Group ID or URL-encoded path. Provide either project_id or group_id, not both."),
|
|
2508
|
+
hook_id: z.coerce.number().describe("ID of the webhook"),
|
|
2509
|
+
event_id: z.coerce.number().describe("ID of the webhook event to retrieve"),
|
|
2510
|
+
page: z
|
|
2511
|
+
.number()
|
|
2512
|
+
.optional()
|
|
2513
|
+
.describe("If known, the page where the event is located (from list_webhook_events). Skips auto-pagination and fetches only this page."),
|
|
2514
|
+
})
|
|
2515
|
+
.refine(data => (data.project_id || data.group_id) && !(data.project_id && data.group_id), {
|
|
2516
|
+
message: "Provide exactly one of project_id or group_id",
|
|
2517
|
+
});
|