@zereight/mcp-gitlab 2.1.50 → 2.1.52

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
@@ -1,5 +1,8 @@
1
1
  # GitLab MCP Server
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@zereight/mcp-gitlab.svg)](https://www.npmjs.com/package/@zereight/mcp-gitlab)
4
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_GitLab_MCP-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](vscode:mcp/install?%7B%22name%22%3A%22zereight.gitlab-mcp%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40zereight%2Fmcp-gitlab%40latest%22%5D%2C%22env%22%3A%7B%22GITLAB_PERSONAL_ACCESS_TOKEN%22%3A%22%24%7Binput%3Agitlab-token%7D%22%2C%22GITLAB_API_URL%22%3A%22https%3A%2F%2Fgitlab.com%2Fapi%2Fv4%22%2C%22GITLAB_PERMISSION_MODE%22%3A%22full%22%7D%7D)
5
+
3
6
  [English](./README.md) | [한국어](./README.ko.md) | [简体中文](./README.zh-CN.md)
4
7
 
5
8
  📖 **[문서 →](https://zereight.github.io/gitlab-mcp/)** 설정 가이드, 환경 변수, 전체 도구 레퍼런스는 호스팅된 문서 사이트에서 확인할 수 있습니다.
@@ -8,19 +11,35 @@
8
11
 
9
12
  ## @zereight/mcp-gitlab
10
13
 
11
- AI 클라이언트를 위한 포괄적인 GitLab MCP 서버입니다. stdio, SSE, Streamable HTTP를 통해 프로젝트, 머지 리퀘스트, 이슈, 파이프라인, 위키, 릴리스, 마일스톤 등을 관리할 수 있습니다.
14
+ **에이전트 워크플로우에 최적화된 GitLab MCP** — stdio, SSE, Streamable HTTP를 통해 프로젝트, 머지 리퀘스트, 이슈, 파이프라인, 위키, 릴리스, 마일스톤 등을 관리할 수 있습니다.
15
+
16
+ 동일한 서버가 [`@zereight/gitlab-mcp`](https://www.npmjs.com/package/@zereight/gitlab-mcp) 이름으로도 배포됩니다 (npm 검색용 별칭).
12
17
 
13
18
  PAT, OAuth, 읽기 전용 모드, 동적 API URL, 원격 인증을 지원하며 VS Code, Claude, Cursor, Copilot 및 기타 MCP 클라이언트에서 사용할 수 있습니다.
14
19
 
15
20
  ### 왜 이 GitLab MCP를 사용하나요?
16
21
 
17
- - 넓은 GitLab 지원 범위: 프로젝트, 저장소 탐색, 머지 리퀘스트, 이슈, 파이프라인, 위키, 릴리스, 라벨, 마일스톤 등
18
- - 유연한 인증: Personal Access Token, 로컬 OAuth2 브라우저 플로우, MCP OAuth 프록시, 요청별 원격 인증
19
- - 여러 전송 방식: 로컬 클라이언트용 stdio, 레거시 클라이언트용 SSE, 최신 원격 배포용 Streamable HTTP
20
- - 클라이언트 친화적 설정: Claude Code, Codex, Antigravity, OpenCode, Copilot, Cline, Roo Code, Cursor, Kilo Code, Amp Code 예시 제공
21
- - 셀프 호스팅 대응: 커스텀 GitLab 인스턴스, 프록시 설정, 동적 API URL 라우팅 지원
22
+ - **217개 도구 + `discover_tools`** — 작은 toolset으로 시작하고, 런타임에 카테고리 활성화
23
+ - **MR 2단계 리뷰** — `list_merge_request_changed_files` → 배치 `get_merge_request_file_diff`
24
+ - **Agent Skill 내장** — `skills/gitlab-mcp/` 워크플로우 가이드
25
+ - **유연한 인증** — Personal Access Token, 로컬 OAuth2 브라우저 플로우, MCP OAuth 프록시, 요청별 원격 인증
26
+ - **여러 전송 방식** — 로컬 클라이언트용 stdio, 레거시 클라이언트용 SSE, 최신 원격 배포용 Streamable HTTP
27
+ - **클라이언트 친화적 설정** — Claude Code, Codex, Antigravity, OpenCode, Copilot, Cline, Roo Code, Cursor, Kilo Code, Amp Code 예시 제공
28
+ - **셀프 호스팅 대응** — 커스텀 GitLab 인스턴스, 프록시 설정, 동적 API URL 라우팅 지원
29
+
30
+ ### 비교 요약
22
31
 
23
- 빠른 시작: 아래에서 Personal Access Token 또는 OAuth2 설정 중 하나를 선택하고 `@zereight/mcp-gitlab`을 설치한 뒤 MCP 클라이언트 설정에서 `zereight-mcp-gitlab`을 사용하세요.
32
+ | | @zereight/mcp-gitlab | GitLab MCP A (커뮤니티 CQRS형) |
33
+ |---|----------------------|--------------------------------|
34
+ | **적합한 경우** | AI 에이전트 워크플로우 | 엔터프라이즈 멀티 인스턴스 / 그룹형 도구 |
35
+ | **도구 모델** | ~217개 세분화 도구 + `discover_tools` | ~50–60개 `browse_*` / `manage_*` 그룹 도구 |
36
+ | **MR 리뷰** | 2단계 배치 diff | 서버마다 다름 |
37
+ | **Node.js** | >=18 | 보통 >=24 |
38
+ | **라이선스** | MIT | 서버마다 다름 |
39
+
40
+ [전체 비교 →](./docs/comparison/community-gitlab-mcp-a.md)
41
+
42
+ 빠른 시작: 아래에서 Personal Access Token 또는 OAuth2 설정 중 하나를 선택하고 `@zereight/mcp-gitlab`(또는 `@zereight/gitlab-mcp`)을 설치한 뒤 MCP 클라이언트 설정에서 `zereight-mcp-gitlab`을 사용하세요.
24
43
 
25
44
  ### 클라이언트 설정 가이드
26
45
 
@@ -62,6 +81,7 @@ PAT, OAuth, 읽기 전용 모드, 동적 API URL, 원격 인증을 지원하며
62
81
  - **Cursor**: [Cursor 설정 가이드](./docs/clients/cursor.md)
63
82
  - **Factory AI Droid / OpenClaw / OpenCode 스타일 클라이언트**: [JSON 기반 MCP 클라이언트 설정 가이드](./docs/clients/json-clients.md)
64
83
  - **OAuth 브라우저 플로우 상세**: [OAuth2 인증 설정 가이드](./docs/auth/oauth-setup.md)
84
+ - **localhost callback 없이 OAuth** (SSO, 원격 셸, 백그라운드 클라이언트): `zereight-mcp-gitlab auth`를 먼저 실행하세요 (GitLab 17.9+ device flow; 17.2–17.8은 `oauth2_device_grant_flow` 필요). 그다음 서버는 `GITLAB_USE_OAUTH=true`로 시작합니다. [독립 device-flow 커맨드](./docs/auth/oauth-setup.md#standalone-device-flow-auth-command)를 참고하세요.
65
85
 
66
86
  가장 단순한 로컬 설정은 Personal Access Token으로 시작하세요. 브라우저 기반 로컬 인증은 OAuth2를 사용하세요. 원격 또는 멀티 유저 배포는 아래 MCP OAuth 및 원격 인증 섹션을 참고하세요.
67
87
 
@@ -72,15 +92,17 @@ brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp
72
92
  brew install zereight/gitlab-mcp/zereight-mcp-gitlab
73
93
  ```
74
94
 
75
- npm으로 설치할 수도 있습니다:
95
+ npm으로 설치할 수도 있습니다 (두 패키지명 모두 동일한 서버입니다):
76
96
 
77
97
  ```shell
78
98
  npm install -g @zereight/mcp-gitlab
99
+ # 또는
100
+ npm install -g @zereight/gitlab-mcp
79
101
  ```
80
102
 
81
103
  예시는 기존 `mcp-gitlab`보다 충돌 가능성이 낮은 `zereight-mcp-gitlab` 별칭을 사용합니다. MCP 클라이언트가 찾지 못하면 `which zereight-mcp-gitlab`의 절대 경로를 사용하세요.
82
104
 
83
- 전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.1.49`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
105
+ 전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.1.51`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
84
106
 
85
107
  #### CLI 인자 사용하기(환경 변수 문제가 있는 클라이언트용)
86
108
 
@@ -111,8 +133,10 @@ npm install -g @zereight/mcp-gitlab
111
133
 
112
134
  CLI 인자는 환경 변수보다 우선합니다.
113
135
 
136
+ `zereight-mcp-gitlab auth`는 MCP 서버 플래그가 아니라 서브커맨드입니다. GitLab device flow를 실행한 뒤 종료합니다. [CLI 인자](./docs/getting-started/cli-arguments.md#auth)를 참고하세요.
137
+
114
138
  > **세밀한 도구 필터링:** `GITLAB_PERMISSION_MODE=modify`로 생성/수정은 허용하고 모든 삭제 도구를
115
- > 차단하거나, `GITLAB_PERMISSION_MODE=readonly`로 읽기 전용으로 운영할 수 있습니다. 또한
139
+ > 차단하거나(`execute_graphql` 삭제 mutation과 `push_files`의 `delete`/`move` 포함), `GITLAB_PERMISSION_MODE=readonly`로 읽기 전용으로 운영할 수 있습니다. 또한
116
140
  > `GITLAB_TOOLSETS=<group,…>`로 도구 그룹을 활성화하고, `GITLAB_TOOLS=<tool,…>`로 개별 도구만
117
141
  > 허용하며(예: 읽기 도구 + 특정 쓰기 도구 몇 개), `GITLAB_DENIED_TOOLS_REGEX`로 패턴 차단할 수
118
142
  > 있습니다. 레거시 `USE_GITLAB_WIKI` / `USE_MILESTONE` / `USE_PIPELINE` 플래그는 하위 호환용으로만
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # GitLab MCP Server
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@zereight/mcp-gitlab.svg)](https://www.npmjs.com/package/@zereight/mcp-gitlab)
4
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_GitLab_MCP-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](vscode:mcp/install?%7B%22name%22%3A%22zereight.gitlab-mcp%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40zereight%2Fmcp-gitlab%40latest%22%5D%2C%22env%22%3A%7B%22GITLAB_PERSONAL_ACCESS_TOKEN%22%3A%22%24%7Binput%3Agitlab-token%7D%22%2C%22GITLAB_API_URL%22%3A%22https%3A%2F%2Fgitlab.com%2Fapi%2Fv4%22%2C%22GITLAB_PERMISSION_MODE%22%3A%22full%22%7D%7D)
3
5
  [![MCP Toplist](https://mcptoplist.com/badge/io.github.zereight%2Fgitlab-mcp.svg)](https://mcptoplist.com/server/io.github.zereight%2Fgitlab-mcp) [![mcpindex](https://mcpindex.ai/api/v1/badge/io-github-zereight-gitlab-mcp)](https://mcpindex.ai/server/io-github-zereight-gitlab-mcp)
4
6
 
5
7
  [English](./README.md) | [한국어](./README.ko.md) | [简体中文](./README.zh-CN.md)
@@ -10,19 +12,35 @@
10
12
 
11
13
  ## @zereight/mcp-gitlab
12
14
 
13
- A comprehensive GitLab MCP server for AI clients. Manage projects, merge requests, issues, pipelines, wiki, releases, tags, milestones, and more through stdio, SSE, and Streamable HTTP.
15
+ **Agent-workflow-optimized GitLab MCP** — manage projects, merge requests, issues, pipelines, wiki, releases, tags, milestones, and more through stdio, SSE, and Streamable HTTP.
16
+
17
+ Also published as [`@zereight/gitlab-mcp`](https://www.npmjs.com/package/@zereight/gitlab-mcp) (search-friendly alias for the same server).
14
18
 
15
19
  Supports PAT, OAuth, read-only mode, dynamic API URLs, and remote authorization for VS Code, Claude, Cursor, Copilot, and other MCP clients.
16
20
 
17
21
  ### Why use this GitLab MCP?
18
22
 
19
- - Broad GitLab coverage — projects, repository browsing, merge requests, issues, pipelines, wiki, releases, tags, labels, milestones, and more
20
- - Flexible auth — Personal Access Token, local OAuth2 browser flow, MCP OAuth proxy, and per-request remote authorization
21
- - Multiple transports — stdio for local clients, SSE for legacy clients, and Streamable HTTP for modern remote deployments
22
- - Client-friendly setup — examples for Claude Code, Codex, Antigravity, OpenCode, Copilot, Cline, Roo Code, Cursor, Kilo Code, and Amp Code
23
- - Self-hosted ready — works with custom GitLab instances, proxy settings, and dynamic API URL routing
23
+ - **217 tools + `discover_tools`** — start with a small toolset; activate more at runtime without CQRS-style grouping
24
+ - **MR 2-step review** — `list_merge_request_changed_files` → batched `get_merge_request_file_diff`
25
+ - **Agent Skill built in** — workflow guidance in `skills/gitlab-mcp/`
26
+ - **Flexible auth** — Personal Access Token, local OAuth2 browser flow, MCP OAuth proxy, and per-request remote authorization
27
+ - **Multiple transports** — stdio for local clients, SSE for legacy clients, and Streamable HTTP for modern remote deployments
28
+ - **Client-friendly setup** — examples for Claude Code, Codex, Antigravity, OpenCode, Copilot, Cline, Roo Code, Cursor, Kilo Code, and Amp Code
29
+ - **Self-hosted ready** — works with custom GitLab instances, proxy settings, and dynamic API URL routing
30
+
31
+ ### How we compare
32
+
33
+ | | @zereight/mcp-gitlab | GitLab MCP A (community CQRS-style) |
34
+ |---|----------------------|-------------------------------------|
35
+ | **Best for** | AI agent workflows | Enterprise multi-instance / grouped tools |
36
+ | **Tool model** | ~217 granular tools + `discover_tools` | ~50–60 grouped `browse_*` / `manage_*` tools |
37
+ | **MR review** | 2-step batched diff | Varies |
38
+ | **Node.js** | >=18 | Often >=24 |
39
+ | **License** | MIT | Varies |
24
40
 
25
- Quick start: choose either Personal Access Token or OAuth2 setup below, install `@zereight/mcp-gitlab`, and use `zereight-mcp-gitlab` in your MCP client configuration.
41
+ [Full comparison →](./docs/comparison/community-gitlab-mcp-a.md)
42
+
43
+ Quick start: choose either Personal Access Token or OAuth2 setup below, install `@zereight/mcp-gitlab` (or `@zereight/gitlab-mcp`), and use `zereight-mcp-gitlab` in your MCP client configuration.
26
44
 
27
45
  ### Client Setup Guides
28
46
 
@@ -64,6 +82,7 @@ The server supports four authentication methods:
64
82
  - **Cursor**: see [Cursor Setup Guide](./docs/clients/cursor.md)
65
83
  - **Factory AI Droid / OpenClaw / OpenCode style clients**: see [JSON-Based MCP Clients Setup Guide](./docs/clients/json-clients.md)
66
84
  - **OAuth browser flow details**: see [OAuth2 Authentication Setup Guide](./docs/auth/oauth-setup.md)
85
+ - **OAuth without a localhost callback** (SSO, remote shell, background clients): run `zereight-mcp-gitlab auth` (GitLab 17.9+ device flow; 17.2–17.8 need `oauth2_device_grant_flow`), then start the server with `GITLAB_USE_OAUTH=true`. See [standalone device-flow command](./docs/auth/oauth-setup.md#standalone-device-flow-auth-command).
67
86
 
68
87
  For the simplest local setup, start with a Personal Access Token. For browser-based local auth, use OAuth2. For remote or multi-user deployments, continue to the MCP OAuth and Remote Authorization sections later in this README.
69
88
 
@@ -74,15 +93,17 @@ brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp
74
93
  brew install zereight/gitlab-mcp/zereight-mcp-gitlab
75
94
  ```
76
95
 
77
- Or with npm:
96
+ Or with npm (either package name installs the same server):
78
97
 
79
98
  ```shell
80
99
  npm install -g @zereight/mcp-gitlab
100
+ # or
101
+ npm install -g @zereight/gitlab-mcp
81
102
  ```
82
103
 
83
104
  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`.
84
105
 
85
- No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.1.49`. 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`).
106
+ No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.1.51`. 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`).
86
107
 
87
108
  #### Using CLI Arguments (for clients with env var issues)
88
109
 
@@ -113,8 +134,11 @@ Some MCP clients (like GitHub Copilot CLI) have issues with environment variable
113
134
 
114
135
  CLI arguments take precedence over environment variables.
115
136
 
137
+ `zereight-mcp-gitlab auth` is a subcommand (not an MCP server flag). It runs GitLab device flow and exits. See [CLI Arguments](./docs/getting-started/cli-arguments.md#auth).
138
+
116
139
  > **Fine-grained tool filtering:** use `GITLAB_PERMISSION_MODE=modify` to allow create/update while
117
- > blocking every delete tool (including delete mutations through `execute_graphql`), or
140
+ > blocking every delete tool (including delete mutations through `execute_graphql` and
141
+ > `push_files` `delete`/`move` actions), or
118
142
  > `GITLAB_PERMISSION_MODE=readonly` for read-only access. You can also
119
143
  > enable toolset groups with `GITLAB_TOOLSETS=<group,…>`, allow-list individual tools with
120
144
  > `GITLAB_TOOLS=<tool,…>` (e.g. read-only groups plus a few specific write tools), and
package/README.zh-CN.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # GitLab MCP Server
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@zereight/mcp-gitlab.svg)](https://www.npmjs.com/package/@zereight/mcp-gitlab)
4
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_GitLab_MCP-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](vscode:mcp/install?%7B%22name%22%3A%22zereight.gitlab-mcp%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40zereight%2Fmcp-gitlab%40latest%22%5D%2C%22env%22%3A%7B%22GITLAB_PERSONAL_ACCESS_TOKEN%22%3A%22%24%7Binput%3Agitlab-token%7D%22%2C%22GITLAB_API_URL%22%3A%22https%3A%2F%2Fgitlab.com%2Fapi%2Fv4%22%2C%22GITLAB_PERMISSION_MODE%22%3A%22full%22%7D%7D)
5
+
3
6
  [English](./README.md) | [한국어](./README.ko.md) | [简体中文](./README.zh-CN.md)
4
7
 
5
8
  📖 **[文档 →](https://zereight.github.io/gitlab-mcp/)** 设置指南、环境变量和完整工具参考请查看托管文档站点。
@@ -8,19 +11,35 @@
8
11
 
9
12
  ## @zereight/mcp-gitlab
10
13
 
11
- 这是面向 AI 客户端的完整 GitLab MCP 服务器。可通过 stdio、SSE 和 Streamable HTTP 管理项目、合并请求、议题、流水线、Wiki、发布、里程碑等。
14
+ **面向 AI 代理工作流优化的 GitLab MCP** — 可通过 stdio、SSE 和 Streamable HTTP 管理项目、合并请求、议题、流水线、Wiki、发布、里程碑等。
15
+
16
+ 同一服务器也以 [`@zereight/gitlab-mcp`](https://www.npmjs.com/package/@zereight/gitlab-mcp) 名称发布(便于 npm 搜索的别名)。
12
17
 
13
18
  支持 PAT、OAuth、只读模式、动态 API URL 和远程授权,可用于 VS Code、Claude、Cursor、Copilot 以及其他 MCP 客户端。
14
19
 
15
20
  ### 为什么使用这个 GitLab MCP?
16
21
 
17
- - 覆盖范围广:项目、仓库浏览、合并请求、议题、流水线、Wiki、发布、标签、里程碑等
18
- - 认证灵活:Personal Access Token、本地 OAuth2 浏览器流程、MCP OAuth 代理、按请求远程授权
19
- - 多种传输方式:本地客户端使用 stdio,旧客户端使用 SSE,现代远程部署使用 Streamable HTTP
20
- - 客户端设置友好:提供 Claude Code、Codex、Antigravity、OpenCode、Copilot、Cline、Roo Code、Cursor、Kilo Code 和 Amp Code 示例
21
- - 适合自托管:支持自定义 GitLab 实例、代理设置和动态 API URL 路由
22
+ - **217 个工具 + `discover_tools`** — 从小型 toolset 开始,运行时按需激活类别
23
+ - **MR 两步审查** — `list_merge_request_changed_files` → 批量 `get_merge_request_file_diff`
24
+ - **内置 Agent Skill** — `skills/gitlab-mcp/` 工作流指南
25
+ - **认证灵活** — Personal Access Token、本地 OAuth2 浏览器流程、MCP OAuth 代理、按请求远程授权
26
+ - **多种传输方式** — 本地客户端使用 stdio,旧客户端使用 SSE,现代远程部署使用 Streamable HTTP
27
+ - **客户端设置友好** — 提供 Claude Code、Codex、Antigravity、OpenCode、Copilot、Cline、Roo Code、Cursor、Kilo Code 和 Amp Code 示例
28
+ - **适合自托管** — 支持自定义 GitLab 实例、代理设置和动态 API URL 路由
29
+
30
+ ### 对比摘要
22
31
 
23
- 快速开始:在下面选择 Personal Access Token 或 OAuth2 设置,安装 `@zereight/mcp-gitlab`,并在 MCP 客户端配置中使用 `zereight-mcp-gitlab`。
32
+ | | @zereight/mcp-gitlab | GitLab MCP A(社区 CQRS 型) |
33
+ |---|----------------------|------------------------------|
34
+ | **更适合** | AI 代理工作流 | 企业多实例 / 分组工具 |
35
+ | **工具模型** | ~217 个细粒度工具 + `discover_tools` | ~50–60 个 `browse_*` / `manage_*` 分组工具 |
36
+ | **MR 审查** | 两步批量 diff | 因服务器而异 |
37
+ | **Node.js** | >=18 | 通常 >=24 |
38
+ | **许可证** | MIT | 因服务器而异 |
39
+
40
+ [完整对比 →](./docs/comparison/community-gitlab-mcp-a.md)
41
+
42
+ 快速开始:在下面选择 Personal Access Token 或 OAuth2 设置,安装 `@zereight/mcp-gitlab`(或 `@zereight/gitlab-mcp`),并在 MCP 客户端配置中使用 `zereight-mcp-gitlab`。
24
43
 
25
44
  ### 客户端设置指南
26
45
 
@@ -62,6 +81,7 @@
62
81
  - **Cursor**:[Cursor 设置指南](./docs/clients/cursor.md)
63
82
  - **Factory AI Droid / OpenClaw / OpenCode 风格客户端**:[基于 JSON 的 MCP 客户端设置指南](./docs/clients/json-clients.md)
64
83
  - **OAuth 浏览器流程详情**:[OAuth2 认证设置指南](./docs/auth/oauth-setup.md)
84
+ - **无需 localhost callback 的 OAuth**(SSO、远程 shell、后台客户端):先运行 `zereight-mcp-gitlab auth`(GitLab 17.9+ device flow;17.2–17.8 需 `oauth2_device_grant_flow`),再以 `GITLAB_USE_OAUTH=true` 启动服务器。参见[独立 device-flow 命令](./docs/auth/oauth-setup.md#standalone-device-flow-auth-command)。
65
85
 
66
86
  最简单的本地设置可以从 Personal Access Token 开始。基于浏览器的本地认证使用 OAuth2。远程或多用户部署请继续查看下面的 MCP OAuth 和远程授权部分。
67
87
 
@@ -72,15 +92,17 @@ brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp
72
92
  brew install zereight/gitlab-mcp/zereight-mcp-gitlab
73
93
  ```
74
94
 
75
- 也可以使用 npm 安装:
95
+ 也可以使用 npm 安装(两个包名安装的是同一服务器):
76
96
 
77
97
  ```shell
78
98
  npm install -g @zereight/mcp-gitlab
99
+ # 或
100
+ npm install -g @zereight/gitlab-mcp
79
101
  ```
80
102
 
81
103
  示例使用 `zereight-mcp-gitlab`,这是比旧的 `mcp-gitlab` 更不容易冲突的别名。如果 MCP 客户端找不到它,请使用 `which zereight-mcp-gitlab` 输出的绝对路径。
82
104
 
83
- 如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.1.49`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
105
+ 如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.1.51`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
84
106
 
85
107
  #### 使用 CLI 参数(适用于环境变量有问题的客户端)
86
108
 
@@ -111,7 +133,10 @@ npm install -g @zereight/mcp-gitlab
111
133
 
112
134
  CLI 参数优先于环境变量。
113
135
 
114
- > **细粒度工具过滤:**使用 `GITLAB_PERMISSION_MODE=modify` 允许创建/更新并阻止所有删除工具,
136
+ `zereight-mcp-gitlab auth` 是子命令,不是 MCP 服务器参数。它运行 GitLab device flow 后退出。参见 [CLI 参数](./docs/getting-started/cli-arguments.md#auth)。
137
+
138
+ > **细粒度工具过滤:**使用 `GITLAB_PERMISSION_MODE=modify` 允许创建/更新并阻止所有删除工具
139
+ > (包括通过 `execute_graphql` 的删除 mutation 以及 `push_files` 的 `delete`/`move`),
115
140
  > 或使用 `GITLAB_PERMISSION_MODE=readonly` 只读运行。还可以用
116
141
  > `GITLAB_TOOLSETS=<group,…>` 启用工具分组,用 `GITLAB_TOOLS=<tool,…>` 白名单启用单个工具
117
142
  > (例如:只读分组 + 少数几个写工具),用 `GITLAB_DENIED_TOOLS_REGEX` 按正则屏蔽工具。
@@ -0,0 +1,95 @@
1
+ import * as os from "os";
2
+ import * as path from "path";
3
+ import { GitLabOAuth } from "./oauth.js";
4
+ import { createLogger } from "./utils/logger.js";
5
+ const logger = createLogger("gitlab-mcp-auth");
6
+ const DEFAULT_API_URL = "https://gitlab.com";
7
+ const DEFAULT_REDIRECT_URI = "http://127.0.0.1:8888/callback";
8
+ export const AUTH_CLI_HELP = `Usage: zereight-mcp-gitlab auth [options]
9
+
10
+ Run GitLab OAuth Device Authorization Grant (GitLab 17.9+; 17.2–17.8 need
11
+ oauth2_device_grant_flow) and store a token at the same path used by
12
+ GITLAB_USE_OAUTH (default ~/.gitlab-mcp-token.json).
13
+
14
+ This does not replace the local browser callback flow. After auth succeeds,
15
+ start the MCP server with GITLAB_USE_OAUTH=true and GITLAB_OAUTH_CLIENT_ID.
16
+ If you used --token-path, set GITLAB_OAUTH_TOKEN_PATH to the same path.
17
+
18
+ Options:
19
+ --client-id <id> OAuth application ID (or GITLAB_OAUTH_CLIENT_ID)
20
+ --api-url <url> GitLab API URL (or GITLAB_API_URL). Default: https://gitlab.com
21
+ --token-path <path> Token file path (or GITLAB_OAUTH_TOKEN_PATH)
22
+ -h, --help Show this help
23
+ `;
24
+ function readFlag(argv, name) {
25
+ const equalsPrefix = `--${name}=`;
26
+ for (let i = 0; i < argv.length; i++) {
27
+ const arg = argv[i];
28
+ if (arg.startsWith(equalsPrefix)) {
29
+ const value = arg.slice(equalsPrefix.length);
30
+ return value === "" ? undefined : value;
31
+ }
32
+ if (arg === `--${name}`) {
33
+ const next = argv[i + 1];
34
+ if (next && !next.startsWith("-")) {
35
+ return next;
36
+ }
37
+ return undefined;
38
+ }
39
+ }
40
+ return undefined;
41
+ }
42
+ function wantsHelp(argv) {
43
+ return argv.includes("--help") || argv.includes("-h");
44
+ }
45
+ function isReadOnlyMode(argv, env) {
46
+ const readOnly = readFlag(argv, "read-only") ?? env.GITLAB_READ_ONLY_MODE;
47
+ if (readOnly === "true") {
48
+ return true;
49
+ }
50
+ return (readFlag(argv, "permission-mode") ?? env.GITLAB_PERMISSION_MODE) === "readonly";
51
+ }
52
+ export function gitlabOriginFromApiUrl(apiUrl) {
53
+ return apiUrl.replace(/\/api\/v4\/?$/, "");
54
+ }
55
+ export async function runAuthCommandAsync(input = {}) {
56
+ const argv = input.argv ?? process.argv;
57
+ const env = input.env ?? process.env;
58
+ const stdout = input.stdout ?? process.stdout;
59
+ if (wantsHelp(argv)) {
60
+ stdout.write(AUTH_CLI_HELP);
61
+ return;
62
+ }
63
+ const clientId = readFlag(argv, "client-id") ?? env.GITLAB_OAUTH_CLIENT_ID;
64
+ if (!clientId) {
65
+ throw new Error("Missing OAuth client ID. Pass --client-id or set GITLAB_OAUTH_CLIENT_ID.");
66
+ }
67
+ const apiUrl = readFlag(argv, "api-url") ?? env.GITLAB_API_URL ?? DEFAULT_API_URL;
68
+ const tokenPath = readFlag(argv, "token-path") ??
69
+ env.GITLAB_OAUTH_TOKEN_PATH ??
70
+ path.join(os.homedir(), ".gitlab-mcp-token.json");
71
+ const gitlabUrl = gitlabOriginFromApiUrl(apiUrl);
72
+ const scopes = [isReadOnlyMode(argv, env) ? "read_api" : "api"];
73
+ const oauth = new GitLabOAuth({
74
+ clientId,
75
+ clientSecret: env.GITLAB_OAUTH_CLIENT_SECRET,
76
+ redirectUri: env.GITLAB_OAUTH_REDIRECT_URI || DEFAULT_REDIRECT_URI,
77
+ gitlabUrl,
78
+ scopes,
79
+ tokenStoragePath: tokenPath,
80
+ });
81
+ logger.info("Starting GitLab device authorization (no browser will be opened)");
82
+ await oauth.runDeviceFlowAsync({
83
+ fetchImpl: input.fetchImpl,
84
+ sleepAsync: input.sleepAsync,
85
+ onUserCode: info => {
86
+ const visitUrl = info.verificationUriComplete ?? info.verificationUri;
87
+ stdout.write(`Visit: ${visitUrl}\n`);
88
+ stdout.write(`Code: ${info.userCode}\n`);
89
+ stdout.write("Waiting for authorization...\n");
90
+ },
91
+ });
92
+ stdout.write(`Token saved to ${tokenPath}\n`);
93
+ stdout.write("Start the MCP server with GITLAB_USE_OAUTH=true and the same GITLAB_OAUTH_CLIENT_ID.\n" +
94
+ "If you used --token-path, set GITLAB_OAUTH_TOKEN_PATH to that path as well.\n");
95
+ }
@@ -0,0 +1,33 @@
1
+ const SCRIPT_PATH_PATTERN = /\.(cjs|mjs|js|cts|mts|ts)$/;
2
+ const FLAGS_WITHOUT_VALUE = new Set(["--help", "-h"]);
3
+ function isScriptPath(arg) {
4
+ return SCRIPT_PATH_PATTERN.test(arg);
5
+ }
6
+ /**
7
+ * Read the first positional CLI command (not a flag, not a script path).
8
+ * `tsx index.ts auth` and `node build/index.js auth` both resolve to `auth`.
9
+ * Space-separated option values such as `--api-url https://...` are skipped.
10
+ * Flags such as --token stay on the MCP server path.
11
+ */
12
+ export function getPositionalCliCommand(argv) {
13
+ const args = argv.slice(2);
14
+ for (let i = 0; i < args.length; i++) {
15
+ const arg = args[i];
16
+ if (arg.startsWith("--") && arg.includes("=")) {
17
+ continue;
18
+ }
19
+ if (arg.startsWith("-")) {
20
+ if (!FLAGS_WITHOUT_VALUE.has(arg) &&
21
+ i + 1 < args.length &&
22
+ !args[i + 1].startsWith("-")) {
23
+ i += 1;
24
+ }
25
+ continue;
26
+ }
27
+ if (isScriptPath(arg)) {
28
+ continue;
29
+ }
30
+ return arg;
31
+ }
32
+ return undefined;
33
+ }
package/build/index.js CHANGED
@@ -75,6 +75,8 @@ import { CookieJar, parse as parseCookie } from "tough-cookie";
75
75
  import { URL } from "node:url";
76
76
  import { z } from "zod";
77
77
  import { initializeOAuthClient } from "./oauth.js";
78
+ import { getPositionalCliCommand } from "./cli-command.js";
79
+ import { runAuthCommandAsync } from "./auth-cli.js";
78
80
  import { createGitLabOAuthProvider } from "./oauth-proxy.js";
79
81
  import { mcpAuthRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
80
82
  import rateLimit, { ipKeyGenerator } from "express-rate-limit";
@@ -91,6 +93,7 @@ import { normalizeGitLabApiUrl } from "./utils/url.js";
91
93
  import { estimateMergeCommitCount, filterDiffsByPatterns, openSafeOutputWriteStream, readSafeExistingFile, summarizeWebhookEvents, } from "./utils/helpers.js";
92
94
  import { graphqlQueryContainsWriteOperation, graphqlQueryContainsDeleteOperation, } from "./utils/graphql-query.js";
93
95
  import { resolveNestedWikiUpdateTitle } from "./utils/wiki-title.js";
96
+ import { encodeRepoFilePayloadContent, fileOperationsIncludeDeleteOrMove, toGitLabCommitActions, } from "./utils/gitlab-commit-actions.js";
94
97
  import { redactSensitiveGitLabFields } from "./utils/redact-sensitive.js";
95
98
  import { checkForNewVersion } from "./utils/version-check.js";
96
99
  import { assertGitLabVersionAtLeast } from "./utils/gitlab-version-gate.js";
@@ -1100,7 +1103,8 @@ if (GITLAB_MCP_OAUTH) {
1100
1103
  }
1101
1104
  logger.info("MCP OAuth enabled: GitLab OAuth proxy active (Private-Token/JOB-TOKEN headers bypass OAuth)");
1102
1105
  }
1103
- if (!REMOTE_AUTHORIZATION &&
1106
+ if (getPositionalCliCommand(process.argv) !== "auth" &&
1107
+ !REMOTE_AUTHORIZATION &&
1104
1108
  !GITLAB_MCP_OAUTH &&
1105
1109
  !USE_OAUTH &&
1106
1110
  !GITLAB_PERSONAL_ACCESS_TOKEN &&
@@ -3291,12 +3295,6 @@ async function updateMergeRequestNote(projectId, mergeRequestIid, noteId, body)
3291
3295
  const data = await response.json();
3292
3296
  return GitLabDiscussionNoteSchema.parse(data);
3293
3297
  }
3294
- function encodeRepoFilePayloadContent(content) {
3295
- if (GITLAB_REPO_FILE_ENCODING === "base64") {
3296
- return Buffer.from(content).toString("base64");
3297
- }
3298
- return content;
3299
- }
3300
3298
  /**
3301
3299
  * Create or update a file in a GitLab project
3302
3300
  * 파일 생성 또는 업데이트
@@ -3309,15 +3307,18 @@ function encodeRepoFilePayloadContent(content) {
3309
3307
  * @param {string} [previousPath] - The previous path of the file in case of rename
3310
3308
  * @returns {Promise<GitLabCreateUpdateFileResponse>} The file update response
3311
3309
  */
3312
- async function createOrUpdateFile(projectId, filePath, content, commitMessage, branch, previousPath, last_commit_id, commit_id) {
3310
+ async function createOrUpdateFile(projectId, filePath, content, commitMessage, branch, previousPath, last_commit_id, commit_id, encoding) {
3313
3311
  projectId = decodeURIComponent(projectId); // Decode project ID
3314
3312
  const encodedPath = encodeURIComponent(filePath);
3315
3313
  const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/files/${encodedPath}`);
3314
+ const resolvedEncoding = encoding ?? GITLAB_REPO_FILE_ENCODING;
3316
3315
  const body = {
3317
3316
  branch,
3318
- content: encodeRepoFilePayloadContent(content),
3317
+ content: encoding !== undefined
3318
+ ? content
3319
+ : encodeRepoFilePayloadContent(content, GITLAB_REPO_FILE_ENCODING),
3319
3320
  commit_message: commitMessage,
3320
- encoding: GITLAB_REPO_FILE_ENCODING,
3321
+ encoding: resolvedEncoding,
3321
3322
  ...(previousPath ? { previous_path: previousPath } : {}),
3322
3323
  };
3323
3324
  // Check if file exists
@@ -3387,12 +3388,7 @@ async function createCommit(projectId, message, branch, actions) {
3387
3388
  body: JSON.stringify({
3388
3389
  branch,
3389
3390
  commit_message: message,
3390
- actions: actions.map(action => ({
3391
- action: "create",
3392
- file_path: action.path,
3393
- content: encodeRepoFilePayloadContent(action.content),
3394
- encoding: GITLAB_REPO_FILE_ENCODING,
3395
- })),
3391
+ actions: toGitLabCommitActions(actions, GITLAB_REPO_FILE_ENCODING),
3396
3392
  }),
3397
3393
  });
3398
3394
  if (response.status === 400) {
@@ -7280,14 +7276,24 @@ async function handleToolCall(params) {
7280
7276
  }
7281
7277
  case "create_or_update_file": {
7282
7278
  const args = CreateOrUpdateFileSchema.parse(params.arguments);
7283
- const result = await createOrUpdateFile(args.project_id, args.file_path, args.content, args.commit_message, args.branch, args.previous_path, args.last_commit_id, args.commit_id);
7279
+ const result = await createOrUpdateFile(args.project_id, args.file_path, args.content, args.commit_message, args.branch, args.previous_path, args.last_commit_id, args.commit_id, args.encoding);
7284
7280
  return {
7285
7281
  content: [{ type: "text", text: JSON.stringify(result) }],
7286
7282
  };
7287
7283
  }
7288
7284
  case "push_files": {
7289
7285
  const args = PushFilesSchema.parse(params.arguments);
7290
- const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map(f => ({ path: f.file_path, content: f.content })));
7286
+ if (GITLAB_PERMISSION_MODE === "modify" &&
7287
+ fileOperationsIncludeDeleteOrMove(args.files)) {
7288
+ throw new Error("push_files does not allow delete or move actions in modify mode");
7289
+ }
7290
+ const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map(f => ({
7291
+ path: f.file_path,
7292
+ content: f.content,
7293
+ action: f.action,
7294
+ encoding: f.encoding,
7295
+ previous_path: f.previous_path,
7296
+ })));
7291
7297
  return {
7292
7298
  content: [{ type: "text", text: JSON.stringify(result) }],
7293
7299
  };
@@ -10795,8 +10801,22 @@ async function runServer() {
10795
10801
  process.exit(1);
10796
10802
  }
10797
10803
  }
10798
- // 下記の2行を追記
10799
- runServer().catch(error => {
10804
+ async function main() {
10805
+ if (getPositionalCliCommand(process.argv) === "auth") {
10806
+ try {
10807
+ await runAuthCommandAsync();
10808
+ process.exit(0);
10809
+ }
10810
+ catch (error) {
10811
+ const message = error instanceof Error ? error.message : String(error);
10812
+ process.stderr.write(`${message}\n`);
10813
+ logger.error({ err: error }, "auth command failed");
10814
+ process.exit(1);
10815
+ }
10816
+ }
10817
+ await runServer();
10818
+ }
10819
+ main().catch(error => {
10800
10820
  logger.fatal({ err: error }, "Fatal error in main()");
10801
10821
  process.exit(1);
10802
10822
  });