@zereight/mcp-gitlab 2.2.0 → 2.2.1

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
- - **267개 도구 + `discover_tools`** — 작은 toolset으로 시작하고, 런타임에 카테고리 활성화
20
+ - **268개 도구: toolset 266개 + `execute_graphql` + `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 프록시, 요청별 원격 인증
@@ -31,7 +31,7 @@ PAT, OAuth, 읽기 전용 모드, 동적 API URL, 원격 인증을 지원하며
31
31
  | | @zereight/mcp-gitlab | GitLab MCP A (커뮤니티 CQRS형) |
32
32
  |---|----------------------|--------------------------------|
33
33
  | **적합한 경우** | AI 에이전트 워크플로우 | 엔터프라이즈 멀티 인스턴스 / 그룹형 도구 |
34
- | **도구 모델** | ~267개 세분화 도구 + `discover_tools` | ~50–60개 `browse_*` / `manage_*` 그룹 도구 |
34
+ | **도구 모델** | 268개 도구: toolset 266개 + `execute_graphql` + `discover_tools` | ~50–60개 `browse_*` / `manage_*` 그룹 도구 |
35
35
  | **MR 리뷰** | 2단계 배치 diff | 서버마다 다름 |
36
36
  | **Node.js** | >=18.17 | 보통 >=24 |
37
37
  | **라이선스** | MIT | 서버마다 다름 |
@@ -111,7 +111,7 @@ command = lib.getExe inputs.gitlab-mcp.packages.${system}.default;
111
111
 
112
112
  예시는 기존 `mcp-gitlab`보다 충돌 가능성이 낮은 `zereight-mcp-gitlab` 별칭을 사용합니다. MCP 클라이언트가 찾지 못하면 `which zereight-mcp-gitlab`의 절대 경로를 사용하세요.
113
113
 
114
- 전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.1.70`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
114
+ 전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.2.0`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
115
115
 
116
116
  #### CLI 인자 사용하기(환경 변수 문제가 있는 클라이언트용)
117
117
 
@@ -147,6 +147,8 @@ command = lib.getExe inputs.gitlab-mcp.packages.${system}.default;
147
147
  - `--masking-workspace-dir` - 마스킹 파일 경로를 해석하는 기준 디렉터리 (`GITLAB_MASKING_WORKSPACE_DIR` 대체)
148
148
  - `--compact-results=true` - 큰 MCP 도구 응답을 미리보기와 CLI 명령으로 바꿈 (`GITLAB_MCP_COMPACT_RESULTS` 대체, 기본 꺼짐)
149
149
  - `--compact-result-chars` - compact 임계값 문자 수 (`GITLAB_MCP_COMPACT_RESULT_CHARS` 대체, 기본 `4000`)
150
+ - `--compact-tools` - 전역 compact를 켜지 않고, 응답이 클 때 compact할 도구 이름을 쉼표로 지정 (`GITLAB_MCP_COMPACT_TOOLS` 대체)
151
+ - `--tool-profile` - `full`(기본값) 또는 `slim`. `slim`은 draft note, 이모지 반응, 라벨, CI 카탈로그 도구와 `create_group`을 초기 목록에서 뺌. `GITLAB_TOOLSETS`에 공백이 아닌 값이 설정된 경우에만 무시됨 (`GITLAB_TOOL_PROFILE` 대체)
150
152
 
151
153
  CLI 인자는 환경 변수보다 우선합니다.
152
154
 
@@ -166,7 +168,7 @@ CLI 인자는 환경 변수보다 우선합니다.
166
168
  > 유지됩니다. [Tools Reference](./docs/tools/index.md#feature-toggles)와
167
169
  > [Environment Variables](./docs/configuration/environment-variables.md)를 참고하세요.
168
170
 
169
- #### SSE
171
+ - sse
170
172
 
171
173
  ```shell
172
174
  docker run -i --rm \
@@ -195,7 +197,7 @@ docker run -i --rm \
195
197
  }
196
198
  ```
197
199
 
198
- #### Streamable HTTP
200
+ - streamable-http
199
201
 
200
202
  ```shell
201
203
  docker run -i --rm \
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
- - **267 tools + `discover_tools`** — start with a small toolset; activate more at runtime without CQRS-style grouping
25
+ - **268 tools: 266 in toolsets + `execute_graphql` + `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
@@ -36,7 +36,7 @@ Supports PAT, OAuth, read-only mode, dynamic API URLs, and remote authorization
36
36
  | | @zereight/mcp-gitlab | GitLab MCP A (community CQRS-style) |
37
37
  |---|----------------------|-------------------------------------|
38
38
  | **Best for** | AI agent workflows | Enterprise multi-instance / grouped tools |
39
- | **Tool model** | ~267 granular tools + `discover_tools` | ~50–60 grouped `browse_*` / `manage_*` tools |
39
+ | **Tool model** | 268 tools: 266 in toolsets + `execute_graphql` + `discover_tools` | ~50–60 grouped `browse_*` / `manage_*` tools |
40
40
  | **MR review** | 2-step batched diff | Varies |
41
41
  | **Node.js** | >=18.17 | Often >=24 |
42
42
  | **License** | MIT | Varies |
@@ -116,7 +116,7 @@ The store path is pinned by your lock file; update it with `nix flake update git
116
116
 
