@zereight/mcp-gitlab 2.1.60 → 2.1.62

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.ko.md CHANGED
@@ -17,7 +17,7 @@ PAT, OAuth, 읽기 전용 모드, 동적 API URL, 원격 인증을 지원하며
17
17
 
18
18
  ### 왜 이 GitLab MCP를 사용하나요?
19
19
 
20
- - **232개 도구 + `discover_tools`** — 작은 toolset으로 시작하고, 런타임에 카테고리 활성화
20
+ - **261개 도구 + `discover_tools`** — 작은 toolset으로 시작하고, 런타임에 카테고리 활성화
21
21
  - **MR 2단계 리뷰** — `list_merge_request_changed_files` → 배치 `get_merge_request_file_diff`
22
22
  - **Agent Skill 내장** — `skills/gitlab-mcp/` 워크플로우 가이드
23
23
  - **유연한 인증** — Personal Access Token, 로컬 OAuth2 브라우저 플로우, MCP OAuth 프록시, 요청별 원격 인증
@@ -30,7 +30,7 @@ PAT, OAuth, 읽기 전용 모드, 동적 API URL, 원격 인증을 지원하며
30
30
  | | @zereight/mcp-gitlab | GitLab MCP A (커뮤니티 CQRS형) |
31
31
  |---|----------------------|--------------------------------|
32
32
  | **적합한 경우** | AI 에이전트 워크플로우 | 엔터프라이즈 멀티 인스턴스 / 그룹형 도구 |
33
- | **도구 모델** | ~232개 세분화 도구 + `discover_tools` | ~50–60개 `browse_*` / `manage_*` 그룹 도구 |
33
+ | **도구 모델** | ~261개 세분화 도구 + `discover_tools` | ~50–60개 `browse_*` / `manage_*` 그룹 도구 |
34
34
  | **MR 리뷰** | 2단계 배치 diff | 서버마다 다름 |
35
35
  | **Node.js** | >=18.17 | 보통 >=24 |
36
36
  | **라이선스** | MIT | 서버마다 다름 |
@@ -110,7 +110,7 @@ command = lib.getExe inputs.gitlab-mcp.packages.${system}.default;
110
110
 
111
111
  예시는 기존 `mcp-gitlab`보다 충돌 가능성이 낮은 `zereight-mcp-gitlab` 별칭을 사용합니다. MCP 클라이언트가 찾지 못하면 `which zereight-mcp-gitlab`의 절대 경로를 사용하세요.
112
112
 
113
- 전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.1.59`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
113
+ 전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.1.61`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
114
114
 
115
115
  #### CLI 인자 사용하기(환경 변수 문제가 있는 클라이언트용)
116
116
 
package/README.md CHANGED
@@ -22,7 +22,7 @@ Supports PAT, OAuth, read-only mode, dynamic API URLs, and remote authorization
22
22
 
23
23
  ### Why use this GitLab MCP?
24
24
 
25
- - **232 tools + `discover_tools`** — start with a small toolset; activate more at runtime without CQRS-style grouping
25
+ - **261 tools + `discover_tools`** — start with a small toolset; activate more at runtime without CQRS-style grouping
26
26
  - **MR 2-step review** — `list_merge_request_changed_files` → batched `get_merge_request_file_diff`
27
27
  - **Agent Skill built in** — workflow guidance in `skills/gitlab-mcp/`
28
28
  - **Flexible auth** — Personal Access Token, local OAuth2 browser flow, MCP OAuth proxy, and per-request remote authorization
@@ -35,7 +35,7 @@ Supports PAT, OAuth, read-only mode, dynamic API URLs, and remote authorization
35
35
  | | @zereight/mcp-gitlab | GitLab MCP A (community CQRS-style) |
36
36
  |---|----------------------|-------------------------------------|
37
37
  | **Best for** | AI agent workflows | Enterprise multi-instance / grouped tools |
38
- | **Tool model** | ~232 granular tools + `discover_tools` | ~50–60 grouped `browse_*` / `manage_*` tools |
38
+ | **Tool model** | ~261 granular tools + `discover_tools` | ~50–60 grouped `browse_*` / `manage_*` tools |
39
39
  | **MR review** | 2-step batched diff | Varies |
40
40
  | **Node.js** | >=18.17 | Often >=24 |
41
41
  | **License** | MIT | Varies |
