gitlab-mcp 1.1.0 → 1.2.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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/dist/config/env.d.ts +56 -0
  3. package/dist/config/env.js +163 -0
  4. package/dist/config/env.js.map +1 -0
  5. package/dist/http-app.d.ts +45 -0
  6. package/dist/http-app.js +550 -0
  7. package/dist/http-app.js.map +1 -0
  8. package/dist/http.d.ts +2 -0
  9. package/dist/http.js +65 -0
  10. package/dist/http.js.map +1 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +65 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/lib/auth-context.d.ts +9 -0
  15. package/dist/lib/auth-context.js +9 -0
  16. package/dist/lib/auth-context.js.map +1 -0
  17. package/dist/lib/gitlab-client.d.ts +331 -0
  18. package/dist/lib/gitlab-client.js +1025 -0
  19. package/dist/lib/gitlab-client.js.map +1 -0
  20. package/dist/lib/logger.d.ts +2 -0
  21. package/dist/lib/logger.js +13 -0
  22. package/dist/lib/logger.js.map +1 -0
  23. package/dist/lib/network.d.ts +3 -0
  24. package/dist/lib/network.js +38 -0
  25. package/dist/lib/network.js.map +1 -0
  26. package/dist/lib/oauth.d.ts +29 -0
  27. package/dist/lib/oauth.js +220 -0
  28. package/dist/lib/oauth.js.map +1 -0
  29. package/dist/lib/output.d.ts +14 -0
  30. package/dist/lib/output.js +38 -0
  31. package/dist/lib/output.js.map +1 -0
  32. package/dist/lib/policy.d.ts +25 -0
  33. package/dist/lib/policy.js +48 -0
  34. package/dist/lib/policy.js.map +1 -0
  35. package/dist/lib/request-runtime.d.ts +26 -0
  36. package/dist/lib/request-runtime.js +323 -0
  37. package/dist/lib/request-runtime.js.map +1 -0
  38. package/dist/lib/sanitize.d.ts +1 -0
  39. package/dist/lib/sanitize.js +21 -0
  40. package/dist/lib/sanitize.js.map +1 -0
  41. package/dist/lib/session-capacity.d.ts +8 -0
  42. package/dist/lib/session-capacity.js +7 -0
  43. package/dist/lib/session-capacity.js.map +1 -0
  44. package/dist/server/build-server.d.ts +3 -0
  45. package/dist/server/build-server.js +13 -0
  46. package/dist/server/build-server.js.map +1 -0
  47. package/dist/tools/gitlab.d.ts +9 -0
  48. package/dist/tools/gitlab.js +2576 -0
  49. package/dist/tools/gitlab.js.map +1 -0
  50. package/dist/tools/health.d.ts +2 -0
  51. package/dist/tools/health.js +21 -0
  52. package/dist/tools/health.js.map +1 -0
  53. package/dist/tools/mr-code-context.d.ts +38 -0
  54. package/dist/tools/mr-code-context.js +330 -0
  55. package/dist/tools/mr-code-context.js.map +1 -0
  56. package/{src/types/context.ts → dist/types/context.d.ts} +5 -6
  57. package/dist/types/context.js +2 -0
  58. package/dist/types/context.js.map +1 -0
  59. package/docs/configuration.md +6 -6
  60. package/docs/mcp-integration-testing-best-practices.md +981 -0
  61. package/package.json +13 -1
  62. package/.dockerignore +0 -7
  63. package/.editorconfig +0 -9
  64. package/.env.example +0 -75
  65. package/.github/workflows/nodejs.yml +0 -31
  66. package/.github/workflows/npm-publish.yml +0 -31
  67. package/.husky/pre-commit +0 -1
  68. package/.nvmrc +0 -1
  69. package/.prettierrc.json +0 -6
  70. package/Dockerfile +0 -20
  71. package/docker-compose.yml +0 -10
  72. package/eslint.config.js +0 -23
  73. package/scripts/get-oauth-token.example.sh +0 -15
  74. package/src/config/env.ts +0 -171
  75. package/src/http.ts +0 -620
  76. package/src/index.ts +0 -77
  77. package/src/lib/auth-context.ts +0 -19
  78. package/src/lib/gitlab-client.ts +0 -1810
  79. package/src/lib/logger.ts +0 -17
  80. package/src/lib/network.ts +0 -45
  81. package/src/lib/oauth.ts +0 -287
  82. package/src/lib/output.ts +0 -51
  83. package/src/lib/policy.ts +0 -78
  84. package/src/lib/request-runtime.ts +0 -376
  85. package/src/lib/sanitize.ts +0 -25
  86. package/src/lib/session-capacity.ts +0 -14
  87. package/src/server/build-server.ts +0 -17
  88. package/src/tools/gitlab.ts +0 -3135
  89. package/src/tools/health.ts +0 -27
  90. package/src/tools/mr-code-context.ts +0 -473
  91. package/tests/auth-context.test.ts +0 -102
  92. package/tests/gitlab-client.test.ts +0 -672
  93. package/tests/graphql-guard.test.ts +0 -121
  94. package/tests/integration/agent-loop.integration.test.ts +0 -558
  95. package/tests/integration/server.integration.test.ts +0 -543
  96. package/tests/mr-code-context.test.ts +0 -600
  97. package/tests/oauth.test.ts +0 -43
  98. package/tests/output.test.ts +0 -186
  99. package/tests/policy.test.ts +0 -324
  100. package/tests/request-runtime.test.ts +0 -252
  101. package/tests/sanitize.test.ts +0 -123
  102. package/tests/session-capacity.test.ts +0 -49
  103. package/tests/upload-reference.test.ts +0 -88
  104. package/tsconfig.build.json +0 -11
  105. package/tsconfig.json +0 -21
  106. package/vitest.config.ts +0 -12
