@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 +7 -5
- package/README.md +10 -10
- package/README.pt-BR.md +9 -5
- package/README.zh-CN.md +7 -5
- package/build/deprecated-env.js +18 -2
- package/build/index.js +62 -17
- package/build/tools/registry.js +6 -6
- package/build/tools/tool-descriptions.js +3 -3
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -17,7 +17,7 @@ PAT, OAuth, 읽기 전용 모드, 동적 API URL, 원격 인증을 지원하며
|
|
|
17
17
|
|
|
18
18
|
### 왜 이 GitLab MCP를 사용하나요?
|
|
19
19
|
|
|
20
|
-
- **
|
|
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
|
-
| **도구 모델** |
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
- **
|
|
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** |
|
|
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.
|
|
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 (
|
|
638
|
-
27. `get_merge_request_diffs` - Get the changes/diffs of a merge request (
|
|
639
|
-
28. `list_merge_request_changed_files` - List changed file paths in a merge request without diff content (
|
|
640
|
-
29. `list_merge_request_diffs` - List merge request diffs with pagination (
|
|
641
|
-
30. `get_merge_request_file_diff` - Get diffs for specific files from a merge request (
|
|
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 (
|
|
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
|
-
- **
|
|
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** |
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
- **
|
|
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
|
-
| **工具模型** |
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
199
|
+
- streamable-http
|
|
198
200
|
|
|
199
201
|
```shell
|
|
200
202
|
docker run -i --rm \
|
package/build/deprecated-env.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
505
|
+
const isRecoverableToolError = error instanceof GitLabApiError || error instanceof ToolInputError;
|
|
506
506
|
const safeError = logError(error);
|
|
507
|
-
if (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
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
|
},
|
package/build/tools/registry.js
CHANGED
|
@@ -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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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