@@ -115,7 +115,7 @@ The store path is pinned by your lock file; update it with `nix flake update git
115
115
 
116
116
  The examples use `zereight-mcp-gitlab`, a less collision-prone alias for the legacy `mcp-gitlab` binary. If your MCP client cannot find it, use the absolute path from `which zereight-mcp-gitlab`.
117
117
 
118
- No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.1.59`. If you always want the newest release, use `npx -y @zereight/mcp-gitlab@latest` instead. The server prints a notice to stderr on startup when a newer version is available (disable with `GITLAB_DISABLE_VERSION_CHECK=true`).
118
+ No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.1.61`. If you always want the newest release, use `npx -y @zereight/mcp-gitlab@latest` instead. The server prints a notice to stderr on startup when a newer version is available (disable with `GITLAB_DISABLE_VERSION_CHECK=true`).
119
119
 
120
120
  #### Using CLI Arguments (for clients with env var issues)
121
121
 
@@ -583,23 +583,23 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
583
583
 
584
584
  <!-- TOOLS-START -->
585
585
 
586
- 1. `merge_merge_request` - Merge a merge request in a GitLab project
587
- 2. `approve_merge_request` - Approve a merge request (requires appropriate permissions)
588
- 3. `unapprove_merge_request` - Unapprove a previously approved merge request
589
- 4. `get_merge_request_approval_state` - Get merge request approval details including approvers (uses `approval_state` when available, otherwise falls back to `approvals`)
590
- 5. `get_merge_request_conflicts` - Get the conflicts of a merge request in a GitLab project
591
- 6. `list_merge_request_pipelines` - List pipelines for a merge request with pagination support
586
+ 1. `merge_merge_request` - Merge a merge request
587
+ 2. `approve_merge_request` - Approve a merge request
588
+ 3. `unapprove_merge_request` - Unapprove a merge request
589
+ 4. `get_merge_request_approval_state` - Get merge request approval details including approvers
590
+ 5. `get_merge_request_conflicts` - Get the conflicts of a merge request
591
+ 6. `list_merge_request_pipelines` - List pipelines for a merge request with pagination
592
592
  7. `execute_graphql` - Execute a GitLab GraphQL query
593
- 8. `create_or_update_file` - Create or update a single file in a GitLab project
593
+ 8. `create_or_update_file` - Create or update a file in a GitLab project
594
594
  9. `search_repositories` - Search for GitLab projects
595
595
  10. `create_repository` - Create a new GitLab project
596
- 11. `create_group` - Create a new GitLab group or subgroup (name, path, description, visibility, and optional parent_id)
597
- 12. `get_file_contents` - Get the contents of a file or directory from a GitLab project
598
- 13. `push_files` - Push multiple files to a GitLab project in a single commit
599
- 14. `create_issue` - Create a new issue in a GitLab project
600
- 15. `create_merge_request` - Create a new merge request in a GitLab project
601
- 16. `fork_repository` - Fork a GitLab project to your account or specified namespace
602
- 17. `create_branch` - Create a new branch in a GitLab project
596
+ 11. `create_group` - Create new group or subgroup
597
+ 12. `get_file_contents` - Get contents of a file or directory from a GitLab project
598
+ 13. `push_files` - Push multiple files in a single commit
599
+ 14. `create_issue` - Create a new issue
600
+ 15. `create_merge_request` - Create a new merge request
601
+ 16. `fork_repository` - Fork a project to your account or specified namespace
602
+ 17. `create_branch` - Create a new branch
603
603
  18. `get_branch` - Get branch details (commit, protection status)
604
604
  19. `list_branches` - List branches in project with search filter
605
605
  20. `delete_branch` - Delete branch from project
@@ -608,15 +608,15 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
608
608
  23. `protect_branch` - Protect a repository branch (set push/merge/unprotect access levels)
609
609
  24. `unprotect_branch` - Remove protection from a previously protected branch
610
610
  25. `update_default_branch` - Change the default branch of a project
611
- 26. `get_merge_request` - Get details of a merge request with compact deployment summary, behind-count, commit addition summary, and approval summary (Either mergeRequestIid or branchName must be provided)
612
- 27. `get_merge_request_diffs` - Get the changes/diffs of a merge request (Either mergeRequestIid or branchName must be provided)
613
- 28. `list_merge_request_changed_files` - STEP 1 of code review workflow. Returns ONLY the list of changed file paths in a merge request — WITHOUT diff content. Call this first to get file paths, then call get_merge_request_file_diff with multiple files in a single batched call (recommended 3-5 files per call). Supports excluded_file_patterns filtering using regex. (Either mergeRequestIid or branchName must be provided)
614
- 29. `list_merge_request_diffs` - List merge request diffs with pagination support (Either mergeRequestIid or branchName must be provided)
615
- 30. `get_merge_request_file_diff` - STEP 2 of code review workflow. Get diffs for one or more files from a merge request. Call list_merge_request_changed_files first, then pass them as an array to fetch diffs efficiently. Batching multiple files (recommended 3-5) is supported. (Either mergeRequestIid or branchName must be provided)
611
+ 26. `get_merge_request` - Get details of a merge request (mergeRequestIid or branchName required). Set include_summaries=true for deployment/commit/approval summaries
612
+ 27. `get_merge_request_diffs` - Get the changes/diffs of a merge request (mergeRequestIid or branchName required)
613
+ 28. `list_merge_request_changed_files` - List changed file paths in a merge request without diff content (mergeRequestIid or branchName required)
614
+ 29. `list_merge_request_diffs` - List merge request diffs with pagination (mergeRequestIid or branchName required)
615
+ 30. `get_merge_request_file_diff` - Get diffs for specific files from a merge request (mergeRequestIid or branchName required)
616
616
  31. `list_merge_request_versions` - List all versions of a merge request
617
617
  32. `get_merge_request_version` - Get a specific version of a merge request
618
- 33. `get_branch_diffs` - Get the changes/diffs between two branches or commits in a GitLab project
619
- 34. `update_merge_request` - Update a merge request (Either mergeRequestIid or branchName must be provided)
618
+ 33. `get_branch_diffs` - Get diffs between two branches or commits
619
+ 34. `update_merge_request` - Update a merge request (mergeRequestIid or branchName required)
620
620
  35. `create_note` - Create a new note (comment) to an issue or merge request
621
621
  36. `create_merge_request_thread` - Create a new thread on a merge request
622
622
  37. `resolve_merge_request_thread` - Resolve a thread on a merge request
@@ -624,18 +624,18 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
624
624
  39. `delete_merge_request_discussion_note` - Delete a discussion note on a merge request
625
625
  40. `update_merge_request_discussion_note` - Update a discussion note on a merge request
626
626
  41. `create_merge_request_discussion_note` - Add a new discussion note to an existing merge request thread
627
- 42. `create_merge_request_note` - Add a new note to an existing merge request thread
627
+ 42. `create_merge_request_note` - Add a new note to a merge request
628
628
  43. `delete_merge_request_note` - Delete an existing merge request note
629
629
  44. `get_merge_request_note` - Get a specific note for a merge request
630
630
  45. `get_merge_request_notes` - List notes for a merge request
631
- 46. `update_merge_request_note` - Modify an existing merge request thread note
631
+ 46. `update_merge_request_note` - Modify an existing merge request note
632
632
  47. `get_draft_note` - Get a single draft note from a merge request
633
633
  48. `list_draft_notes` - List draft notes for a merge request
634
634
  49. `create_draft_note` - Create a draft note for a merge request
635
635
  50. `update_draft_note` - Update an existing draft note
636
636
  51. `delete_draft_note` - Delete a draft note
637
637
  52. `publish_draft_note` - Publish a single draft note
638
- 53. `bulk_publish_draft_notes` - Publish all draft notes for a merge request
638
+ 53. `bulk_publish_draft_notes` - Publish all draft notes for a merge request. Optionally sets reviewer_state and posts a summary note (GitLab 19.2+). Can set reviewer_state even with no drafts.
639
639
  54. `list_merge_request_emoji_reactions` - List all emoji reactions on a merge request
640
640
  55. `list_merge_request_note_emoji_reactions` - List all emoji reactions on a merge request note. Pass discussion_id for discussion thread replies.
641
641
  56. `create_merge_request_emoji_reaction` - Add an emoji reaction to a merge request (e.g. thumbsup, rocket, eyes)
@@ -643,30 +643,30 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
643
643
  58. `create_merge_request_note_emoji_reaction` - Add an emoji reaction to a merge request note. Pass discussion_id for discussion thread replies.
644
644
  59. `delete_merge_request_note_emoji_reaction` - Remove an emoji reaction from a merge request note. Pass discussion_id for discussion thread replies.
645
645
  60. `update_issue_note` - Modify an existing issue thread note
646
- 61. `create_issue_note` - Add a new note to an existing issue thread
646
+ 61. `create_issue_note` - Add a note to an issue, optionally replying to a discussion thread
647
647
  62. `list_issue_emoji_reactions` - List all emoji reactions on an issue
648
648
  63. `list_issue_note_emoji_reactions` - List all emoji reactions on an issue note. Pass discussion_id for discussion thread replies.
649
649
  64. `create_issue_emoji_reaction` - Add an emoji reaction to an issue (e.g. thumbsup, rocket, eyes)
650
650
  65. `delete_issue_emoji_reaction` - Remove an emoji reaction from an issue
651
651
  66. `create_issue_note_emoji_reaction` - Add an emoji reaction to an issue note. Pass discussion_id for discussion thread replies.
652
652
  67. `delete_issue_note_emoji_reaction` - Remove an emoji reaction from an issue note. Pass discussion_id for discussion thread replies.
653
- 68. `list_issues` - List issues (default: created by current user only; use scope='all' for all accessible issues)
654
- 69. `my_issues` - List issues assigned to the authenticated user (defaults to open issues)
655
- 70. `get_issue` - Get details of a specific issue in a GitLab project
656
- 71. `update_issue` - Update an issue in a GitLab project
657
- 72. `update_issue_description_patch` - Apply a patch (search/replace or unified diff) to an issue description. Reduces token usage by sending only the change instead of the full description. Supports `dry_run` to preview and `create_note` to summarize.
658
- 73. `delete_issue` - Delete an issue from a GitLab project
653
+ 68. `list_issues` - List issues (default: created by current user; use scope='all' for all)
654
+ 69. `my_issues` - List issues assigned to the authenticated user
655
+ 70. `get_issue` - Get details of a specific issue. Returns a slim milestone by default; set full_response=true for the complete milestone object
656
+ 71. `update_issue` - Update an issue. Returns a slim confirmation by default; set full_response=true for the complete updated issue object
657
+ 72. `update_issue_description_patch` - Apply a patch (search/replace or unified diff) to an issue description. Reduces token usage by allowing small changes without sending the full description. Supports dry_run to preview changes and create_note to summarize updates.
658
+ 73. `delete_issue` - Delete an issue
659
659
  74. `list_todos` - List GitLab to-do items for the current user
660
660
  75. `mark_todo_done` - Mark a GitLab to-do item as done
661
661
  76. `mark_all_todos_done` - Mark all pending GitLab to-do items as done for the current user
662
662
  77. `list_issue_links` - List all issue links for a specific issue
663
- 78. `list_issue_discussions` - List discussions for an issue in a GitLab project
663
+ 78. `list_issue_discussions` - List discussions for an issue
664
664
  79. `get_issue_link` - Get a specific issue link
665
665
  80. `create_issue_link` - Create an issue link between two issues
666
666
  81. `delete_issue_link` - Delete an issue link
667
- 82. `list_namespaces` - List all namespaces available to the current user
668
- 83. `get_namespace` - Get details of a namespace by ID or path
669
- 84. `verify_namespace` - Verify if a namespace path exists
667
+ 82. `list_namespaces` - List all namespaces (users and groups) available to the current user. Filter by kind='group' for groups only.
668
+ 83. `get_namespace` - Get details of a namespace (user or group) by ID or path. Groups are namespaces with kind='group'.
669
+ 84. `verify_namespace` - Verify if a namespace path exists. Use parent_id to scope the check to a specific parent namespace — required for nested namespaces where the same path may exist under different parents.
670
670
  85. `get_project` - Get details of a specific project
671
671
  86. `list_projects` - List projects accessible by the current user
672
672
  87. `update_project` - Update project settings such as description, visibility, default branch, and feature access levels
@@ -677,39 +677,39 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
677
677
  92. `create_label` - Create a new label in a project
678
678
  93. `update_label` - Update an existing label in a project
679
679
  94. `delete_label` - Delete a label from a project
680
- 95. `list_group_projects` - List projects in a GitLab group with filtering options
681
- 96. `list_wiki_pages` - List wiki pages in a GitLab project
680
+ 95. `list_group_projects` - List projects in a group
681
+ 96. `list_wiki_pages` - List wiki pages in a project
682
682
  97. `get_wiki_page` - Get details of a specific wiki page
683
- 98. `create_wiki_page` - Create a new wiki page in a GitLab project
684
- 99. `update_wiki_page` - Update an existing wiki page in a GitLab project
685
- 100. `delete_wiki_page` - Delete a wiki page from a GitLab project
686
- 101. `list_group_wiki_pages` - List wiki pages in a GitLab group
683
+ 98. `create_wiki_page` - Create a wiki page in a project
684
+ 99. `update_wiki_page` - Update a wiki page in a project
685
+ 100. `delete_wiki_page` - Delete a wiki page from a project
686
+ 101. `list_group_wiki_pages` - List wiki pages in a group
687
687
  102. `get_group_wiki_page` - Get details of a specific group wiki page
688
- 103. `create_group_wiki_page` - Create a new wiki page in a GitLab group
689
- 104. `update_group_wiki_page` - Update an existing wiki page in a GitLab group
690
- 105. `delete_group_wiki_page` - Delete a wiki page from a GitLab group
691
- 106. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories)
692
- 107. `list_pipelines` - List pipelines in a GitLab project with filtering options
693
- 108. `get_pipeline` - Get details of a specific pipeline in a GitLab project
688
+ 103. `create_group_wiki_page` - Create a wiki page in a group
689
+ 104. `update_group_wiki_page` - Update a wiki page in a group
690
+ 105. `delete_group_wiki_page` - Delete a wiki page from a group
691
+ 106. `get_repository_tree` - List files and directories in a repository
692
+ 107. `list_pipelines` - List pipelines with filtering options
693
+ 108. `get_pipeline` - Get details of a specific pipeline
694
694
  109. `get_pipeline_variables` - Get variables configured for a pipeline
695
695
  110. `get_pipeline_test_report` - Get pipeline test report
696
696
  111. `get_pipeline_test_report_summary` - Get pipeline test report summary
697
697
  112. `delete_pipeline` - Delete a pipeline. Requires the project Owner role, cannot be undone, and does not automatically delete child pipelines.
698
698
  113. `update_pipeline_metadata` - Update pipeline metadata
699
- 114. `list_deployments` - List deployments in a GitLab project with filtering options
700
- 115. `get_deployment` - Get details of a specific deployment in a GitLab project
699
+ 114. `list_deployments` - List deployments with filtering options
700
+ 115. `get_deployment` - Get deployment details, including approval_summary, approvals, and pending_approval_count when GitLab provides them
701
701
  116. `create_deployment` - Create a deployment
702
702
  117. `update_deployment` - Update a deployment status
703
703
  118. `delete_deployment` - Delete a deployment
704
704
  119. `list_deployment_merge_requests` - List merge requests shipped with a deployment
705
705
  120. `approve_deployment` - Approve or reject a protected-environment deployment
706
- 121. `list_environments` - List environments in a GitLab project
707
- 122. `get_environment` - Get details of a specific environment in a GitLab project
706
+ 121. `list_environments` - List environments in a project
707
+ 122. `get_environment` - Get details of a specific environment
708
708
  123. `update_environment` - Update an environment
709
709
  124. `delete_environment` - Delete a stopped environment
710
710
  125. `stop_environment` - Stop an environment
711
711
  126. `stop_stale_environments` - Stop eligible stale environments; protected environments are excluded and environments are stopped, not deleted
712
- 127. `delete_review_app_environments` - Schedule deletion of stopped review-app environments one week later; `dry_run` defaults to true and actual scheduling requires `dry_run=false`
712
+ 127. `delete_review_app_environments` - Schedule deletion of stopped review-app environments one week later; dry_run defaults to true and actual scheduling requires dry_run=false
713
713
  128. `list_pipeline_triggers` - List project pipeline trigger tokens
714
714
  129. `get_pipeline_trigger` - Get a project pipeline trigger
715
715
  130. `create_pipeline_trigger` - Create a project pipeline trigger
@@ -717,11 +717,11 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
717
717
  132. `delete_pipeline_trigger` - Delete a project pipeline trigger
718
718
  133. `trigger_pipeline` - Trigger a pipeline with a pipeline trigger token
719
719
  134. `list_pipeline_jobs` - List all jobs in a specific pipeline
720
- 135. `list_pipeline_trigger_jobs` - List all trigger jobs (bridges) in a specific pipeline that trigger downstream pipelines
720
+ 135. `list_pipeline_trigger_jobs` - List trigger jobs (bridges) in a pipeline
721
721
  136. `get_pipeline_job` - Get details of a GitLab pipeline job number
722
- 137. `get_pipeline_job_output` - Get the output/trace of a GitLab pipeline job with optional pagination to limit context window usage
722
+ 137. `get_pipeline_job_output` - Get the output/trace of a pipeline job with optional pagination
723
723
  138. `validate_ci_lint` - Validate provided GitLab CI/CD YAML content for a project
724
- 139. `validate_project_ci_lint` - Validate an existing `.gitlab-ci.yml` configuration for a project
724
+ 139. `validate_project_ci_lint` - Validate an existing .gitlab-ci.yml configuration for a project
725
725
  140. `list_ci_catalog_resources` - List GitLab CI/CD Catalog resources/components visible to the user
726
726
  141. `get_ci_catalog_resource` - Get details for a GitLab CI/CD Catalog resource, including versions and components
727
727
  142. `create_pipeline` - Create a new pipeline for a branch or tag
@@ -733,7 +733,7 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
733
733
  148. `create_pipeline_schedule` - Create a new pipeline schedule for a branch or tag
734
734
  149. `update_pipeline_schedule` - Update an existing pipeline schedule
735
735
  150. `delete_pipeline_schedule` - Delete a pipeline schedule
736
- 151. `play_pipeline_schedule` - Run a pipeline schedule immediately, without changing its next scheduled run
736
+ 151. `play_pipeline_schedule` - Run a pipeline schedule immediately
737
737
  152. `take_ownership_pipeline_schedule` - Take ownership of a pipeline schedule
738
738
  153. `get_pipeline_schedule_variable` - Get a single variable of a pipeline schedule
739
739
  154. `create_pipeline_schedule_variable` - Create a variable for a pipeline schedule
@@ -746,25 +746,25 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
746
746
  161. `erase_pipeline_job` - Erase a pipeline job log and artifacts
747
747
  162. `wait_for_pipeline` - Wait for a pipeline to reach a terminal status
748
748
  163. `wait_for_job` - Wait for a job to reach a terminal status
749
- 164. `list_job_artifacts` - List artifact files in a job's artifacts archive. Returns file names, paths, types, and sizes
750
- 165. `download_job_artifacts` - Download the entire artifact archive (zip) for a job to a local path. Returns the saved file path
751
- 166. `get_job_artifact_file` - Get the content of a single file from a job's artifacts by its path within the archive
752
- 167. `list_merge_requests` - List merge requests globally or in a specific GitLab project with filtering options (project_id is now optional)
753
- 168. `list_group_merge_requests` - List merge requests across all projects of a group and its subgroups with filtering options
754
- 169. `list_milestones` - List milestones in a GitLab project with filtering options
749
+ 164. `list_job_artifacts` - List artifact files in a job's archive
750
+ 165. `download_job_artifacts` - Download job artifact archive (zip) and save to a local path
751
+ 166. `get_job_artifact_file` - Get content of a single file from a job's artifacts
752
+ 167. `list_merge_requests` - List merge requests (without project_id: user's MRs; with project_id: project MRs)
753
+ 168. `list_group_merge_requests` - List merge requests across all projects of a group and its subgroups
754
+ 169. `list_milestones` - List milestones with filtering options
755
755
  170. `get_milestone` - Get details of a specific milestone
756
- 171. `create_milestone` - Create a new milestone in a GitLab project
757
- 172. `edit_milestone` - Edit an existing milestone in a GitLab project
758
- 173. `delete_milestone` - Delete a milestone from a GitLab project
756
+ 171. `create_milestone` - Create a new milestone
757
+ 172. `edit_milestone` - Edit an existing milestone
758
+ 173. `delete_milestone` - Delete a milestone
759
759
  174. `get_milestone_issue` - Get issues associated with a specific milestone
760
760
  175. `get_milestone_merge_requests` - Get merge requests associated with a specific milestone
761
761
  176. `promote_milestone` - Promote a milestone to the next stage
762
762
  177. `get_milestone_burndown_events` - Get burndown events for a specific milestone
763
- 178. `list_group_milestones` - List milestones in a GitLab group with filtering options
763
+ 178. `list_group_milestones` - List group milestones with filtering options
764
764
  179. `get_group_milestone` - Get details of a specific group milestone
765
- 180. `create_group_milestone` - Create a new milestone in a GitLab group
765
+ 180. `create_group_milestone` - Create a new group milestone
766
766
  181. `edit_group_milestone` - Edit an existing group milestone
767
- 182. `delete_group_milestone` - Delete a milestone from a GitLab group
767
+ 182. `delete_group_milestone` - Delete a group milestone
768
768
  183. `get_group_milestone_issue` - Get issues associated with a specific group milestone
769
769
  184. `get_group_milestone_merge_requests` - Get merge requests associated with a specific group milestone
770
770
  185. `get_group_milestone_burndown_events` - Get burndown events for a specific group milestone
@@ -775,72 +775,76 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
775
775
  190. `get_commit` - Get details of a specific commit
776
776
  191. `get_commit_diff` - Get changes/diffs of a specific commit
777
777
  192. `get_file_blame` - Get git blame for a file at a given ref. Each entry maps a contiguous range of source lines to the commit that last changed them (id, author, authored_date, message). Use range_start/range_end to limit blame to specific lines.
778
- 193. `list_commit_statuses` - List statuses for a specific commit
779
- 194. `create_commit_status` - Create or update the status of a specific commit
778
+ 193. `list_commit_statuses` - List statuses for a commit
779
+ 194. `create_commit_status` - Create or update the status of a commit
780
780
  195. `list_group_iterations` - List group iterations with filtering options
781
- 196. `upload_markdown` - Upload a file to a GitLab project for use in markdown content
782
- 197. `download_attachment` - Download an uploaded file from a GitLab project by secret and filename
783
- 198. `health_check` - Verify server status and authentication; when authenticated, reports GitLab instance version from `/api/v4/version` (`version`, `revision`, `enterprise`)
784
- 199. `list_events` - List all events for the currently authenticated user
785
- 200. `get_project_events` - List all visible events for a specified project
781
+ 196. `upload_markdown` - Upload a file for use in markdown content
782
+ 197. `download_attachment` - Download an uploaded file from a project (images returned as base64; use local_path to save to disk)
783
+ 198. `health_check` - Verify server status and authentication. Always reports the MCP server version (mcp_server_version). When authenticated, also reports the GitLab instance version from GET /api/v4/version (version, revision, enterprise). Version lookup failures do not fail the health check — those fields are omitted.
784
+ 199. `list_events` - List events for the authenticated user (before/after: YYYY-MM-DD)
785
+ 200. `get_project_events` - List events for a project (before/after: YYYY-MM-DD)
786
786
  201. `list_releases` - List all releases for a project
787
787
  202. `get_release` - Get a release by tag name
788
- 203. `create_release` - Create a new release in a GitLab project
789
- 204. `update_release` - Update an existing release in a GitLab project
790
- 205. `delete_release` - Delete a release from a GitLab project (does not delete the associated tag)
791
- 206. `create_release_evidence` - Create release evidence for an existing release (GitLab Premium/Ultimate only)
788
+ 203. `create_release` - Create a new release
789
+ 204. `update_release` - Update an existing release
790
+ 205. `delete_release` - Delete a release (does not delete the tag)
791
+ 206. `create_release_evidence` - Create release evidence (Premium/Ultimate)
792
792
  207. `download_release_asset` - Download a release asset file by direct asset path
793
- 208. `list_tags` - List repository tags with filtering and pagination support
794
- 209. `get_tag` - Get details of a specific repository tag
795
- 210. `create_tag` - Create a new tag in the repository
796
- 211. `delete_tag` - Delete a tag from the repository
797
- 212. `get_tag_signature` - Get the signature of a signed tag
798
- 213. `get_work_item` - Get a single work item with full details including status, hierarchy (parent/children), type, labels, assignees, and all widgets
799
- 214. `list_work_items` - List work items in a project with filters (type, state, search, assignees, labels). Returns items with status and hierarchy info
800
- 215. `create_work_item` - Create a new work item (issue, task, incident, test_case, epic, key_result, objective, requirement, ticket). Supports setting title, description, labels, assignees, weight, parent, health status, start/due dates, milestone, and confidentiality
801
- 216. `update_work_item` - Update a work item. Can modify title, description, labels, assignees, weight, state, status, parent hierarchy, children, health status, start/due dates, milestone, confidentiality, linked items, and custom fields
802
- 217. `convert_work_item_type` - Convert a work item to a different type (e.g. issue to task, task to incident)
803
- 218. `list_work_item_statuses` - List available statuses for a work item type in a project. Requires GitLab Premium/Ultimate with configurable statuses
804
- 219. `list_custom_field_definitions` - List available custom field definitions for a work item type in a project. Returns field names, types, and IDs needed for setting custom fields via update_work_item
805
- 220. `move_work_item` - Move a work item (issue, task, etc.) to a different project. Uses GitLab GraphQL issueMove mutation
806
- 221. `list_work_item_notes` - List notes and discussions on a work item. Returns threaded discussions with author, body, timestamps, and system/internal flags
807
- 222. `create_work_item_note` - Add a note/comment to a work item. Supports Markdown, internal notes, and threaded replies
793
+ 208. `list_tags` - List repository tags for a project
794
+ 209. `get_tag` - Get a repository tag by name
795
+ 210. `create_tag` - Create a new repository tag
796
+ 211. `delete_tag` - Delete a repository tag
797
+ 212. `get_tag_signature` - Get the X.509 signature of a signed tag (404 if unsigned)
798
+ 213. `get_work_item` - Get a work item with full details including status, hierarchy, type, and widgets
799
+ 214. `list_work_items` - List work items with filters (type, state, search, assignees, labels)
800
+ 215. `create_work_item` - Create a work item (issue, task, incident, epic, etc.) with full field support
801
+ 216. `update_work_item` - Update a work item (title, description, labels, assignees, state, parent, custom fields, etc.)
802
+ 217. `convert_work_item_type` - Convert a work item to a different type
803
+ 218. `list_work_item_statuses` - List available statuses for a work item type (Premium/Ultimate)
804
+ 219. `list_custom_field_definitions` - List custom field definitions for a work item type
805
+ 220. `move_work_item` - Move a work item to a different project
806
+ 221. `list_work_item_notes` - List notes and discussions on a work item
807
+ 222. `create_work_item_note` - Add a note to a work item (supports Markdown, internal notes, threads)
808
808
  223. `list_work_item_emoji_reactions` - List all emoji reactions on a work item
809
809
  224. `list_work_item_note_emoji_reactions` - List all emoji reactions on a work item note (comment, thread, or thread reply)
810
810
  225. `create_work_item_emoji_reaction` - Add an emoji reaction to a work item (e.g. thumbsup, rocket, eyes)
811
811
  226. `delete_work_item_emoji_reaction` - Remove an emoji reaction from a work item
812
812
  227. `create_work_item_note_emoji_reaction` - Add an emoji reaction to a work item note (comment, thread, or thread reply)
813
813
  228. `delete_work_item_note_emoji_reaction` - Remove an emoji reaction from a work item note (comment, thread, or thread reply)
814
- 229. `get_timeline_events` - List timeline events for an incident. Returns chronological events with notes, timestamps, and tags
815
- 230. `create_timeline_event` - Create a timeline event on an incident. Supports tags: 'Start time', 'End time', 'Impact detected', 'Response initiated', 'Impact mitigated', 'Cause identified'
816
- 231. `list_webhooks` - List all configured webhooks for a GitLab project or group. Provide either project_id or group_id
817
- 232. `create_webhook` - Create a webhook on a GitLab project or group
814
+ 229. `get_timeline_events` - List timeline events for an incident
815
+ 230. `create_timeline_event` - Create a timeline event on an incident
816
+ 231. `list_webhooks` - List webhooks for a project or group
817
+ 232. `create_webhook` - Create a webhook on a project or group
818
818
  233. `update_webhook` - Update an existing project or group webhook
819
819
  234. `delete_webhook` - Delete a project or group webhook
820
- 235. `list_webhook_events` - List recent webhook events (past 7 days) for a project or group webhook. Use summary mode for overview, then get_webhook_event for full details
821
- 236. `get_webhook_event` - Get full details of a specific webhook event by ID, including request/response payloads
822
- 237. `search_code` - Search for code across all projects on the GitLab instance (requires advanced search or exact code search to be enabled)
823
- 238. `search_project_code` - Search for code within a specific GitLab project (requires advanced search or exact code search to be enabled)
824
- 239. `search_group_code` - Search for code within a specific GitLab group (requires advanced search or exact code search to be enabled)
825
- 240. `list_project_variables` - List CI/CD variables for a project with optional environment scope filter
826
- 241. `get_project_variable` - Get a single CI/CD variable from a project by key, with optional environment scope filter
827
- 242. `create_project_variable` - Create a new CI/CD variable in a project
828
- 243. `update_project_variable` - Update an existing CI/CD variable in a project, with optional filter to disambiguate by environment scope
829
- 244. `delete_project_variable` - Delete a CI/CD variable from a project, with optional filter to disambiguate by environment scope
830
- 245. `list_group_variables` - List CI/CD variables for a group with optional environment scope filter
831
- 246. `get_group_variable` - Get a single CI/CD variable from a group by key, with optional environment scope filter
832
- 247. `create_group_variable` - Create a new CI/CD variable in a group
833
- 248. `update_group_variable` - Update an existing CI/CD variable in a group, with optional filter to disambiguate by environment scope
834
- 249. `delete_group_variable` - Delete a CI/CD variable from a group, with optional filter to disambiguate by environment scope
835
- 250. `get_dependency_proxy_settings` - Get dependency proxy settings for a group (enabled status, blob count, total size, image prefix, TTL policy)
820
+ 235. `list_webhook_events` - List recent webhook events (past 7 days)
821
+ 236. `get_webhook_event` - Get full details of a specific webhook event
822
+ 237. `search_code` - Search for code across all projects (requires advanced search or Zoekt)
823
+ 238. `search_project_code` - Search for code within a specific project (requires advanced search or Zoekt)
824
+ 239. `search_group_code` - Search for code within a specific group (requires advanced search or Zoekt)
825
+ 240. `list_project_variables` - List CI/CD variables for a project
826
+ 241. `get_project_variable` - Get a single CI/CD variable from a project
827
+ 242. `create_project_variable` - Create a CI/CD variable for a project
828
+ 243. `update_project_variable` - Update an existing CI/CD variable in a project
829
+ 244. `delete_project_variable` - Delete a CI/CD variable from a project
830
+ 245. `list_group_variables` - List CI/CD variables for a group
831
+ 246. `get_group_variable` - Get a single CI/CD variable from a group
832
+ 247. `create_group_variable` - Create a CI/CD variable for a group
833
+ 248. `update_group_variable` - Update an existing CI/CD variable in a group
834
+ 249. `delete_group_variable` - Delete a CI/CD variable from a group
835
+ 250. `get_dependency_proxy_settings` - Get dependency proxy settings for a group
836
836
  251. `update_dependency_proxy_settings` - Update dependency proxy settings for a group (enable/disable, credentials for authenticated Docker Hub pulls)
837
- 252. `list_dependency_proxy_blobs` - List cached dependency proxy blobs for a group with cursor-based pagination
837
+ 252. `list_dependency_proxy_blobs` - List cached dependency proxy blobs for a group
838
838
  253. `purge_dependency_proxy_cache` - Schedule purge of all cached dependency proxy blobs for a group
839
839
  254. `list_project_vulnerabilities` - List vulnerabilities for a project with optional state, severity, and report type filters (GraphQL-backed, cursor pagination)
840
840
  255. `get_vulnerability` - Get full details of a specific vulnerability
841
841
  256. `dismiss_vulnerability` - Dismiss a vulnerability with a reason (acceptable_risk, false_positive, used_in_tests, mitigating_control, not_applicable) and optional comment
842
842
  257. `confirm_vulnerability` - Confirm a vulnerability as a real finding requiring remediation
843
- 258. `discover_tools` - Discover and activate additional tool categories for this session. Available categories: merge_requests, issues, repositories, branches, projects, labels, ci, groups, pipelines, milestones, wiki, releases, tags, users, workitems, webhooks, search, variables, dependency_proxy, vulnerabilities. Already-active categories are listed in the response.
843
+ 258. `orbit_query` - Execute a GitLab Orbit graph query over the indexed SDLC knowledge graph
844
+ 259. `orbit_get_schema` - Fetch the current GitLab Orbit graph schema (node and edge types)
845
+ 260. `orbit_get_status` - Check GitLab Orbit indexing status for the enabled scope
846
+ 261. `orbit_list_tools` - List the MCP tool definitions exposed by GitLab Orbit
847
+ 262. `discover_tools` - Discover and activate additional tool categories for this session. Available categories: merge_requests, issues, repositories, branches, projects, labels, ci, groups, pipelines, milestones, wiki, releases, tags, users, workitems, webhooks, search, variables, dependency_proxy, vulnerabilities, orbit. Already-active categories are listed in the response.
844
848
 
845
849
  <!-- TOOLS-END -->
846
850
 
package/README.zh-CN.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  ### 为什么使用这个 GitLab MCP?
19
19
 
20
- - **232 个工具 + `discover_tools`** — 从小型 toolset 开始,运行时按需激活类别
20
+ - **261 个工具 + `discover_tools`** — 从小型 toolset 开始,运行时按需激活类别
21
21
  - **MR 两步审查** — `list_merge_request_changed_files` → 批量 `get_merge_request_file_diff`
22
22
  - **内置 Agent Skill** — `skills/gitlab-mcp/` 工作流指南
23
23
  - **认证灵活** — Personal Access Token、本地 OAuth2 浏览器流程、MCP OAuth 代理、按请求远程授权
@@ -30,7 +30,7 @@
30
30
  | | @zereight/mcp-gitlab | GitLab MCP A(社区 CQRS 型) |
31
31
  |---|----------------------|------------------------------|
32
32
  | **更适合** | AI 代理工作流 | 企业多实例 / 分组工具 |
33
- | **工具模型** | ~232 个细粒度工具 + `discover_tools` | ~50–60 个 `browse_*` / `manage_*` 分组工具 |
33
+ | **工具模型** | ~261 个细粒度工具 + `discover_tools` | ~50–60 个 `browse_*` / `manage_*` 分组工具 |
34
34
  | **MR 审查** | 两步批量 diff | 因服务器而异 |
35
35
  | **Node.js** | >=18.17 | 通常 >=24 |
36
36
  | **许可证** | MIT | 因服务器而异 |
@@ -110,7 +110,7 @@ command = lib.getExe inputs.gitlab-mcp.packages.${system}.default;
110
110
 
111
111
  示例使用 `zereight-mcp-gitlab`,这是比旧的 `mcp-gitlab` 更不容易冲突的别名。如果 MCP 客户端找不到它,请使用 `which zereight-mcp-gitlab` 输出的绝对路径。
112
112
 
113
- 如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.1.59`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
113
+ 如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.1.61`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
114
114
 
115
115
  #### 使用 CLI 参数(适用于环境变量有问题的客户端)
116
116
 
package/build/index.js CHANGED
@@ -10233,6 +10233,7 @@ async function handleToolCall(params) {
10233
10233
  status: authenticated ? "ok" : "error",
10234
10234
  authenticated,
10235
10235
  gitlab_url: getEffectiveApiUrl(),
10236
+ mcp_server_version: SERVER_VERSION,
10236
10237
  ...(versionMetadata ?? {}),
10237
10238
  }),
10238
10239
  },
@@ -10389,7 +10390,7 @@ async function handleToolCall(params) {
10389
10390
  // Sensitive fields are also covered by REDACT_PATHS if arguments are logged elsewhere.
10390
10391
  logger.debug({ tool: params.name }, "Tool call failed");
10391
10392
  if (error instanceof z.ZodError) {
10392
- throw new Error(`Invalid arguments: ${error.errors
10393
+ throw new Error(`Invalid arguments: ${error.issues
10393
10394
  .map(e => `${e.path.join(".")}: ${e.message}`)
10394
10395
  .join(", ")}`);
10395
10396
  }
