@structured-world/gitlab-mcp 6.36.0 → 6.38.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.
Files changed (64) hide show
  1. package/README.md +4 -4
  2. package/dist/src/cli/list-tools.js +51 -33
  3. package/dist/src/cli/list-tools.js.map +1 -1
  4. package/dist/src/cli/setup/presets.js +30 -17
  5. package/dist/src/cli/setup/presets.js.map +1 -1
  6. package/dist/src/config.d.ts +1 -0
  7. package/dist/src/config.js +3 -2
  8. package/dist/src/config.js.map +1 -1
  9. package/dist/src/entities/core/registry.js +234 -204
  10. package/dist/src/entities/core/registry.js.map +1 -1
  11. package/dist/src/entities/core/schema-readonly.d.ts +23 -47
  12. package/dist/src/entities/core/schema-readonly.js +22 -42
  13. package/dist/src/entities/core/schema-readonly.js.map +1 -1
  14. package/dist/src/entities/core/schema.d.ts +60 -15
  15. package/dist/src/entities/core/schema.js +76 -12
  16. package/dist/src/entities/core/schema.js.map +1 -1
  17. package/dist/src/entities/files/registry.js +13 -0
  18. package/dist/src/entities/files/registry.js.map +1 -1
  19. package/dist/src/entities/files/schema-readonly.d.ts +5 -0
  20. package/dist/src/entities/files/schema-readonly.js +7 -0
  21. package/dist/src/entities/files/schema-readonly.js.map +1 -1
  22. package/dist/src/entities/index.d.ts +1 -0
  23. package/dist/src/entities/index.js +1 -0
  24. package/dist/src/entities/index.js.map +1 -1
  25. package/dist/src/entities/iterations/index.d.ts +2 -0
  26. package/dist/src/entities/iterations/index.js +19 -0
  27. package/dist/src/entities/iterations/index.js.map +1 -0
  28. package/dist/src/entities/iterations/registry.d.ts +5 -0
  29. package/dist/src/entities/iterations/registry.js +106 -0
  30. package/dist/src/entities/iterations/registry.js.map +1 -0
  31. package/dist/src/entities/iterations/schema-readonly.d.ts +21 -0
  32. package/dist/src/entities/iterations/schema-readonly.js +28 -0
  33. package/dist/src/entities/iterations/schema-readonly.js.map +1 -0
  34. package/dist/src/entities/pipelines/schema-readonly.d.ts +1 -1
  35. package/dist/src/entities/search/schema-readonly.d.ts +7 -7
  36. package/dist/src/entities/shared.d.ts +2 -2
  37. package/dist/src/entities/snippets/schema-readonly.d.ts +2 -2
  38. package/dist/src/entities/snippets/schema.d.ts +4 -4
  39. package/dist/src/entities/workitems/registry.js +169 -5
  40. package/dist/src/entities/workitems/registry.js.map +1 -1
  41. package/dist/src/entities/workitems/schema.d.ts +51 -0
  42. package/dist/src/entities/workitems/schema.js +125 -0
  43. package/dist/src/entities/workitems/schema.js.map +1 -1
  44. package/dist/src/graphql/workItems.d.ts +125 -1
  45. package/dist/src/graphql/workItems.js +158 -1
  46. package/dist/src/graphql/workItems.js.map +1 -1
  47. package/dist/src/profiles/builtin/code-reviewer.yaml +2 -2
  48. package/dist/src/profiles/builtin/developer.yaml +1 -1
  49. package/dist/src/registry-manager.js +11 -0
  50. package/dist/src/registry-manager.js.map +1 -1
  51. package/dist/src/services/ToolAvailability.d.ts +1 -2
  52. package/dist/src/services/ToolAvailability.js +38 -384
  53. package/dist/src/services/ToolAvailability.js.map +1 -1
  54. package/dist/src/services/WidgetAvailability.js +10 -5
  55. package/dist/src/services/WidgetAvailability.js.map +1 -1
  56. package/dist/src/utils/error-handler.js +1 -1
  57. package/dist/src/utils/error-handler.js.map +1 -1
  58. package/dist/src/utils/idConversion.d.ts +11 -0
  59. package/dist/src/utils/idConversion.js +13 -0
  60. package/dist/src/utils/idConversion.js.map +1 -1
  61. package/dist/structured-world-gitlab-mcp-6.38.0.tgz +0 -0
  62. package/dist/tsconfig.build.tsbuildinfo +1 -1
  63. package/package.json +1 -1
  64. package/dist/structured-world-gitlab-mcp-6.36.0.tgz +0 -0
