gitlab-mcp 0.1.5 → 1.1.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.
Files changed (57) hide show
  1. package/.dockerignore +7 -0
  2. package/.editorconfig +9 -0
  3. package/.env.example +75 -0
  4. package/.github/workflows/nodejs.yml +31 -0
  5. package/.github/workflows/npm-publish.yml +31 -0
  6. package/.husky/pre-commit +1 -0
  7. package/.nvmrc +1 -0
  8. package/.prettierrc.json +6 -0
  9. package/Dockerfile +20 -0
  10. package/README.md +416 -251
  11. package/docker-compose.yml +10 -0
  12. package/docs/architecture.md +310 -0
  13. package/docs/authentication.md +299 -0
  14. package/docs/configuration.md +149 -0
  15. package/docs/deployment.md +336 -0
  16. package/docs/tools.md +294 -0
  17. package/eslint.config.js +23 -0
  18. package/package.json +78 -32
  19. package/scripts/get-oauth-token.example.sh +15 -0
  20. package/src/config/env.ts +171 -0
  21. package/src/http.ts +620 -0
  22. package/src/index.ts +77 -0
  23. package/src/lib/auth-context.ts +19 -0
  24. package/src/lib/gitlab-client.ts +1810 -0
  25. package/src/lib/logger.ts +17 -0
  26. package/src/lib/network.ts +45 -0
  27. package/src/lib/oauth.ts +287 -0
  28. package/src/lib/output.ts +51 -0
  29. package/src/lib/policy.ts +78 -0
  30. package/src/lib/request-runtime.ts +376 -0
  31. package/src/lib/sanitize.ts +25 -0
  32. package/src/lib/session-capacity.ts +14 -0
  33. package/src/server/build-server.ts +17 -0
  34. package/src/tools/gitlab.ts +3135 -0
  35. package/src/tools/health.ts +27 -0
  36. package/src/tools/mr-code-context.ts +473 -0
  37. package/src/types/context.ts +13 -0
  38. package/tests/auth-context.test.ts +102 -0
  39. package/tests/gitlab-client.test.ts +672 -0
  40. package/tests/graphql-guard.test.ts +121 -0
  41. package/tests/integration/agent-loop.integration.test.ts +558 -0
  42. package/tests/integration/server.integration.test.ts +543 -0
  43. package/tests/mr-code-context.test.ts +600 -0
  44. package/tests/oauth.test.ts +43 -0
  45. package/tests/output.test.ts +186 -0
  46. package/tests/policy.test.ts +324 -0
  47. package/tests/request-runtime.test.ts +252 -0
  48. package/tests/sanitize.test.ts +123 -0
  49. package/tests/session-capacity.test.ts +49 -0
  50. package/tests/upload-reference.test.ts +88 -0
  51. package/tsconfig.build.json +11 -0
  52. package/tsconfig.json +21 -0
  53. package/vitest.config.ts +12 -0
  54. package/LICENSE +0 -21
  55. package/build/index.js +0 -1642
  56. package/build/schemas.js +0 -684
  57. package/build/test-note.js +0 -54
