gitlab-mcp 0.1.5 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +7 -0
- package/.editorconfig +9 -0
- package/.env.example +75 -0
- package/.github/workflows/nodejs.yml +31 -0
- package/.github/workflows/npm-publish.yml +31 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.prettierrc.json +6 -0
- package/Dockerfile +20 -0
- package/README.md +416 -251
- package/docker-compose.yml +10 -0
- package/docs/architecture.md +310 -0
- package/docs/authentication.md +299 -0
- package/docs/configuration.md +149 -0
- package/docs/deployment.md +336 -0
- package/docs/tools.md +294 -0
- package/eslint.config.js +23 -0
- package/package.json +70 -32
- package/scripts/get-oauth-token.example.sh +15 -0
- package/src/config/env.ts +171 -0
- package/src/http.ts +605 -0
- package/src/index.ts +77 -0
- package/src/lib/auth-context.ts +19 -0
- package/src/lib/gitlab-client.ts +1810 -0
- package/src/lib/logger.ts +17 -0
- package/src/lib/network.ts +45 -0
- package/src/lib/oauth.ts +287 -0
- package/src/lib/output.ts +51 -0
- package/src/lib/policy.ts +78 -0
- package/src/lib/request-runtime.ts +376 -0
- package/src/lib/sanitize.ts +25 -0
- package/src/server/build-server.ts +17 -0
- package/src/tools/gitlab.ts +3128 -0
- package/src/tools/health.ts +27 -0
- package/src/tools/mr-code-context.ts +473 -0
- package/src/types/context.ts +13 -0
- package/tests/auth-context.test.ts +102 -0
- package/tests/gitlab-client.test.ts +674 -0
- package/tests/graphql-guard.test.ts +121 -0
- package/tests/integration/agent-loop.integration.test.ts +552 -0
- package/tests/integration/server.integration.test.ts +543 -0
- package/tests/mr-code-context.test.ts +600 -0
- package/tests/oauth.test.ts +43 -0
- package/tests/output.test.ts +186 -0
- package/tests/policy.test.ts +324 -0
- package/tests/request-runtime.test.ts +252 -0
- package/tests/sanitize.test.ts +123 -0
- package/tests/upload-reference.test.ts +84 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +12 -0
- package/LICENSE +0 -21
- package/build/index.js +0 -1642
- package/build/schemas.js +0 -684
- package/build/test-note.js +0 -54
package/README.md
CHANGED
|
@@ -1,276 +1,441 @@
|
|
|
1
|
-
#
|
|
1
|
+
# gitlab-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+
[](https://www.npmjs.com/package/gitlab-mcp)
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
A production-ready [MCP](https://modelcontextprotocol.io/) server for GitLab. Provides **80+ tools** that let AI assistants read and manage GitLab projects, merge requests, issues, pipelines, wikis, releases, and more through a unified, policy-controlled interface.
|
|
8
|
+
|
|
9
|
+
## Highlights
|
|
10
|
+
|
|
11
|
+
- **Comprehensive GitLab coverage** — projects, merge requests (with code-context analysis), issues, pipelines, wikis, milestones, releases, labels, commits, branches, GraphQL, and file management
|
|
12
|
+
- **Multiple transports** — stdio for local CLI usage, Streamable HTTP for remote deployments, optional SSE
|
|
13
|
+
- **Flexible authentication** — personal access tokens, OAuth 2.0 PKCE, external token scripts, token files, cookie-based auth, and per-request remote authorization
|
|
14
|
+
- **Policy engine** — read-only mode, tool allowlist/denylist, feature toggles, and project-scoped restrictions
|
|
15
|
+
- **Enterprise networking** — HTTP/HTTPS proxy, custom CA certificates, Cloudflare bypass, multi-instance API rotation
|
|
16
|
+
- **Output control** — JSON, compact JSON, or YAML formatting with configurable response size limits
|
|
4
17
|
|
|
5
18
|
## Usage
|
|
6
19
|
|
|
7
|
-
###
|
|
20
|
+
### Supported clients
|
|
21
|
+
|
|
22
|
+
Claude Desktop, Claude Code, VS Code, GitHub Copilot Chat (VS Code), Cursor, JetBrains AI Assistant, GitLab Duo, and any MCP client that supports stdio or streamable HTTP.
|
|
23
|
+
|
|
24
|
+
Current client format references:
|
|
25
|
+
|
|
26
|
+
- [MCP transports and protocol](https://modelcontextprotocol.io/docs/concepts/transports)
|
|
27
|
+
- [Claude Code MCP](https://docs.anthropic.com/en/docs/claude-code/mcp)
|
|
28
|
+
- [VS Code MCP servers](https://code.visualstudio.com/docs/copilot/customization/mcp-servers)
|
|
29
|
+
- [Cursor MCP](https://docs.cursor.com/context/model-context-protocol)
|
|
30
|
+
- [JetBrains AI Assistant MCP](https://www.jetbrains.com/help/ai-assistant/configure-an-mcp-server.html)
|
|
31
|
+
|
|
32
|
+
### Authentication methods
|
|
33
|
+
|
|
34
|
+
The server supports three auth patterns:
|
|
35
|
+
|
|
36
|
+
1. Personal Access Token (PAT)
|
|
37
|
+
2. OAuth 2.0 PKCE (recommended for local interactive use)
|
|
38
|
+
3. Remote per-request auth (`REMOTE_AUTHORIZATION=true`, HTTP mode)
|
|
8
39
|
|
|
9
|
-
|
|
40
|
+
### OAuth2 setup (stdio, recommended for local interactive use)
|
|
41
|
+
|
|
42
|
+
1. Create a GitLab OAuth application in `Settings -> Applications`.
|
|
43
|
+
2. Set redirect URI to `http://127.0.0.1:8765/callback` (or your custom callback).
|
|
44
|
+
3. Set scope to `api`.
|
|
45
|
+
4. Copy the Application ID as `GITLAB_OAUTH_CLIENT_ID`.
|
|
10
46
|
|
|
11
47
|
```json
|
|
12
48
|
{
|
|
13
49
|
"mcpServers": {
|
|
14
|
-
"
|
|
50
|
+
"gitlab": {
|
|
15
51
|
"command": "npx",
|
|
16
|
-
"args": ["-y", "mcp
|
|
52
|
+
"args": ["-y", "gitlab-mcp@latest"],
|
|
53
|
+
"env": {
|
|
54
|
+
"GITLAB_USE_OAUTH": "true",
|
|
55
|
+
"GITLAB_OAUTH_CLIENT_ID": "your_oauth_client_id",
|
|
56
|
+
"GITLAB_OAUTH_REDIRECT_URI": "http://127.0.0.1:8765/callback",
|
|
57
|
+
"GITLAB_API_URL": "https://gitlab.com/api/v4",
|
|
58
|
+
"GITLAB_ALLOWED_PROJECT_IDS": "",
|
|
59
|
+
"GITLAB_READ_ONLY_MODE": "false",
|
|
60
|
+
"USE_GITLAB_WIKI": "true",
|
|
61
|
+
"USE_MILESTONE": "true",
|
|
62
|
+
"USE_PIPELINE": "true"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If your OAuth app is confidential, also set `GITLAB_OAUTH_CLIENT_SECRET`.
|
|
70
|
+
|
|
71
|
+
### Personal Access Token setup (stdio)
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"mcpServers": {
|
|
76
|
+
"gitlab": {
|
|
77
|
+
"command": "npx",
|
|
78
|
+
"args": ["-y", "gitlab-mcp@latest"],
|
|
79
|
+
"env": {
|
|
80
|
+
"GITLAB_PERSONAL_ACCESS_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx",
|
|
81
|
+
"GITLAB_API_URL": "https://gitlab.com/api/v4",
|
|
82
|
+
"GITLAB_ALLOWED_PROJECT_IDS": "",
|
|
83
|
+
"GITLAB_READ_ONLY_MODE": "false",
|
|
84
|
+
"USE_GITLAB_WIKI": "true",
|
|
85
|
+
"USE_MILESTONE": "true",
|
|
86
|
+
"USE_PIPELINE": "true"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### VS Code `.vscode/mcp.json` examples
|
|
94
|
+
|
|
95
|
+
PAT with secure prompt input:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"inputs": [
|
|
100
|
+
{
|
|
101
|
+
"type": "promptString",
|
|
102
|
+
"id": "gitlab_token",
|
|
103
|
+
"description": "GitLab Personal Access Token",
|
|
104
|
+
"password": true
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"servers": {
|
|
108
|
+
"gitlab": {
|
|
109
|
+
"type": "stdio",
|
|
110
|
+
"command": "node",
|
|
111
|
+
"args": ["/absolute/path/to/gitlab-mcp/dist/index.js"],
|
|
17
112
|
"env": {
|
|
18
|
-
"GITLAB_PERSONAL_ACCESS_TOKEN": "
|
|
19
|
-
"GITLAB_API_URL": "
|
|
20
|
-
"GITLAB_READ_ONLY_MODE": "
|
|
113
|
+
"GITLAB_PERSONAL_ACCESS_TOKEN": "${input:gitlab_token}",
|
|
114
|
+
"GITLAB_API_URL": "https://gitlab.com/api/v4",
|
|
115
|
+
"GITLAB_READ_ONLY_MODE": "false"
|
|
21
116
|
}
|
|
22
117
|
}
|
|
23
118
|
}
|
|
24
119
|
}
|
|
25
120
|
```
|
|
26
121
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token.
|
|
30
|
-
- `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`)
|
|
31
|
-
- `GITLAB_READ_ONLY_MODE`: When set to 'true', restricts the server to only expose read-only operations. Useful for enhanced security or when write access is not needed. Also useful for using with Cursor and it's 40 tool limit.
|
|
32
|
-
|
|
33
|
-
## Tools 🛠️
|
|
34
|
-
|
|
35
|
-
1. `create_or_update_file`
|
|
36
|
-
|
|
37
|
-
- Create or update a single file in a GitLab project. 📝
|
|
38
|
-
- Inputs:
|
|
39
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
40
|
-
- `file_path` (string): Path to create/update the file
|
|
41
|
-
- `content` (string): File content
|
|
42
|
-
- `commit_message` (string): Commit message
|
|
43
|
-
- `branch` (string): Branch to create/update the file in
|
|
44
|
-
- `previous_path` (optional string): Previous file path when renaming a file
|
|
45
|
-
- Returns: File content and commit details
|
|
46
|
-
|
|
47
|
-
2. `push_files`
|
|
48
|
-
|
|
49
|
-
- Push multiple files in a single commit. 📤
|
|
50
|
-
- Inputs:
|
|
51
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
52
|
-
- `branch` (string): Branch to push to
|
|
53
|
-
- `files` (array): Array of files to push, each with `file_path` and `content` properties
|
|
54
|
-
- `commit_message` (string): Commit message
|
|
55
|
-
- Returns: Updated branch reference
|
|
56
|
-
|
|
57
|
-
3. `search_repositories`
|
|
58
|
-
|
|
59
|
-
- Search for GitLab projects. 🔍
|
|
60
|
-
- Inputs:
|
|
61
|
-
- `search` (string): Search query
|
|
62
|
-
- `page` (optional number): Page number (default: 1)
|
|
63
|
-
- `per_page` (optional number): Results per page (default: 20, max: 100)
|
|
64
|
-
- Returns: Project search results
|
|
65
|
-
|
|
66
|
-
4. `create_repository`
|
|
67
|
-
|
|
68
|
-
- Create a new GitLab project. ➕
|
|
69
|
-
- Inputs:
|
|
70
|
-
- `name` (string): Project name
|
|
71
|
-
- `description` (optional string): Project description
|
|
72
|
-
- `visibility` (optional string): Project visibility level (public, private, internal)
|
|
73
|
-
- `initialize_with_readme` (optional boolean): Initialize with README
|
|
74
|
-
- Returns: Details of the created project
|
|
75
|
-
|
|
76
|
-
5. `get_file_contents`
|
|
77
|
-
|
|
78
|
-
- Get the contents of a file or directory. 📂
|
|
79
|
-
- Inputs:
|
|
80
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
81
|
-
- `file_path` (string): Path to the file/directory
|
|
82
|
-
- `ref` (optional string): Branch, tag, or commit SHA (default: default branch)
|
|
83
|
-
- Returns: File/directory content
|
|
84
|
-
|
|
85
|
-
6. `create_issue`
|
|
86
|
-
|
|
87
|
-
- Create a new issue. 🐛
|
|
88
|
-
- Inputs:
|
|
89
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
90
|
-
- `title` (string): Issue title
|
|
91
|
-
- `description` (string): Issue description
|
|
92
|
-
- `assignee_ids` (optional number[]): Array of assignee IDs
|
|
93
|
-
- `milestone_id` (optional number): Milestone ID
|
|
94
|
-
- `labels` (optional string[]): Array of labels
|
|
95
|
-
- Returns: Details of the created issue
|
|
96
|
-
|
|
97
|
-
7. `create_merge_request`
|
|
98
|
-
|
|
99
|
-
- Create a new merge request. 🚀
|
|
100
|
-
- Inputs:
|
|
101
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
102
|
-
- `title` (string): Merge request title
|
|
103
|
-
- `description` (string): Merge request description
|
|
104
|
-
- `source_branch` (string): Branch with changes
|
|
105
|
-
- `target_branch` (string): Branch to merge into
|
|
106
|
-
- `allow_collaboration` (optional boolean): Allow collaborators to push commits to the source branch
|
|
107
|
-
- `draft` (optional boolean): Create as a draft merge request
|
|
108
|
-
- Returns: Details of the created merge request
|
|
109
|
-
|
|
110
|
-
8. `fork_repository`
|
|
111
|
-
|
|
112
|
-
- Fork a project. 🍴
|
|
113
|
-
- Inputs:
|
|
114
|
-
- `project_id` (string): Project ID or namespace/project_path to fork
|
|
115
|
-
- `namespace` (optional string): Namespace to fork into (default: user namespace)
|
|
116
|
-
- Returns: Details of the forked project
|
|
117
|
-
|
|
118
|
-
9. `create_branch`
|
|
119
|
-
|
|
120
|
-
- Create a new branch. 🌿
|
|
121
|
-
- Inputs:
|
|
122
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
123
|
-
- `name` (string): New branch name
|
|
124
|
-
- `ref` (optional string): Ref to create the branch from (branch, tag, commit SHA, default: default branch)
|
|
125
|
-
- Returns: Created branch reference
|
|
126
|
-
|
|
127
|
-
10. `get_merge_request`
|
|
128
|
-
|
|
129
|
-
- Get details of a merge request. ℹ️
|
|
130
|
-
- Inputs:
|
|
131
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
132
|
-
- `merge_request_iid` (number): Merge request IID
|
|
133
|
-
- Returns: Merge request details
|
|
134
|
-
|
|
135
|
-
11. `get_merge_request_diffs`
|
|
136
|
-
|
|
137
|
-
- Get changes (diffs) of a merge request. diff
|
|
138
|
-
- Inputs:
|
|
139
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
140
|
-
- `merge_request_iid` (number): Merge request IID
|
|
141
|
-
- `view` (optional string): Diff view type ('inline' or 'parallel')
|
|
142
|
-
- Returns: Array of merge request diff information
|
|
143
|
-
|
|
144
|
-
12. `update_merge_request`
|
|
145
|
-
|
|
146
|
-
- Update a merge request. 🔄
|
|
147
|
-
- Inputs:
|
|
148
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
149
|
-
- `merge_request_iid` (number): Merge request IID
|
|
150
|
-
- `title` (optional string): New title
|
|
151
|
-
- `description` (string): New description
|
|
152
|
-
- `target_branch` (optional string): New target branch
|
|
153
|
-
- `state_event` (optional string): Merge request state change event ('close', 'reopen')
|
|
154
|
-
- `remove_source_branch` (optional boolean): Remove source branch after merge
|
|
155
|
-
- `allow_collaboration` (optional boolean): Allow collaborators to push commits to the source branch
|
|
156
|
-
- Returns: Updated merge request details
|
|
157
|
-
|
|
158
|
-
13. `create_note`
|
|
159
|
-
|
|
160
|
-
- Create a new note (comment) to an issue or merge request. 💬
|
|
161
|
-
- Inputs:
|
|
162
|
-
- `project_id` (string): Project ID or namespace/project_path
|
|
163
|
-
- `noteable_type` (string): Type of noteable ("issue" or "merge_request")
|
|
164
|
-
- `noteable_iid` (number): IID of the issue or merge request
|
|
165
|
-
- `body` (string): Note content
|
|
166
|
-
- Returns: Details of the created note
|
|
167
|
-
|
|
168
|
-
14. `list_projects`
|
|
169
|
-
- List accessible projects with rich filtering options 📊
|
|
170
|
-
- Inputs:
|
|
171
|
-
- Search/filtering:
|
|
172
|
-
- `search`
|
|
173
|
-
- `owned`
|
|
174
|
-
- `membership`
|
|
175
|
-
- `archived`
|
|
176
|
-
- `visibility`
|
|
177
|
-
- Features filtering:
|
|
178
|
-
- `with_issues_enabled`
|
|
179
|
-
- `with_merge_requests_enabled`
|
|
180
|
-
- Sorting:
|
|
181
|
-
- `order_by`
|
|
182
|
-
- `sort`
|
|
183
|
-
- Access control:
|
|
184
|
-
- `min_access_level`
|
|
185
|
-
- Pagination:
|
|
186
|
-
- `page`
|
|
187
|
-
- `per_page`
|
|
188
|
-
- `simple`
|
|
189
|
-
- Returns: Array of projects
|
|
190
|
-
15. `list_labels`
|
|
191
|
-
- List all labels for a project with filtering options 🏷️
|
|
192
|
-
- Inputs:
|
|
193
|
-
- `project_id` (string): Project ID or path
|
|
194
|
-
- `with_counts` (optional): Include issue and merge request counts
|
|
195
|
-
- `include_ancestor_groups` (optional): Include ancestor groups
|
|
196
|
-
- `search` (optional): Filter labels by keyword
|
|
197
|
-
- Returns: Array of labels
|
|
198
|
-
16. `get_label`
|
|
199
|
-
- Get a single label from a project
|
|
200
|
-
- Inputs:
|
|
201
|
-
- `project_id` (string): Project ID or path
|
|
202
|
-
- `label_id` (number/string): Label ID or name
|
|
203
|
-
- `include_ancestor_groups` (optional): Include ancestor groups
|
|
204
|
-
- Returns: label details
|
|
205
|
-
17. `create_label`
|
|
206
|
-
- Create a new label in an object 🏷️➕
|
|
207
|
-
- Inputs:
|
|
208
|
-
- `project_id` (string): Project ID or path
|
|
209
|
-
- `name` (string): Label name
|
|
210
|
-
- `color` (string): Color in hex format (e.g., "#FF0000")
|
|
211
|
-
- `description` (optional): Label description
|
|
212
|
-
- `priority` (optional): Label priority
|
|
213
|
-
- Returns: Created label details
|
|
214
|
-
18. `update_label`
|
|
215
|
-
- Update an existing label in a project 🏷️✏️
|
|
216
|
-
- Inputs:
|
|
217
|
-
- `project_id` (string): Project ID or path
|
|
218
|
-
- `label_id` (number/string): Label ID or name
|
|
219
|
-
- `new_name` (optional): New label name
|
|
220
|
-
- `color` (optional): New color in hex format
|
|
221
|
-
- `description` (optional): New description
|
|
222
|
-
- `priority` (optional): New priority
|
|
223
|
-
- Returns: Updated label details
|
|
224
|
-
19. `delete_label`
|
|
225
|
-
|
|
226
|
-
- Delete a label from a project 🏷️❌
|
|
227
|
-
- Inputs:
|
|
228
|
-
- `project_id` (string): Project ID or path
|
|
229
|
-
- `label_id` (number/string): Label ID or name
|
|
230
|
-
- Returns: Success message
|
|
231
|
-
|
|
232
|
-
20. `list_group_projects`
|
|
233
|
-
|
|
234
|
-
- List all projects in a GitLab group. 📂
|
|
235
|
-
- Inputs:
|
|
236
|
-
- `group_id` (string): Project ID or namespace/project_path
|
|
237
|
-
- Filtering options:
|
|
238
|
-
- `include_subgroups` (optional boolean): Include projects from subgroups
|
|
239
|
-
- `search` (optional string): Search term to filter projects
|
|
240
|
-
- `archived` (optional boolean): Filter for archived projects
|
|
241
|
-
- `visibility` (optional string): Filter by project visibility (public/internal/private)
|
|
242
|
-
- `with_programming_language` (optional string): Filter by programming language
|
|
243
|
-
- `starred` (optional boolean): Filter by starred projects
|
|
244
|
-
- Feature filtering:
|
|
245
|
-
- `with_issues_enabled` (optional boolean): Filter projects with issues feature enabled
|
|
246
|
-
- `with_merge_requests_enabled` (optional boolean): Filter projects with merge requests feature enabled
|
|
247
|
-
- `min_access_level` (optional number): Filter by minimum access level
|
|
248
|
-
- Pagination:
|
|
249
|
-
- `page` (optional number): Page number
|
|
250
|
-
- `per_page` (optional number): Results per page
|
|
251
|
-
- Sorting:
|
|
252
|
-
- `order_by` (optional string): Field to sort by
|
|
253
|
-
- `sort` (optional string): Sort direction (asc/desc)
|
|
254
|
-
- Additional data:
|
|
255
|
-
- `statistics` (optional boolean): Include project statistics
|
|
256
|
-
- `with_custom_attributes` (optional boolean): Include custom attributes
|
|
257
|
-
- `with_security_reports` (optional boolean): Include security reports
|
|
258
|
-
- Returns: List of projects
|
|
259
|
-
|
|
260
|
-
## Environment Variable Configuration
|
|
261
|
-
|
|
262
|
-
Before running the server, you need to set the following environment variables:
|
|
122
|
+
OAuth (confidential app) with secure prompt input:
|
|
263
123
|
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"inputs": [
|
|
127
|
+
{
|
|
128
|
+
"type": "promptString",
|
|
129
|
+
"id": "gitlab_oauth_secret",
|
|
130
|
+
"description": "GitLab OAuth Client Secret",
|
|
131
|
+
"password": true
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
"servers": {
|
|
135
|
+
"gitlab": {
|
|
136
|
+
"type": "stdio",
|
|
137
|
+
"command": "node",
|
|
138
|
+
"args": ["/absolute/path/to/gitlab-mcp/dist/index.js"],
|
|
139
|
+
"env": {
|
|
140
|
+
"GITLAB_USE_OAUTH": "true",
|
|
141
|
+
"GITLAB_OAUTH_CLIENT_ID": "your_oauth_client_id",
|
|
142
|
+
"GITLAB_OAUTH_CLIENT_SECRET": "${input:gitlab_oauth_secret}",
|
|
143
|
+
"GITLAB_OAUTH_REDIRECT_URI": "http://127.0.0.1:8765/callback",
|
|
144
|
+
"GITLAB_API_URL": "https://gitlab.com/api/v4"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
264
149
|
```
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
150
|
+
|
|
151
|
+
GitHub Copilot Chat in VS Code uses the same `.vscode/mcp.json` format.
|
|
152
|
+
|
|
153
|
+
### Claude Desktop / Claude Code / Cursor
|
|
154
|
+
|
|
155
|
+
Claude Desktop reads `claude_desktop_config.json`.
|
|
156
|
+
Claude Code supports project-level `.mcp.json` and `claude mcp add-json`.
|
|
157
|
+
Cursor uses `.cursor/mcp.json`.
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"mcpServers": {
|
|
162
|
+
"gitlab": {
|
|
163
|
+
"command": "node",
|
|
164
|
+
"args": ["/absolute/path/to/gitlab-mcp/dist/index.js"],
|
|
165
|
+
"env": {
|
|
166
|
+
"GITLAB_PERSONAL_ACCESS_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx",
|
|
167
|
+
"GITLAB_API_URL": "https://gitlab.com/api/v4"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### GitLab Duo (`~/.gitlab/duo/mcp.json`)
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"mcpServers": {
|
|
179
|
+
"gitlab": {
|
|
180
|
+
"command": "node",
|
|
181
|
+
"args": ["/absolute/path/to/gitlab-mcp/dist/index.js"],
|
|
182
|
+
"env": {
|
|
183
|
+
"GITLAB_PERSONAL_ACCESS_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx",
|
|
184
|
+
"GITLAB_API_URL": "https://gitlab.com/api/v4"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
"approvedTools": ["gitlab_get_project", "gitlab_list_merge_requests"]
|
|
189
|
+
}
|
|
268
190
|
```
|
|
269
191
|
|
|
270
|
-
|
|
192
|
+
### JetBrains AI Assistant
|
|
193
|
+
|
|
194
|
+
JetBrains can import an existing MCP JSON config or register the server manually.
|
|
195
|
+
Use stdio command `node /absolute/path/to/gitlab-mcp/dist/index.js`, or HTTP endpoint `http://127.0.0.1:3333/mcp` with required headers.
|
|
196
|
+
|
|
197
|
+
### Remote authorization (multi-user HTTP)
|
|
198
|
+
|
|
199
|
+
Start server:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
REMOTE_AUTHORIZATION=true \
|
|
203
|
+
HTTP_HOST=0.0.0.0 \
|
|
204
|
+
HTTP_PORT=3333 \
|
|
205
|
+
node dist/http.js
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Client config:
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"mcpServers": {
|
|
213
|
+
"gitlab": {
|
|
214
|
+
"url": "http://127.0.0.1:3333/mcp",
|
|
215
|
+
"headers": {
|
|
216
|
+
"Authorization": "Bearer glpat-xxxxxxxxxxxxxxxxxxxx"
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Dynamic per-request API URL:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
REMOTE_AUTHORIZATION=true \
|
|
227
|
+
ENABLE_DYNAMIC_API_URL=true \
|
|
228
|
+
HTTP_HOST=0.0.0.0 \
|
|
229
|
+
HTTP_PORT=3333 \
|
|
230
|
+
node dist/http.js
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Add header in client requests:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"headers": {
|
|
238
|
+
"Authorization": "Bearer glpat-xxxxxxxxxxxxxxxxxxxx",
|
|
239
|
+
"X-GitLab-API-URL": "https://gitlab.example.com/api/v4"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Remote auth behavior matrix:
|
|
245
|
+
|
|
246
|
+
| Server Mode | Required Request Headers | Token Fallback Chain |
|
|
247
|
+
| ----------------------------------------------------------- | ------------------------------------------------------------------------------- | -------------------- |
|
|
248
|
+
| `REMOTE_AUTHORIZATION=false` | none | enabled |
|
|
249
|
+
| `REMOTE_AUTHORIZATION=true` | `Authorization: Bearer <token>` or `Private-Token: <token>` | disabled |
|
|
250
|
+
| `REMOTE_AUTHORIZATION=true` + `ENABLE_DYNAMIC_API_URL=true` | `Authorization` or `Private-Token`, and `X-GitLab-API-URL: https://host/api/v4` | disabled |
|
|
251
|
+
|
|
252
|
+
### Docker
|
|
253
|
+
|
|
254
|
+
For containerized deployments, PAT or remote auth is recommended.
|
|
255
|
+
OAuth interactive callback flow is usually less convenient in containers.
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
docker compose up --build -d
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
or:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
docker build -t gitlab-mcp .
|
|
265
|
+
|
|
266
|
+
docker run -d \
|
|
267
|
+
--name gitlab-mcp \
|
|
268
|
+
-p 3333:3333 \
|
|
269
|
+
-e GITLAB_API_URL=https://gitlab.com/api/v4 \
|
|
270
|
+
-e GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx \
|
|
271
|
+
gitlab-mcp
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Compatibility notes
|
|
275
|
+
|
|
276
|
+
- `GITLAB_PROJECT_ID` is not a supported environment variable in this repository.
|
|
277
|
+
- To set an effective default project, use `GITLAB_ALLOWED_PROJECT_IDS` with one project ID, or pass `project_id` in tool arguments.
|
|
278
|
+
- CLI argument overrides such as `--token` or `--api-url` are not implemented.
|
|
279
|
+
- JSON config files do not support comments (`//`).
|
|
280
|
+
|
|
281
|
+
## MCP Server Configuration
|
|
282
|
+
|
|
283
|
+
## HTTP server
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
pnpm install
|
|
287
|
+
cp .env.example .env
|
|
288
|
+
pnpm build
|
|
289
|
+
|
|
290
|
+
# stdio (local MCP)
|
|
291
|
+
pnpm start
|
|
292
|
+
|
|
293
|
+
# streamable HTTP server (http://127.0.0.1:3333/mcp)
|
|
294
|
+
pnpm start:http
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Transport and entrypoint
|
|
298
|
+
|
|
299
|
+
| Transport | Entry Point | Endpoint | Best For |
|
|
300
|
+
| ------------------- | -------------------- | ---------------------------- | ------------------------------------ |
|
|
301
|
+
| **stdio** | `node dist/index.js` | stdin/stdout | Local single-user MCP clients |
|
|
302
|
+
| **Streamable HTTP** | `node dist/http.js` | `POST/GET/DELETE /mcp` | Remote/shared deployments |
|
|
303
|
+
| **SSE (legacy)** | `node dist/http.js` | `GET /sse`, `POST /messages` | Legacy SSE-only clients (`SSE=true`) |
|
|
304
|
+
| **Health** | `node dist/http.js` | `GET /healthz` | Liveness/readiness checks |
|
|
305
|
+
|
|
306
|
+
`SSE=true` is not compatible with `REMOTE_AUTHORIZATION=true`.
|
|
307
|
+
|
|
308
|
+
## Tool Categories
|
|
309
|
+
|
|
310
|
+
Tools are organized into these categories. All GitLab tools use the `gitlab_` prefix, except `health_check`.
|
|
311
|
+
|
|
312
|
+
| Category | Examples | Count |
|
|
313
|
+
| ------------------- | ------------------------------------------------------------------------- | ----- |
|
|
314
|
+
| **Projects** | `get_project`, `list_projects`, `create_repository`, `fork_repository` | 8 |
|
|
315
|
+
| **Repository** | `get_repository_tree`, `get_file_contents`, `push_files`, `create_branch` | 7 |
|
|
316
|
+
| **Merge Requests** | `list_merge_requests`, `create_merge_request`, `merge_merge_request` | 12 |
|
|
317
|
+
| **MR Code Context** | `get_merge_request_code_context` (advanced code review) | 1 |
|
|
318
|
+
| **MR Discussions** | `list_merge_request_discussions`, `create_merge_request_thread` | 7 |
|
|
319
|
+
| **MR Notes** | `list_merge_request_notes`, `create_merge_request_note` | 7 |
|
|
320
|
+
| **Draft Notes** | `list_draft_notes`, `create_draft_note`, `bulk_publish_draft_notes` | 7 |
|
|
321
|
+
| **Issues** | `list_issues`, `create_issue`, `update_issue`, issue links | 13 |
|
|
322
|
+
| **Pipelines** | `list_pipelines`, `get_pipeline_job_output`, `create_pipeline` | 12 |
|
|
323
|
+
| **Commits** | `list_commits`, `get_commit`, `get_commit_diff` | 3 |
|
|
324
|
+
| **Labels** | `list_labels`, `create_label`, `update_label` | 5 |
|
|
325
|
+
| **Milestones** | `list_milestones`, `create_milestone`, burndown events | 10 |
|
|
326
|
+
| **Releases** | `list_releases`, `create_release`, `download_release_asset` | 7 |
|
|
327
|
+
| **Wiki** | `list_wiki_pages`, `create_wiki_page`, `update_wiki_page` | 5 |
|
|
328
|
+
| **Uploads** | `upload_markdown`, `download_attachment` | 2 |
|
|
329
|
+
| **GraphQL** | `execute_graphql_query`, `execute_graphql_mutation` | 3 |
|
|
330
|
+
| **Users & Groups** | `get_users`, `list_namespaces`, `list_events` | 6 |
|
|
331
|
+
| **Health** | `health_check` | 1 |
|
|
332
|
+
|
|
333
|
+
See [docs/tools.md](docs/tools.md) for the complete reference.
|
|
334
|
+
|
|
335
|
+
## Policy & Security
|
|
336
|
+
|
|
337
|
+
The policy engine controls which tools are available at registration time:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
# Read-only mode — disables all mutating tools
|
|
341
|
+
GITLAB_READ_ONLY_MODE=true
|
|
342
|
+
|
|
343
|
+
# Only expose specific tools (supports with or without gitlab_ prefix)
|
|
344
|
+
GITLAB_ALLOWED_TOOLS=get_project,list_merge_requests,get_merge_request
|
|
345
|
+
|
|
346
|
+
# Block tools by regex pattern
|
|
347
|
+
GITLAB_DENIED_TOOLS_REGEX=^gitlab_(delete|create)_
|
|
348
|
+
|
|
349
|
+
# Restrict to specific projects
|
|
350
|
+
GITLAB_ALLOWED_PROJECT_IDS=123,456,789
|
|
351
|
+
|
|
352
|
+
# Keep GraphQL tools enabled in project-scoped mode (disabled by default)
|
|
353
|
+
GITLAB_ALLOW_GRAPHQL_WITH_PROJECT_SCOPE=true
|
|
354
|
+
|
|
355
|
+
# Disable feature groups
|
|
356
|
+
USE_PIPELINE=false
|
|
357
|
+
USE_GITLAB_WIKI=false
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Configuration
|
|
361
|
+
|
|
362
|
+
All configuration is done through environment variables. Key settings:
|
|
363
|
+
|
|
364
|
+
| Area | Variable | Default | Description |
|
|
365
|
+
| --------------- | ----------------------------------------- | --------------------------- | ----------------------------------------------------------------------------------- |
|
|
366
|
+
| GitLab API | `GITLAB_API_URL` | `https://gitlab.com/api/v4` | Base API URL. Supports comma-separated multi-instance URLs. |
|
|
367
|
+
| GitLab API | `GITLAB_PERSONAL_ACCESS_TOKEN` | — | Static default token used when `REMOTE_AUTHORIZATION=false`. |
|
|
368
|
+
| Remote Auth | `REMOTE_AUTHORIZATION` | `false` | Require per-request token headers in HTTP mode (disables fallback token chain). |
|
|
369
|
+
| Remote Auth | `ENABLE_DYNAMIC_API_URL` | `false` | Require `X-GitLab-API-URL` per request. Requires `REMOTE_AUTHORIZATION=true`. |
|
|
370
|
+
| HTTP Server | `HTTP_HOST` | `127.0.0.1` | HTTP bind host (`0.0.0.0` for external access). |
|
|
371
|
+
| HTTP Server | `HTTP_PORT` | `3333` | HTTP server port. |
|
|
372
|
+
| HTTP Server | `HTTP_JSON_ONLY` | `false` | Force JSON-only responses (no streaming framing). |
|
|
373
|
+
| HTTP Server | `SSE` | `false` | Enable legacy SSE endpoints (`/sse`, `/messages`). Not compatible with remote auth. |
|
|
374
|
+
| Sessions | `SESSION_TIMEOUT_SECONDS` | `3600` | Idle session timeout in HTTP mode. |
|
|
375
|
+
| Sessions | `MAX_SESSIONS` | `1000` | Maximum concurrent sessions (`503` when reached). |
|
|
376
|
+
| Sessions | `MAX_REQUESTS_PER_MINUTE` | `300` | Per-session rate limit (`429` when exceeded). |
|
|
377
|
+
| Policy | `GITLAB_READ_ONLY_MODE` | `false` | Disable mutating tools at registration time. |
|
|
378
|
+
| Policy | `GITLAB_ALLOWED_PROJECT_IDS` | — | Restrict access to specific GitLab project IDs. |
|
|
379
|
+
| Policy | `GITLAB_ALLOWED_TOOLS` | — | Tool allowlist (supports names with or without `gitlab_` prefix). |
|
|
380
|
+
| Policy | `GITLAB_DENIED_TOOLS_REGEX` | — | Regex denylist for tool names. |
|
|
381
|
+
| Policy | `GITLAB_ALLOW_GRAPHQL_WITH_PROJECT_SCOPE` | `false` | Keep GraphQL tools enabled when project scope restriction is active. |
|
|
382
|
+
| Auth Extensions | `GITLAB_USE_OAUTH` | `false` | Enable OAuth 2.0 PKCE flow. |
|
|
383
|
+
| Auth Extensions | `GITLAB_TOKEN_SCRIPT` | — | Resolve token from an external script. |
|
|
384
|
+
| Auth Extensions | `GITLAB_TOKEN_FILE` | — | Resolve token from a local file. |
|
|
385
|
+
| Auth Extensions | `GITLAB_AUTH_COOKIE_PATH` | — | Enable cookie-jar based session auth from Netscape cookie file. |
|
|
386
|
+
| Output | `GITLAB_RESPONSE_MODE` | `json` | Response format: `json`, `compact-json`, `yaml`. |
|
|
387
|
+
| Output | `GITLAB_MAX_RESPONSE_BYTES` | `200000` | Max response payload (1KB–2MB), oversized payloads are truncated safely. |
|
|
388
|
+
| Output | `GITLAB_HTTP_TIMEOUT_MS` | `20000` | Upstream GitLab HTTP timeout (1s–120s). |
|
|
389
|
+
| Output | `GITLAB_ERROR_DETAIL_MODE` | `safe/full` | Error verbosity (`safe` by default in production, `full` otherwise). |
|
|
390
|
+
| Network/TLS | `HTTP_PROXY`, `HTTPS_PROXY` | — | Proxy settings for outbound GitLab requests. |
|
|
391
|
+
| Network/TLS | `GITLAB_CA_CERT_PATH` | — | Custom CA certificate path (PEM). |
|
|
392
|
+
| Network/TLS | `GITLAB_CLOUDFLARE_BYPASS` | `false` | Add browser-like headers for Cloudflare-protected instances. |
|
|
393
|
+
| Network/TLS | `GITLAB_USER_AGENT` | — | Custom User-Agent for GitLab requests. |
|
|
394
|
+
|
|
395
|
+
See [docs/configuration.md](docs/configuration.md) for the complete reference.
|
|
396
|
+
|
|
397
|
+
## Authentication Methods
|
|
398
|
+
|
|
399
|
+
Authentication behavior depends on mode:
|
|
400
|
+
|
|
401
|
+
1. **`REMOTE_AUTHORIZATION=true` (HTTP strong mode)**
|
|
402
|
+
Each request must include `Authorization: Bearer <token>` or `Private-Token: <token>`.
|
|
403
|
+
When `ENABLE_DYNAMIC_API_URL=true`, each request must also include `X-GitLab-API-URL`.
|
|
404
|
+
2. **`REMOTE_AUTHORIZATION=false` (default mode)**
|
|
405
|
+
The server resolves credentials in this order:
|
|
406
|
+
`GITLAB_PERSONAL_ACCESS_TOKEN` -> OAuth PKCE (`GITLAB_USE_OAUTH=true`) -> `GITLAB_TOKEN_SCRIPT` -> `GITLAB_TOKEN_FILE`.
|
|
407
|
+
|
|
408
|
+
Cookie-based auth (`GITLAB_AUTH_COOKIE_PATH`) is applied independently via a cookie jar and can work with or without a token.
|
|
409
|
+
|
|
410
|
+
See [docs/authentication.md](docs/authentication.md) for setup guides.
|
|
411
|
+
|
|
412
|
+
## Development
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
pnpm dev # stdio mode with hot-reload
|
|
416
|
+
pnpm dev:http # HTTP mode with hot-reload
|
|
417
|
+
pnpm test # Run tests
|
|
418
|
+
pnpm test:watch # Run tests in watch mode
|
|
419
|
+
pnpm lint # Lint
|
|
420
|
+
pnpm typecheck # Type check
|
|
421
|
+
pnpm inspector # Launch MCP Inspector
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Project Structure
|
|
425
|
+
|
|
426
|
+
See [docs/architecture.md](docs/architecture.md) for detailed design documentation.
|
|
427
|
+
|
|
428
|
+
## Documentation
|
|
429
|
+
|
|
430
|
+
- [Configuration Reference](docs/configuration.md) — All environment variables
|
|
431
|
+
- [Tools Reference](docs/tools.md) — Complete list of MCP tools
|
|
432
|
+
- [Authentication Guide](docs/authentication.md) — Auth methods and setup
|
|
433
|
+
- [Deployment Guide](docs/deployment.md) — Docker, production, and multi-instance
|
|
434
|
+
- [Architecture](docs/architecture.md) — Internal design and patterns
|
|
435
|
+
|
|
436
|
+
## Acknowledgements
|
|
271
437
|
|
|
272
|
-
|
|
273
|
-
- [GitLab MCP](https://github.com/zereight/gitlab-mcp)
|
|
438
|
+
This repository references and learns from parts of the implementation in [zereight/gitlab-mcp](https://github.com/zereight/gitlab-mcp). Thanks to the maintainers and contributors for their work.
|
|
274
439
|
|
|
275
440
|
## License
|
|
276
441
|
|