@@ -11583,6 +11584,7 @@ async function startStreamableHTTPServer() {
11583
11584
  }
11584
11585
  res.status(isHealthy ? 200 : 503).json({
11585
11586
  status: isHealthy ? "healthy" : "degraded",
11587
+ version: SERVER_VERSION,
11586
11588
  activeSessions,
11587
11589
  maxSessions: MAX_SESSIONS,
11588
11590
  uptime: process.uptime(),
package/build/schemas.js CHANGED
@@ -589,9 +589,9 @@ export const CreatePipelineTriggerSchema = z.object({ project_id: z.coerce.strin
589
589
  export const UpdatePipelineTriggerSchema = PipelineTriggerIdSchema.extend({ description: z.string() });
590
590
  export const TriggerPipelineSchema = z.object({
591
591
  project_id: z.coerce.string(), token: z.string(), ref: z.string(),
592
- variables: z.record(z.string()).optional(),
592
+ variables: z.record(z.string(), z.string()).optional(),
593
593
  inputs: z
594
- .record(z.unknown())
594
+ .record(z.string(), z.unknown())
595
595
  .optional()
596
596
  .describe("Structured pipeline inputs; supported from GitLab 17.10 behind a feature flag and generally available from GitLab 18.1. Omit on older GitLab versions."),
597
597
  });
@@ -663,7 +663,9 @@ export const GitLabPipelineScheduleSchema = z.object({
663
663
  inputs: z
664
664
  .array(z.object({
665
665
  name: z.string(),
666
- value: z.unknown(),
666
+ // Optional: Zod 4 rejects a missing key on required unknown (Zod 3 accepted it),
667
+ // and schedule inputs is a new API surface (18.1+) whose response shape is still settling.
668
+ value: z.unknown().optional(),
667
669
  }))
668
670
  .optional(),
669
671
  });
@@ -870,11 +872,11 @@ export const PlayPipelineJobSchema = z.object({
870
872
  }))
871
873
  .optional()
872
874
  .describe("Custom job variables to use when running the job"),
873
- job_inputs: z.record(z.unknown()).optional().describe("Typed job input values"),
875
+ job_inputs: z.record(z.string(), z.unknown()).optional().describe("Typed job input values"),
874
876
  });
875
877
  // Schema for retrying a job
876
878
  export const RetryPipelineJobSchema = PipelineJobControlSchema.extend({
877
- job_inputs: z.record(z.unknown()).optional().describe("Typed job input values"),
879
+ job_inputs: z.record(z.string(), z.unknown()).optional().describe("Typed job input values"),
878
880
  });
879
881
  // Schema for canceling a job
880
882
  export const CancelPipelineJobSchema = z.object({
@@ -1263,8 +1265,8 @@ export const GitLabCommitSchema = z.object({
1263
1265
  total: z.coerce.number().optional().nullable(),
1264
1266
  })
1265
1267
  .optional(), // Only present when with_stats=true
1266
- trailers: z.record(z.string()).optional().default({}), // Git trailers, may be empty object
1267
- extended_trailers: z.record(z.array(z.string())).optional().default({}), // Extended trailers, may be empty object
1268
+ trailers: z.record(z.string(), z.string()).optional().default({}), // Git trailers, may be empty object
1269
+ extended_trailers: z.record(z.string(), z.array(z.string())).optional().default({}), // Extended trailers, may be empty object
1268
1270
  });
1269
1271
  export const GitLabCommitStatusSchema = z
1270
1272
  .object({
@@ -2129,8 +2131,7 @@ const MergeRequestParamsSchema = ProjectParamsSchema.extend({
2129
2131
  project_id: z
2130
2132
  .preprocess(value => (value === undefined || value === null ? value : String(value)), z
2131
2133
  .string({
2132
- required_error: "project_id is required",
2133
- invalid_type_error: "project_id is required",
2134
+ error: "project_id is required",
2134
2135
  })
2135
2136
  .refine(value => value === "" || value.trim().length > 0, "project_id is required")
2136
2137
  .transform(value => (value === "" ? value : value.trim())))
@@ -2283,8 +2284,7 @@ export const ListMergeRequestPipelinesSchema = ProjectParamsSchema.extend({
2283
2284
  merge_request_iid: z
2284
2285
  .preprocess(value => (value === undefined || value === null ? value : String(value)), z
2285
2286
  .string({
2286
- required_error: "merge_request_iid is required",
2287
- invalid_type_error: "merge_request_iid is required",
2287
+ error: "merge_request_iid is required",
2288
2288
  })
2289
2289
  .refine(value => value.trim().length > 0, "merge_request_iid is required")
2290
2290
  .transform(value => value.trim()))
@@ -2806,7 +2806,7 @@ export const GitLabWikiPageSchema = z.object({
2806
2806
  slug: z.string(),
2807
2807
  format: z.string(),
2808
2808
  content: z.string().optional(),
2809
- front_matter: z.record(z.unknown()).optional(),
2809
+ front_matter: z.record(z.string(), z.unknown()).optional(),
2810
2810
  created_at: z.string().optional(),
2811
2811
  updated_at: z.string().optional(),
2812
2812
  });
@@ -2978,7 +2978,7 @@ export const GitLabDraftNoteSchema = z
2978
2978
  merge_request_id: z.coerce.number().nullable().optional(),
2979
2979
  commit_id: z.string().nullable().optional(),
2980
2980
  discussion_id: z.string().nullable().optional(),
2981
- position: z.record(z.unknown()).nullable().optional(),
2981
+ position: z.record(z.string(), z.unknown()).nullable().optional(),
2982
2982
  resolve_discussion: z.coerce.boolean().optional(),
2983
2983
  })
2984
2984
  .transform(data => ({
@@ -3564,7 +3564,10 @@ export const GitLabMergeRequestVersionDetailSchema = GitLabMergeRequestVersionSc
3564
3564
  // GraphQL generic execution schema
3565
3565
  export const ExecuteGraphQLSchema = z.object({
3566
3566
  query: z.string().describe("GraphQL query string"),
3567
- variables: z.record(z.any()).optional().describe("Variables object for the GraphQL query"),
3567
+ variables: z
3568
+ .record(z.string(), z.any())
3569
+ .optional()
3570
+ .describe("Variables object for the GraphQL query"),
3568
3571
  });
3569
3572
  // Release schemas
3570
3573
  export const GitLabReleaseAssetLinkSchema = z.object({
@@ -3857,10 +3860,10 @@ export const GitLabTagSignatureSchema = z.object({
3857
3860
  });
3858
3861
  // --- Work item schemas (GraphQL-based) ---
3859
3862
  // Case-insensitive work item type enum (accepts "ISSUE", "Issue", "issue")
3860
- const workItemTypeEnum = z
3861
- .string()
3862
- .transform(v => v.toLowerCase())
3863
- .pipe(z.enum([
3863
+ // Case-insensitive enum: lowercase the input, then validate against allowed values.
3864
+ // (z.preprocess form so the enum stays visible to JSON Schema conversion;
3865
+ // a transform().pipe() chain only exposes its input side as a plain string.)
3866
+ const workItemTypeEnum = z.preprocess(v => (typeof v === "string" ? v.toLowerCase() : v), z.enum([
3864
3867
  "issue",
3865
3868
  "task",
3866
3869
  "incident",
@@ -0,0 +1,32 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+ import { GitLabPipelineScheduleSchema } from "../schemas.js";
4
+ const BASE_SCHEDULE = {
5
+ id: 13,
6
+ description: "Nightly build",
7
+ cron: "0 1 * * *",
8
+ active: true,
9
+ created_at: "2026-01-01T00:00:00Z",
10
+ updated_at: "2026-02-01T00:00:00Z",
11
+ };
12
+ test("pipeline schedule response accepts inputs without value", () => {
13
+ // Zod 4 rejects a missing key on required unknown (Zod 3 accepted it);
14
+ // schedule inputs must tolerate a valueless entry.
15
+ const parsed = GitLabPipelineScheduleSchema.parse({
16
+ ...BASE_SCHEDULE,
17
+ inputs: [{ name: "deploy_strategy" }],
18
+ });
19
+ assert.equal(parsed.inputs?.[0].name, "deploy_strategy");
20
+ assert.equal("value" in (parsed.inputs?.[0] ?? {}), false);
21
+ });
22
+ test("pipeline schedule response keeps inputs with value", () => {
23
+ const parsed = GitLabPipelineScheduleSchema.parse({
24
+ ...BASE_SCHEDULE,
25
+ inputs: [
26
+ { name: "deploy_strategy", value: "blue-green" },
27
+ { name: "matrix", value: { os: "linux" } },
28
+ ],
29
+ });
30
+ assert.equal(parsed.inputs?.[0].value, "blue-green");
31
+ assert.deepEqual(parsed.inputs?.[1].value, { os: "linux" });
32
+ });
@@ -17,8 +17,9 @@ test("group milestone response keeps group_id and rejects missing group_id", ()
17
17
  });
18
18
  assert.equal(parsed.group_id, "16");
19
19
  assert.equal("project_id" in parsed, false);
20
- // Project schema coerces missing project_id to "undefined" — why group schema exists
21
- const coerced = GitLabMilestonesSchema.parse({
20
+ // Project schema rejects a group payload (missing project_id) — why group schema exists.
21
+ // (Zod 3 coerced the missing key to the string "undefined"; Zod 4 rejects it.)
22
+ assert.throws(() => GitLabMilestonesSchema.parse({
22
23
  id: 12,
23
24
  iid: 3,
24
25
  group_id: 16,
@@ -30,8 +31,7 @@ test("group milestone response keeps group_id and rejects missing group_id", ()
30
31
  updated_at: "2013-10-02T09:24:18Z",
31
32
  created_at: "2013-10-02T09:24:18Z",
32
33
  expired: false,
33
- });
34
- assert.equal(coerced.project_id, "undefined");
34
+ }));
35
35
  });
36
36
  test("group milestone issues schema accepts page and per_page", () => {
37
37
  const parsed = GetGroupMilestoneIssuesSchema.parse({
@@ -1,11 +1,18 @@
1
1
  import { after, before, describe, test } from "node:test";
2
2
  import assert from "node:assert";
3
3
  import { Buffer } from "node:buffer";
4
+ import fs from "node:fs";
5
+ import path, { dirname } from "node:path";
6
+ import { fileURLToPath } from "node:url";
4
7
  import { cleanupServers, findAvailablePort, HOST, launchServer, TransportMode, } from "./utils/server-launcher.js";
5
8
  import { findMockServerPort, MockGitLabServer } from "./utils/mock-gitlab-server.js";
6
9
  import { CustomHeaderClient } from "./clients/custom-header-client.js";
7
10
  const MOCK_TOKEN = "mock-concurrent-token-12345";
8
11
  const TEST_PROJECT_ID = "123";
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+ const packageJsonPath = path.resolve(__dirname, "../package.json");
15
+ const PACKAGE_VERSION = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")).version;
9
16
  function fileResponse(filePath, content) {
10
17
  return {
11
18
  file_name: filePath.split("/").at(-1),
@@ -129,6 +136,14 @@ describe("Streamable HTTP health check capacity logging", { timeout: 20_000 }, (
129
136
  if (mockGitLab)
130
137
  await mockGitLab.stop();
131
138
  });
139
+ test("reports server version when healthy", async () => {
140
+ const response = await fetch(`${baseUrl}/health`);
141
+ const body = (await response.json());
142
+ assert.strictEqual(response.status, 200);
143
+ assert.strictEqual(body.status, "healthy");
144
+ assert.strictEqual(body.version, PACKAGE_VERSION);
145
+ assert.strictEqual(body.activeSessions, 0);
146
+ });
132
147
  test("logs active session capacity when health check is degraded", async () => {
133
148
  const client = new CustomHeaderClient({ Authorization: `Bearer ${MOCK_TOKEN}` });
134
149
  await client.connect(mcpUrl);
@@ -137,6 +152,7 @@ describe("Streamable HTTP health check capacity logging", { timeout: 20_000 }, (
137
152
  const body = (await response.json());
138
153
  assert.strictEqual(response.status, 503);
139
154
  assert.strictEqual(body.status, "degraded");
155
+ assert.strictEqual(body.version, PACKAGE_VERSION);
140
156
  assert.strictEqual(body.activeSessions, 1);
141
157
  assert.strictEqual(body.maxSessions, 1);
142
158
  // ponytail: poll stdout; fixed 100ms sleep flakes under CI jitter
@@ -1,8 +1,15 @@
1
1
  import { describe, test } from "node:test";
2
2
  import assert from "node:assert";
3
3
  import { spawn } from "child_process";
4
+ import fs from "node:fs";
5
+ import path, { dirname } from "node:path";
6
+ import { fileURLToPath } from "node:url";
4
7
  import { MockGitLabServer, findMockServerPort } from "./utils/mock-gitlab-server.js";
5
8
  const MOCK_TOKEN = "glpat-mock-token-12345";
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ const packageJsonPath = path.resolve(__dirname, "../package.json");
12
+ const PACKAGE_VERSION = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")).version;
6
13
  function createMockGitLabServer(port) {
7
14
  return new MockGitLabServer({
8
15
  port,
@@ -73,6 +80,7 @@ describe("When health_check runs", () => {
73
80
  const result = await callHealthCheckAsync(baseEnv(mockGitLab.getUrl()));
74
81
  assert.equal(result.status, "ok");
75
82
  assert.equal(result.authenticated, true);
83
+ assert.equal(result.mcp_server_version, PACKAGE_VERSION);
76
84
  assert.equal(result.version, "18.3.1-ee");
77
85
  assert.equal(result.revision, "abc1234");
78
86
  assert.equal(result.enterprise, true);
@@ -94,6 +102,7 @@ describe("When health_check runs", () => {
94
102
  const result = await callHealthCheckAsync(baseEnv(mockGitLab.getUrl()));
95
103
  assert.equal(result.status, "ok");
96
104
  assert.equal(result.authenticated, true);
105
+ assert.equal(result.mcp_server_version, PACKAGE_VERSION);
97
106
  assert.equal("version" in result, false);
98
107
  assert.equal("revision" in result, false);
99
108
  assert.equal("enterprise" in result, false);
@@ -101,7 +101,7 @@ describe("update_project", () => {
101
101
  await assert.rejects(() => callUpdateProject({ project_id: TEST_PROJECT_ID, issues_access_level: "public" }, {
102
102
  GITLAB_API_URL: "https://gitlab.example.com/api/v4",
103
103
  GITLAB_PERSONAL_ACCESS_TOKEN: MOCK_TOKEN,
104
- }), /Invalid enum value/);
104
+ }), /(Invalid enum value|Invalid option)/);
105
105
  });
106
106
  test("rejects empty updates before calling GitLab", async () => {
107
107
  await assert.rejects(() => callUpdateProject({ project_id: TEST_PROJECT_ID }, {
@@ -1,6 +1,11 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
3
  import { allTools, TOOLSET_DEFINITIONS } from "../tools/registry.js";
4
+ function getExposedTool(name) {
5
+ const tool = allTools.find(candidate => candidate.name === name);
6
+ assert.ok(tool);
7
+ return tool;
8
+ }
4
9
  function getDefaultToolNames() {
5
10
  return new Set(TOOLSET_DEFINITIONS.filter(definition => definition.isDefault).flatMap(definition => [
6
11
  ...definition.tools,
@@ -17,9 +22,29 @@ describe("When MCP tool descriptions are exposed", () => {
17
22
  });
18
23
  describe("with create_branch", () => {
19
24
  it("should explain its source revision and neighboring branch operations", () => {
20
- const tool = allTools.find(candidate => candidate.name === "create_branch");
21
- assert.ok(tool);
25
+ const tool = getExposedTool("create_branch");
22
26
  assert.match(tool.description, /source branch|tag|commit[\s\S]*protect_branch[\s\S]*already-exists/i);
23
27
  });
24
28
  });
29
+ describe("with list_issues", () => {
30
+ it("should include issue-management guidance and omit group_id", () => {
31
+ const tool = getExposedTool("list_issues");
32
+ assert.match(tool.description, /issue management/i);
33
+ assert.doesNotMatch(tool.description, /group_id/);
34
+ });
35
+ });
36
+ describe("with my_issues", () => {
37
+ it("should include issue-management guidance and omit group_id", () => {
38
+ const tool = getExposedTool("my_issues");
39
+ assert.match(tool.description, /issue management/i);
40
+ assert.doesNotMatch(tool.description, /group_id/);
41
+ });
42
+ });
43
+ describe("with get_issue", () => {
44
+ it("should include issue-management guidance and omit group_id", () => {
45
+ const tool = getExposedTool("get_issue");
46
+ assert.match(tool.description, /issue management/i);
47
+ assert.doesNotMatch(tool.description, /group_id/);
48
+ });
49
+ });
25
50
  });
@@ -51,7 +51,7 @@ describe("When omitIncompleteMergeRequestPosition runs", () => {
51
51
  start_sha: "ghi",
52
52
  position_type: "text",
53
53
  }),
54
- }), /Expected string, received null/);
54
+ }), /expected string, received null/i);
55
55
  });
56
56
  });
57
57
  });
@@ -1,4 +1,3 @@
1
- import { zodToJsonSchema } from "zod-to-json-schema";
2
1
  import { toJSONSchema } from "../utils/schema.js";
3
2
  import { USE_GITLAB_WIKI, USE_MILESTONE, USE_PIPELINE, SSE, STREAMABLE_HTTP, } from "../config.js";
4
3
  import { getToolDescription } from "./tool-descriptions.js";
@@ -39,7 +38,7 @@ export const allTools = [
39
38
  {
40
39
  name: "execute_graphql",
41
40
  description: "Execute a GitLab GraphQL query",
42
- inputSchema: zodToJsonSchema(ExecuteGraphQLSchema),
41
+ inputSchema: toJSONSchema(ExecuteGraphQLSchema),
43
42
  },
44
43
  {
45
44
  name: "create_or_update_file",
@@ -917,7 +916,7 @@ export const allTools = [
917
916
  },
918
917
  {
919
918
  name: "health_check",
920
- description: "Verify server status and authentication. When authenticated, also reports the GitLab instance version from GET /api/v4/version (version, revision, enterprise). Version lookup failures do not fail the health check — those fields are omitted.",
919
+ description: "Verify server status and authentication. Always reports the MCP server version (mcp_server_version). When authenticated, also reports the GitLab instance version from GET /api/v4/version (version, revision, enterprise). Version lookup failures do not fail the health check — those fields are omitted.",
921
920
  inputSchema: toJSONSchema(HealthCheckSchema),
922
921
  },
923
922
  {
@@ -8,6 +8,9 @@ const TOOL_GUIDANCE = {
8
8
  create_or_update_file: "Use this for a single repository file when you know whether the target path is new or already exists; use `push_files` for a multi-file commit. Optional `encoding` (`text` or `base64`) defaults to `GITLAB_REPO_FILE_ENCODING` so existing callers stay unchanged. The operation creates or updates remote content in a commit, requires repository write permission, and returns the commit result or a conflict/validation error.",
9
9
  push_files: "Use this to commit several file changes atomically; use `create_or_update_file` when only one path is involved. Each file defaults to action `create`; optional per-file `action` (create/update/delete/move) and `encoding` (text/base64) are additive. `GITLAB_PERMISSION_MODE=modify` rejects `delete` and `move`. The operation writes repository history on the selected branch, requires repository write permission, and returns the commit result or a validation, conflict, or protected-branch error.",
10
10
  create_issue: "Use this to open a new issue; use `update_issue` for an existing issue and `create_issue_note` to add discussion without changing issue fields. The operation creates remote project data, requires issue creation permission, and returns the new issue or a validation, permission, or duplicate-related error.",
11
+ list_issues: "Use this for issue management: list GitLab issues, optionally scoped with `project_id`. Use `get_issue` when the issue iid is already known and `my_issues` for issues assigned to the current user. It is read-only and paginated, requires issue read permission, and returns issue records or GitLab errors for invalid identifiers, missing resources, or rate limits.",
12
+ my_issues: "Use this for issue management: list issues assigned to the authenticated user. Use `list_issues` for project-wide or author-scoped listing and `get_issue` for one issue. It is read-only and paginated, requires authentication, and returns assigned issue records or permission/rate-limit errors.",
13
+ get_issue: "Use this for issue management: inspect one issue's fields; use `list_issues` or `my_issues` to discover issues first. It is read-only, requires issue read permission, and returns the issue or an error when the identifier is invalid, the issue is missing, or access is denied.",
11
14
  create_merge_request: "Use this to open a new merge request from an existing source branch to a target branch; use `update_merge_request` after it exists. The operation creates remote review state, requires project access, and returns the new merge request or a validation, permission, branch, or duplicate-related error.",
12
15
  fork_repository: "Use this to create a copy of an existing project in the current user's namespace or a permitted namespace; use `search_repositories` or `get_project` to inspect projects without copying them. The operation creates a new project, requires fork permission, and returns the forked project or a namespace/permission error.",
13
16
  create_branch: "Use this to create a branch from a branch, tag, or commit; use `get_branch` or `list_branches` to inspect branches and `protect_branch` to configure protection afterward. The operation changes remote repository state, requires branch-creation permission, and returns the new branch or a validation, missing-ref, protected-project, or already-exists error. `project_id` accepts a numeric ID or URL-encoded path, `branch` is the new name, and `ref` selects its starting revision.",
@@ -1,30 +1,50 @@
1
1
  import { z } from "zod";
2
- import { zodToJsonSchema } from "zod-to-json-schema";
3
2
  /**
4
3
  * Convert a Zod schema to JSON Schema, fixing nullable/optional fields
5
4
  * so they are not marked as required, and extracting required fields from the Zod schema.
6
5
  */
7
6
  export const toJSONSchema = (schema) => {
8
- const jsonSchema = zodToJsonSchema(schema, { $refStrategy: "none" });
7
+ // Zod 4 native conversion. (zod-to-json-schema only understands Zod 3
8
+ // internals and silently emits an empty schema for Zod 4 schemas.)
9
+ // draft-7 keeps output closest to the previous converter; io:"input"
10
+ // describes tool arguments (preprocess/coerce evaluated input-side).
11
+ const jsonSchema = z.toJSONSchema(schema, { target: "draft-7", io: "input" });
12
+ // Public classes only (no _def access): works on both Zod 3 and Zod 4,
13
+ // whose internals differ (_def.typeName vs _zod.def.type).
9
14
  const isOptionalLikeField = (zodType) => {
10
- const def = zodType._def;
11
- const typeName = def?.typeName;
12
- if (["ZodOptional", "ZodNullable", "ZodDefault", "ZodCatch"].includes(typeName)) {
15
+ if (zodType instanceof z.ZodOptional ||
16
+ zodType instanceof z.ZodNullable ||
17
+ zodType instanceof z.ZodDefault ||
18
+ zodType instanceof z.ZodCatch) {
13
19
  return true;
14
20
  }
15
- if (typeName === "ZodEffects") {
16
- return isOptionalLikeField(def.schema);
21
+ // ZodEffects (v3 preprocess/refine/transform): unwrap the inner schema.
22
+ const ZodEffects = z.ZodEffects;
23
+ if (ZodEffects &&
24
+ zodType instanceof ZodEffects &&
25
+ typeof zodType.innerType === "function") {
26
+ return isOptionalLikeField(zodType.innerType());
17
27
  }
18
- if (typeName === "ZodBranded") {
19
- return isOptionalLikeField(def.type);
28
+ // ZodBranded: unwrap to the branded schema.
29
+ const ZodBranded = z.ZodBranded;
30
+ if (ZodBranded &&
31
+ zodType instanceof ZodBranded &&
32
+ typeof zodType.unwrap === "function") {
33
+ return isOptionalLikeField(zodType.unwrap());
20
34
  }
21
- if (typeName === "ZodPipeline") {
22
- return isOptionalLikeField(def.in);
35
+ // Pipes (v3 ZodPipeline / v4 ZodPipe/ZodPreprocess from preprocess/transform):
36
+ // optional-like when either side is optional-like.
37
+ const maybeIn = zodType.in;
38
+ const maybeOut = zodType.out;
39
+ if (maybeIn instanceof z.ZodType && maybeOut instanceof z.ZodType) {
40
+ return isOptionalLikeField(maybeIn) || isOptionalLikeField(maybeOut);
23
41
  }
24
42
  return false;
25
43
  };
26
- // Extract required fields from Zod schema
27
- const zodRequiredFields = (() => {
44
+ // Extract required fields from Zod schema (authoritative for the root object:
45
+ // the native converter marks defaulted fields as required, we keep the
46
+ // previous convention that defaulted/optional-like fields are not required).
47
+ const { zodRequiredFields, hasAuthoritativeRequired } = (() => {
28
48
  if (schema instanceof z.ZodObject) {
29
49
  const shape = schema.shape;
30
50
  const requiredFields = [];
@@ -34,9 +54,9 @@ export const toJSONSchema = (schema) => {
34
54
  requiredFields.push(key);
35
55
  }
36
56
  });
37
- return requiredFields;
57
+ return { zodRequiredFields: requiredFields, hasAuthoritativeRequired: true };
38
58
  }
39
- return [];
59
+ return { zodRequiredFields: [], hasAuthoritativeRequired: false };
40
60
  })();
41
61
  // Post-process to fix nullable/optional fields and strip verbose keys
42
62
  function fixNullableOptional(obj, isRoot = false) {
@@ -47,14 +67,13 @@ export const toJSONSchema = (schema) => {
47
67
  delete obj.additionalProperties;
48
68
  // If this object has properties, process them
49
69
  if (obj.properties) {
50
- const requiredSet = new Set(obj.required || []);
51
- // Add required fields extracted from Zod schema (only for root object)
52
- if (isRoot) {
53
- zodRequiredFields.forEach(field => {
54
- if (obj.properties[field]) {
55
- requiredSet.add(field);
56
- }
57
- });
70
+ let requiredSet;
71
+ if (isRoot && hasAuthoritativeRequired) {
72
+ // Zod shape is the source of truth at root level.
73
+ requiredSet = new Set(zodRequiredFields.filter(field => obj.properties[field]));
74
+ }
75
+ else {
76
+ requiredSet = new Set(obj.required || []);
58
77
  }
59
78
  Object.keys(obj.properties).forEach(key => {
60
79
  const prop = obj.properties[key];
@@ -66,6 +85,10 @@ export const toJSONSchema = (schema) => {
66
85
  else if (Array.isArray(prop.type) && prop.type.includes("null")) {
67
86
  requiredSet.delete(key);
68
87
  }
88
+ // Fields with defaults are not required (previous converter semantics).
89
+ if (prop && typeof prop === "object" && "default" in prop) {
90
+ requiredSet.delete(key);
91
+ }
69
92
  // Recursively process nested objects (not root)
70
93
  obj.properties[key] = fixNullableOptional(prop, false);
71
94
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "2.1.60",
3
+ "version": "2.1.62",
4
4
  "mcpName": "io.github.zereight/gitlab-mcp",
5
5
  "description": "GitLab MCP server for projects, merge requests, issues, pipelines, wiki, releases, and more",
6
6
  "keywords": [
@@ -85,8 +85,7 @@
85
85
  "tldts": "^6.1.86",
86
86
  "tough-cookie": "^5.1.2",
87
87
  "undici": "^6.28.0",
88
- "zod": "^3.24.2",
89
- "zod-to-json-schema": "3.24.5"
88
+ "zod": "^4.6.5"
90
89
  },
91
90
  "devDependencies": {
92
91
  "@types/express": "^5.0.2",