117
117
  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`.
118
118
 
119
- No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.1.70`. 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
+ No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.2.0`. 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`).
120
120
 
121
121
  #### Using CLI Arguments (for clients with env var issues)
122
122
 
@@ -634,15 +634,15 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
634
634
  23. `protect_branch` - Protect a repository branch (set push/merge/unprotect access levels)
635
635
  24. `unprotect_branch` - Remove protection from a previously protected branch
636
636
  25. `update_default_branch` - Change the default branch of a project
637
- 26. `get_merge_request` - Get details of a merge request (mergeRequestIid or branchName required). Set include_summaries=true for deployment/commit/approval summaries
638
- 27. `get_merge_request_diffs` - Get the changes/diffs of a merge request (mergeRequestIid or branchName required)
639
- 28. `list_merge_request_changed_files` - List changed file paths in a merge request without diff content (mergeRequestIid or branchName required)
640
- 29. `list_merge_request_diffs` - List merge request diffs with pagination (mergeRequestIid or branchName required)
641
- 30. `get_merge_request_file_diff` - Get diffs for specific files from a merge request (mergeRequestIid or branchName required)
637
+ 26. `get_merge_request` - Get details of a merge request (merge_request_iid or source_branch required). Set include_summaries=true for deployment/commit/approval summaries
638
+ 27. `get_merge_request_diffs` - Get the changes/diffs of a merge request (merge_request_iid or source_branch required)
639
+ 28. `list_merge_request_changed_files` - List changed file paths in a merge request without diff content (merge_request_iid or source_branch required)
640
+ 29. `list_merge_request_diffs` - List merge request diffs with pagination (merge_request_iid or source_branch required)
641
+ 30. `get_merge_request_file_diff` - Get diffs for specific files from a merge request (merge_request_iid or source_branch required)
642
642
  31. `list_merge_request_versions` - List all versions of a merge request
643
643
  32. `get_merge_request_version` - Get a specific version of a merge request
644
644
  33. `get_branch_diffs` - Get diffs between two branches or commits
645
- 34. `update_merge_request` - Update a merge request (mergeRequestIid or branchName required)
645
+ 34. `update_merge_request` - Update a merge request (merge_request_iid or source_branch required)
646
646
  35. `create_note` - Create a new note (comment) to an issue or merge request
647
647
  36. `create_merge_request_thread` - Create a new thread on a merge request
648
648
  37. `resolve_merge_request_thread` - Resolve a thread on a merge request
@@ -876,7 +876,7 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
876
876
  265. `create_snippet` - Create a snippet — project-scoped when project_id is given, otherwise a personal snippet. Requires title plus either file_name + content (single file) or files[] (multi-file); the two shapes cannot be mixed.
877
877
  266. `update_snippet` - Update an existing snippet (provide at least one field to change). For multi-file edits — renames, deletions, additions — pass files[] with action (create/update/delete/move) and previous_path. The file_name + content shortcut still works for single-file content replacement.
878
878
  267. `delete_snippet` - Delete a snippet
