gitflic-cli-mcp 0.1.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.
package/tools.mjs ADDED
@@ -0,0 +1,1629 @@
1
+ // Tool definitions for the GitFlic MCP server.
2
+ // Every tool is a thin wrapper around the gitflic CLI — see lib/cmd/*.mjs.
3
+
4
+ export const TOOLS = [
5
+ // ===== meta ============================================================
6
+ {
7
+ name: "gitflic_auth",
8
+ description:
9
+ "Check authentication: returns the current authenticated user (id, username, email, fullName). Use this first to verify the token works.",
10
+ inputSchema: { type: "object", properties: {} },
11
+ handler: () => ["auth"],
12
+ annotations: {
13
+ title: "Auth",
14
+ readOnlyHint: true,
15
+ destructiveHint: false,
16
+ idempotentHint: false,
17
+ openWorldHint: true,
18
+ },
19
+ },
20
+ // ===== auth: multi-account + per-project bindings =====================
21
+ {
22
+ name: "gitflic_auth_login",
23
+ description:
24
+ "Validate a GitFlic API token against /user/me, then store it in the OS keychain (macOS Keychain / Linux libsecret) or chmod-0600 file. Refuses to store an invalid token. Use --as <name> to store under a named account; --stdin to read the token from stdin.",
25
+ inputSchema: {
26
+ type: "object",
27
+ properties: {
28
+ token: { type: "string", description: "API token (else read from stdin if stdin=true)" },
29
+ stdin: { type: "boolean", description: "Read token from stdin" },
30
+ as: { type: "string", description: "Named account (e.g. 'work', 'personal')" },
31
+ apiBase: { type: "string", description: "Override API base (for self-hosted)" },
32
+ },
33
+ },
34
+ handler: (a) => {
35
+ const args = ["auth", "login"];
36
+ if (a.token) args.push("--token", a.token);
37
+ if (a.stdin) args.push("--stdin");
38
+ if (a.as) args.push("--as", a.as);
39
+ if (a.apiBase) args.push("--api-base", a.apiBase);
40
+ return args;
41
+ },
42
+ annotations: {
43
+ title: "Auth Login",
44
+ readOnlyHint: false,
45
+ destructiveHint: true,
46
+ idempotentHint: false,
47
+ openWorldHint: false,
48
+ },
49
+ },
50
+ {
51
+ name: "gitflic_auth_logout",
52
+ description:
53
+ "Remove a stored token. Default removes the active account. Pass --as <name> to remove a specific named account. Pass --all to remove every token (and all per-project bindings).",
54
+ inputSchema: {
55
+ type: "object",
56
+ properties: {
57
+ as: { type: "string", description: "Remove only token:<name>" },
58
+ all: { type: "boolean", description: "Remove every account + binding" },
59
+ },
60
+ },
61
+ handler: (a) => {
62
+ const args = ["auth", "logout"];
63
+ if (a.as) args.push("--as", a.as);
64
+ if (a.all) args.push("--all");
65
+ return args;
66
+ },
67
+ annotations: {
68
+ title: "Auth Logout",
69
+ readOnlyHint: false,
70
+ destructiveHint: true,
71
+ idempotentHint: true,
72
+ openWorldHint: false,
73
+ },
74
+ },
75
+ {
76
+ name: "gitflic_auth_list",
77
+ description:
78
+ "List all known accounts (default + named) with masked token values. Marks the active account with '*'. Use before switching or binding to confirm available identities.",
79
+ inputSchema: { type: "object", properties: {} },
80
+ handler: () => ["auth", "list"],
81
+ annotations: {
82
+ title: "Auth List",
83
+ readOnlyHint: true,
84
+ destructiveHint: false,
85
+ idempotentHint: false,
86
+ openWorldHint: false,
87
+ },
88
+ },
89
+ {
90
+ name: "gitflic_auth_switch",
91
+ description:
92
+ "Make <name> the active account. Subsequent tool calls without a per-project binding resolve to this account's token. Refuses if no token is stored for the named account.",
93
+ inputSchema: {
94
+ type: "object",
95
+ required: ["name"],
96
+ properties: {
97
+ name: { type: "string", description: "Account name to activate" },
98
+ },
99
+ },
100
+ handler: (a) => ["auth", "switch", a.name],
101
+ annotations: {
102
+ title: "Auth Switch",
103
+ readOnlyHint: false,
104
+ destructiveHint: true,
105
+ idempotentHint: true,
106
+ openWorldHint: false,
107
+ },
108
+ },
109
+ {
110
+ name: "gitflic_auth_bind",
111
+ description:
112
+ "Bind <project> (owner/alias) to <account>. All subsequent commands scoped to that project resolve to <account>'s token, overriding the active account. Refuses if <account> has no token stored. Use to route different repos to different identities (work vs personal).",
113
+ inputSchema: {
114
+ type: "object",
115
+ required: ["project", "account"],
116
+ properties: {
117
+ project: { type: "string", description: "owner/alias, e.g. 'FerdBur/wave'" },
118
+ account: { type: "string", description: "account name (must already exist via auth_login --as)" },
119
+ },
120
+ },
121
+ handler: (a) => ["auth", "bind", a.project, a.account],
122
+ annotations: {
123
+ title: "Auth Bind",
124
+ readOnlyHint: false,
125
+ destructiveHint: true,
126
+ idempotentHint: true,
127
+ openWorldHint: false,
128
+ },
129
+ },
130
+ {
131
+ name: "gitflic_auth_unbind",
132
+ description: "Remove the binding for <project>. Falls back to active-account token resolution for that project afterwards.",
133
+ inputSchema: {
134
+ type: "object",
135
+ required: ["project"],
136
+ properties: {
137
+ project: { type: "string", description: "owner/alias to unbind" },
138
+ },
139
+ },
140
+ handler: (a) => ["auth", "unbind", a.project],
141
+ annotations: {
142
+ title: "Auth Unbind",
143
+ readOnlyHint: false,
144
+ destructiveHint: true,
145
+ idempotentHint: true,
146
+ openWorldHint: false,
147
+ },
148
+ },
149
+ {
150
+ name: "gitflic_auth_bindings",
151
+ description: "List all current project → account bindings. Sorted alphabetically by project.",
152
+ inputSchema: { type: "object", properties: {} },
153
+ handler: () => ["auth", "bindings"],
154
+ annotations: {
155
+ title: "Auth Bindings",
156
+ readOnlyHint: true,
157
+ destructiveHint: false,
158
+ idempotentHint: false,
159
+ openWorldHint: false,
160
+ },
161
+ },
162
+ {
163
+ name: "gitflic_project_resolve",
164
+ description:
165
+ "Resolve a project owner/alias to its UUID + defaultBranch. Cached locally; needed before most other calls.",
166
+ inputSchema: {
167
+ type: "object",
168
+ required: ["project"],
169
+ properties: {
170
+ project: { type: "string", description: "owner/alias, e.g. 'tikhon/wave'" },
171
+ },
172
+ },
173
+ handler: (args) => ["project", "get", args.project],
174
+ annotations: {
175
+ title: "Project Resolve",
176
+ readOnlyHint: true,
177
+ destructiveHint: false,
178
+ idempotentHint: true,
179
+ openWorldHint: true,
180
+ },
181
+ },
182
+
183
+ // ===== merge requests ===================================================
184
+ {
185
+ name: "gitflic_mr_list",
186
+ description: "List merge requests in a project. Returns array with localId, title, status, author, source/target branches.",
187
+ inputSchema: {
188
+ type: "object",
189
+ required: ["project"],
190
+ properties: {
191
+ project: { type: "string" },
192
+ page: { type: "number", description: "0-based page index" },
193
+ size: { type: "number", description: "Page size, default 10" },
194
+ },
195
+ },
196
+ handler: (a) => ["mr", "list", "--project", a.project, "--format", "json", a.page !== undefined ? ["--page", String(a.page)] : [], a.size !== undefined ? ["--size", String(a.size)] : []].flat(),
197
+ annotations: {
198
+ title: "Mr List",
199
+ readOnlyHint: true,
200
+ destructiveHint: false,
201
+ idempotentHint: false,
202
+ openWorldHint: true,
203
+ },
204
+ },
205
+ {
206
+ name: "gitflic_mr_view",
207
+ description: "Get one merge request by its localId. Returns full MR object including description, status, conflicts, canMerge.",
208
+ inputSchema: {
209
+ type: "object",
210
+ required: ["project", "id"],
211
+ properties: {
212
+ project: { type: "string" },
213
+ id: { type: "string", description: "localId (e.g. '42')" },
214
+ },
215
+ },
216
+ handler: (a) => ["mr", "view", a.id, "--project", a.project, "--format", "json"],
217
+ annotations: {
218
+ title: "Mr View",
219
+ readOnlyHint: true,
220
+ destructiveHint: false,
221
+ idempotentHint: false,
222
+ openWorldHint: true,
223
+ },
224
+ },
225
+ {
226
+ name: "gitflic_mr_diff",
227
+ description: "Get the file-level diff for a merge request (hunks + per-line annotations).",
228
+ inputSchema: {
229
+ type: "object",
230
+ required: ["project", "id"],
231
+ properties: { project: { type: "string" }, id: { type: "string" } },
232
+ },
233
+ handler: (a) => ["mr", "diff", a.id, "--project", a.project, "--format", "json"],
234
+ annotations: {
235
+ title: "Mr Diff",
236
+ readOnlyHint: true,
237
+ destructiveHint: false,
238
+ idempotentHint: false,
239
+ openWorldHint: true,
240
+ },
241
+ },
242
+ {
243
+ name: "gitflic_mr_create",
244
+ description:
245
+ "Create a new merge request. If --from is omitted, uses current git branch. If --to is omitted, uses project default branch.",
246
+ inputSchema: {
247
+ type: "object",
248
+ required: ["project", "title"],
249
+ properties: {
250
+ project: { type: "string" },
251
+ title: { type: "string" },
252
+ description: { type: "string", description: "Markdown body" },
253
+ from: { type: "string", description: "Source branch (default: current git branch)" },
254
+ to: { type: "string", description: "Target branch (default: project default branch)" },
255
+ squash: { type: "boolean", description: "Squash commits on merge" },
256
+ removeBranch: { type: "boolean", description: "Remove source branch on merge (default true)" },
257
+ },
258
+ },
259
+ handler: (a) => {
260
+ const args = ["mr", "create", "--project", a.project, "--title", a.title, "--format", "json"];
261
+ if (a.description) args.push("--description", a.description);
262
+ if (a.from) args.push("--from", a.from);
263
+ if (a.to) args.push("--to", a.to);
264
+ if (a.squash) args.push("--squash");
265
+ if (a.removeBranch === false) args.push("--no-remove-branch");
266
+ return args;
267
+ },
268
+ annotations: {
269
+ title: "Mr Create",
270
+ readOnlyHint: false,
271
+ destructiveHint: true,
272
+ idempotentHint: true,
273
+ openWorldHint: true,
274
+ },
275
+ },
276
+ {
277
+ name: "gitflic_mr_approve",
278
+ description: "Toggle approve on a merge request (calling twice removes the approval).",
279
+ inputSchema: {
280
+ type: "object",
281
+ required: ["project", "id"],
282
+ properties: { project: { type: "string" }, id: { type: "string" } },
283
+ },
284
+ handler: (a) => ["mr", "approve", a.id, "--project", a.project, "--format", "json"],
285
+ annotations: {
286
+ title: "Mr Approve",
287
+ readOnlyHint: false,
288
+ destructiveHint: true,
289
+ idempotentHint: false,
290
+ openWorldHint: true,
291
+ },
292
+ },
293
+ {
294
+ name: "gitflic_mr_merge",
295
+ description: "Merge a merge request. May fail with MergeCheckException if branch protection requires more approvals.",
296
+ inputSchema: {
297
+ type: "object",
298
+ required: ["project", "id"],
299
+ properties: {
300
+ project: { type: "string" },
301
+ id: { type: "string" },
302
+ message: { type: "string", description: "Merge commit message" },
303
+ squashMessage: { type: "string", description: "Squash commit message (if squash enabled)" },
304
+ },
305
+ },
306
+ handler: (a) => {
307
+ const args = ["mr", "merge", a.id, "--project", a.project, "--format", "json"];
308
+ if (a.message) args.push("--message", a.message);
309
+ if (a.squashMessage) args.push("--squash-message", a.squashMessage);
310
+ return args;
311
+ },
312
+ annotations: {
313
+ title: "Mr Merge",
314
+ readOnlyHint: false,
315
+ destructiveHint: true,
316
+ idempotentHint: false,
317
+ openWorldHint: true,
318
+ },
319
+ },
320
+ {
321
+ name: "gitflic_mr_close",
322
+ description: "Close a merge request.",
323
+ inputSchema: {
324
+ type: "object",
325
+ required: ["project", "id"],
326
+ properties: { project: { type: "string" }, id: { type: "string" } },
327
+ },
328
+ handler: (a) => ["mr", "close", a.id, "--project", a.project, "--format", "json"],
329
+ annotations: {
330
+ title: "Mr Close",
331
+ readOnlyHint: false,
332
+ destructiveHint: true,
333
+ idempotentHint: true,
334
+ openWorldHint: true,
335
+ },
336
+ },
337
+ {
338
+ name: "gitflic_mr_comment",
339
+ description: "Add a comment to a merge request. For inline-anchored comments, pass new_path/new_line and old_path/old_line.",
340
+ inputSchema: {
341
+ type: "object",
342
+ required: ["project", "id", "message"],
343
+ properties: {
344
+ project: { type: "string" },
345
+ id: { type: "string" },
346
+ message: { type: "string" },
347
+ newPath: { type: "string" },
348
+ newLine: { type: "number" },
349
+ oldPath: { type: "string" },
350
+ oldLine: { type: "number" },
351
+ },
352
+ },
353
+ handler: (a) => {
354
+ const args = ["mr", "comment", a.id, a.message, "--project", a.project, "--format", "json"];
355
+ if (a.newPath) args.push("--new-path", a.newPath);
356
+ if (a.newLine !== undefined) args.push("--new-line", String(a.newLine));
357
+ if (a.oldPath) args.push("--old-path", a.oldPath);
358
+ if (a.oldLine !== undefined) args.push("--old-line", String(a.oldLine));
359
+ return args;
360
+ },
361
+ annotations: {
362
+ title: "Mr Comment",
363
+ readOnlyHint: false,
364
+ destructiveHint: true,
365
+ idempotentHint: false,
366
+ openWorldHint: true,
367
+ },
368
+ },
369
+ {
370
+ name: "gitflic_mr_discuss_list",
371
+ description: "List discussions (with replies) on a merge request.",
372
+ inputSchema: {
373
+ type: "object",
374
+ required: ["project", "id"],
375
+ properties: { project: { type: "string" }, id: { type: "string" } },
376
+ },
377
+ handler: (a) => ["mr", "discuss", "list", a.id, "--project", a.project],
378
+ annotations: {
379
+ title: "Mr Discuss List",
380
+ readOnlyHint: true,
381
+ destructiveHint: false,
382
+ idempotentHint: false,
383
+ openWorldHint: true,
384
+ },
385
+ },
386
+ {
387
+ name: "gitflic_mr_discuss_reply",
388
+ description: "Reply to a discussion thread on a merge request.",
389
+ inputSchema: {
390
+ type: "object",
391
+ required: ["project", "id", "discussionUuid", "message"],
392
+ properties: {
393
+ project: { type: "string" },
394
+ id: { type: "string" },
395
+ discussionUuid: { type: "string" },
396
+ message: { type: "string" },
397
+ },
398
+ },
399
+ handler: (a) => ["mr", "discuss", "reply", a.id, a.discussionUuid, a.message, "--project", a.project, "--format", "json"],
400
+ annotations: {
401
+ title: "Mr Discuss Reply",
402
+ readOnlyHint: false,
403
+ destructiveHint: true,
404
+ idempotentHint: false,
405
+ openWorldHint: true,
406
+ },
407
+ },
408
+ {
409
+ name: "gitflic_mr_discuss_resolve",
410
+ description: "Resolve a discussion thread on a merge request.",
411
+ inputSchema: {
412
+ type: "object",
413
+ required: ["project", "id", "discussionUuid"],
414
+ properties: {
415
+ project: { type: "string" },
416
+ id: { type: "string" },
417
+ discussionUuid: { type: "string" },
418
+ },
419
+ },
420
+ handler: (a) => ["mr", "discuss", "resolve", a.id, a.discussionUuid, "--project", a.project, "--format", "json"],
421
+ annotations: {
422
+ title: "Mr Discuss Resolve",
423
+ readOnlyHint: true,
424
+ destructiveHint: false,
425
+ idempotentHint: true,
426
+ openWorldHint: true,
427
+ },
428
+ },
429
+
430
+ // ===== issues ===========================================================
431
+ {
432
+ name: "gitflic_issue_list",
433
+ description: "List issues. Filter by --status if needed.",
434
+ inputSchema: {
435
+ type: "object",
436
+ required: ["project"],
437
+ properties: {
438
+ project: { type: "string" },
439
+ status: { type: "string", enum: ["OPEN", "IN_PROGRESS", "CLOSED", "COMPLETED"] },
440
+ page: { type: "number" },
441
+ size: { type: "number" },
442
+ },
443
+ },
444
+ handler: (a) => {
445
+ const args = ["issue", "list", "--project", a.project, "--format", "json"];
446
+ if (a.status) args.push("--status", a.status);
447
+ if (a.page !== undefined) args.push("--page", String(a.page));
448
+ if (a.size !== undefined) args.push("--size", String(a.size));
449
+ return args;
450
+ },
451
+ annotations: {
452
+ title: "Issue List",
453
+ readOnlyHint: true,
454
+ destructiveHint: false,
455
+ idempotentHint: false,
456
+ openWorldHint: true,
457
+ },
458
+ },
459
+ {
460
+ name: "gitflic_issue_view",
461
+ description: "Get one issue by localId.",
462
+ inputSchema: {
463
+ type: "object",
464
+ required: ["project", "id"],
465
+ properties: { project: { type: "string" }, id: { type: "string" } },
466
+ },
467
+ handler: (a) => ["issue", "view", a.id, "--project", a.project, "--format", "json"],
468
+ annotations: {
469
+ title: "Issue View",
470
+ readOnlyHint: true,
471
+ destructiveHint: false,
472
+ idempotentHint: false,
473
+ openWorldHint: true,
474
+ },
475
+ },
476
+ {
477
+ name: "gitflic_issue_create",
478
+ description: "Create a new issue.",
479
+ inputSchema: {
480
+ type: "object",
481
+ required: ["project", "title", "description"],
482
+ properties: {
483
+ project: { type: "string" },
484
+ title: { type: "string" },
485
+ description: { type: "string" },
486
+ status: { type: "string", enum: ["OPEN", "IN_PROGRESS", "CLOSED", "COMPLETED"], description: "default: OPEN" },
487
+ },
488
+ },
489
+ handler: (a) => {
490
+ const args = ["issue", "create", "--project", a.project, "--title", a.title, "--description", a.description, "--format", "json"];
491
+ if (a.status) args.push("--status", a.status);
492
+ return args;
493
+ },
494
+ annotations: {
495
+ title: "Issue Create",
496
+ readOnlyHint: false,
497
+ destructiveHint: true,
498
+ idempotentHint: true,
499
+ openWorldHint: true,
500
+ },
501
+ },
502
+ {
503
+ name: "gitflic_issue_close",
504
+ description: "Close an issue (shortcut: edit with --status CLOSED).",
505
+ inputSchema: {
506
+ type: "object",
507
+ required: ["project", "id"],
508
+ properties: { project: { type: "string" }, id: { type: "string" } },
509
+ },
510
+ handler: (a) => ["issue", "close", a.id, "--project", a.project, "--format", "json"],
511
+ annotations: {
512
+ title: "Issue Close",
513
+ readOnlyHint: false,
514
+ destructiveHint: true,
515
+ idempotentHint: true,
516
+ openWorldHint: true,
517
+ },
518
+ },
519
+ {
520
+ name: "gitflic_issue_comment",
521
+ description: "Add a comment to an issue.",
522
+ inputSchema: {
523
+ type: "object",
524
+ required: ["project", "id", "message"],
525
+ properties: { project: { type: "string" }, id: { type: "string" }, message: { type: "string" } },
526
+ },
527
+ handler: (a) => ["issue", "comments", "add", a.id, a.message, "--project", a.project, "--format", "json"],
528
+ annotations: {
529
+ title: "Issue Comment",
530
+ readOnlyHint: false,
531
+ destructiveHint: true,
532
+ idempotentHint: false,
533
+ openWorldHint: true,
534
+ },
535
+ },
536
+
537
+ // ===== branches / tags / releases ======================================
538
+ {
539
+ name: "gitflic_branch_list",
540
+ description: "List branches in a project.",
541
+ inputSchema: {
542
+ type: "object",
543
+ required: ["project"],
544
+ properties: { project: { type: "string" } },
545
+ },
546
+ handler: (a) => ["branch", "list", "--project", a.project],
547
+ annotations: {
548
+ title: "Branch List",
549
+ readOnlyHint: true,
550
+ destructiveHint: false,
551
+ idempotentHint: false,
552
+ openWorldHint: true,
553
+ },
554
+ },
555
+ {
556
+ name: "gitflic_branch_create",
557
+ description: "Create a new branch from an existing one.",
558
+ inputSchema: {
559
+ type: "object",
560
+ required: ["project", "newBranch"],
561
+ properties: {
562
+ project: { type: "string" },
563
+ newBranch: { type: "string" },
564
+ from: { type: "string", description: "Origin branch (default: master)" },
565
+ },
566
+ },
567
+ handler: (a) => {
568
+ const args = ["branch", "create", a.newBranch, "--project", a.project, "--format", "json"];
569
+ if (a.from) args.push("--from", a.from);
570
+ return args;
571
+ },
572
+ annotations: {
573
+ title: "Branch Create",
574
+ readOnlyHint: false,
575
+ destructiveHint: true,
576
+ idempotentHint: true,
577
+ openWorldHint: true,
578
+ },
579
+ },
580
+ {
581
+ name: "gitflic_branch_delete",
582
+ description: "Delete a branch.",
583
+ inputSchema: {
584
+ type: "object",
585
+ required: ["project", "name"],
586
+ properties: { project: { type: "string" }, name: { type: "string" } },
587
+ },
588
+ handler: (a) => ["branch", "delete", a.name, "--project", a.project],
589
+ annotations: {
590
+ title: "Branch Delete",
591
+ readOnlyHint: false,
592
+ destructiveHint: true,
593
+ idempotentHint: true,
594
+ openWorldHint: true,
595
+ },
596
+ },
597
+ {
598
+ name: "gitflic_tag_list",
599
+ description: "List tags.",
600
+ inputSchema: {
601
+ type: "object",
602
+ required: ["project"],
603
+ properties: { project: { type: "string" } },
604
+ },
605
+ handler: (a) => ["tag", "list", "--project", a.project],
606
+ annotations: {
607
+ title: "Tag List",
608
+ readOnlyHint: true,
609
+ destructiveHint: false,
610
+ idempotentHint: false,
611
+ openWorldHint: true,
612
+ },
613
+ },
614
+ {
615
+ name: "gitflic_tag_create",
616
+ description: "Create a new tag (lightweight or annotated).",
617
+ inputSchema: {
618
+ type: "object",
619
+ required: ["project", "name"],
620
+ properties: {
621
+ project: { type: "string" },
622
+ name: { type: "string" },
623
+ branch: { type: "string", description: "Branch to tag (default: master)" },
624
+ commit: { type: "string", description: "Commit hash to tag (overrides --branch)" },
625
+ message: { type: "string", description: "Annotation message" },
626
+ },
627
+ },
628
+ handler: (a) => {
629
+ const args = ["tag", "create", a.name, "--project", a.project, "--format", "json"];
630
+ if (a.commit) args.push("--commit", a.commit);
631
+ else if (a.branch) args.push("--branch", a.branch);
632
+ if (a.message) args.push("--message", a.message);
633
+ return args;
634
+ },
635
+ annotations: {
636
+ title: "Tag Create",
637
+ readOnlyHint: false,
638
+ destructiveHint: true,
639
+ idempotentHint: true,
640
+ openWorldHint: true,
641
+ },
642
+ },
643
+ {
644
+ name: "gitflic_release_list",
645
+ description: "List releases.",
646
+ inputSchema: {
647
+ type: "object",
648
+ required: ["project"],
649
+ properties: { project: { type: "string" } },
650
+ },
651
+ handler: (a) => ["release", "list", "--project", a.project],
652
+ annotations: {
653
+ title: "Release List",
654
+ readOnlyHint: true,
655
+ destructiveHint: false,
656
+ idempotentHint: false,
657
+ openWorldHint: true,
658
+ },
659
+ },
660
+ {
661
+ name: "gitflic_release_create",
662
+ description: "Create a new release.",
663
+ inputSchema: {
664
+ type: "object",
665
+ required: ["project", "title"],
666
+ properties: {
667
+ project: { type: "string" },
668
+ title: { type: "string" },
669
+ description: { type: "string" },
670
+ tag: { type: "string", description: "Tag name (e.g. v1.0.0)" },
671
+ preRelease: { type: "boolean" },
672
+ },
673
+ },
674
+ handler: (a) => {
675
+ const args = ["release", "create", "--project", a.project, "--title", a.title, "--format", "json"];
676
+ if (a.description) args.push("--description", a.description);
677
+ if (a.tag) args.push("--tag", a.tag);
678
+ if (a.preRelease) args.push("--pre-release");
679
+ return args;
680
+ },
681
+ annotations: {
682
+ title: "Release Create",
683
+ readOnlyHint: false,
684
+ destructiveHint: true,
685
+ idempotentHint: true,
686
+ openWorldHint: true,
687
+ },
688
+ },
689
+
690
+ // ===== commit / user ====================================================
691
+ {
692
+ name: "gitflic_commit_list",
693
+ description: "List commits (default branch or --branch).",
694
+ inputSchema: {
695
+ type: "object",
696
+ required: ["project"],
697
+ properties: {
698
+ project: { type: "string" },
699
+ branch: { type: "string" },
700
+ },
701
+ },
702
+ handler: (a) => {
703
+ const args = ["commit", "list", "--project", a.project];
704
+ if (a.branch) args.push("--branch", a.branch);
705
+ return args;
706
+ },
707
+ annotations: {
708
+ title: "Commit List",
709
+ readOnlyHint: true,
710
+ destructiveHint: false,
711
+ idempotentHint: false,
712
+ openWorldHint: true,
713
+ },
714
+ },
715
+ {
716
+ name: "gitflic_user_me",
717
+ description: "Get the current authenticated user (id, username, email, name, fullName).",
718
+ inputSchema: { type: "object", properties: {} },
719
+ handler: () => ["user", "me", "--format", "json"],
720
+ annotations: {
721
+ title: "User Me",
722
+ readOnlyHint: true,
723
+ destructiveHint: false,
724
+ idempotentHint: false,
725
+ openWorldHint: true,
726
+ },
727
+ },
728
+ {
729
+ name: "gitflic_user_search",
730
+ description: "Search users by username substring.",
731
+ inputSchema: {
732
+ type: "object",
733
+ required: ["q"],
734
+ properties: { q: { type: "string" } },
735
+ },
736
+ handler: (a) => ["user", "search", "--q", a.q, "--format", "json"],
737
+ annotations: {
738
+ title: "User Search",
739
+ readOnlyHint: true,
740
+ destructiveHint: false,
741
+ idempotentHint: false,
742
+ openWorldHint: true,
743
+ },
744
+ },
745
+
746
+ // ===== project admin ====================================================
747
+ {
748
+ name: "gitflic_project_list_my",
749
+ description: "List projects where the current user is owner/member.",
750
+ inputSchema: { type: "object", properties: {} },
751
+ handler: () => ["project", "list", "--scope", "my"],
752
+ annotations: {
753
+ title: "Project List My",
754
+ readOnlyHint: false,
755
+ destructiveHint: true,
756
+ idempotentHint: false,
757
+ openWorldHint: true,
758
+ },
759
+ },
760
+ {
761
+ name: "gitflic_webhook_list",
762
+ description: "List webhooks for a project.",
763
+ inputSchema: {
764
+ type: "object",
765
+ required: ["project"],
766
+ properties: { project: { type: "string" } },
767
+ },
768
+ handler: (a) => ["webhook", "list", "--project", a.project],
769
+ annotations: {
770
+ title: "Webhook List",
771
+ readOnlyHint: true,
772
+ destructiveHint: false,
773
+ idempotentHint: false,
774
+ openWorldHint: true,
775
+ },
776
+ },
777
+ {
778
+ name: "gitflic_branch_protection_list",
779
+ description: "List branch protection rules.",
780
+ inputSchema: {
781
+ type: "object",
782
+ required: ["project"],
783
+ properties: { project: { type: "string" } },
784
+ },
785
+ handler: (a) => ["branch", "protection", "list", "--project", a.project],
786
+ annotations: {
787
+ title: "Branch Protection List",
788
+ readOnlyHint: true,
789
+ destructiveHint: false,
790
+ idempotentHint: false,
791
+ openWorldHint: true,
792
+ },
793
+ },
794
+
795
+ // ===== MR: edits / cancel / by-commit / discuss delete ==================
796
+ {
797
+ name: "gitflic_mr_edit",
798
+ description: "Edit MR title/description/target. Source branch cannot change.",
799
+ inputSchema: {
800
+ type: "object",
801
+ required: ["project", "id"],
802
+ properties: {
803
+ project: { type: "string" }, id: { type: "string" },
804
+ title: { type: "string" }, description: { type: "string" },
805
+ to: { type: "string" }, squash: { type: "boolean" },
806
+ removeBranch: { type: "boolean" },
807
+ },
808
+ },
809
+ handler: (a) => {
810
+ const args = ["mr", "edit", a.id, "--project", a.project, "--format", "json"];
811
+ if (a.title) args.push("--title", a.title);
812
+ if (a.description !== undefined) args.push("--description", a.description);
813
+ if (a.to) args.push("--to", a.to);
814
+ if (a.squash) args.push("--squash");
815
+ if (a.removeBranch === false) args.push("--no-remove-branch");
816
+ return args;
817
+ },
818
+ annotations: {
819
+ title: "Mr Edit",
820
+ readOnlyHint: false,
821
+ destructiveHint: true,
822
+ idempotentHint: true,
823
+ openWorldHint: true,
824
+ },
825
+ },
826
+ {
827
+ name: "gitflic_mr_cancel",
828
+ description: "Cancel a merge request.",
829
+ inputSchema: {
830
+ type: "object",
831
+ required: ["project", "id"],
832
+ properties: { project: { type: "string" }, id: { type: "string" } },
833
+ },
834
+ handler: (a) => ["mr", "cancel", a.id, "--project", a.project, "--format", "json"],
835
+ annotations: {
836
+ title: "Mr Cancel",
837
+ readOnlyHint: false,
838
+ destructiveHint: true,
839
+ idempotentHint: true,
840
+ openWorldHint: true,
841
+ },
842
+ },
843
+ {
844
+ name: "gitflic_mr_by_commit",
845
+ description: "Find MR(s) containing a specific commit hash. Optionally filter by status.",
846
+ inputSchema: {
847
+ type: "object",
848
+ required: ["project", "hash"],
849
+ properties: {
850
+ project: { type: "string" },
851
+ hash: { type: "string" },
852
+ status: { type: "string", enum: ["OPENED", "MERGED", "CLOSED", "CANCELED", "FAILED"] },
853
+ },
854
+ },
855
+ handler: (a) => {
856
+ const args = ["mr", "by-commit", a.hash, "--project", a.project, "--format", "json"];
857
+ if (a.status) args.push("--status", a.status);
858
+ return args;
859
+ },
860
+ annotations: {
861
+ title: "Mr By Commit",
862
+ readOnlyHint: false,
863
+ destructiveHint: true,
864
+ idempotentHint: false,
865
+ openWorldHint: true,
866
+ },
867
+ },
868
+ {
869
+ name: "gitflic_mr_discuss_delete",
870
+ description: "Delete a discussion thread on a merge request.",
871
+ inputSchema: {
872
+ type: "object",
873
+ required: ["project", "id", "discussionUuid"],
874
+ properties: { project: { type: "string" }, id: { type: "string" }, discussionUuid: { type: "string" } },
875
+ },
876
+ handler: (a) => ["mr", "discuss", "delete", a.id, a.discussionUuid, "--project", a.project, "--format", "json"],
877
+ annotations: {
878
+ title: "Mr Discuss Delete",
879
+ readOnlyHint: false,
880
+ destructiveHint: true,
881
+ idempotentHint: true,
882
+ openWorldHint: true,
883
+ },
884
+ },
885
+
886
+ // ===== Issue: edit / reopen / delete / comments / relations ============
887
+ {
888
+ name: "gitflic_issue_edit",
889
+ description: "Edit an issue's title, description, status, assignee, or labels.",
890
+ inputSchema: {
891
+ type: "object",
892
+ required: ["project", "id"],
893
+ properties: {
894
+ project: { type: "string" }, id: { type: "string" },
895
+ title: { type: "string" }, description: { type: "string" },
896
+ status: { type: "string", enum: ["OPEN", "IN_PROGRESS", "CLOSED", "COMPLETED"] },
897
+ assignTo: { type: "string", description: "user uuid" },
898
+ label: { type: "string", description: "label uuid" },
899
+ },
900
+ },
901
+ handler: (a) => {
902
+ const args = ["issue", "edit", a.id, "--project", a.project, "--format", "json"];
903
+ if (a.title) args.push("--title", a.title);
904
+ if (a.description !== undefined) args.push("--description", a.description);
905
+ if (a.status) args.push("--status", a.status);
906
+ if (a.assignTo) args.push("--assign-to", a.assignTo);
907
+ if (a.label) args.push("--label", a.label);
908
+ return args;
909
+ },
910
+ annotations: {
911
+ title: "Issue Edit",
912
+ readOnlyHint: false,
913
+ destructiveHint: true,
914
+ idempotentHint: true,
915
+ openWorldHint: true,
916
+ },
917
+ },
918
+ {
919
+ name: "gitflic_issue_reopen",
920
+ description: "Reopen a closed issue.",
921
+ inputSchema: {
922
+ type: "object",
923
+ required: ["project", "id"],
924
+ properties: { project: { type: "string" }, id: { type: "string" } },
925
+ },
926
+ handler: (a) => ["issue", "reopen", a.id, "--project", a.project, "--format", "json"],
927
+ annotations: {
928
+ title: "Issue Reopen",
929
+ readOnlyHint: false,
930
+ destructiveHint: true,
931
+ idempotentHint: false,
932
+ openWorldHint: true,
933
+ },
934
+ },
935
+ {
936
+ name: "gitflic_issue_delete",
937
+ description: "Delete an issue. Irreversible.",
938
+ inputSchema: {
939
+ type: "object",
940
+ required: ["project", "id"],
941
+ properties: { project: { type: "string" }, id: { type: "string" } },
942
+ },
943
+ handler: (a) => ["issue", "delete", a.id, "--project", a.project, "--format", "json"],
944
+ annotations: {
945
+ title: "Issue Delete",
946
+ readOnlyHint: false,
947
+ destructiveHint: true,
948
+ idempotentHint: true,
949
+ openWorldHint: true,
950
+ },
951
+ },
952
+ {
953
+ name: "gitflic_issue_comments_list",
954
+ description: "List comments on an issue.",
955
+ inputSchema: {
956
+ type: "object",
957
+ required: ["project", "id"],
958
+ properties: { project: { type: "string" }, id: { type: "string" } },
959
+ },
960
+ handler: (a) => ["issue", "comments", "list", a.id, "--project", a.project, "--format", "json"],
961
+ annotations: {
962
+ title: "Issue Comments List",
963
+ readOnlyHint: true,
964
+ destructiveHint: false,
965
+ idempotentHint: false,
966
+ openWorldHint: true,
967
+ },
968
+ },
969
+ {
970
+ name: "gitflic_issue_comments_edit",
971
+ description: "Edit an existing issue comment.",
972
+ inputSchema: {
973
+ type: "object",
974
+ required: ["project", "id", "commentId", "message"],
975
+ properties: {
976
+ project: { type: "string" }, id: { type: "string" },
977
+ commentId: { type: "string" }, message: { type: "string" },
978
+ },
979
+ },
980
+ handler: (a) => ["issue", "comments", "edit", a.id, a.commentId, a.message, "--project", a.project, "--format", "json"],
981
+ annotations: {
982
+ title: "Issue Comments Edit",
983
+ readOnlyHint: false,
984
+ destructiveHint: true,
985
+ idempotentHint: true,
986
+ openWorldHint: true,
987
+ },
988
+ },
989
+ {
990
+ name: "gitflic_issue_comments_delete",
991
+ description: "Delete an issue comment.",
992
+ inputSchema: {
993
+ type: "object",
994
+ required: ["project", "id", "commentId"],
995
+ properties: { project: { type: "string" }, id: { type: "string" }, commentId: { type: "string" } },
996
+ },
997
+ handler: (a) => ["issue", "comments", "delete", a.id, a.commentId, "--project", a.project, "--format", "json"],
998
+ annotations: {
999
+ title: "Issue Comments Delete",
1000
+ readOnlyHint: false,
1001
+ destructiveHint: true,
1002
+ idempotentHint: true,
1003
+ openWorldHint: true,
1004
+ },
1005
+ },
1006
+ {
1007
+ name: "gitflic_issue_relations_list",
1008
+ description: "List issue relations (linked/related/blocks).",
1009
+ inputSchema: {
1010
+ type: "object",
1011
+ required: ["project", "id"],
1012
+ properties: { project: { type: "string" }, id: { type: "string" } },
1013
+ },
1014
+ handler: (a) => ["issue", "relations", "list", a.id, "--project", a.project, "--format", "json"],
1015
+ annotations: {
1016
+ title: "Issue Relations List",
1017
+ readOnlyHint: true,
1018
+ destructiveHint: false,
1019
+ idempotentHint: false,
1020
+ openWorldHint: true,
1021
+ },
1022
+ },
1023
+
1024
+ // ===== Branch: get / default / compare / protection CRUD ==============
1025
+ {
1026
+ name: "gitflic_branch_get",
1027
+ description: "Get a single branch by name.",
1028
+ inputSchema: {
1029
+ type: "object",
1030
+ required: ["project", "name"],
1031
+ properties: { project: { type: "string" }, name: { type: "string" } },
1032
+ },
1033
+ handler: (a) => ["branch", "get", a.name, "--project", a.project, "--format", "json"],
1034
+ annotations: {
1035
+ title: "Branch Get",
1036
+ readOnlyHint: true,
1037
+ destructiveHint: false,
1038
+ idempotentHint: false,
1039
+ openWorldHint: true,
1040
+ },
1041
+ },
1042
+ {
1043
+ name: "gitflic_branch_default",
1044
+ description: "Get the project's default branch.",
1045
+ inputSchema: {
1046
+ type: "object",
1047
+ required: ["project"],
1048
+ properties: { project: { type: "string" } },
1049
+ },
1050
+ handler: (a) => ["branch", "default", "--project", a.project, "--format", "json"],
1051
+ annotations: {
1052
+ title: "Branch Default",
1053
+ readOnlyHint: true,
1054
+ destructiveHint: false,
1055
+ idempotentHint: false,
1056
+ openWorldHint: true,
1057
+ },
1058
+ },
1059
+ {
1060
+ name: "gitflic_branch_protection_get",
1061
+ description: "Get a branch-protection rule by uuid.",
1062
+ inputSchema: {
1063
+ type: "object",
1064
+ required: ["project", "uuid"],
1065
+ properties: { project: { type: "string" }, uuid: { type: "string" } },
1066
+ },
1067
+ handler: (a) => ["branch", "protection", "get", a.uuid, "--project", a.project, "--format", "json"],
1068
+ annotations: {
1069
+ title: "Branch Protection Get",
1070
+ readOnlyHint: true,
1071
+ destructiveHint: false,
1072
+ idempotentHint: false,
1073
+ openWorldHint: true,
1074
+ },
1075
+ },
1076
+ {
1077
+ name: "gitflic_branch_protection_create",
1078
+ description: "Create a branch-protection rule.",
1079
+ inputSchema: {
1080
+ type: "object",
1081
+ required: ["project", "branchTemplate", "allowedToPush", "allowedToMerge"],
1082
+ properties: {
1083
+ project: { type: "string" },
1084
+ branchTemplate: { type: "string", description: "Branch pattern (e.g. master, release/*)" },
1085
+ allowedToPush: { type: "string", enum: ["NO_ONE", "DEVELOPER", "ADMINS"] },
1086
+ allowedToMerge: { type: "string", enum: ["NO_ONE", "DEVELOPER", "ADMINS"] },
1087
+ allowForcePush: { type: "boolean" },
1088
+ codeOwnerApproval: { type: "boolean" },
1089
+ autoMerge: { type: "boolean" },
1090
+ },
1091
+ },
1092
+ handler: (a) => {
1093
+ const args = ["branch", "protection", "create", "--project", a.project,
1094
+ "--branch-template", a.branchTemplate,
1095
+ "--allowed-to-push", a.allowedToPush,
1096
+ "--allowed-to-merge", a.allowedToMerge,
1097
+ "--format", "json"];
1098
+ if (a.allowForcePush) args.push("--allow-force-push");
1099
+ if (a.codeOwnerApproval) args.push("--code-owner-approval");
1100
+ if (a.autoMerge) args.push("--auto-merge");
1101
+ return args;
1102
+ },
1103
+ annotations: {
1104
+ title: "Branch Protection Create",
1105
+ readOnlyHint: false,
1106
+ destructiveHint: true,
1107
+ idempotentHint: true,
1108
+ openWorldHint: true,
1109
+ },
1110
+ },
1111
+ {
1112
+ name: "gitflic_branch_protection_edit",
1113
+ description: "Edit a branch-protection rule.",
1114
+ inputSchema: {
1115
+ type: "object",
1116
+ required: ["project", "uuid"],
1117
+ properties: {
1118
+ project: { type: "string" }, uuid: { type: "string" },
1119
+ allowedToPush: { type: "string", enum: ["NO_ONE", "DEVELOPER", "ADMINS"] },
1120
+ allowedToMerge: { type: "string", enum: ["NO_ONE", "DEVELOPER", "ADMINS"] },
1121
+ allowForcePush: { type: "boolean" },
1122
+ codeOwnerApproval: { type: "boolean" },
1123
+ autoMerge: { type: "boolean" },
1124
+ },
1125
+ },
1126
+ handler: (a) => {
1127
+ const args = ["branch", "protection", "edit", a.uuid, "--project", a.project, "--format", "json"];
1128
+ if (a.allowedToPush) args.push("--allowed-to-push", a.allowedToPush);
1129
+ if (a.allowedToMerge) args.push("--allowed-to-merge", a.allowedToMerge);
1130
+ if (a.allowForcePush !== undefined) args.push("--allow-force-push", String(a.allowForcePush));
1131
+ if (a.codeOwnerApproval !== undefined) args.push("--code-owner-approval", String(a.codeOwnerApproval));
1132
+ if (a.autoMerge !== undefined) args.push("--auto-merge", String(a.autoMerge));
1133
+ return args;
1134
+ },
1135
+ annotations: {
1136
+ title: "Branch Protection Edit",
1137
+ readOnlyHint: false,
1138
+ destructiveHint: true,
1139
+ idempotentHint: true,
1140
+ openWorldHint: true,
1141
+ },
1142
+ },
1143
+ {
1144
+ name: "gitflic_branch_protection_delete",
1145
+ description: "Delete a branch-protection rule.",
1146
+ inputSchema: {
1147
+ type: "object",
1148
+ required: ["project", "uuid"],
1149
+ properties: { project: { type: "string" }, uuid: { type: "string" } },
1150
+ },
1151
+ handler: (a) => ["branch", "protection", "delete", a.uuid, "--project", a.project, "--format", "json"],
1152
+ annotations: {
1153
+ title: "Branch Protection Delete",
1154
+ readOnlyHint: false,
1155
+ destructiveHint: true,
1156
+ idempotentHint: true,
1157
+ openWorldHint: true,
1158
+ },
1159
+ },
1160
+
1161
+ // ===== Tag: get / protection CRUD ======================================
1162
+ {
1163
+ name: "gitflic_tag_get",
1164
+ description: "Get a tag by name. NOTE: GitFlic returns 400 for project-scoped tag get; prefer tag list.",
1165
+ inputSchema: {
1166
+ type: "object",
1167
+ required: ["project", "name"],
1168
+ properties: { project: { type: "string" }, name: { type: "string" } },
1169
+ },
1170
+ handler: (a) => ["tag", "get", a.name, "--project", a.project, "--format", "json"],
1171
+ annotations: {
1172
+ title: "Tag Get",
1173
+ readOnlyHint: true,
1174
+ destructiveHint: false,
1175
+ idempotentHint: false,
1176
+ openWorldHint: true,
1177
+ },
1178
+ },
1179
+
1180
+ // ===== Release: get / latest / edit / delete ============================
1181
+ {
1182
+ name: "gitflic_release_get",
1183
+ description: "Get one release by uuid.",
1184
+ inputSchema: {
1185
+ type: "object",
1186
+ required: ["project", "uuid"],
1187
+ properties: { project: { type: "string" }, uuid: { type: "string" } },
1188
+ },
1189
+ handler: (a) => ["release", "get", a.uuid, "--project", a.project, "--format", "json"],
1190
+ annotations: {
1191
+ title: "Release Get",
1192
+ readOnlyHint: true,
1193
+ destructiveHint: false,
1194
+ idempotentHint: false,
1195
+ openWorldHint: true,
1196
+ },
1197
+ },
1198
+ {
1199
+ name: "gitflic_release_latest",
1200
+ description: "Get the most recent release.",
1201
+ inputSchema: {
1202
+ type: "object",
1203
+ required: ["project"],
1204
+ properties: { project: { type: "string" } },
1205
+ },
1206
+ handler: (a) => ["release", "latest", "--project", a.project, "--format", "json"],
1207
+ annotations: {
1208
+ title: "Release Latest",
1209
+ readOnlyHint: true,
1210
+ destructiveHint: false,
1211
+ idempotentHint: false,
1212
+ openWorldHint: true,
1213
+ },
1214
+ },
1215
+ {
1216
+ name: "gitflic_release_edit",
1217
+ description: "Edit release metadata.",
1218
+ inputSchema: {
1219
+ type: "object",
1220
+ required: ["project", "uuid"],
1221
+ properties: {
1222
+ project: { type: "string" }, uuid: { type: "string" },
1223
+ title: { type: "string" }, description: { type: "string" },
1224
+ tag: { type: "string" }, preRelease: { type: "boolean" },
1225
+ },
1226
+ },
1227
+ handler: (a) => {
1228
+ const args = ["release", "edit", a.uuid, "--project", a.project, "--format", "json"];
1229
+ if (a.title) args.push("--title", a.title);
1230
+ if (a.description !== undefined) args.push("--description", a.description);
1231
+ if (a.tag) args.push("--tag", a.tag);
1232
+ if (a.preRelease !== undefined) args.push("--pre-release", String(a.preRelease));
1233
+ return args;
1234
+ },
1235
+ annotations: {
1236
+ title: "Release Edit",
1237
+ readOnlyHint: false,
1238
+ destructiveHint: true,
1239
+ idempotentHint: true,
1240
+ openWorldHint: true,
1241
+ },
1242
+ },
1243
+ {
1244
+ name: "gitflic_release_delete",
1245
+ description: "Delete a release.",
1246
+ inputSchema: {
1247
+ type: "object",
1248
+ required: ["project", "uuid"],
1249
+ properties: { project: { type: "string" }, uuid: { type: "string" } },
1250
+ },
1251
+ handler: (a) => ["release", "delete", a.uuid, "--project", a.project, "--format", "json"],
1252
+ annotations: {
1253
+ title: "Release Delete",
1254
+ readOnlyHint: false,
1255
+ destructiveHint: true,
1256
+ idempotentHint: true,
1257
+ openWorldHint: true,
1258
+ },
1259
+ },
1260
+
1261
+ // ===== Commit: get / files / compare ===================================
1262
+ {
1263
+ name: "gitflic_commit_get",
1264
+ description: "Get one commit by hash.",
1265
+ inputSchema: {
1266
+ type: "object",
1267
+ required: ["project", "hash"],
1268
+ properties: { project: { type: "string" }, hash: { type: "string" } },
1269
+ },
1270
+ handler: (a) => ["commit", "get", a.hash, "--project", a.project, "--format", "json"],
1271
+ annotations: {
1272
+ title: "Commit Get",
1273
+ readOnlyHint: true,
1274
+ destructiveHint: false,
1275
+ idempotentHint: false,
1276
+ openWorldHint: true,
1277
+ },
1278
+ },
1279
+ {
1280
+ name: "gitflic_commit_files",
1281
+ description: "List files touched by a commit.",
1282
+ inputSchema: {
1283
+ type: "object",
1284
+ required: ["project", "hash"],
1285
+ properties: { project: { type: "string" }, hash: { type: "string" } },
1286
+ },
1287
+ handler: (a) => ["commit", "files", a.hash, "--project", a.project, "--format", "json"],
1288
+ annotations: {
1289
+ title: "Commit Files",
1290
+ readOnlyHint: true,
1291
+ destructiveHint: false,
1292
+ idempotentHint: false,
1293
+ openWorldHint: true,
1294
+ },
1295
+ },
1296
+ {
1297
+ name: "gitflic_commit_compare",
1298
+ description: "Compare two commits.",
1299
+ inputSchema: {
1300
+ type: "object",
1301
+ required: ["project", "source", "target"],
1302
+ properties: { project: { type: "string" }, source: { type: "string" }, target: { type: "string" } },
1303
+ },
1304
+ handler: (a) => ["commit", "compare", a.source, a.target, "--project", a.project, "--format", "json"],
1305
+ annotations: {
1306
+ title: "Commit Compare",
1307
+ readOnlyHint: true,
1308
+ destructiveHint: false,
1309
+ idempotentHint: false,
1310
+ openWorldHint: true,
1311
+ },
1312
+ },
1313
+
1314
+ // ===== Webhook: get / create / delete ==================================
1315
+ {
1316
+ name: "gitflic_webhook_get",
1317
+ description: "Get one webhook by uuid.",
1318
+ inputSchema: {
1319
+ type: "object",
1320
+ required: ["project", "uuid"],
1321
+ properties: { project: { type: "string" }, uuid: { type: "string" } },
1322
+ },
1323
+ handler: (a) => ["webhook", "get", a.uuid, "--project", a.project, "--format", "json"],
1324
+ annotations: {
1325
+ title: "Webhook Get",
1326
+ readOnlyHint: true,
1327
+ destructiveHint: false,
1328
+ idempotentHint: false,
1329
+ openWorldHint: true,
1330
+ },
1331
+ },
1332
+ {
1333
+ name: "gitflic_webhook_create",
1334
+ description: "Create a webhook. Pass one or more event names.",
1335
+ inputSchema: {
1336
+ type: "object",
1337
+ required: ["project", "url"],
1338
+ properties: {
1339
+ project: { type: "string" },
1340
+ url: { type: "string" },
1341
+ event: { type: "string", description: "Event name (e.g. PUSH). Pass array for multiple." },
1342
+ active: { type: "boolean" },
1343
+ },
1344
+ },
1345
+ handler: (a) => {
1346
+ const args = ["webhook", "create", "--url", a.url, "--project", a.project, "--format", "json"];
1347
+ if (a.event) {
1348
+ const evs = Array.isArray(a.event) ? a.event : [a.event];
1349
+ for (const e of evs) args.push("--event", e);
1350
+ }
1351
+ if (a.active !== undefined) args.push("--active", String(a.active));
1352
+ return args;
1353
+ },
1354
+ annotations: {
1355
+ title: "Webhook Create",
1356
+ readOnlyHint: false,
1357
+ destructiveHint: true,
1358
+ idempotentHint: true,
1359
+ openWorldHint: true,
1360
+ },
1361
+ },
1362
+ {
1363
+ name: "gitflic_webhook_delete",
1364
+ description: "Delete a webhook by uuid.",
1365
+ inputSchema: {
1366
+ type: "object",
1367
+ required: ["project", "uuid"],
1368
+ properties: { project: { type: "string" }, uuid: { type: "string" } },
1369
+ },
1370
+ handler: (a) => ["webhook", "delete", a.uuid, "--project", a.project, "--format", "json"],
1371
+ annotations: {
1372
+ title: "Webhook Delete",
1373
+ readOnlyHint: false,
1374
+ destructiveHint: true,
1375
+ idempotentHint: true,
1376
+ openWorldHint: true,
1377
+ },
1378
+ },
1379
+
1380
+ // ===== Project: get / create / edit / member list =====================
1381
+ {
1382
+ name: "gitflic_project_get",
1383
+ description: "Resolve an owner/alias to its UUID + metadata.",
1384
+ inputSchema: {
1385
+ type: "object",
1386
+ required: ["project"],
1387
+ properties: { project: { type: "string" } },
1388
+ },
1389
+ handler: (a) => ["project", "get", a.project, "--format", "json"],
1390
+ annotations: {
1391
+ title: "Project Get",
1392
+ readOnlyHint: true,
1393
+ destructiveHint: false,
1394
+ idempotentHint: false,
1395
+ openWorldHint: true,
1396
+ },
1397
+ },
1398
+ {
1399
+ name: "gitflic_project_create",
1400
+ description: "Create a new project.",
1401
+ inputSchema: {
1402
+ type: "object",
1403
+ required: ["title", "alias"],
1404
+ properties: {
1405
+ title: { type: "string" },
1406
+ alias: { type: "string" },
1407
+ isPrivate: { type: "boolean" },
1408
+ language: { type: "string" },
1409
+ description: { type: "string" },
1410
+ ownerAlias: { type: "string" },
1411
+ ownerAliasType: { type: "string", enum: ["USER", "TEAM", "COMPANY"] },
1412
+ },
1413
+ },
1414
+ handler: (a) => {
1415
+ const args = ["project", "create", "--title", a.title, "--alias", a.alias, "--format", "json"];
1416
+ if (a.isPrivate) args.push("--private");
1417
+ if (a.language) args.push("--language", a.language);
1418
+ if (a.description) args.push("--description", a.description);
1419
+ if (a.ownerAlias) args.push("--owner-alias", a.ownerAlias);
1420
+ if (a.ownerAliasType) args.push("--owner-alias-type", a.ownerAliasType);
1421
+ return args;
1422
+ },
1423
+ annotations: {
1424
+ title: "Project Create",
1425
+ readOnlyHint: false,
1426
+ destructiveHint: true,
1427
+ idempotentHint: true,
1428
+ openWorldHint: true,
1429
+ },
1430
+ },
1431
+ {
1432
+ name: "gitflic_project_edit",
1433
+ description: "Edit project metadata (title, description, default branch, visibility).",
1434
+ inputSchema: {
1435
+ type: "object",
1436
+ required: ["project"],
1437
+ properties: {
1438
+ project: { type: "string" },
1439
+ title: { type: "string" },
1440
+ description: { type: "string" },
1441
+ isPrivate: { type: "boolean" },
1442
+ language: { type: "string" },
1443
+ defaultBranch: { type: "string" },
1444
+ },
1445
+ },
1446
+ handler: (a) => {
1447
+ const args = ["project", "edit", a.project, "--format", "json"];
1448
+ if (a.title) args.push("--title", a.title);
1449
+ if (a.description !== undefined) args.push("--description", a.description);
1450
+ if (a.isPrivate !== undefined) args.push(a.isPrivate ? "--private" : "--public");
1451
+ if (a.language) args.push("--language", a.language);
1452
+ if (a.defaultBranch) args.push("--default-branch", a.defaultBranch);
1453
+ return args;
1454
+ },
1455
+ annotations: {
1456
+ title: "Project Edit",
1457
+ readOnlyHint: false,
1458
+ destructiveHint: true,
1459
+ idempotentHint: true,
1460
+ openWorldHint: true,
1461
+ },
1462
+ },
1463
+ {
1464
+ name: "gitflic_project_member_list",
1465
+ description: "List project members.",
1466
+ inputSchema: {
1467
+ type: "object",
1468
+ required: ["project"],
1469
+ properties: { project: { type: "string" } },
1470
+ },
1471
+ handler: (a) => ["project", "member", "list", "--project", a.project, "--format", "json"],
1472
+ annotations: {
1473
+ title: "Project Member List",
1474
+ readOnlyHint: true,
1475
+ destructiveHint: false,
1476
+ idempotentHint: false,
1477
+ openWorldHint: true,
1478
+ },
1479
+ },
1480
+ {
1481
+ name: "gitflic_project_member_invite",
1482
+ description: "Invite a user to a project with a role.",
1483
+ inputSchema: {
1484
+ type: "object",
1485
+ required: ["project", "userAlias", "role"],
1486
+ properties: {
1487
+ project: { type: "string" },
1488
+ userAlias: { type: "string" },
1489
+ role: { type: "string", enum: ["GUEST", "REPORTER", "DEVELOPER", "MAINTAINER", "OWNER"] },
1490
+ },
1491
+ },
1492
+ handler: (a) => ["project", "member", "invite", a.userAlias, "--role", a.role, "--project", a.project, "--format", "json"],
1493
+ annotations: {
1494
+ title: "Project Member Invite",
1495
+ readOnlyHint: false,
1496
+ destructiveHint: true,
1497
+ idempotentHint: false,
1498
+ openWorldHint: true,
1499
+ },
1500
+ },
1501
+
1502
+ // ===== Blob / file: get / files / archive =============================
1503
+ {
1504
+ name: "gitflic_blob_get",
1505
+ description: "Get a file's text content. Binary files are base64-encoded by the server.",
1506
+ inputSchema: {
1507
+ type: "object",
1508
+ required: ["project", "path"],
1509
+ properties: {
1510
+ project: { type: "string" },
1511
+ path: { type: "string", description: "File path inside the repo, e.g. 'README.md'" },
1512
+ branch: { type: "string" },
1513
+ commit: { type: "string" },
1514
+ },
1515
+ },
1516
+ handler: (a) => {
1517
+ const args = ["blob", "get", a.path, "--project", a.project, "--format", "json"];
1518
+ if (a.branch) args.push("--branch", a.branch);
1519
+ if (a.commit) args.push("--commit", a.commit);
1520
+ return args;
1521
+ },
1522
+ annotations: {
1523
+ title: "Blob Get",
1524
+ readOnlyHint: true,
1525
+ destructiveHint: false,
1526
+ idempotentHint: false,
1527
+ openWorldHint: true,
1528
+ },
1529
+ },
1530
+ {
1531
+ name: "gitflic_blob_files",
1532
+ description: "List files in the repo (optionally under a sub-path).",
1533
+ inputSchema: {
1534
+ type: "object",
1535
+ required: ["project"],
1536
+ properties: {
1537
+ project: { type: "string" },
1538
+ path: { type: "string", description: "Sub-path, e.g. 'lib/cmd'" },
1539
+ branch: { type: "string" },
1540
+ },
1541
+ },
1542
+ handler: (a) => {
1543
+ const args = ["blob", "files", "--project", a.project, "--format", "json"];
1544
+ if (a.path) args.push("--path", a.path);
1545
+ if (a.branch) args.push("--branch", a.branch);
1546
+ return args;
1547
+ },
1548
+ annotations: {
1549
+ title: "Blob Files",
1550
+ readOnlyHint: true,
1551
+ destructiveHint: false,
1552
+ idempotentHint: false,
1553
+ openWorldHint: true,
1554
+ },
1555
+ },
1556
+ {
1557
+ name: "gitflic_blob_archive",
1558
+ description: "Download a repo archive (tarball). Returns JSON metadata; use shell to stream the body.",
1559
+ inputSchema: {
1560
+ type: "object",
1561
+ required: ["project", "branch"],
1562
+ properties: { project: { type: "string" }, branch: { type: "string" } },
1563
+ },
1564
+ handler: (a) => ["blob", "archive", a.branch, "--project", a.project, "--format", "json"],
1565
+ annotations: {
1566
+ title: "Blob Archive",
1567
+ readOnlyHint: true,
1568
+ destructiveHint: false,
1569
+ idempotentHint: false,
1570
+ openWorldHint: true,
1571
+ },
1572
+ },
1573
+
1574
+ // ===== User: get / projects / followers =================================
1575
+ {
1576
+ name: "gitflic_user_get",
1577
+ description: "Get a user by alias.",
1578
+ inputSchema: {
1579
+ type: "object",
1580
+ required: ["alias"],
1581
+ properties: { alias: { type: "string" } },
1582
+ },
1583
+ handler: (a) => ["user", "get", a.alias, "--format", "json"],
1584
+ annotations: {
1585
+ title: "User Get",
1586
+ readOnlyHint: true,
1587
+ destructiveHint: false,
1588
+ idempotentHint: false,
1589
+ openWorldHint: true,
1590
+ },
1591
+ },
1592
+ {
1593
+ name: "gitflic_user_projects",
1594
+ description: "List public projects of a user.",
1595
+ inputSchema: {
1596
+ type: "object",
1597
+ required: ["alias"],
1598
+ properties: { alias: { type: "string" } },
1599
+ },
1600
+ handler: (a) => ["user", "projects", a.alias, "--format", "json"],
1601
+ annotations: {
1602
+ title: "User Projects",
1603
+ readOnlyHint: true,
1604
+ destructiveHint: false,
1605
+ idempotentHint: false,
1606
+ openWorldHint: true,
1607
+ },
1608
+ },
1609
+ {
1610
+ name: "gitflic_user_followers",
1611
+ description: "List a user's followers.",
1612
+ inputSchema: {
1613
+ type: "object",
1614
+ required: ["alias"],
1615
+ properties: { alias: { type: "string" } },
1616
+ },
1617
+ handler: (a) => ["user", "followers", a.alias, "--format", "json"],
1618
+ annotations: {
1619
+ title: "User Followers",
1620
+ readOnlyHint: true,
1621
+ destructiveHint: false,
1622
+ idempotentHint: false,
1623
+ openWorldHint: true,
1624
+ },
1625
+ },
1626
+ ];
1627
+
1628
+ // Build the JSON Schema for ListTools request, just to help debugging.
1629
+ export const TOOL_NAMES = TOOLS.map((t) => t.name);