@@ -0,0 +1,10 @@
1
+ services:
2
+ gitlab-mcp:
3
+ build:
4
+ context: .
5
+ dockerfile: Dockerfile
6
+ env_file:
7
+ - .env
8
+ ports:
9
+ - "3333:3333"
10
+ restart: unless-stopped
@@ -0,0 +1,310 @@
1
+ # Architecture
2
+
3
+ This document describes the internal design of gitlab-mcp, its module relationships, and key design decisions.
4
+
5
+ ## Overview
6
+
7
+ gitlab-mcp is structured as a layered MCP server with clear separation between transport, tool registration, API client, and cross-cutting concerns (policy, auth, output formatting).
8
+
9
+ ```
10
+ ┌──────────────────────────────────────────────────────┐
11
+ │ Transport Layer │
12
+ │ index.ts (stdio) │ http.ts (HTTP/SSE) │
13
+ └─────────┬──────────┴──────────┬──────────────────────┘
14
+ │ │
15
+ │ ┌────────────────▼────────────────────┐
16
+ │ │ Session Management (HTTP only) │
17
+ │ │ - Serial request queuing │
18
+ │ │ - Rate limiting per session │
19
+ │ │ - TTL-based garbage collection │
20
+ │ │ - AsyncLocalStorage auth context │
21
+ │ └────────────────┬────────────────────┘
22
+ │ │
23
+ ┌─────────▼─────────────────────▼──────────────────────┐
24
+ │ MCP Server Factory │
25
+ │ build-server.ts │
26
+ │ ┌──────────────────────────────────────────────┐ │
27
+ │ │ registerHealthTool() │ │
28
+ │ │ registerGitLabTools() ──▶ Policy filtering │ │
29
+ │ └──────────────────────────────────────────────┘ │
30
+ └───────────────────────┬──────────────────────────────┘
31
+
32
+ ┌───────────────────────▼──────────────────────────────┐
33
+ │ AppContext │
34
+ │ ┌──────────┐ ┌───────────┐ ┌──────────────────┐ │
35
+ │ │ env │ │ logger │ │ gitlab (Client) │ │
36
+ │ │ (AppEnv) │ │ (Pino) │ │ │ │
37
+ │ └──────────┘ └───────────┘ └──────────────────┘ │
38
+ │ ┌──────────────────┐ ┌─────────────────────────┐ │
39
+ │ │ policy (Engine) │ │ formatter (Output) │ │
40
+ │ └──────────────────┘ └─────────────────────────┘ │
41
+ └──────────────────────────────────────────────────────┘
42
+ ```
43
+
44
+ ## Module Dependency Graph
45
+
46
+ ```
47
+ config/env.ts
48
+
49
+ │ (imported by all entry points)
50
+
51
+ ├── index.ts (stdio)
52
+ │ └── lib/gitlab-client.ts
53
+ │ └── lib/policy.ts
54
+ │ └── lib/output.ts
55
+ │ └── lib/network.ts
56
+ │ └── lib/request-runtime.ts
57
+ │ └── lib/oauth.ts
58
+ │ └── server/build-server.ts
59
+ │ └── tools/gitlab.ts
60
+ │ │ └── tools/mr-code-context.ts
61
+ │ └── tools/health.ts
62
+
63
+ └── http.ts (HTTP)
64
+ └── (same dependencies as index.ts)
65
+ └── lib/auth-context.ts (AsyncLocalStorage)
66
+ ```
67
+
68
+ ## Key Modules
69
+
70
+ ### `config/env.ts` — Configuration
71
+
72
+ - Parses and validates all environment variables using Zod schemas
73
+ - Enforces cross-field constraints (e.g. OAuth requires client ID, dynamic API URL requires remote auth)
74
+ - Normalizes API URLs to `/api/v4` suffix
75
+ - Exports a typed `env` singleton and `AppEnv` type
76
+ - Fails fast at startup with descriptive error messages
77
+
78
+ ### `server/build-server.ts` — Server Factory
79
+
80
+ - Creates an `McpServer` instance with configured name and version
81
+ - Registers the health check tool
82
+ - Registers all GitLab tools after policy filtering
83
+ - Pure factory function — no side effects
84
+
85
+ ### `tools/gitlab.ts` — Tool Definitions
86
+
87
+ - Defines 80+ tools as a `GitLabToolDefinition[]` array
88
+ - Each definition specifies: `name`, `title`, `description`, `mutating`, optional `requiresFeature`, `inputSchema` (Zod), and `handler`
89
+ - Tools are filtered by the policy engine at registration time
90
+ - Tool execution wraps results through the output formatter
91
+ - Error handling converts `GitLabApiError` to structured MCP error responses
92
+
93
+ **Tool execution flow:**
94
+
95
+ ```
96
+ Raw args ──▶ stripNullsDeep ──▶ handler(args, context) ──▶ formatter.format() ──▶ MCP response
97
+
98
+ └── assertAuthReady() (check token exists)
99
+ └── resolveProjectId() (apply project allowlist)
100
+ ```
101
+
102
+ ### `tools/mr-code-context.ts` — MR Code Context
103
+
104
+ A specialized tool for AI-assisted code review:
105
+
106
+ 1. Fetches MR diff files
107
+ 2. Filters by glob patterns, extensions, or languages
108
+ 3. Sorts by changed lines, path, or file size
109
+ 4. Retrieves file content within a character budget
110
+ 5. Supports three content modes:
111
+ - **patch** — Raw unified diff
112
+ - **surrounding** — Changed lines with N lines of context from the full file
113
+ - **fullfile** — Complete file content
114
+ 6. Supports `list_only` mode for two-stage retrieval (list first, then fetch selectively)
115
+
116
+ ### `lib/gitlab-client.ts` — GitLab API Client
117
+
118
+ - Wraps the GitLab REST API v4 with typed methods
119
+ - Supports multi-instance URL rotation (round-robin)
120
+ - Pre-request hook system (`beforeRequest`) for token/header injection
121
+ - Per-session auth via `AsyncLocalStorage` (checked before each request)
122
+ - Configurable timeout with `AbortSignal`
123
+ - Error wrapping via `GitLabApiError` with status code and details
124
+
125
+ **Request lifecycle:**
126
+
127
+ ```
128
+ Method call ──▶ Build URL ──▶ Set headers ──▶ beforeRequest hook
129
+ │ │
130
+ │ ┌───────────────────┘
131
+ │ ▼
132
+ │ Apply session auth (if available)
133
+ │ Apply token header
134
+ │ Apply compatibility headers
135
+ │ │
136
+ └──────────────────────────▼
137
+ fetch() with timeout
138
+
139
+ Parse response / throw GitLabApiError
140
+ ```
141
+
142
+ ### `lib/policy.ts` — Tool Policy Engine
143
+
144
+ Controls which tools are available:
145
+
146
+ 1. **Read-only mode** — Blocks all tools marked `mutating: true`
147
+ 2. **Feature toggles** — Blocks tools requiring disabled features (wiki, milestone, pipeline, release)
148
+ 3. **Allowlist** — If set, only listed tools are available. Tool names are normalized (accepts `get_project` or `gitlab_get_project`)
149
+ 4. **Deny regex** — Blocks tools matching a regex pattern
150
+
151
+ Policy is applied in two places:
152
+
153
+ - **Registration time** — `filterTools()` removes tools from the MCP server entirely
154
+ - **Execution time** — `assertCanExecute()` double-checks (defense in depth)
155
+
156
+ ### `lib/auth-context.ts` — Session Auth Context
157
+
158
+ Uses Node.js `AsyncLocalStorage` to provide per-request authentication context:
159
+
160
+ ```typescript
161
+ interface SessionAuth {
162
+ sessionId?: string;
163
+ token?: string;
164
+ apiUrl?: string;
165
+ header?: "authorization" | "private-token";
166
+ updatedAt: number;
167
+ }
168
+ ```
169
+
170
+ - In HTTP mode, each request runs within `runWithSessionAuth()` which sets the context
171
+ - The GitLab client reads `getSessionAuth()` to get per-request credentials
172
+ - In stdio mode, session auth is not used (static PAT is the primary method)
173
+
174
+ ### `lib/request-runtime.ts` — Request Preprocessing
175
+
176
+ Orchestrates authentication and request modifications:
177
+
178
+ 1. **Cookie management** — Loads Netscape cookie files, auto-reloads on changes, creates `fetch-cookie` wrapper
179
+ 2. **Session warmup** — Sends a warmup request to establish cookie sessions
180
+ 3. **Token resolution** — When no request/session/PAT token is present, tries OAuth, then token script, then token file
181
+ 4. **Compatibility headers** — Applies User-Agent, Accept-Language for Cloudflare bypass
182
+ 5. **Token caching** — Caches resolved tokens with configurable TTL
183
+
184
+ ### `lib/oauth.ts` — OAuth PKCE Manager
185
+
186
+ Implements the full OAuth 2.0 PKCE flow:
187
+
188
+ 1. Check stored token → use if not expired
189
+ 2. Try refresh token → persist new token
190
+ 3. Fall back to interactive flow:
191
+ - Generate PKCE challenge
192
+ - Build authorization URL
193
+ - Start local HTTP callback server
194
+ - Open browser (optional)
195
+ - Wait for callback (3 minute timeout)
196
+ - Exchange code for token
197
+ - Persist token (chmod 600)
198
+
199
+ ### `lib/network.ts` — Network Runtime
200
+
201
+ Configures global fetch behavior using `undici`:
202
+
203
+ - Sets up proxy agent (`ProxyAgent`) if `HTTP_PROXY`/`HTTPS_PROXY` is set
204
+ - Loads custom CA certificates from `GITLAB_CA_CERT_PATH`
205
+ - Controls TLS verification via `rejectUnauthorized`
206
+ - Applied globally via `setGlobalDispatcher()`
207
+
208
+ ### `lib/output.ts` — Response Formatting
209
+
210
+ - Serializes tool output to JSON (pretty), compact JSON, or YAML
211
+ - Enforces `GITLAB_MAX_RESPONSE_BYTES` limit
212
+ - Truncates oversized responses with a `[truncated N bytes]` marker
213
+ - Returns metadata: `truncated` flag and `bytes` count
214
+
215
+ ### `lib/sanitize.ts` — Null Stripping
216
+
217
+ `stripNullsDeep()` recursively removes `null` values from objects and arrays before passing them to the GitLab API. This prevents sending `null` in JSON payloads where `undefined` (omission) is the correct behavior.
218
+
219
+ ## HTTP Server Session Management
220
+
221
+ The HTTP server (`http.ts`) implements a sophisticated session management system:
222
+
223
+ ### Streamable HTTP Sessions
224
+
225
+ ```
226
+ Client POST /mcp (no session-id)
227
+ └── Create new session
228
+ ├── Create McpServer instance
229
+ ├── Create StreamableHTTPServerTransport
230
+ ├── Add to pending sessions
231
+ ├── Connect server to transport
232
+ ├── On session init → move to active sessions
233
+ └── Return Mcp-Session-Id header
234
+
235
+ Client POST /mcp (with Mcp-Session-Id)
236
+ └── Look up existing session
237
+ ├── Refresh auth from request headers
238
+ ├── Check rate limit
239
+ ├── Enqueue request (serial per session)
240
+ └── Process within session auth context
241
+ ```
242
+
243
+ ### Session Lifecycle
244
+
245
+ 1. **Creation** — New session created on first POST without session ID
246
+ 2. **Active** — Session receives requests, each queued serially
247
+ 3. **Idle timeout** — Garbage collected after `SESSION_TIMEOUT_SECONDS` of inactivity
248
+ 4. **Rate limited** — Returns 429 after `MAX_REQUESTS_PER_MINUTE` per session
249
+ 5. **Capacity** — Returns 503 when `MAX_SESSIONS` is reached
250
+ 6. **Shutdown** — All sessions closed gracefully on SIGINT/SIGTERM
251
+
252
+ ### SSE Sessions (Legacy)
253
+
254
+ When `SSE=true`:
255
+
256
+ - Clients connect via `GET /sse` to establish an SSE stream
257
+ - Messages are sent via `POST /messages?sessionId=...`
258
+ - Sessions are cleaned up on client disconnect or idle timeout
259
+
260
+ ## Design Patterns
261
+
262
+ ### Dependency Injection via AppContext
263
+
264
+ All shared services are bundled into an `AppContext` interface:
265
+
266
+ ```typescript
267
+ interface AppContext {
268
+ env: AppEnv;
269
+ logger: Logger;
270
+ gitlab: GitLabClient;
271
+ policy: ToolPolicyEngine;
272
+ formatter: OutputFormatter;
273
+ }
274
+ ```
275
+
276
+ This is created once at startup and passed to tool registration functions. Tools access all services through this context.
277
+
278
+ ### Null Preprocessing in Zod Schemas
279
+
280
+ Many MCP clients send `null` for optional parameters. The tool schemas use `z.preprocess()` to convert `null` to `undefined`:
281
+
282
+ ```typescript
283
+ const optionalString = z.preprocess(
284
+ (value) => (value === null ? undefined : value),
285
+ z.string().optional()
286
+ );
287
+ ```
288
+
289
+ ### Backward Compatibility Aliases
290
+
291
+ Several tools have backward-compatible aliases to support existing integrations:
292
+
293
+ - `gitlab_mr_discussions` → alias of `gitlab_list_merge_request_discussions`
294
+ - `gitlab_get_merge_request_notes` → alias of `gitlab_list_merge_request_notes`
295
+ - `gitlab_edit_milestone` → alias of `gitlab_update_milestone`
296
+ - `gitlab_execute_graphql` → backward-compatible executor honoring read-only policy
297
+
298
+ ### Structured Content in Responses
299
+
300
+ Tool responses include both text content (for display) and structured content (for programmatic access):
301
+
302
+ ```typescript
303
+ return {
304
+ content: [{ type: "text", text: formatted.text }],
305
+ structuredContent: {
306
+ result: toStructuredContent(result),
307
+ meta: { truncated: formatted.truncated, bytes: formatted.bytes }
308
+ }
309
+ };
310
+ ```
@@ -0,0 +1,299 @@
1
+ # Authentication Guide
2
+
3
+ gitlab-mcp supports multiple authentication methods. Token behavior depends on whether remote authorization is enabled.
4
+
5
+ ## Token Resolution
6
+
7
+ ### Default Mode (`REMOTE_AUTHORIZATION=false`)
8
+
9
+ ```
10
+ Static PAT (GITLAB_PERSONAL_ACCESS_TOKEN)
11
+ └─> OAuth 2.0 PKCE
12
+ └─> External token script
13
+ └─> Token file
14
+ ```
15
+
16
+ ### Remote Authorization Mode (`REMOTE_AUTHORIZATION=true`, HTTP)
17
+
18
+ ```
19
+ Per-request auth only (required)
20
+ ```
21
+
22
+ In remote authorization mode, each request must include `Authorization: Bearer <token>` or
23
+ `Private-Token: <token>`. If `ENABLE_DYNAMIC_API_URL=true`, each request must also include
24
+ `X-GitLab-API-URL`.
25
+
26
+ Cookie-based auth (`GITLAB_AUTH_COOKIE_PATH`) is applied independently through a cookie jar and is not part of the token chain.
27
+
28
+ ---
29
+
30
+ ## 1. Personal Access Token (PAT)
31
+
32
+ The simplest method. Create a token at **GitLab > Settings > Access Tokens** with the `api` scope.
33
+
34
+ ```bash
35
+ GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx
36
+ ```
37
+
38
+ This token is the default request token in stdio and HTTP modes when `REMOTE_AUTHORIZATION=false`.
39
+ When `REMOTE_AUTHORIZATION=true`, request headers are required and PAT fallback is disabled.
40
+
41
+ ---
42
+
43
+ ## 2. OAuth 2.0 PKCE
44
+
45
+ Browser-based OAuth flow for interactive use. The server launches a local callback server and opens the browser for authorization.
46
+
47
+ ### Setup
48
+
49
+ 1. Register an OAuth application in GitLab (**Settings > Applications** or admin area):
50
+ - **Redirect URI:** `http://127.0.0.1:8765/callback`
51
+ - **Scopes:** `api`
52
+ - **Confidential:** No (for PKCE public clients)
53
+ - Note the **Application ID**
54
+
55
+ 2. Configure environment variables:
56
+
57
+ ```bash
58
+ GITLAB_USE_OAUTH=true
59
+ GITLAB_OAUTH_CLIENT_ID=your-application-id
60
+ GITLAB_OAUTH_REDIRECT_URI=http://127.0.0.1:8765/callback
61
+ GITLAB_OAUTH_SCOPES=api
62
+ ```
63
+
64
+ ### Optional Settings
65
+
66
+ ```bash
67
+ # For confidential applications
68
+ GITLAB_OAUTH_CLIENT_SECRET=your-client-secret
69
+
70
+ # Custom GitLab URL (derived from GITLAB_API_URL if not set)
71
+ GITLAB_OAUTH_GITLAB_URL=https://gitlab.example.com
72
+
73
+ # Token storage location (default: ~/.gitlab-mcp-oauth-token.json)
74
+ GITLAB_OAUTH_TOKEN_PATH=~/.gitlab-mcp-oauth-token.json
75
+
76
+ # Disable auto-opening the browser
77
+ GITLAB_OAUTH_AUTO_OPEN_BROWSER=false
78
+ ```
79
+
80
+ ### How It Works
81
+
82
+ 1. On first request, the server checks for a stored token at `GITLAB_OAUTH_TOKEN_PATH`
83
+ 2. If no valid token exists, it generates a PKCE challenge and opens the browser
84
+ 3. The user authorizes the application in GitLab
85
+ 4. GitLab redirects back to the local callback server with an authorization code
86
+ 5. The server exchanges the code for an access token (with PKCE verifier)
87
+ 6. The token is persisted to disk (chmod 600) for future sessions
88
+ 7. On subsequent requests, the stored token is reused until it expires
89
+ 8. Expired tokens are automatically refreshed using the refresh token
90
+
91
+ ### Notes
92
+
93
+ - The callback server listens for up to 3 minutes before timing out
94
+ - Token files are stored with `0600` permissions
95
+ - If refresh fails, the server falls back to interactive authorization
96
+
97
+ ---
98
+
99
+ ## 3. External Token Script
100
+
101
+ Execute a shell command to obtain a token dynamically. Useful for integration with secret managers, vault systems, or custom token providers.
102
+
103
+ ```bash
104
+ GITLAB_TOKEN_SCRIPT=/path/to/get-token.sh
105
+ GITLAB_TOKEN_SCRIPT_TIMEOUT_MS=10000 # 500ms–120s (default: 10s)
106
+ GITLAB_TOKEN_CACHE_SECONDS=300 # 0–86400s (default: 5min)
107
+ ```
108
+
109
+ ### Script Output Format
110
+
111
+ The script must output one of:
112
+
113
+ 1. **Raw token string** (plain text on stdout):
114
+
115
+ ```
116
+ glpat-xxxxxxxxxxxxxxxxxxxx
117
+ ```
118
+
119
+ 2. **JSON object** with any of these keys:
120
+ ```json
121
+ { "access_token": "glpat-xxxxxxxxxxxxxxxxxxxx" }
122
+ ```
123
+ ```json
124
+ { "token": "glpat-xxxxxxxxxxxxxxxxxxxx" }
125
+ ```
126
+ ```json
127
+ { "private_token": "glpat-xxxxxxxxxxxxxxxxxxxx" }
128
+ ```
129
+
130
+ ### Example Script
131
+
132
+ ```bash
133
+ #!/usr/bin/env bash
134
+ set -euo pipefail
135
+
136
+ # Example: read from environment or secret manager
137
+ if [[ -n "${GITLAB_OAUTH_ACCESS_TOKEN:-}" ]]; then
138
+ printf '{"access_token":"%s"}\n' "${GITLAB_OAUTH_ACCESS_TOKEN}"
139
+ exit 0
140
+ fi
141
+
142
+ echo "Token not available" >&2
143
+ exit 1
144
+ ```
145
+
146
+ The resolved token is cached for `GITLAB_TOKEN_CACHE_SECONDS` to avoid repeated script executions.
147
+
148
+ ---
149
+
150
+ ## 4. Token File
151
+
152
+ Read a token from a file on disk. The file should contain a raw token string or JSON (same format as the token script output).
153
+
154
+ ```bash
155
+ GITLAB_TOKEN_FILE=~/.gitlab-token
156
+ ```
157
+
158
+ ### Security
159
+
160
+ By default, the server enforces strict file permissions — the token file must be readable only by the owner (`chmod 600`). If the file has group or other permissions, the server rejects it.
161
+
162
+ To override this check:
163
+
164
+ ```bash
165
+ GITLAB_ALLOW_INSECURE_TOKEN_FILE=true
166
+ ```
167
+
168
+ The token is cached for `GITLAB_TOKEN_CACHE_SECONDS` (default: 300s).
169
+
170
+ ---
171
+
172
+ ## 5. Cookie-Based Auth
173
+
174
+ Use browser cookies from a Netscape-format cookie file. This is useful when working with GitLab instances that use SSO or other browser-based authentication.
175
+
176
+ ```bash
177
+ GITLAB_AUTH_COOKIE_PATH=~/.gitlab-cookies.txt
178
+ ```
179
+
180
+ ### How It Works
181
+
182
+ 1. The server reads cookies from the file in Netscape cookie format
183
+ 2. A cookie jar is created and attached to all API requests via `fetch-cookie`
184
+ 3. Before the first API call to each GitLab instance, a warmup request is sent to establish the session
185
+ 4. If the cookie file changes on disk, it is automatically reloaded
186
+
187
+ ### Warmup Path
188
+
189
+ The warmup request hits a lightweight endpoint to establish the session:
190
+
191
+ ```bash
192
+ GITLAB_COOKIE_WARMUP_PATH=/user # default
193
+ ```
194
+
195
+ ### Cookie File Format
196
+
197
+ Standard Netscape cookie format (tab-separated):
198
+
199
+ ```
200
+ # Netscape HTTP Cookie File
201
+ .gitlab.example.com TRUE / TRUE 0 _gitlab_session abc123...
202
+ ```
203
+
204
+ Lines starting with `#HttpOnly_` are parsed as HttpOnly cookies.
205
+
206
+ ---
207
+
208
+ ## 6. Remote Authorization (HTTP Mode)
209
+
210
+ In HTTP transport mode, this enables strict per-request credentials. This is the recommended approach for shared/multi-user deployments.
211
+
212
+ ```bash
213
+ REMOTE_AUTHORIZATION=true
214
+ ```
215
+
216
+ `REMOTE_AUTHORIZATION=true` enforces per-request credentials. If a request does not include a token header, the request is rejected.
217
+
218
+ ### Client Headers
219
+
220
+ The server accepts tokens via:
221
+
222
+ - **`Authorization: Bearer <token>`** — Standard bearer token
223
+ - **`Private-Token: <token>`** — GitLab private token header
224
+
225
+ ### Dynamic API URL
226
+
227
+ When serving multiple GitLab instances, enable dynamic API URL per request:
228
+
229
+ ```bash
230
+ REMOTE_AUTHORIZATION=true
231
+ ENABLE_DYNAMIC_API_URL=true
232
+ ```
233
+
234
+ Clients can then send:
235
+
236
+ ```
237
+ X-GitLab-API-URL: https://other-gitlab.example.com/api/v4
238
+ ```
239
+
240
+ ### Authentication Flow (HTTP Mode)
241
+
242
+ 1. Client sends a request with auth headers
243
+ 2. The server extracts the token and required API URL (when dynamic API URLs are enabled) from headers
244
+ 3. Auth context is stored in `AsyncLocalStorage` for the duration of the request
245
+ 4. All GitLab API calls within that request use the per-session credentials
246
+ 5. Requests missing required headers are rejected before tool execution
247
+
248
+ ---
249
+
250
+ ## Cloudflare Bypass
251
+
252
+ If your GitLab instance is behind Cloudflare, enable browser-like headers:
253
+
254
+ ```bash
255
+ GITLAB_CLOUDFLARE_BYPASS=true
256
+ ```
257
+
258
+ This adds:
259
+
260
+ - A Chrome-like `User-Agent` header
261
+ - `Accept-Language: en-US,en;q=0.9`
262
+ - `Cache-Control: no-cache`
263
+ - `Pragma: no-cache`
264
+
265
+ You can also set a custom User-Agent:
266
+
267
+ ```bash
268
+ GITLAB_USER_AGENT="MyApp/1.0"
269
+ ```
270
+
271
+ ---
272
+
273
+ ## TLS & Proxy Configuration
274
+
275
+ ### Custom CA Certificate
276
+
277
+ For self-signed or internal CA certificates:
278
+
279
+ ```bash
280
+ GITLAB_CA_CERT_PATH=/path/to/ca-bundle.pem
281
+ ```
282
+
283
+ ### HTTP Proxy
284
+
285
+ ```bash
286
+ HTTP_PROXY=http://proxy.example.com:8080
287
+ HTTPS_PROXY=http://proxy.example.com:8080
288
+ ```
289
+
290
+ ### Insecure TLS (Not Recommended)
291
+
292
+ To disable TLS certificate verification, you must explicitly acknowledge the risk:
293
+
294
+ ```bash
295
+ NODE_TLS_REJECT_UNAUTHORIZED=0
296
+ GITLAB_ALLOW_INSECURE_TLS=true
297
+ ```
298
+
299
+ Both settings are required — setting only `NODE_TLS_REJECT_UNAUTHORIZED=0` without the acknowledgment flag will cause a startup error.