879
- 268. `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, snippets, users, workitems, webhooks, search, variables, dependency_proxy, vulnerabilities, orbit. Already-active categories are listed in the response.
879
+ 268. `discover_tools` - Discover and activate additional tool categories for this session. Available categories: core, merge_requests, issues, repositories, branches, projects, labels, ci, groups, pipelines, milestones, wiki, releases, tags, snippets, users, workitems, webhooks, search, variables, dependency_proxy, vulnerabilities, orbit. Already-active categories are listed in the response.
880
880
 
881
881
  <!-- TOOLS-END -->
882
882
 
package/README.pt-BR.md CHANGED
@@ -17,7 +17,7 @@ Suporta PAT, OAuth, modo somente leitura, URLs de API dinâmicas e autorização
17
17
 
18
18
  ### Por que usar este GitLab MCP?
19
19
 
20
- - **267 ferramentas + `discover_tools`** — comece com um toolset pequeno e ative mais categorias em tempo de execução
20
+ - **268 ferramentas: 266 em toolsets + `execute_graphql` + `discover_tools`** — comece com um toolset pequeno e ative mais categorias em tempo de execução
21
21
  - **Revisão de MR em 2 etapas** — `list_merge_request_changed_files` → `get_merge_request_file_diff` em lote
22
22
  - **Agent Skill integrado** — orientação de workflows em `skills/gitlab-mcp/`
23
23
  - **Autenticação flexível** — Personal Access Token, fluxo OAuth2 local no navegador, proxy MCP OAuth e autorização remota por requisição
@@ -31,7 +31,7 @@ Suporta PAT, OAuth, modo somente leitura, URLs de API dinâmicas e autorização
31
31
  | | @zereight/mcp-gitlab | GitLab MCP A (comunidade, estilo CQRS) |
32
32
  | ------------------------- | ---------------------------------------------- | --------------------------------------------------------- |
33
33
  | **Mais indicado para** | Workflows de agentes de IA | Múltiplas instâncias corporativas / ferramentas agrupadas |
34
- | **Modelo de ferramentas** | ~267 ferramentas granulares + `discover_tools` | ~50–60 ferramentas agrupadas `browse_*` / `manage_*` |
34
+ | **Modelo de ferramentas** | 268 ferramentas: 266 em toolsets + `execute_graphql` + `discover_tools` | ~50–60 ferramentas agrupadas `browse_*` / `manage_*` |
35
35
  | **Revisão de MR** | Diff em lote em 2 etapas | Varia |
36
36
  | **Node.js** | >=18.17 | Frequentemente >=24 |
37
37
  | **Licença** | MIT | Varia |
@@ -111,7 +111,7 @@ O caminho no store é fixado pelo seu lock file; atualize-o com `nix flake updat
111
111
 
112
112
  Os exemplos usam `zereight-mcp-gitlab`, um alias com menor chance de conflito que o binário legado `mcp-gitlab`. Se o seu cliente MCP não o encontrar, use o caminho absoluto retornado por `which zereight-mcp-gitlab`.
113
113
 
114
- Não quer instalar globalmente? Fixe o `npx` na versão estável anterior (a versão recomendada por esta documentação), por exemplo `npx -y @zereight/mcp-gitlab@2.1.70`. Se quiser sempre a versão mais recente, use `npx -y @zereight/mcp-gitlab@latest`. O servidor exibe um aviso no stderr durante a inicialização quando existe uma versão mais nova disponível (desative com `GITLAB_DISABLE_VERSION_CHECK=true`).
114
+ Não quer instalar globalmente? Fixe o `npx` na versão estável anterior (a versão recomendada por esta documentação), por exemplo `npx -y @zereight/mcp-gitlab@2.2.0`. Se quiser sempre a versão mais recente, use `npx -y @zereight/mcp-gitlab@latest`. O servidor exibe um aviso no stderr durante a inicialização quando existe uma versão mais nova disponível (desative com `GITLAB_DISABLE_VERSION_CHECK=true`).
115
115
 
116
116
  #### Usando argumentos de CLI (para clientes com problemas com variáveis de ambiente)
117
117
 
@@ -135,6 +135,8 @@ Alguns clientes MCP, como o GitHub Copilot CLI, têm problemas com variáveis de
135
135
  - `--api-url` - URL da API do GitLab (substitui `GITLAB_API_URL`)
136
136
  - `--read-only=true` - ativa o modo somente leitura (substitui `GITLAB_READ_ONLY_MODE`, obsoleto — prefira `--permission-mode=readonly`)
137
137
  - `--permission-mode` - nível de permissão: `readonly`, `modify` (sem ferramentas de exclusão ou teardown) ou `full` (substitui `GITLAB_PERMISSION_MODE`, padrão `full`)
138
+ - `--toolsets=all` - ativa toolsets nomeados (substitui `GITLAB_TOOLSETS`; sem valor usa o padrão enxuto `core`)
139
+ - `--tools=list_issues` - adiciona ferramentas individuais (substitui `GITLAB_TOOLS`)
138
140
  - `--use-wiki=true` - ativa a API de Wiki (substitui `USE_GITLAB_WIKI`, legado — prefira `GITLAB_TOOLSETS=wiki`)
139
141
  - `--use-milestone=true` - ativa a API de milestones (substitui `USE_MILESTONE`, legado — prefira `GITLAB_TOOLSETS=milestones`)
140
142
  - `--use-pipeline=true` - ativa a API de pipelines (substitui `USE_PIPELINE`, legado — prefira `GITLAB_TOOLSETS=pipelines`)
@@ -145,6 +147,8 @@ Alguns clientes MCP, como o GitHub Copilot CLI, têm problemas com variáveis de
145
147
  - `--masking-workspace-dir` - diretório usado para resolver arquivos de mascaramento (substitui `GITLAB_MASKING_WORKSPACE_DIR`)
146
148
  - `--compact-results=true` - trunca respostas muito grandes de ferramentas MCP e anexa um comando CLI para a carga completa (substitui `GITLAB_MCP_COMPACT_RESULTS`; desativado por padrão)