@@ -50,7 +50,7 @@ exports.coreToolRegistry = new Map([
50
50
  "browse_projects",
51
51
  {
52
52
  name: "browse_projects",
53
- description: "PROJECT DISCOVERY: Find, browse, or inspect GitLab projects. Use 'search' to find projects by name/topic across all GitLab. Use 'list' to browse your accessible projects or projects within a specific group. Use 'get' with project_id to retrieve full details of a known project. Filter by visibility, language, or ownership.",
53
+ description: "Find, list, or inspect GitLab projects. Actions: search (find by name/topic across GitLab), list (browse accessible projects or group projects), get (retrieve full project details). Related: manage_project to create/update/delete projects.",
54
54
  inputSchema: z.toJSONSchema(schema_readonly_1.BrowseProjectsSchema),
55
55
  handler: async (args) => {
56
56
  const input = schema_readonly_1.BrowseProjectsSchema.parse(args);
@@ -173,7 +173,7 @@ exports.coreToolRegistry = new Map([
173
173
  "browse_namespaces",
174
174
  {
175
175
  name: "browse_namespaces",
176
- description: "NAMESPACE OPERATIONS: Explore GitLab groups and user namespaces. Use 'list' to discover available namespaces for project creation. Use 'get' with namespace_id to retrieve full details including storage stats. Use 'verify' to check if a namespace path exists before creating projects or groups.",
176
+ description: "Explore GitLab groups and user namespaces. Actions: list (discover available namespaces), get (retrieve details with storage stats), verify (check if path exists). Related: manage_namespace to create/update/delete groups.",
177
177
  inputSchema: z.toJSONSchema(schema_readonly_1.BrowseNamespacesSchema),
178
178
  handler: async (args) => {
179
179
  const input = schema_readonly_1.BrowseNamespacesSchema.parse(args);
@@ -237,7 +237,7 @@ exports.coreToolRegistry = new Map([
237
237
  "browse_commits",
238
238
  {
239
239
  name: "browse_commits",
240
- description: "COMMIT HISTORY: Explore repository commit history. Use 'list' to browse commits with optional date range, author, or file path filters. Use 'get' with sha to retrieve commit metadata and stats. Use 'diff' to see actual code changes in a commit. Essential for code review and change tracking.",
240
+ description: "Explore repository commit history and diffs. Actions: list (browse commits with filters), get (retrieve commit metadata and stats), diff (view code changes). Related: browse_refs for branch/tag info.",
241
241
  inputSchema: z.toJSONSchema(schema_readonly_1.BrowseCommitsSchema),
242
242
  handler: async (args) => {
243
243
  const input = schema_readonly_1.BrowseCommitsSchema.parse(args);
@@ -317,7 +317,7 @@ exports.coreToolRegistry = new Map([
317
317
  "browse_events",
318
318
  {
319
319
  name: "browse_events",
320
- description: "ACTIVITY FEED: Track GitLab activity and events. Use 'user' to see YOUR recent activity across all projects (commits, issues, MRs). Use 'project' with project_id to monitor a specific project's activity feed. Filter by date range or action type (pushed, commented, merged, etc.).",
320
+ description: "Track GitLab activity and events. Actions: user (your activity across all projects), project (specific project activity feed). Filter by date range, action type, or target type.",
321
321
  inputSchema: z.toJSONSchema(schema_readonly_1.BrowseEventsSchema),
322
322
  handler: async (args) => {
323
323
  const input = schema_readonly_1.BrowseEventsSchema.parse(args);
@@ -369,15 +369,110 @@ exports.coreToolRegistry = new Map([
369
369
  },
370
370
  ],
371
371
  [
372
- "manage_repository",
372
+ "browse_users",
373
373
  {
374
- name: "manage_repository",
375
- description: "REPOSITORY MANAGEMENT: Create or fork GitLab projects. Use 'create' to start a new project with custom settings (visibility, features, namespace). Use 'fork' with project_id to create your own copy of an existing project for independent development or contribution back via MRs.",
376
- inputSchema: z.toJSONSchema(schema_1.ManageRepositorySchema),
374
+ name: "browse_users",
375
+ description: "Find GitLab users with smart pattern detection. Actions: search (find users by name/email/username with transliteration support), get (retrieve specific user by ID). Related: browse_members for project/group membership.",
376
+ inputSchema: z.toJSONSchema(schema_readonly_1.BrowseUsersSchema),
377
377
  handler: async (args) => {
378
- const input = schema_1.ManageRepositorySchema.parse(args);
379
- if ((0, config_1.isActionDenied)("manage_repository", input.action)) {
380
- throw new Error(`Action '${input.action}' is not allowed for manage_repository tool`);
378
+ const input = schema_readonly_1.BrowseUsersSchema.parse(args);
379
+ if ((0, config_1.isActionDenied)("browse_users", input.action)) {
380
+ throw new Error(`Action '${input.action}' is not allowed for browse_users tool`);
381
+ }
382
+ switch (input.action) {
383
+ case "search": {
384
+ const { smart_search, search, username, public_email, ...otherParams } = input;
385
+ const hasUsernameOrEmail = Boolean(username) || Boolean(public_email);
386
+ const hasOnlySearch = Boolean(search) && !hasUsernameOrEmail;
387
+ const shouldUseSmartSearch = smart_search === false ? false : smart_search === true || hasOnlySearch;
388
+ if (shouldUseSmartSearch && (search || username || public_email)) {
389
+ const query = search ?? username ?? public_email ?? "";
390
+ const additionalParams = {};
391
+ Object.entries(otherParams).forEach(([key, value]) => {
392
+ if (value !== undefined && key !== "smart_search" && key !== "action") {
393
+ additionalParams[key] = value;
394
+ }
395
+ });
396
+ return await (0, smart_user_search_1.smartUserSearch)(query, additionalParams);
397
+ }
398
+ else {
399
+ const queryParams = new URLSearchParams();
400
+ Object.entries(input).forEach(([key, value]) => {
401
+ if (value !== undefined && key !== "smart_search" && key !== "action") {
402
+ queryParams.set(key, String(value));
403
+ }
404
+ });
405
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/users?${queryParams}`;
406
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl);
407
+ if (!response.ok) {
408
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
409
+ }
410
+ const users = await response.json();
411
+ return (0, idConversion_1.cleanGidsFromObject)(users);
412
+ }
413
+ }
414
+ case "get": {
415
+ const { user_id } = input;
416
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/users/${encodeURIComponent(user_id)}`;
417
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl);
418
+ if (!response.ok) {
419
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
420
+ }
421
+ const user = await response.json();
422
+ return (0, idConversion_1.cleanGidsFromObject)(user);
423
+ }
424
+ default:
425
+ throw new Error(`Unknown action: ${input.action}`);
426
+ }
427
+ },
428
+ },
429
+ ],
430
+ [
431
+ "browse_todos",
432
+ {
433
+ name: "browse_todos",
434
+ description: "View your GitLab todo queue (notifications requiring action). Actions: list (filter by state, action type, target type). Todos are auto-created for assignments, mentions, reviews, and pipeline failures. Related: manage_todos to mark done/restore.",
435
+ inputSchema: z.toJSONSchema(schema_readonly_1.BrowseTodosSchema),
436
+ handler: async (args) => {
437
+ const input = schema_readonly_1.BrowseTodosSchema.parse(args);
438
+ if ((0, config_1.isActionDenied)("browse_todos", input.action)) {
439
+ throw new Error(`Action '${input.action}' is not allowed for browse_todos tool`);
440
+ }
441
+ switch (input.action) {
442
+ case "list": {
443
+ const queryParams = new URLSearchParams();
444
+ const { action: _action, todo_action, ...rest } = input;
445
+ if (todo_action)
446
+ queryParams.set("action", todo_action);
447
+ Object.entries(rest).forEach(([key, value]) => {
448
+ if (value !== undefined) {
449
+ queryParams.set(key, String(value));
450
+ }
451
+ });
452
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/todos?${queryParams}`;
453
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl);
454
+ if (!response.ok) {
455
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
456
+ }
457
+ const todos = await response.json();
458
+ return (0, idConversion_1.cleanGidsFromObject)(todos);
459
+ }
460
+ default:
461
+ throw new Error(`Unknown action: ${input.action}`);
462
+ }
463
+ },
464
+ },
465
+ ],
466
+ [
467
+ "manage_project",
468
+ {
469
+ name: "manage_project",
470
+ description: "Create, update, or manage GitLab projects. Actions: create (new project with settings), fork (copy existing project), update (modify settings), delete (remove permanently), archive/unarchive (toggle read-only), transfer (move to different namespace). Related: browse_projects for discovery.",
471
+ inputSchema: z.toJSONSchema(schema_1.ManageProjectSchema),
472
+ handler: async (args) => {
473
+ const input = schema_1.ManageProjectSchema.parse(args);
474
+ if ((0, config_1.isActionDenied)("manage_project", input.action)) {
475
+ throw new Error(`Action '${input.action}' is not allowed for manage_project tool`);
381
476
  }
382
477
  switch (input.action) {
383
478
  case "create": {
@@ -479,6 +574,67 @@ exports.coreToolRegistry = new Map([
479
574
  }
480
575
  return await response.json();
481
576
  }
577
+ case "update": {
578
+ const { project_id, action: _action, ...updateParams } = input;
579
+ const body = new URLSearchParams();
580
+ Object.entries(updateParams).forEach(([key, value]) => {
581
+ if (value !== undefined) {
582
+ body.set(key, String(value));
583
+ }
584
+ });
585
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/projects/${(0, projectIdentifier_1.normalizeProjectId)(project_id)}`;
586
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl, {
587
+ method: "PUT",
588
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
589
+ body: body.toString(),
590
+ });
591
+ if (!response.ok) {
592
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
593
+ }
594
+ return await response.json();
595
+ }
596
+ case "delete": {
597
+ const { project_id } = input;
598
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/projects/${(0, projectIdentifier_1.normalizeProjectId)(project_id)}`;
599
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl, { method: "DELETE" });
600
+ if (!response.ok) {
601
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
602
+ }
603
+ return { success: true, message: `Project ${project_id} deleted` };
604
+ }
605
+ case "archive": {
606
+ const { project_id } = input;
607
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/projects/${(0, projectIdentifier_1.normalizeProjectId)(project_id)}/archive`;
608
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl, { method: "POST" });
609
+ if (!response.ok) {
610
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
611
+ }
612
+ return await response.json();
613
+ }
614
+ case "unarchive": {
615
+ const { project_id } = input;
616
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/projects/${(0, projectIdentifier_1.normalizeProjectId)(project_id)}/unarchive`;
617
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl, { method: "POST" });
618
+ if (!response.ok) {
619
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
620
+ }
621
+ return await response.json();
622
+ }
623
+ case "transfer": {
624
+ const { project_id, namespace } = input;
625
+ const body = new URLSearchParams();
626
+ body.set("namespace", namespace);
627
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/projects/${(0, projectIdentifier_1.normalizeProjectId)(project_id)}/transfer`;
628
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl, {
629
+ method: "PUT",
630
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
631
+ body: body.toString(),
632
+ });
633
+ if (!response.ok) {
634
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
635
+ }
636
+ return await response.json();
637
+ }
482
638
  default:
483
639
  throw new Error(`Unknown action: ${input.action}`);
484
640
  }
@@ -486,200 +642,77 @@ exports.coreToolRegistry = new Map([
486
642
  },
487
643
  ],
488
644
  [
489
- "get_users",
645
+ "manage_namespace",
490
646
  {
491
- name: "get_users",
492
- description: "FIND USERS: Search GitLab users with smart pattern detection. Auto-detects emails, usernames, or names. Supports transliteration and multi-phase fallback search.",
493
- inputSchema: z.toJSONSchema(schema_readonly_1.GetUsersSchema),
647
+ name: "manage_namespace",
648
+ description: "Create, update, or delete GitLab groups/namespaces. Actions: create (new group with visibility/settings), update (modify group settings), delete (remove permanently). Related: browse_namespaces for discovery.",
649
+ inputSchema: z.toJSONSchema(schema_1.ManageNamespaceSchema),
494
650
  handler: async (args) => {
495
- const options = schema_readonly_1.GetUsersSchema.parse(args);
496
- const { smart_search, search, username, public_email, ...otherParams } = options;
497
- const hasUsernameOrEmail = Boolean(username) || Boolean(public_email);
498
- const hasOnlySearch = Boolean(search) && !hasUsernameOrEmail;
499
- const shouldUseSmartSearch = smart_search === false ? false : smart_search === true || hasOnlySearch;
500
- if (shouldUseSmartSearch && (search || username || public_email)) {
501
- const query = search ?? username ?? public_email ?? "";
502
- const additionalParams = {};
503
- Object.entries(otherParams).forEach(([key, value]) => {
504
- if (value !== undefined && key !== "smart_search") {
505
- additionalParams[key] = value;
506
- }
507
- });
508
- return await (0, smart_user_search_1.smartUserSearch)(query, additionalParams);
651
+ const input = schema_1.ManageNamespaceSchema.parse(args);
652
+ if ((0, config_1.isActionDenied)("manage_namespace", input.action)) {
653
+ throw new Error(`Action '${input.action}' is not allowed for manage_namespace tool`);
509
654
  }
510
- else {
511
- const queryParams = new URLSearchParams();
512
- Object.entries(options).forEach(([key, value]) => {
513
- if (value !== undefined && key !== "smart_search") {
514
- queryParams.set(key, String(value));
515
- }
516
- });
517
- const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/users?${queryParams}`;
518
- const response = await (0, fetch_1.enhancedFetch)(apiUrl);
519
- if (!response.ok) {
520
- throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
521
- }
522
- const users = await response.json();
523
- return (0, idConversion_1.cleanGidsFromObject)(users);
524
- }
525
- },
526
- },
527
- ],
528
- [
529
- "list_project_members",
530
- {
531
- name: "list_project_members",
532
- description: "TEAM MEMBERS: List project members with access levels. Shows: 10=Guest, 20=Reporter, 30=Developer, 40=Maintainer, 50=Owner.",
533
- inputSchema: z.toJSONSchema(schema_readonly_1.ListProjectMembersSchema),
534
- handler: async (args) => {
535
- const options = schema_readonly_1.ListProjectMembersSchema.parse(args);
536
- const { project_id } = options;
537
- const queryParams = new URLSearchParams();
538
- Object.entries(options).forEach(([key, value]) => {
539
- if (value !== undefined && key !== "project_id") {
540
- queryParams.set(key, String(value));
655
+ switch (input.action) {
656
+ case "create": {
657
+ const body = new URLSearchParams();
658
+ body.set("name", input.name);
659
+ body.set("path", input.path);
660
+ if (input.description)
661
+ body.set("description", input.description);
662
+ if (input.visibility)
663
+ body.set("visibility", input.visibility);
664
+ if (input.parent_id !== undefined)
665
+ body.set("parent_id", String(input.parent_id));
666
+ if (input.lfs_enabled !== undefined)
667
+ body.set("lfs_enabled", String(input.lfs_enabled));
668
+ if (input.request_access_enabled !== undefined)
669
+ body.set("request_access_enabled", String(input.request_access_enabled));
670
+ if (input.default_branch_protection !== undefined)
671
+ body.set("default_branch_protection", String(input.default_branch_protection));
672
+ if (input.avatar)
673
+ body.set("avatar", input.avatar);
674
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/groups`;
675
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl, {
676
+ method: "POST",
677
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
678
+ body: body.toString(),
679
+ });
680
+ if (!response.ok) {
681
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
682
+ }
683
+ return await response.json();
541
684
  }
542
- });
543
- const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(project_id)}/members?${queryParams}`;
544
- const response = await (0, fetch_1.enhancedFetch)(apiUrl);
545
- if (!response.ok) {
546
- throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
547
- }
548
- const members = await response.json();
549
- return (0, idConversion_1.cleanGidsFromObject)(members);
550
- },
551
- },
552
- ],
553
- [
554
- "list_group_iterations",
555
- {
556
- name: "list_group_iterations",
557
- description: "SPRINTS: List iterations/sprints for agile planning. Filter by state: current, upcoming, closed.",
558
- inputSchema: z.toJSONSchema(schema_readonly_1.ListGroupIterationsSchema),
559
- handler: async (args) => {
560
- const options = schema_readonly_1.ListGroupIterationsSchema.parse(args);
561
- const { group_id } = options;
562
- const queryParams = new URLSearchParams();
563
- Object.entries(options).forEach(([key, value]) => {
564
- if (value !== undefined && key !== "group_id") {
565
- queryParams.set(key, String(value));
685
+ case "update": {
686
+ const { group_id, action: _action, ...updateParams } = input;
687
+ const body = new URLSearchParams();
688
+ Object.entries(updateParams).forEach(([key, value]) => {
689
+ if (value !== undefined) {
690
+ body.set(key, String(value));
691
+ }
692
+ });
693
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/groups/${encodeURIComponent(group_id)}`;
694
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl, {
695
+ method: "PUT",
696
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
697
+ body: body.toString(),
698
+ });
699
+ if (!response.ok) {
700
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
701
+ }
702
+ return await response.json();
566
703
  }
567
- });
568
- const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/groups/${encodeURIComponent(group_id)}/iterations?${queryParams}`;
569
- const response = await (0, fetch_1.enhancedFetch)(apiUrl);
570
- if (!response.ok) {
571
- throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
572
- }
573
- return await response.json();
574
- },
575
- },
576
- ],
577
- [
578
- "download_attachment",
579
- {
580
- name: "download_attachment",
581
- description: "DOWNLOAD: Retrieve file attachments from issues/MRs. Requires secret token and filename from attachment URL.",
582
- inputSchema: z.toJSONSchema(schema_readonly_1.DownloadAttachmentSchema),
583
- handler: async (args) => {
584
- const options = schema_readonly_1.DownloadAttachmentSchema.parse(args);
585
- const { project_id, secret, filename } = options;
586
- const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(project_id)}/uploads/${secret}/${filename}`;
587
- const response = await (0, fetch_1.enhancedFetch)(apiUrl);
588
- if (!response.ok) {
589
- throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
590
- }
591
- const attachment = await response.arrayBuffer();
592
- return {
593
- filename,
594
- content: Buffer.from(attachment).toString("base64"),
595
- contentType: response.headers.get("content-type") ?? "application/octet-stream",
596
- };
597
- },
598
- },
599
- ],
600
- [
601
- "create_branch",
602
- {
603
- name: "create_branch",
604
- description: "NEW BRANCH: Create a Git branch from existing ref. Required before creating MRs. Ref can be branch name, tag, or commit SHA.",
605
- inputSchema: z.toJSONSchema(schema_1.CreateBranchSchema),
606
- handler: async (args) => {
607
- const options = schema_1.CreateBranchSchema.parse(args);
608
- const body = new URLSearchParams();
609
- body.set("branch", options.branch);
610
- body.set("ref", options.ref);
611
- const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(options.project_id)}/repository/branches`;
612
- const response = await (0, fetch_1.enhancedFetch)(apiUrl, {
613
- method: "POST",
614
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
615
- body: body.toString(),
616
- });
617
- if (!response.ok) {
618
- throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
619
- }
620
- return await response.json();
621
- },
622
- },
623
- ],
624
- [
625
- "create_group",
626
- {
627
- name: "create_group",
628
- description: "CREATE GROUP: Create a new GitLab group/namespace. Groups organize projects and teams. Can create subgroups with parent_id.",
629
- inputSchema: z.toJSONSchema(schema_1.CreateGroupSchema),
630
- handler: async (args) => {
631
- const options = schema_1.CreateGroupSchema.parse(args);
632
- const body = new URLSearchParams();
633
- body.set("name", options.name);
634
- body.set("path", options.path);
635
- if (options.description)
636
- body.set("description", options.description);
637
- if (options.visibility)
638
- body.set("visibility", options.visibility);
639
- if (options.parent_id !== undefined)
640
- body.set("parent_id", String(options.parent_id));
641
- if (options.lfs_enabled !== undefined)
642
- body.set("lfs_enabled", String(options.lfs_enabled));
643
- if (options.request_access_enabled !== undefined)
644
- body.set("request_access_enabled", String(options.request_access_enabled));
645
- if (options.default_branch_protection !== undefined)
646
- body.set("default_branch_protection", String(options.default_branch_protection));
647
- if (options.avatar)
648
- body.set("avatar", options.avatar);
649
- const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/groups`;
650
- const response = await (0, fetch_1.enhancedFetch)(apiUrl, {
651
- method: "POST",
652
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
653
- body: body.toString(),
654
- });
655
- if (!response.ok) {
656
- throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
657
- }
658
- return await response.json();
659
- },
660
- },
661
- ],
662
- [
663
- "list_todos",
664
- {
665
- name: "list_todos",
666
- description: "TASK QUEUE: View your GitLab todos (notifications requiring action). Todos are auto-created when you're assigned to issues/MRs, @mentioned, requested as reviewer, or CI pipelines fail. Filter by state (pending/done), action type (assigned, mentioned, review_requested), or target type (Issue, MergeRequest).",
667
- inputSchema: z.toJSONSchema(schema_readonly_1.ListTodosSchema),
668
- handler: async (args) => {
669
- const options = schema_readonly_1.ListTodosSchema.parse(args);
670
- const queryParams = new URLSearchParams();
671
- Object.entries(options).forEach(([key, value]) => {
672
- if (value !== undefined) {
673
- queryParams.set(key, String(value));
704
+ case "delete": {
705
+ const { group_id } = input;
706
+ const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/groups/${encodeURIComponent(group_id)}`;
707
+ const response = await (0, fetch_1.enhancedFetch)(apiUrl, { method: "DELETE" });
708
+ if (!response.ok) {
709
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
710
+ }
711
+ return { success: true, message: `Group ${group_id} deleted` };
674
712
  }
675
- });
676
- const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/todos?${queryParams}`;
677
- const response = await (0, fetch_1.enhancedFetch)(apiUrl);
678
- if (!response.ok) {
679
- throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
713
+ default:
714
+ throw new Error(`Unknown action: ${input.action}`);
680
715
  }
681
- const todos = await response.json();
682
- return (0, idConversion_1.cleanGidsFromObject)(todos);
683
716
  },
684
717
  },
685
718
  ],
@@ -687,7 +720,7 @@ exports.coreToolRegistry = new Map([
687
720
  "manage_todos",
688
721
  {
689
722
  name: "manage_todos",
690
- description: "TODO ACTIONS: Manage your GitLab todo items. Use 'mark_done' with id to complete a single todo (returns the updated todo object). Use 'mark_all_done' to clear your entire todo queue (returns success status). Use 'restore' with id to undo a completed todo (returns the restored todo object).",
723
+ description: "Manage your GitLab todo queue. Actions: mark_done (complete a single todo), mark_all_done (clear entire queue), restore (undo completion). Related: browse_todos to view your todo list.",
691
724
  inputSchema: z.toJSONSchema(schema_1.ManageTodosSchema),
692
725
  handler: async (args) => {
693
726
  const input = schema_1.ManageTodosSchema.parse(args);
@@ -734,11 +767,8 @@ function getCoreReadOnlyToolNames() {
734
767
  "browse_namespaces",
735
768
  "browse_commits",
736
769
  "browse_events",
737
- "get_users",
738
- "list_project_members",
739
- "list_group_iterations",
740
- "download_attachment",
741
- "list_todos",
770
+ "browse_users",
771
+ "browse_todos",
742
772
  ];
743
773
  }
744
774
  function getCoreToolDefinitions() {