package/package.json CHANGED
@@ -1,8 +1,18 @@
1
1
  {
2
2
  "name": "gitlab-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A MCP server for GitLab",
5
5
  "type": "module",
6
+ "bin": {
7
+ "gitlab-mcp": "dist/index.js",
8
+ "gitlab-mcp-http": "dist/http.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "LICENSE",
13
+ "README.md",
14
+ "docs"
15
+ ],
6
16
  "packageManager": "pnpm@10.28.1",
7
17
  "engines": {
8
18
  "node": ">=20.11.0"
@@ -17,6 +27,7 @@
17
27
  },
18
28
  "scripts": {
19
29
  "build": "tsc -p tsconfig.build.json",
30
+ "prepack": "npm run build",
20
31
  "typecheck": "tsc --noEmit",
21
32
  "dev": "tsx watch src/index.ts",
22
33
  "dev:http": "tsx watch src/http.ts",
@@ -38,6 +49,7 @@
38
49
  "dotenv": "^17.3.1",
39
50
  "express": "^5.2.1",
40
51
  "fetch-cookie": "^3.2.0",
52
+ "graphql": "^16.12.0",
41
53
  "open": "^11.0.0",
42
54
  "picomatch": "^4.0.3",
43
55
  "pino": "^10.3.1",
package/.dockerignore DELETED
@@ -1,7 +0,0 @@
1
- node_modules
2
- dist
3
- coverage
4
- .git
5
- .gitignore
6
- .vscode
7
- .DS_Store
package/.editorconfig DELETED
@@ -1,9 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- charset = utf-8
5
- indent_style = space
6
- indent_size = 2
7
- end_of_line = lf
8
- insert_final_newline = true
9
- trim_trailing_whitespace = true
package/.env.example DELETED
@@ -1,75 +0,0 @@
1
- NODE_ENV=development
2
- LOG_LEVEL=info
3
-
4
- MCP_SERVER_NAME=gitlab-mcp
5
- MCP_SERVER_VERSION=0.1.0
6
-
7
- # Base API URL(s). Supports comma-separated multi-instance URLs.
8
- # Each URL will be normalized to /api/v4 automatically.
9
- GITLAB_API_URL=https://gitlab.com/api/v4
10
-
11
- # For stdio/local mode. In REMOTE_AUTHORIZATION mode this can stay empty.
12
- GITLAB_PERSONAL_ACCESS_TOKEN=
13
-
14
- # Optional built-in OAuth (PKCE)
15
- GITLAB_USE_OAUTH=false
16
- GITLAB_OAUTH_CLIENT_ID=
17
- GITLAB_OAUTH_CLIENT_SECRET=
18
- GITLAB_OAUTH_GITLAB_URL=
19
- GITLAB_OAUTH_REDIRECT_URI=http://127.0.0.1:8765/callback
20
- GITLAB_OAUTH_SCOPES=api
21
- GITLAB_OAUTH_TOKEN_PATH=~/.gitlab-mcp-oauth-token.json
22
- GITLAB_OAUTH_AUTO_OPEN_BROWSER=true
23
-
24
- # Tool/runtime policy
25
- GITLAB_READ_ONLY_MODE=false
26
- GITLAB_ALLOWED_PROJECT_IDS=
27
- GITLAB_ALLOWED_TOOLS=
28
- GITLAB_DENIED_TOOLS_REGEX=
29
- GITLAB_ALLOW_GRAPHQL_WITH_PROJECT_SCOPE=false
30
-
31
- # Output tuning
32
- GITLAB_RESPONSE_MODE=json
33
- GITLAB_MAX_RESPONSE_BYTES=200000
34
- GITLAB_HTTP_TIMEOUT_MS=20000
35
- GITLAB_ERROR_DETAIL_MODE=
36
-
37
- # Optional auth/runtime enhancements
38
- GITLAB_AUTH_COOKIE_PATH=
39
- GITLAB_COOKIE_WARMUP_PATH=/user
40
- GITLAB_TOKEN_SCRIPT=
41
- GITLAB_TOKEN_SCRIPT_TIMEOUT_MS=10000
42
- GITLAB_TOKEN_CACHE_SECONDS=300
43
- GITLAB_TOKEN_FILE=
44
- GITLAB_ALLOW_INSECURE_TOKEN_FILE=false
45
-
46
- # Cloudflare/proxy compatibility
47
- GITLAB_CLOUDFLARE_BYPASS=false
48
- GITLAB_USER_AGENT=
49
- GITLAB_ACCEPT_LANGUAGE=en-US,en;q=0.9
50
-
51
- # TLS safety guard (must acknowledge if disabled)
52
- NODE_TLS_REJECT_UNAUTHORIZED=
53
- GITLAB_ALLOW_INSECURE_TLS=false
54
- GITLAB_CA_CERT_PATH=
55
- HTTP_PROXY=
56
- HTTPS_PROXY=
57
-
58
- # Feature toggles
59
- USE_GITLAB_WIKI=true
60
- USE_MILESTONE=true
61
- USE_PIPELINE=true
62
- USE_RELEASE=true
63
-
64
- # Remote auth/session controls (for Streamable HTTP)
65
- REMOTE_AUTHORIZATION=false
66
- ENABLE_DYNAMIC_API_URL=false
67
- SESSION_TIMEOUT_SECONDS=3600
68
- MAX_SESSIONS=1000
69
- MAX_REQUESTS_PER_MINUTE=300
70
-
71
- # HTTP server
72
- HTTP_HOST=127.0.0.1
73
- HTTP_PORT=3333
74
- HTTP_JSON_ONLY=false
75
- SSE=false
@@ -1,31 +0,0 @@
1
- name: Node CI
2
-
3
- on: [push]
4
-
5
- jobs:
6
- build:
7
- runs-on: ubuntu-latest
8
-
9
- strategy:
10
- matrix:
11
- node-version: [22.x]
12
-
13
- steps:
14
- - uses: actions/checkout@v4
15
- - name: Setup pnpm
16
- uses: pnpm/action-setup@v4
17
- with:
18
- version: 10.28.1
19
- run_install: false
20
- - name: Use Node.js ${{ matrix.node-version }}
21
- uses: actions/setup-node@v4
22
- with:
23
- node-version: ${{ matrix.node-version }}
24
- cache: "pnpm"
25
- - name: pnpm install, build, and test
26
- run: |
27
- pnpm install --frozen-lockfile
28
- pnpm build
29
- pnpm test
30
- env:
31
- CI: true
@@ -1,31 +0,0 @@
1
- name: Node.js Package
2
-
3
- on:
4
- release:
5
- types: [created]
6
-
7
- jobs:
8
- publish-npm:
9
- runs-on: ubuntu-latest
10
- permissions:
11
- contents: read
12
- id-token: write
13
- steps:
14
- - uses: actions/checkout@v4
15
- - name: Setup pnpm
16
- uses: pnpm/action-setup@v4
17
- with:
18
- version: 10.28.1
19
- run_install: false
20
- - uses: actions/setup-node@v4
21
- with:
22
- node-version: "22.x"
23
- cache: "pnpm"
24
- registry-url: "https://registry.npmjs.org"
25
- - run: pnpm install --frozen-lockfile
26
- - run: pnpm build
27
- - run: pnpm test
28
- - run: npm publish --provenance
29
- env:
30
- NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
31
- CI: true
package/.husky/pre-commit DELETED
@@ -1 +0,0 @@
1
- npm run lint-staged
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- 22
package/.prettierrc.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "semi": true,
3
- "singleQuote": false,
4
- "trailingComma": "none",
5
- "printWidth": 100
6
- }
package/Dockerfile DELETED
@@ -1,20 +0,0 @@
1
- FROM node:22-alpine AS deps
2
- WORKDIR /app
3
- RUN corepack enable
4
- COPY package.json pnpm-lock.yaml ./
5
- RUN pnpm install --frozen-lockfile
6
-
7
- FROM deps AS build
8
- COPY . .
9
- RUN pnpm build
10
-
11
- FROM node:22-alpine AS runtime
12
- WORKDIR /app
13
- ENV NODE_ENV=production
14
- RUN corepack enable
15
- COPY package.json pnpm-lock.yaml ./
16
- RUN pnpm install --prod --frozen-lockfile
17
- COPY --from=build /app/dist ./dist
18
- COPY .env.example ./.env.example
19
- EXPOSE 3333
20
- CMD ["node", "dist/http.js"]
@@ -1,10 +0,0 @@
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
package/eslint.config.js DELETED
@@ -1,23 +0,0 @@
1
- import js from "@eslint/js";
2
- import tseslint from "typescript-eslint";
3
- import prettier from "eslint-config-prettier";
4
-
5
- export default tseslint.config(
6
- {
7
- ignores: ["dist/**", "coverage/**", "node_modules/**", "other/**"]
8
- },
9
- js.configs.recommended,
10
- ...tseslint.configs.recommended,
11
- {
12
- files: ["**/*.ts"],
13
- rules: {
14
- "@typescript-eslint/consistent-type-imports": [
15
- "error",
16
- {
17
- prefer: "type-imports"
18
- }
19
- ]
20
- }
21
- },
22
- prettier
23
- );
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- # Example external token script for GITLAB_TOKEN_SCRIPT.
5
- # The MCP server accepts either:
6
- # 1) Raw token on stdout
7
- # 2) JSON: {"access_token":"..."} or {"token":"..."}
8
-
9
- if [[ -n "${GITLAB_OAUTH_ACCESS_TOKEN:-}" ]]; then
10
- printf '{"access_token":"%s"}\n' "${GITLAB_OAUTH_ACCESS_TOKEN}"
11
- exit 0
12
- fi
13
-
14
- echo "GITLAB_OAUTH_ACCESS_TOKEN is not set" >&2
15
- exit 1
package/src/config/env.ts DELETED
@@ -1,171 +0,0 @@
1
- import "dotenv/config";
2
-
3
- import { z } from "zod";
4
-
5
- const logLevelSchema = z.enum(["fatal", "error", "warn", "info", "debug", "trace", "silent"]);
6
-
7
- const responseModeSchema = z.enum(["json", "compact-json", "yaml"]);
8
- const errorDetailModeSchema = z.enum(["safe", "full"]);
9
-
10
- function parseBoolean(value: string | undefined, fallback: boolean): boolean {
11
- if (value === undefined) {
12
- return fallback;
13
- }
14
-
15
- return value.toLowerCase() === "true";
16
- }
17
-
18
- function parseCsv(value: string | undefined): string[] {
19
- if (!value) {
20
- return [];
21
- }
22
-
23
- return value
24
- .split(",")
25
- .map((item) => item.trim())
26
- .filter((item) => item.length > 0);
27
- }
28
-
29
- const envSchema = z.object({
30
- NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
31
- LOG_LEVEL: logLevelSchema.default("info"),
32
- MCP_SERVER_NAME: z.string().min(1).default("gitlab-mcp"),
33
- MCP_SERVER_VERSION: z.string().min(1).default("0.1.0"),
34
- GITLAB_API_URL: z.string().min(1).default("https://gitlab.com/api/v4"),
35
- GITLAB_PERSONAL_ACCESS_TOKEN: z.string().min(1).optional(),
36
- GITLAB_USE_OAUTH: z.enum(["true", "false"]).default("false"),
37
- GITLAB_OAUTH_CLIENT_ID: z.string().optional(),
38
- GITLAB_OAUTH_CLIENT_SECRET: z.string().optional(),
39
- GITLAB_OAUTH_GITLAB_URL: z.string().optional(),
40
- GITLAB_OAUTH_REDIRECT_URI: z.string().url().optional(),
41
- GITLAB_OAUTH_SCOPES: z.string().default("api"),
42
- GITLAB_OAUTH_TOKEN_PATH: z.string().optional(),
43
- GITLAB_OAUTH_AUTO_OPEN_BROWSER: z.enum(["true", "false"]).default("true"),
44
- GITLAB_READ_ONLY_MODE: z
45
- .enum(["true", "false"])
46
- .default("false")
47
- .transform((value) => value === "true"),
48
- GITLAB_ALLOWED_PROJECT_IDS: z.string().optional(),
49
- GITLAB_ALLOWED_TOOLS: z.string().optional(),
50
- GITLAB_DENIED_TOOLS_REGEX: z.string().optional(),
51
- GITLAB_ALLOW_GRAPHQL_WITH_PROJECT_SCOPE: z.enum(["true", "false"]).default("false"),
52
- GITLAB_RESPONSE_MODE: responseModeSchema.default("json"),
53
- GITLAB_MAX_RESPONSE_BYTES: z.coerce.number().int().min(1024).max(2_000_000).default(200_000),
54
- GITLAB_HTTP_TIMEOUT_MS: z.coerce.number().int().min(1_000).max(120_000).default(20_000),
55
- GITLAB_ERROR_DETAIL_MODE: errorDetailModeSchema.optional(),
56
- GITLAB_AUTH_COOKIE_PATH: z.string().optional(),
57
- GITLAB_COOKIE_WARMUP_PATH: z.string().default("/user"),
58
- GITLAB_CLOUDFLARE_BYPASS: z.enum(["true", "false"]).default("false"),
59
- GITLAB_USER_AGENT: z.string().optional(),
60
- GITLAB_ACCEPT_LANGUAGE: z.string().optional(),
61
- GITLAB_TOKEN_SCRIPT: z.string().optional(),
62
- GITLAB_TOKEN_SCRIPT_TIMEOUT_MS: z.coerce.number().int().min(500).max(120_000).default(10_000),
63
- GITLAB_TOKEN_CACHE_SECONDS: z.coerce.number().int().min(0).max(86_400).default(300),
64
- GITLAB_TOKEN_FILE: z.string().optional(),
65
- GITLAB_ALLOW_INSECURE_TOKEN_FILE: z.enum(["true", "false"]).default("false"),
66
- GITLAB_ALLOW_INSECURE_TLS: z.enum(["true", "false"]).default("false"),
67
- NODE_TLS_REJECT_UNAUTHORIZED: z.string().optional(),
68
- GITLAB_CA_CERT_PATH: z.string().optional(),
69
- HTTP_PROXY: z.string().optional(),
70
- HTTPS_PROXY: z.string().optional(),
71
- USE_GITLAB_WIKI: z.enum(["true", "false"]).default("true"),
72
- USE_MILESTONE: z.enum(["true", "false"]).default("true"),
73
- USE_PIPELINE: z.enum(["true", "false"]).default("true"),
74
- USE_RELEASE: z.enum(["true", "false"]).default("true"),
75
- REMOTE_AUTHORIZATION: z.enum(["true", "false"]).default("false"),
76
- ENABLE_DYNAMIC_API_URL: z.enum(["true", "false"]).default("false"),
77
- SESSION_TIMEOUT_SECONDS: z.coerce.number().int().min(1).max(86_400).default(3_600),
78
- MAX_SESSIONS: z.coerce.number().int().min(1).max(10_000).default(1_000),
79
- MAX_REQUESTS_PER_MINUTE: z.coerce.number().int().min(1).max(10_000).default(300),
80
- HTTP_HOST: z.string().min(1).default("127.0.0.1"),
81
- HTTP_PORT: z.coerce.number().int().min(1).max(65_535).default(3333),
82
- HTTP_JSON_ONLY: z.enum(["true", "false"]).default("false"),
83
- SSE: z.enum(["true", "false"]).default("false")
84
- });
85
-
86
- const parsed = envSchema.safeParse(process.env);
87
-
88
- if (!parsed.success) {
89
- const issues = parsed.error.issues
90
- .map((issue) => `- ${issue.path.join(".") || "(root)"}: ${issue.message}`)
91
- .join("\n");
92
-
93
- throw new Error(`Invalid environment variables:\n${issues}`);
94
- }
95
-
96
- const data = parsed.data;
97
- const rawApiUrls = parseCsv(data.GITLAB_API_URL);
98
-
99
- if (rawApiUrls.length === 0) {
100
- throw new Error("GITLAB_API_URL must contain at least one URL");
101
- }
102
-
103
- const normalizedApiUrls = rawApiUrls.map((item) => {
104
- try {
105
- return normalizeApiUrl(item);
106
- } catch {
107
- throw new Error(`Invalid GITLAB_API_URL entry: '${item}'`);
108
- }
109
- });
110
-
111
- if (data.ENABLE_DYNAMIC_API_URL === "true" && data.REMOTE_AUTHORIZATION !== "true") {
112
- throw new Error("ENABLE_DYNAMIC_API_URL=true requires REMOTE_AUTHORIZATION=true");
113
- }
114
-
115
- if (data.GITLAB_USE_OAUTH === "true" && !data.GITLAB_OAUTH_CLIENT_ID) {
116
- throw new Error("GITLAB_USE_OAUTH=true requires GITLAB_OAUTH_CLIENT_ID");
117
- }
118
-
119
- if (data.SSE === "true" && data.REMOTE_AUTHORIZATION === "true") {
120
- throw new Error("SSE=true is not compatible with REMOTE_AUTHORIZATION=true");
121
- }
122
-
123
- if (data.NODE_TLS_REJECT_UNAUTHORIZED === "0" && data.GITLAB_ALLOW_INSECURE_TLS !== "true") {
124
- throw new Error(
125
- "NODE_TLS_REJECT_UNAUTHORIZED=0 requires GITLAB_ALLOW_INSECURE_TLS=true acknowledgment"
126
- );
127
- }
128
-
129
- export const env = {
130
- ...data,
131
- GITLAB_READ_ONLY_MODE: data.GITLAB_READ_ONLY_MODE,
132
- GITLAB_ERROR_DETAIL_MODE:
133
- data.GITLAB_ERROR_DETAIL_MODE ?? (data.NODE_ENV === "production" ? "safe" : "full"),
134
- GITLAB_USE_OAUTH: parseBoolean(data.GITLAB_USE_OAUTH, false),
135
- GITLAB_OAUTH_AUTO_OPEN_BROWSER: parseBoolean(data.GITLAB_OAUTH_AUTO_OPEN_BROWSER, true),
136
- GITLAB_CLOUDFLARE_BYPASS: parseBoolean(data.GITLAB_CLOUDFLARE_BYPASS, false),
137
- GITLAB_ALLOW_INSECURE_TOKEN_FILE: parseBoolean(data.GITLAB_ALLOW_INSECURE_TOKEN_FILE, false),
138
- GITLAB_ALLOW_INSECURE_TLS: parseBoolean(data.GITLAB_ALLOW_INSECURE_TLS, false),
139
- USE_GITLAB_WIKI: parseBoolean(data.USE_GITLAB_WIKI, true),
140
- USE_MILESTONE: parseBoolean(data.USE_MILESTONE, true),
141
- USE_PIPELINE: parseBoolean(data.USE_PIPELINE, true),
142
- USE_RELEASE: parseBoolean(data.USE_RELEASE, true),
143
- REMOTE_AUTHORIZATION: parseBoolean(data.REMOTE_AUTHORIZATION, false),
144
- ENABLE_DYNAMIC_API_URL: parseBoolean(data.ENABLE_DYNAMIC_API_URL, false),
145
- HTTP_JSON_ONLY: parseBoolean(data.HTTP_JSON_ONLY, false),
146
- SSE: parseBoolean(data.SSE, false),
147
- GITLAB_ALLOWED_PROJECT_IDS: parseCsv(data.GITLAB_ALLOWED_PROJECT_IDS),
148
- GITLAB_ALLOWED_TOOLS: parseCsv(data.GITLAB_ALLOWED_TOOLS),
149
- GITLAB_ALLOW_GRAPHQL_WITH_PROJECT_SCOPE: parseBoolean(
150
- data.GITLAB_ALLOW_GRAPHQL_WITH_PROJECT_SCOPE,
151
- false
152
- ),
153
- GITLAB_API_URLS: normalizedApiUrls,
154
- GITLAB_API_URL: normalizedApiUrls[0] ?? normalizeApiUrl("https://gitlab.com/api/v4")
155
- };
156
-
157
- export type AppEnv = typeof env;
158
-
159
- function normalizeApiUrl(rawUrl: string): string {
160
- const url = new URL(rawUrl);
161
- const pathname = url.pathname.replace(/\/+$/, "");
162
-
163
- if (pathname.endsWith("/api/v4")) {
164
- url.pathname = pathname;
165
- return url.toString();
166
- }
167
-
168
- url.pathname = `${pathname}/api/v4`.replace(/\/\//g, "/");
169
-
170
- return url.toString();
171
- }