147
149
  - `--compact-result-chars` - limite de caracteres para compactação (substitui `GITLAB_MCP_COMPACT_RESULT_CHARS`; padrão `4000`)
150
+ - `--compact-tools` - nomes de ferramentas separados por vírgula para compactar quando a resposta for grande, sem ativar o compact global (substitui `GITLAB_MCP_COMPACT_TOOLS`)
151
+ - `--tool-profile` - `full` (padrão) ou `slim`. `slim` remove draft notes, reações com emoji, labels, ferramentas do catálogo de CI e `create_group` da lista inicial. Ignorado apenas quando `GITLAB_TOOLSETS` tem um valor não em branco (substitui `GITLAB_TOOL_PROFILE`)
148
152
 
149
153
  Os argumentos de CLI têm precedência sobre as variáveis de ambiente.
150
154
 
@@ -164,7 +168,7 @@ O mesmo binário também é uma CLI do GitLab no estilo `gh` (`tool <name>` ou f
164
168
  > Consulte a [Referência de ferramentas](./docs/tools/index.md#feature-toggles) e
165
169
  > [Variáveis de ambiente](./docs/configuration/environment-variables.md).
166
170
 
167
- #### SSE
171
+ - sse
168
172
 
169
173
  ```shell
170
174
  docker run -i --rm \
@@ -193,7 +197,7 @@ docker run -i --rm \
193
197
  }
194
198
  ```
195
199
 
196
- #### Streamable HTTP
200
+ - streamable-http
197
201
 
198
202
  ```shell
199
203
  docker run -i --rm \
package/README.zh-CN.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  ### 为什么使用这个 GitLab MCP?
19
19
 
20
- - **267 个工具 + `discover_tools`** — 从小型 toolset 开始,运行时按需激活类别
20
+ - **268 个工具:toolset 中 266 个 + `execute_graphql` + `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 代理、按请求远程授权
@@ -31,7 +31,7 @@
31
31
  | | @zereight/mcp-gitlab | GitLab MCP A(社区 CQRS 型) |
32
32
  |---|----------------------|------------------------------|
33
33
  | **更适合** | AI 代理工作流 | 企业多实例 / 分组工具 |
34
- | **工具模型** | ~267 个细粒度工具 + `discover_tools` | ~50–60 个 `browse_*` / `manage_*` 分组工具 |
34
+ | **工具模型** | 268 个工具:toolset 中 266 个 + `execute_graphql` + `discover_tools` | ~50–60 个 `browse_*` / `manage_*` 分组工具 |
35
35
  | **MR 审查** | 两步批量 diff | 因服务器而异 |
36
36
  | **Node.js** | >=18.17 | 通常 >=24 |
37
37
  | **许可证** | MIT | 因服务器而异 |
@@ -111,7 +111,7 @@ command = lib.getExe inputs.gitlab-mcp.packages.${system}.default;
111
111
 
112
112
  示例使用 `zereight-mcp-gitlab`,这是比旧的 `mcp-gitlab` 更不容易冲突的别名。如果 MCP 客户端找不到它,请使用 `which zereight-mcp-gitlab` 输出的绝对路径。
113
113
 
114
- 如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.1.70`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
114
+ 如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.2.0`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
115
115
 
116
116
  #### 使用 CLI 参数(适用于环境变量有问题的客户端)
117
117
 
@@ -147,6 +147,8 @@ command = lib.getExe inputs.gitlab-mcp.packages.${system}.default;
147
147
  - `--masking-workspace-dir` - 用于解析掩码文件的目录(替代 `GITLAB_MASKING_WORKSPACE_DIR`)
148
148
  - `--compact-results=true` - 将过大的 MCP 工具回复截断为预览并附带 CLI 命令(替代 `GITLAB_MCP_COMPACT_RESULTS`,默认关闭)
149
149
  - `--compact-result-chars` - compact 字符阈值(替代 `GITLAB_MCP_COMPACT_RESULT_CHARS`,默认 `4000`)
150
+ - `--compact-tools` - 以逗号分隔的工具名,仅在回复过大时对这些工具进行 compact,而不开启全局 compact(替代 `GITLAB_MCP_COMPACT_TOOLS`)
151
+ - `--tool-profile` - `full`(默认)或 `slim`。`slim` 会从初始列表中移除 draft note、表情回应、标签、CI 目录工具以及 `create_group`。仅当 `GITLAB_TOOLSETS` 设置为非空白值时忽略(替代 `GITLAB_TOOL_PROFILE`)
150
152
 
151
153
  CLI 参数优先于环境变量。
152
154
 
@@ -165,7 +167,7 @@ CLI 参数优先于环境变量。
165
167
  > 参见 [Tools Reference](./docs/tools/index.md#feature-toggles) 和
166
168
  > [Environment Variables](./docs/configuration/environment-variables.md)。
167
169
 
168
- #### SSE
170
+ - sse
169
171
 
170
172
  ```shell
171
173
  docker run -i --rm \
@@ -194,7 +196,7 @@ docker run -i --rm \
194
196
  }
195
197
  ```
196
198
 
197
- #### Streamable HTTP
199
+ - streamable-http
198
200
 
199
201
  ```shell
200
202
  docker run -i --rm \
@@ -5,15 +5,31 @@ function shownEnvValue(rawValue) {
5
5
  }
6
6
  return `${singleLine.slice(0, 77)}...`;
7
7
  }
8
+ // USE_PIPELINE exposes these two tools, but they live in the `ci` toolset, not `pipelines`.
9
+ // Suggesting `ci` would also turn on catalog tools the flag does not expose.
10
+ const LEGACY_TOOLS_OUTSIDE_NAMED_TOOLSET = {
11
+ pipelines: "validate_ci_lint,validate_project_ci_lint",
12
+ };
13
+ function legacyToolsetReplacement(toolset) {
14
+ const toolsets = `GITLAB_TOOLSETS=core,${toolset}`;
15
+ const extraTools = LEGACY_TOOLS_OUTSIDE_NAMED_TOOLSET[toolset];
16
+ if (extraTools === undefined) {
17
+ return toolsets;
18
+ }
19
+ return `${toolsets} and GITLAB_TOOLS=${extraTools}`;
20
+ }
8
21
  function legacyToolsetWarning(envName, rawValue, toolset) {
22
+ // An explicit GITLAB_TOOLSETS list replaces the default toolsets; it does not merge.
23
+ // Legacy USE_* flags add tools on top of defaults, so the hint must keep `core`.
24
+ const replacement = legacyToolsetReplacement(toolset);
9
25
  if (rawValue === "true") {
10
26
  return (`${envName} is deprecated and will be removed in the next major version. ` +
11
- `Use GITLAB_TOOLSETS=${toolset} instead.`);
27
+ `Use ${replacement} instead.`);
12
28
  }
13
29
  const shown = shownEnvValue(rawValue);
14
30
  return (`${envName} is set to "${shown}" and is deprecated. ` +
15
31
  `This value does not enable the ${toolset} toolset. Remove it. ` +
16
- `Set GITLAB_TOOLSETS=${toolset} only if you want those tools enabled.`);
32
+ `Set ${replacement} only if you want those tools in addition to core.`);
17
33
  }
18
34
  /**
19
35
  * Builds startup warnings for deprecated env vars / CLI flags.
package/build/index.js CHANGED
@@ -502,9 +502,9 @@ function createServer() {
502
502
  return logCompletion(result);
503
503
  }
504
504
  catch (error) {
505
- const isGitLabApiError = error instanceof GitLabApiError;
505
+ const isRecoverableToolError = error instanceof GitLabApiError || error instanceof ToolInputError;
506
506
  const safeError = logError(error);
507
- if (isGitLabApiError) {
507
+ if (isRecoverableToolError) {
508
508
  return {
509
509
  content: [
510
510
  {
@@ -1320,6 +1320,13 @@ class GitLabApiError extends Error {
1320
1320
  this.name = "GitLabApiError";
1321
1321
  }
1322
1322
  }
1323
+ class ToolInputError extends Error {
1324
+ constructor(message) {
1325
+ super(message);
1326
+ this.name = "ToolInputError";
1327
+ }
1328
+ }
1329
+ const MERGE_REQUEST_IDENTIFIER_REQUIRED = "Either merge_request_iid or source_branch must be provided";
1323
1330
  /**
1324
1331
  * Utility function for handling GitLab API errors
1325
1332
  * API 에러 처리를 위한 유틸리티 함수 (Utility function for handling API errors)
@@ -1346,6 +1353,39 @@ async function handleGitLabError(response) {
1346
1353
  }
1347
1354
  }
1348
1355
  }
1356
+ const USER_API_INSUFFICIENT_SCOPE_MESSAGE = "GitLab API error: 403 Forbidden on GET /api/v4/user (insufficient scope). The token was accepted, but read_user (classic PAT) or User API read (fine-grained User:Read) is required for whoami, my_issues, and other current-user tools.";
1357
+ const USER_API_HEALTH_WARNING = "GET /api/v4/user returned 403 (insufficient scope). The token was accepted and other tools may still work. Grant read_user (classic) or User:Read (fine-grained) for whoami, my_issues, and version metadata.";
1358
+ function isGitLabUserApiRateLimitBody(errorBody) {
1359
+ return errorBody.includes("User API Key Rate limit exceeded");
1360
+ }
1361
+ function isGitLabInsufficientUserScopeBody(errorBody) {
1362
+ // Fine-grained PATs use insufficient_granular_scope, which does not contain insufficient_scope.
1363
+ return (errorBody.includes("insufficient_granular_scope") || errorBody.includes("insufficient_scope"));
1364
+ }
1365
+ async function isInsufficientUserScopeForbiddenAsync(response) {
1366
+ if (response.status !== 403) {
1367
+ return false;
1368
+ }
1369
+ const errorBody = await response.text();
1370
+ return isGitLabInsufficientUserScopeBody(errorBody);
1371
+ }
1372
+ async function throwUserApiScopeErrorUnlessRateLimitedAsync(response) {
1373
+ const errorBody = await response.clone().text();
1374
+ // Rate-limit and other 403s keep the upstream body via handleGitLabError.
1375
+ if (isGitLabUserApiRateLimitBody(errorBody) || !isGitLabInsufficientUserScopeBody(errorBody)) {
1376
+ return;
1377
+ }
1378
+ throw new GitLabApiError(USER_API_INSUFFICIENT_SCOPE_MESSAGE);
1379
+ }
1380
+ async function authenticateWithJobTokenFallbackAsync(response) {
1381
+ if (response.ok ||
1382
+ (response.status !== 401 && response.status !== 403) ||
1383
+ !(GITLAB_JOB_TOKEN || usesJobTokenHeader())) {
1384
+ return false;
1385
+ }
1386
+ const jobResponse = await fetch(`${getEffectiveApiUrl()}/job`, getFetchConfig());
1387
+ return jobResponse.ok;
1388
+ }
1349
1389
  /**
1350
1390
  * @param {string} projectId - The project ID parameter passed to the function
1351
1391
  * @returns {string} The project ID to use for the API call
@@ -3793,7 +3833,7 @@ async function getMergeRequest(projectId, mergeRequestIid, branchName) {
3793
3833
  url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests?source_branch=${encodeURIComponent(branchName)}`);
3794
3834
  }
3795
3835
  else {
3796
- throw new Error("Either mergeRequestIid or branchName must be provided");
3836
+ throw new ToolInputError(MERGE_REQUEST_IDENTIFIER_REQUIRED);
3797
3837
  }
3798
3838
  const response = await fetch(url.toString(), {
3799
3839
  ...getFetchConfig(),
@@ -4027,7 +4067,7 @@ async function buildMergeRequestDeploymentSummary(projectId, mergeRequest) {
4027
4067
  async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view) {
4028
4068
  projectId = decodeURIComponent(projectId); // Decode project ID
4029
4069
  if (!mergeRequestIid && !branchName) {
4030
- throw new Error("Either mergeRequestIid or branchName must be provided");
4070
+ throw new ToolInputError(MERGE_REQUEST_IDENTIFIER_REQUIRED);
4031
4071
  }
4032
4072
  if (branchName && !mergeRequestIid) {
4033
4073
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
@@ -4057,7 +4097,7 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view
4057
4097
  async function listMergeRequestDiffs(projectId, mergeRequestIid, branchName, page, perPage, unidiff) {
4058
4098
  projectId = decodeURIComponent(projectId); // Decode project ID
4059
4099
  if (!mergeRequestIid && !branchName) {
4060
- throw new Error("Either mergeRequestIid or branchName must be provided");
4100
+ throw new ToolInputError(MERGE_REQUEST_IDENTIFIER_REQUIRED);
4061
4101
  }
4062
4102
  if (branchName && !mergeRequestIid) {
4063
4103
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
@@ -4093,7 +4133,7 @@ async function listMergeRequestDiffs(projectId, mergeRequestIid, branchName, pag
4093
4133
  async function listMergeRequestChangedFiles(projectId, mergeRequestIid, branchName, excludedFilePatterns) {
4094
4134
  projectId = decodeURIComponent(projectId);
4095
4135
  if (!mergeRequestIid && !branchName) {
4096
- throw new Error("Either mergeRequestIid or branchName must be provided");
4136
+ throw new ToolInputError(MERGE_REQUEST_IDENTIFIER_REQUIRED);
4097
4137
  }
4098
4138
  if (branchName && !mergeRequestIid) {
4099
4139
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
@@ -4127,7 +4167,7 @@ async function listMergeRequestChangedFiles(projectId, mergeRequestIid, branchNa
4127
4167
  async function getMergeRequestFileDiff(projectId, filePaths, mergeRequestIid, branchName, unidiff) {
4128
4168
  projectId = decodeURIComponent(projectId);
4129
4169
  if (!mergeRequestIid && !branchName) {
4130
- throw new Error("Either mergeRequestIid or branchName must be provided");
4170
+ throw new ToolInputError(MERGE_REQUEST_IDENTIFIER_REQUIRED);
4131
4171
  }
4132
4172
  if (branchName && !mergeRequestIid) {
4133
4173
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
@@ -4212,7 +4252,7 @@ async function getBranchDiffs(projectId, from, to, straight) {
4212
4252
  async function updateMergeRequest(projectId, options, mergeRequestIid, branchName) {
4213
4253
  projectId = decodeURIComponent(projectId); // Decode project ID
4214
4254
  if (!mergeRequestIid && !branchName) {
4215
- throw new Error("Either mergeRequestIid or branchName must be provided");
4255
+ throw new ToolInputError(MERGE_REQUEST_IDENTIFIER_REQUIRED);
4216
4256
  }
4217
4257
  if (branchName && !mergeRequestIid) {
4218
4258
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
@@ -6593,6 +6633,9 @@ async function getCurrentUser() {
6593
6633
  }
6594
6634
  }
6595
6635
  }
6636
+ if (response.status === 403) {
6637
+ await throwUserApiScopeErrorUnlessRateLimitedAsync(response);
6638
+ }
6596
6639
  await handleGitLabError(response);
6597
6640
  throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
6598
6641
  }
@@ -8512,6 +8555,9 @@ async function handleToolCall(params) {
8512
8555
  const response = await fetch(url.toString(), {
8513
8556
  ...getFetchConfig(),
8514
8557
  });
8558
+ if (response.status === 403) {
8559
+ await throwUserApiScopeErrorUnlessRateLimitedAsync(response);
8560
+ }
8515
8561
  await handleGitLabError(response);
8516
8562
  const data = await response.json();
8517
8563
  const user = GitLabCurrentUserSchema.parse(data);
@@ -10605,15 +10651,13 @@ async function handleToolCall(params) {
10605
10651
  HealthCheckSchema.parse(params.arguments ?? {});
10606
10652
  const url = new URL(`${getEffectiveApiUrl()}/user`);
10607
10653
  const response = await fetch(url.toString(), getFetchConfig());
10608
- let authenticated = response.ok;
10609
- if (!authenticated &&
10610
- (response.status === 401 || response.status === 403) &&
10611
- (GITLAB_JOB_TOKEN || usesJobTokenHeader())) {
10612
- const jobUrl = new URL(`${getEffectiveApiUrl()}/job`);
10613
- const jobResponse = await fetch(jobUrl.toString(), getFetchConfig());
10614
- authenticated = jobResponse.ok;
10615
- }
10616
- const versionMetadata = authenticated ? await fetchGitLabVersionMetadata() : null;
10654
+ const jobAuthenticated = await authenticateWithJobTokenFallbackAsync(response);
10655
+ // insufficient_scope and insufficient_granular_scope mean the token was accepted
10656
+ // but User API scope is missing.
10657
+ // Rate-limit and authentication-ban 403s stay unauthenticated.
10658
+ const insufficientUserScope = !jobAuthenticated && (await isInsufficientUserScopeForbiddenAsync(response));
10659
+ const authenticated = response.ok || jobAuthenticated || insufficientUserScope;
10660
+ const versionMetadata = authenticated && !insufficientUserScope ? await fetchGitLabVersionMetadata() : null;
10617
10661
  return {
10618
10662
  content: [
10619
10663
  {
@@ -10623,6 +10667,7 @@ async function handleToolCall(params) {
10623
10667
  authenticated,
10624
10668
  gitlab_url: getEffectiveApiUrl(),
10625
10669
  mcp_server_version: SERVER_VERSION,
10670
+ ...(insufficientUserScope ? { user_api_warning: USER_API_HEALTH_WARNING } : {}),
10626
10671
  ...(versionMetadata ?? {}),
10627
10672
  }),
10628
10673
  },
@@ -132,27 +132,27 @@ export const allTools = [
132
132
  },
133
133
  {
134
134
  name: "get_merge_request",
135
- description: "Get details of a merge request (mergeRequestIid or branchName required). Set include_summaries=true for deployment/commit/approval summaries",
135
+ description: "Get details of a merge request (merge_request_iid or source_branch required). Set include_summaries=true for deployment/commit/approval summaries",
136
136
  inputSchema: toJSONSchema(GetMergeRequestSchema),
137
137
  },
138
138
  {
139
139
  name: "get_merge_request_diffs",
140
- description: "Get the changes/diffs of a merge request (mergeRequestIid or branchName required)",
140
+ description: "Get the changes/diffs of a merge request (merge_request_iid or source_branch required)",
141
141
  inputSchema: toJSONSchema(GetMergeRequestDiffsSchema),
142
142
  },
143
143
  {
144
144
  name: "list_merge_request_changed_files",
145
- description: "List changed file paths in a merge request without diff content (mergeRequestIid or branchName required)",
145
+ description: "List changed file paths in a merge request without diff content (merge_request_iid or source_branch required)",
146
146
  inputSchema: toJSONSchema(ListMergeRequestChangedFilesSchema),
147
147
  },
148
148
  {
149
149
  name: "list_merge_request_diffs",
150
- description: "List merge request diffs with pagination (mergeRequestIid or branchName required)",
150
+ description: "List merge request diffs with pagination (merge_request_iid or source_branch required)",
151
151
  inputSchema: toJSONSchema(ListMergeRequestDiffsSchema),
152
152
  },
153
153
  {
154
154
  name: "get_merge_request_file_diff",
155
- description: "Get diffs for specific files from a merge request (mergeRequestIid or branchName required)",
155
+ description: "Get diffs for specific files from a merge request (merge_request_iid or source_branch required)",
156
156
  inputSchema: toJSONSchema(GetMergeRequestFileDiffSchema),
157
157
  },
158
158
  {
@@ -172,7 +172,7 @@ export const allTools = [
172
172
  },
173
173
  {
174
174
  name: "update_merge_request",
175
- description: "Update a merge request (mergeRequestIid or branchName required)",
175
+ description: "Update a merge request (merge_request_iid or source_branch required)",
176
176
  inputSchema: toJSONSchema(UpdateMergeRequestSchema),
177
177
  },
178
178
  {
@@ -9,7 +9,7 @@ const TOOL_GUIDANCE = {
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
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.",
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. Resolving the current user requires classic read_user or fine-grained User:Read. It is read-only and paginated, requires authentication, and returns assigned issue records or permission/rate-limit errors.",
13
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.",
14
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.",
15
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.",
@@ -36,8 +36,8 @@ const TOOL_GUIDANCE = {
36
36
  download_attachment: "Use this to retrieve a previously uploaded project attachment; remote mode returns inline base64 for images or a download URL, while local mode can save to a path. It is read-only with respect to GitLab, requires project access, and returns the file content or an attachment/permission error.",
37
37
  execute_graphql: "Use this only when a supported GitLab REST tool does not cover the requested operation; prefer a typed tool when one exists. The query is sent directly to GitLab and can include mutations when permission allows, so callers must treat it as potentially state-changing and handle GraphQL errors in the returned response.",
38
38
  discover_tools: "Use this when a needed opt-in category is not currently exposed; omit `category` to inspect available categories, then call it with a category to activate that group for the current session. It changes only the session's tool registry, returns the active-tool summary, and does not change GitLab data.",
39
- health_check: "Use this to verify server connectivity and authentication before making GitLab requests; use `whoami` when the authenticated user's identity is the goal. It does not mutate GitLab state and returns server/authentication status plus GitLab version details when available.",
40
- whoami: "Use this to identify the authenticated GitLab user; use `get_user` or `get_users` when looking up another user. It is read-only and returns the current user profile, while missing credentials or GitLab permission failures are reported as errors.",
39
+ health_check: "Use this to verify server connectivity and authentication before making GitLab requests; use `whoami` when the authenticated user's identity is the goal. A 403 from GET /user with an insufficient_scope error means the token is accepted but User API scope is missing (read_user or fine-grained User:Read); the check stays ok, sets user_api_warning, and omits version metadata. Rate-limit and authentication-ban 403 responses stay unauthenticated. It does not mutate GitLab state and returns server/authentication status plus GitLab version details when available.",
40
+ whoami: "Use this to identify the authenticated GitLab user; use `get_user` or `get_users` when looking up another user. It requires classic read_user or fine-grained User:Read. It is read-only and returns the current user profile, while missing credentials or GitLab permission failures are reported as errors.",
41
41
  list_pipeline_schedules: "Use this to survey a project's scheduled pipelines and their `cron`, `next_run_at`, and `active` state, optionally narrowed with `scope` to `active` or `inactive`; use `list_pipelines` for pipelines that already ran and `get_pipeline_schedule` when the schedule ID is known. The list response carries no `variables` — call `get_pipeline_schedule` for those — and includes `inputs` only for Maintainers, Owners, or the schedule owner. It is read-only and paginated, requires project access, and returns schedule records or an error when the project is missing or access is denied.",
42
42
  get_pipeline_schedule: "Use this to inspect one schedule's cron, owner, last pipeline, variables, and inputs; use `list_pipeline_schedules` to discover schedule IDs and `list_pipeline_schedule_pipelines` for the pipelines it produced. It is read-only, returns `variables` and `inputs` only to Maintainers, Owners, or the schedule owner, and returns the schedule or an error when the schedule is missing or access is denied.",
43
43
  list_pipeline_schedule_pipelines: "Use this to review the pipelines a schedule has actually triggered, for example to see whether the nightly run succeeded; use `get_pipeline` for one pipeline's full detail and `list_pipelines` for project-wide filtering. Narrow the result with `scope`, `status`, `sort`, and the `created_*` / `updated_*` date bounds. It is read-only and paginated, requires project access, and returns pipeline records or an error when the schedule is missing or access is denied.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
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": [