@zereight/mcp-gitlab 2.0.13 → 2.0.19

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/build/index.js CHANGED
@@ -18,6 +18,7 @@ import { fileURLToPath } from "url";
18
18
  import { z } from "zod";
19
19
  import { zodToJsonSchema } from "zod-to-json-schema";
20
20
  import { initializeOAuth } from "./oauth.js";
21
+ import { GitLabClientPool } from "./gitlab-client-pool.js";
21
22
  // Add type imports for proxy agents
22
23
  import { Agent } from "http";
23
24
  import { Agent as HttpsAgent } from "https";
@@ -120,13 +121,15 @@ function validateConfiguration() {
120
121
  }
121
122
  }
122
123
  // Validate GITLAB_API_URL format
123
- const apiUrl = process.env.GITLAB_API_URL;
124
- if (apiUrl) {
125
- try {
126
- new URL(apiUrl);
127
- }
128
- catch (error) {
129
- errors.push(`GITLAB_API_URL must be a valid URL, got: ${apiUrl}`);
124
+ const apiUrls = process.env.GITLAB_API_URL?.split(',') || [];
125
+ if (apiUrls.length > 0) {
126
+ for (const url of apiUrls) {
127
+ try {
128
+ new URL(url.trim());
129
+ }
130
+ catch (error) {
131
+ errors.push(`GITLAB_API_URL contains an invalid URL: ${url.trim()}`);
132
+ }
130
133
  }
131
134
  }
132
135
  // Validate auth configuration
@@ -137,6 +140,9 @@ function validateConfiguration() {
137
140
  if (!remoteAuth && !useOAuth && !hasToken && !hasCookie) {
138
141
  errors.push('Either GITLAB_PERSONAL_ACCESS_TOKEN, GITLAB_AUTH_COOKIE_PATH, GITLAB_USE_OAUTH=true, or REMOTE_AUTHORIZATION=true must be set');
139
142
  }
143
+ if (ENABLE_DYNAMIC_API_URL && !REMOTE_AUTHORIZATION) {
144
+ errors.push('ENABLE_DYNAMIC_API_URL=true requires REMOTE_AUTHORIZATION=true');
145
+ }
140
146
  if (errors.length > 0) {
141
147
  logger.error('Configuration validation failed:');
142
148
  errors.forEach(err => logger.error(` - ${err}`));
@@ -157,6 +163,7 @@ const USE_PIPELINE = process.env.USE_PIPELINE === "true";
157
163
  const SSE = process.env.SSE === "true";
158
164
  const STREAMABLE_HTTP = process.env.STREAMABLE_HTTP === "true";
159
165
  const REMOTE_AUTHORIZATION = process.env.REMOTE_AUTHORIZATION === "true";
166
+ const ENABLE_DYNAMIC_API_URL = process.env.ENABLE_DYNAMIC_API_URL === "true";
160
167
  const SESSION_TIMEOUT_SECONDS = process.env.SESSION_TIMEOUT_SECONDS ? parseInt(process.env.SESSION_TIMEOUT_SECONDS) : 3600;
161
168
  const HOST = process.env.HOST || "0.0.0.0";
162
169
  const PORT = process.env.PORT || 3002;
@@ -165,6 +172,7 @@ const HTTP_PROXY = process.env.HTTP_PROXY;
165
172
  const HTTPS_PROXY = process.env.HTTPS_PROXY;
166
173
  const NODE_TLS_REJECT_UNAUTHORIZED = process.env.NODE_TLS_REJECT_UNAUTHORIZED;
167
174
  const GITLAB_CA_CERT_PATH = process.env.GITLAB_CA_CERT_PATH;
175
+ const GITLAB_POOL_MAX_SIZE = process.env.GITLAB_POOL_MAX_SIZE ? parseInt(process.env.GITLAB_POOL_MAX_SIZE) : 100;
168
176
  let sslOptions = undefined;
169
177
  if (NODE_TLS_REJECT_UNAUTHORIZED === "0") {
170
178
  sslOptions = { rejectUnauthorized: false };
@@ -194,6 +202,15 @@ if (HTTPS_PROXY) {
194
202
  }
195
203
  httpsAgent = httpsAgent || new HttpsAgent(sslOptions);
196
204
  httpAgent = httpAgent || new Agent();
205
+ // Initialize the client pool for managing multiple GitLab instances
206
+ const clientPool = new GitLabClientPool({
207
+ apiUrls: (process.env.GITLAB_API_URL || "https://gitlab.com").split(',').map(normalizeGitLabApiUrl),
208
+ httpProxy: HTTP_PROXY,
209
+ httpsProxy: HTTPS_PROXY,
210
+ rejectUnauthorized: NODE_TLS_REJECT_UNAUTHORIZED !== "0",
211
+ caCertPath: GITLAB_CA_CERT_PATH,
212
+ poolMaxSize: GITLAB_POOL_MAX_SIZE,
213
+ });
197
214
  // Create cookie jar with clean Netscape file parsing
198
215
  const createCookieJar = () => {
199
216
  if (!GITLAB_AUTH_COOKIE_PATH)
@@ -251,7 +268,7 @@ async function ensureSessionForRequest() {
251
268
  try {
252
269
  // Establish session with a lightweight request
253
270
  await fetch(`${GITLAB_API_URL}/user`, {
254
- ...DEFAULT_FETCH_CONFIG,
271
+ ...getFetchConfig(),
255
272
  redirect: "follow",
256
273
  }).catch(() => {
257
274
  // Ignore errors - the important thing is that cookies get set during redirects
@@ -265,6 +282,9 @@ async function ensureSessionForRequest() {
265
282
  }
266
283
  }
267
284
  const sessionAuthStore = new AsyncLocalStorage();
285
+ // Session context map for storing auth data by session ID
286
+ // This survives async boundaries where AsyncLocalStorage might not
287
+ const authBySession = {};
268
288
  // Base headers without authentication
269
289
  const BASE_HEADERS = {
270
290
  Accept: "application/json",
@@ -278,6 +298,7 @@ const BASE_HEADERS = {
278
298
  function buildAuthHeaders() {
279
299
  if (REMOTE_AUTHORIZATION) {
280
300
  const ctx = sessionAuthStore.getStore();
301
+ logger.debug({ context: ctx }, "buildAuthHeaders: session context");
281
302
  if (ctx && ctx.token) {
282
303
  return {
283
304
  [ctx.header]: ctx.header === 'Authorization' ? `Bearer ${ctx.token}` : ctx.token
@@ -295,17 +316,38 @@ function buildAuthHeaders() {
295
316
  }
296
317
  return {};
297
318
  }
298
- // Create a default fetch configuration object that includes proxy agents if set
299
- const DEFAULT_FETCH_CONFIG = {
300
- get headers() {
301
- return { ...BASE_HEADERS, ...buildAuthHeaders() };
302
- },
303
- agent: (parsedUrl) => {
304
- if (parsedUrl.protocol === "https:") {
305
- return httpsAgent;
319
+ /**
320
+ * Get the effective GitLab API URL for the current request
321
+ * In REMOTE_AUTHORIZATION mode with ENABLE_DYNAMIC_API_URL, reads from session context
322
+ * Otherwise, uses environment GITLAB_API_URL
323
+ */
324
+ function getEffectiveApiUrl() {
325
+ if (ENABLE_DYNAMIC_API_URL) {
326
+ const ctx = sessionAuthStore.getStore();
327
+ if (ctx && ctx.apiUrl) {
328
+ return ctx.apiUrl;
306
329
  }
307
- return httpAgent;
308
- },
330
+ logger.warn({ ctx }, "getEffectiveApiUrl: No context or apiUrl found, falling back to default");
331
+ }
332
+ return GITLAB_API_URL;
333
+ }
334
+ /**
335
+ * Get fetch configuration with proper client from pool
336
+ * Uses connection pooling when dynamic API URLs are enabled
337
+ */
338
+ /**
339
+ * Constructs the fetch configuration dynamically.
340
+ * This function is called immediately before each `fetch` to ensure it runs
341
+ * within the correct AsyncLocalStorage context, capturing the necessary auth
342
+ * and API URL information for the current request.
343
+ */
344
+ const getFetchConfig = () => {
345
+ const effectiveApiUrl = getEffectiveApiUrl();
346
+ const agent = clientPool.getOrCreateAgentForUrl(effectiveApiUrl);
347
+ return {
348
+ headers: { ...BASE_HEADERS, ...buildAuthHeaders() },
349
+ agent: agent,
350
+ };
309
351
  };
310
352
  const toJSONSchema = (schema) => zodToJsonSchema(schema, { $refStrategy: 'none' });
311
353
  // Define all available tools
@@ -924,17 +966,18 @@ function normalizeGitLabApiUrl(url) {
924
966
  if (!url) {
925
967
  return "https://gitlab.com/api/v4";
926
968
  }
927
- // Remove trailing slash if present
928
- let normalizedUrl = url.endsWith("/") ? url.slice(0, -1) : url;
929
- // Check if URL already has /api/v4
930
- if (!normalizedUrl.endsWith("/api/v4") && !normalizedUrl.endsWith("/api/v4/")) {
931
- // Append /api/v4 if not already present
969
+ let normalizedUrl = url.trim();
970
+ if (normalizedUrl.endsWith("/")) {
971
+ normalizedUrl = normalizedUrl.slice(0, -1);
972
+ }
973
+ if (!normalizedUrl.endsWith("/api/v4")) {
932
974
  normalizedUrl = `${normalizedUrl}/api/v4`;
933
975
  }
934
976
  return normalizedUrl;
935
977
  }
936
978
  // Use the normalizeGitLabApiUrl function to handle various URL formats
937
- const GITLAB_API_URL = normalizeGitLabApiUrl(process.env.GITLAB_API_URL || "");
979
+ const GITLAB_API_URLS = (process.env.GITLAB_API_URL || "https://gitlab.com").split(',').map(normalizeGitLabApiUrl);
980
+ const GITLAB_API_URL = GITLAB_API_URLS[0];
938
981
  const GITLAB_PROJECT_ID = process.env.GITLAB_PROJECT_ID;
939
982
  const GITLAB_ALLOWED_PROJECT_IDS = process.env.GITLAB_ALLOWED_PROJECT_IDS?.split(',').map(id => id.trim()).filter(Boolean) || [];
940
983
  const GITLAB_COMMIT_FILES_PER_PAGE = process.env.GITLAB_COMMIT_FILES_PER_PAGE ? parseInt(process.env.GITLAB_COMMIT_FILES_PER_PAGE) : 20;
@@ -1015,12 +1058,12 @@ function getEffectiveProjectId(projectId) {
1015
1058
  async function forkProject(projectId, namespace) {
1016
1059
  projectId = decodeURIComponent(projectId); // Decode project ID
1017
1060
  const effectiveProjectId = getEffectiveProjectId(projectId);
1018
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/fork`);
1061
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/fork`);
1019
1062
  if (namespace) {
1020
1063
  url.searchParams.append("namespace", namespace);
1021
1064
  }
1022
1065
  const response = await fetch(url.toString(), {
1023
- ...DEFAULT_FETCH_CONFIG,
1066
+ ...getFetchConfig(),
1024
1067
  method: "POST",
1025
1068
  });
1026
1069
  // 이미 존재하는 프로젝트인 경우 처리
@@ -1042,9 +1085,9 @@ async function forkProject(projectId, namespace) {
1042
1085
  async function createBranch(projectId, options) {
1043
1086
  projectId = decodeURIComponent(projectId); // Decode project ID
1044
1087
  const effectiveProjectId = getEffectiveProjectId(projectId);
1045
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/repository/branches`);
1088
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/repository/branches`);
1046
1089
  const response = await fetch(url.toString(), {
1047
- ...DEFAULT_FETCH_CONFIG,
1090
+ ...getFetchConfig(),
1048
1091
  method: "POST",
1049
1092
  body: JSON.stringify({
1050
1093
  branch: options.name,
@@ -1064,9 +1107,9 @@ async function createBranch(projectId, options) {
1064
1107
  async function getDefaultBranchRef(projectId) {
1065
1108
  projectId = decodeURIComponent(projectId); // Decode project ID
1066
1109
  const effectiveProjectId = getEffectiveProjectId(projectId);
1067
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}`);
1110
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}`);
1068
1111
  const response = await fetch(url.toString(), {
1069
- ...DEFAULT_FETCH_CONFIG,
1112
+ ...getFetchConfig(),
1070
1113
  });
1071
1114
  await handleGitLabError(response);
1072
1115
  const project = GitLabRepositorySchema.parse(await response.json());
@@ -1089,10 +1132,10 @@ async function getFileContents(projectId, filePath, ref) {
1089
1132
  if (!ref) {
1090
1133
  ref = await getDefaultBranchRef(projectId);
1091
1134
  }
1092
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/repository/files/${encodedPath}`);
1135
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/repository/files/${encodedPath}`);
1093
1136
  url.searchParams.append("ref", ref);
1094
1137
  const response = await fetch(url.toString(), {
1095
- ...DEFAULT_FETCH_CONFIG,
1138
+ ...getFetchConfig(),
1096
1139
  });
1097
1140
  // 파일을 찾을 수 없는 경우 처리
1098
1141
  if (response.status === 404) {
@@ -1119,9 +1162,9 @@ async function getFileContents(projectId, filePath, ref) {
1119
1162
  async function createIssue(projectId, options) {
1120
1163
  projectId = decodeURIComponent(projectId); // Decode project ID
1121
1164
  const effectiveProjectId = getEffectiveProjectId(projectId);
1122
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/issues`);
1165
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/issues`);
1123
1166
  const response = await fetch(url.toString(), {
1124
- ...DEFAULT_FETCH_CONFIG,
1167
+ ...getFetchConfig(),
1125
1168
  method: "POST",
1126
1169
  body: JSON.stringify({
1127
1170
  title: options.title,
@@ -1153,10 +1196,10 @@ async function listIssues(projectId, options = {}) {
1153
1196
  if (projectId) {
1154
1197
  projectId = decodeURIComponent(projectId); // Decode project ID
1155
1198
  const effectiveProjectId = getEffectiveProjectId(projectId);
1156
- url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/issues`);
1199
+ url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/issues`);
1157
1200
  }
1158
1201
  else {
1159
- url = new URL(`${GITLAB_API_URL}/issues`);
1202
+ url = new URL(`${getEffectiveApiUrl()}/issues`);
1160
1203
  }
1161
1204
  // Add all query parameters
1162
1205
  Object.entries(options).forEach(([key, value]) => {
@@ -1179,7 +1222,7 @@ async function listIssues(projectId, options = {}) {
1179
1222
  }
1180
1223
  });
1181
1224
  const response = await fetch(url.toString(), {
1182
- ...DEFAULT_FETCH_CONFIG,
1225
+ ...getFetchConfig(),
1183
1226
  });
1184
1227
  await handleGitLabError(response);
1185
1228
  const data = await response.json();
@@ -1194,7 +1237,7 @@ async function listIssues(projectId, options = {}) {
1194
1237
  */
1195
1238
  async function listMergeRequests(projectId, options = {}) {
1196
1239
  projectId = decodeURIComponent(projectId); // Decode project ID
1197
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests`);
1240
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests`);
1198
1241
  // Add all query parameters
1199
1242
  Object.entries(options).forEach(([key, value]) => {
1200
1243
  if (value !== undefined) {
@@ -1208,7 +1251,7 @@ async function listMergeRequests(projectId, options = {}) {
1208
1251
  }
1209
1252
  });
1210
1253
  const response = await fetch(url.toString(), {
1211
- ...DEFAULT_FETCH_CONFIG,
1254
+ ...getFetchConfig(),
1212
1255
  });
1213
1256
  await handleGitLabError(response);
1214
1257
  const data = await response.json();
@@ -1224,9 +1267,9 @@ async function listMergeRequests(projectId, options = {}) {
1224
1267
  */
1225
1268
  async function getIssue(projectId, issueIid) {
1226
1269
  projectId = decodeURIComponent(projectId); // Decode project ID
1227
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
1270
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
1228
1271
  const response = await fetch(url.toString(), {
1229
- ...DEFAULT_FETCH_CONFIG,
1272
+ ...getFetchConfig(),
1230
1273
  });
1231
1274
  await handleGitLabError(response);
1232
1275
  const data = await response.json();
@@ -1243,14 +1286,14 @@ async function getIssue(projectId, issueIid) {
1243
1286
  */
1244
1287
  async function updateIssue(projectId, issueIid, options) {
1245
1288
  projectId = decodeURIComponent(projectId); // Decode project ID
1246
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
1289
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
1247
1290
  // Convert labels array to comma-separated string if present
1248
1291
  const body = { ...options };
1249
1292
  if (body.labels && Array.isArray(body.labels)) {
1250
1293
  body.labels = body.labels.join(",");
1251
1294
  }
1252
1295
  const response = await fetch(url.toString(), {
1253
- ...DEFAULT_FETCH_CONFIG,
1296
+ ...getFetchConfig(),
1254
1297
  method: "PUT",
1255
1298
  body: JSON.stringify(body),
1256
1299
  });
@@ -1268,9 +1311,9 @@ async function updateIssue(projectId, issueIid, options) {
1268
1311
  */
1269
1312
  async function deleteIssue(projectId, issueIid) {
1270
1313
  projectId = decodeURIComponent(projectId); // Decode project ID
1271
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
1314
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
1272
1315
  const response = await fetch(url.toString(), {
1273
- ...DEFAULT_FETCH_CONFIG,
1316
+ ...getFetchConfig(),
1274
1317
  method: "DELETE",
1275
1318
  });
1276
1319
  await handleGitLabError(response);
@@ -1285,9 +1328,9 @@ async function deleteIssue(projectId, issueIid) {
1285
1328
  */
1286
1329
  async function listIssueLinks(projectId, issueIid) {
1287
1330
  projectId = decodeURIComponent(projectId); // Decode project ID
1288
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links`);
1331
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links`);
1289
1332
  const response = await fetch(url.toString(), {
1290
- ...DEFAULT_FETCH_CONFIG,
1333
+ ...getFetchConfig(),
1291
1334
  });
1292
1335
  await handleGitLabError(response);
1293
1336
  const data = await response.json();
@@ -1304,9 +1347,9 @@ async function listIssueLinks(projectId, issueIid) {
1304
1347
  */
1305
1348
  async function getIssueLink(projectId, issueIid, issueLinkId) {
1306
1349
  projectId = decodeURIComponent(projectId); // Decode project ID
1307
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links/${issueLinkId}`);
1350
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links/${issueLinkId}`);
1308
1351
  const response = await fetch(url.toString(), {
1309
- ...DEFAULT_FETCH_CONFIG,
1352
+ ...getFetchConfig(),
1310
1353
  });
1311
1354
  await handleGitLabError(response);
1312
1355
  const data = await response.json();
@@ -1326,9 +1369,9 @@ async function getIssueLink(projectId, issueIid, issueLinkId) {
1326
1369
  async function createIssueLink(projectId, issueIid, targetProjectId, targetIssueIid, linkType = "relates_to") {
1327
1370
  projectId = decodeURIComponent(projectId); // Decode project ID
1328
1371
  targetProjectId = decodeURIComponent(targetProjectId); // Decode target project ID as well
1329
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links`);
1372
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links`);
1330
1373
  const response = await fetch(url.toString(), {
1331
- ...DEFAULT_FETCH_CONFIG,
1374
+ ...getFetchConfig(),
1332
1375
  method: "POST",
1333
1376
  body: JSON.stringify({
1334
1377
  target_project_id: targetProjectId,
@@ -1351,9 +1394,9 @@ async function createIssueLink(projectId, issueIid, targetProjectId, targetIssue
1351
1394
  */
1352
1395
  async function deleteIssueLink(projectId, issueIid, issueLinkId) {
1353
1396
  projectId = decodeURIComponent(projectId); // Decode project ID
1354
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links/${issueLinkId}`);
1397
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links/${issueLinkId}`);
1355
1398
  const response = await fetch(url.toString(), {
1356
- ...DEFAULT_FETCH_CONFIG,
1399
+ ...getFetchConfig(),
1357
1400
  method: "DELETE",
1358
1401
  });
1359
1402
  await handleGitLabError(response);
@@ -1368,9 +1411,9 @@ async function deleteIssueLink(projectId, issueIid, issueLinkId) {
1368
1411
  */
1369
1412
  async function createMergeRequest(projectId, options) {
1370
1413
  projectId = decodeURIComponent(projectId); // Decode project ID
1371
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests`);
1414
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests`);
1372
1415
  const response = await fetch(url.toString(), {
1373
- ...DEFAULT_FETCH_CONFIG,
1416
+ ...getFetchConfig(),
1374
1417
  method: "POST",
1375
1418
  body: JSON.stringify({
1376
1419
  title: options.title,
@@ -1410,7 +1453,7 @@ async function createMergeRequest(projectId, options) {
1410
1453
  */
1411
1454
  async function listDiscussions(projectId, resourceType, resourceIid, options = {}) {
1412
1455
  projectId = decodeURIComponent(projectId); // Decode project ID
1413
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/${resourceType}/${resourceIid}/discussions`);
1456
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/${resourceType}/${resourceIid}/discussions`);
1414
1457
  // Add query parameters for pagination and sorting
1415
1458
  if (options.page) {
1416
1459
  url.searchParams.append("page", options.page.toString());
@@ -1419,7 +1462,7 @@ async function listDiscussions(projectId, resourceType, resourceIid, options = {
1419
1462
  url.searchParams.append("per_page", options.per_page.toString());
1420
1463
  }
1421
1464
  const response = await fetch(url.toString(), {
1422
- ...DEFAULT_FETCH_CONFIG,
1465
+ ...getFetchConfig(),
1423
1466
  });
1424
1467
  await handleGitLabError(response);
1425
1468
  const discussions = await response.json();
@@ -1470,9 +1513,9 @@ async function listIssueDiscussions(projectId, issueIid, options = {}) {
1470
1513
  }
1471
1514
  async function deleteMergeRequestDiscussionNote(projectId, mergeRequestIid, discussionId, noteId) {
1472
1515
  projectId = decodeURIComponent(projectId); // Decode project ID
1473
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes/${noteId}`);
1516
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes/${noteId}`);
1474
1517
  const response = await fetch(url.toString(), {
1475
- ...DEFAULT_FETCH_CONFIG,
1518
+ ...getFetchConfig(),
1476
1519
  method: "DELETE",
1477
1520
  });
1478
1521
  if (!response.ok) {
@@ -1494,7 +1537,7 @@ async function deleteMergeRequestDiscussionNote(projectId, mergeRequestIid, disc
1494
1537
  */
1495
1538
  async function updateMergeRequestDiscussionNote(projectId, mergeRequestIid, discussionId, noteId, body, resolved) {
1496
1539
  projectId = decodeURIComponent(projectId); // Decode project ID
1497
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes/${noteId}`);
1540
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes/${noteId}`);
1498
1541
  // Only one of body or resolved can be sent according to GitLab API
1499
1542
  const payload = {};
1500
1543
  if (body !== undefined) {
@@ -1504,7 +1547,7 @@ async function updateMergeRequestDiscussionNote(projectId, mergeRequestIid, disc
1504
1547
  payload.resolved = resolved;
1505
1548
  }
1506
1549
  const response = await fetch(url.toString(), {
1507
- ...DEFAULT_FETCH_CONFIG,
1550
+ ...getFetchConfig(),
1508
1551
  method: "PUT",
1509
1552
  body: JSON.stringify(payload),
1510
1553
  });
@@ -1523,10 +1566,10 @@ async function updateMergeRequestDiscussionNote(projectId, mergeRequestIid, disc
1523
1566
  */
1524
1567
  async function updateIssueNote(projectId, issueIid, discussionId, noteId, body) {
1525
1568
  projectId = decodeURIComponent(projectId); // Decode project ID
1526
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/discussions/${discussionId}/notes/${noteId}`);
1569
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/discussions/${discussionId}/notes/${noteId}`);
1527
1570
  const payload = { body };
1528
1571
  const response = await fetch(url.toString(), {
1529
- ...DEFAULT_FETCH_CONFIG,
1572
+ ...getFetchConfig(),
1530
1573
  method: "PUT",
1531
1574
  body: JSON.stringify(payload),
1532
1575
  });
@@ -1545,13 +1588,13 @@ async function updateIssueNote(projectId, issueIid, discussionId, noteId, body)
1545
1588
  */
1546
1589
  async function createIssueNote(projectId, issueIid, discussionId, body, createdAt) {
1547
1590
  projectId = decodeURIComponent(projectId); // Decode project ID
1548
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/discussions/${discussionId}/notes`);
1591
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/discussions/${discussionId}/notes`);
1549
1592
  const payload = { body };
1550
1593
  if (createdAt) {
1551
1594
  payload.created_at = createdAt;
1552
1595
  }
1553
1596
  const response = await fetch(url.toString(), {
1554
- ...DEFAULT_FETCH_CONFIG,
1597
+ ...getFetchConfig(),
1555
1598
  method: "POST",
1556
1599
  body: JSON.stringify(payload),
1557
1600
  });
@@ -1572,13 +1615,13 @@ async function createIssueNote(projectId, issueIid, discussionId, body, createdA
1572
1615
  */
1573
1616
  async function createMergeRequestDiscussionNote(projectId, mergeRequestIid, discussionId, body, createdAt) {
1574
1617
  projectId = decodeURIComponent(projectId); // Decode project ID
1575
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes`);
1618
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes`);
1576
1619
  const payload = { body };
1577
1620
  if (createdAt) {
1578
1621
  payload.created_at = createdAt;
1579
1622
  }
1580
1623
  const response = await fetch(url.toString(), {
1581
- ...DEFAULT_FETCH_CONFIG,
1624
+ ...getFetchConfig(),
1582
1625
  method: "POST",
1583
1626
  body: JSON.stringify(payload),
1584
1627
  });
@@ -1588,14 +1631,14 @@ async function createMergeRequestDiscussionNote(projectId, mergeRequestIid, disc
1588
1631
  }
1589
1632
  async function createMergeRequestNote(projectId, mergeRequestIid, body) {
1590
1633
  projectId = decodeURIComponent(projectId); // Decode project ID
1591
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes`);
1634
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes`);
1592
1635
  const payload = {
1593
1636
  id: projectId,
1594
1637
  merge_request_iid: mergeRequestIid,
1595
1638
  body,
1596
1639
  };
1597
1640
  const response = await fetch(url.toString(), {
1598
- ...DEFAULT_FETCH_CONFIG,
1641
+ ...getFetchConfig(),
1599
1642
  method: "POST",
1600
1643
  body: JSON.stringify(payload),
1601
1644
  });
@@ -1605,9 +1648,9 @@ async function createMergeRequestNote(projectId, mergeRequestIid, body) {
1605
1648
  }
1606
1649
  async function deleteMergeRequestNote(projectId, mergeRequestIid, noteId) {
1607
1650
  projectId = decodeURIComponent(projectId); // Decode project ID
1608
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes/${noteId}`);
1651
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes/${noteId}`);
1609
1652
  const response = await fetch(url.toString(), {
1610
- ...DEFAULT_FETCH_CONFIG,
1653
+ ...getFetchConfig(),
1611
1654
  method: "DELETE",
1612
1655
  });
1613
1656
  if (!response.ok) {
@@ -1617,9 +1660,9 @@ async function deleteMergeRequestNote(projectId, mergeRequestIid, noteId) {
1617
1660
  }
1618
1661
  async function getMergeRequestNote(projectId, mergeRequestIid, noteId) {
1619
1662
  projectId = decodeURIComponent(projectId); // Decode project ID
1620
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes/${noteId}`);
1663
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes/${noteId}`);
1621
1664
  const response = await fetch(url.toString(), {
1622
- ...DEFAULT_FETCH_CONFIG,
1665
+ ...getFetchConfig(),
1623
1666
  method: "GET",
1624
1667
  });
1625
1668
  await handleGitLabError(response);
@@ -1628,7 +1671,7 @@ async function getMergeRequestNote(projectId, mergeRequestIid, noteId) {
1628
1671
  }
1629
1672
  async function getMergeRequestNotes(projectId, mergeRequestIid, sort, order_by) {
1630
1673
  projectId = decodeURIComponent(projectId); // Decode project ID
1631
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes`);
1674
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes`);
1632
1675
  if (sort) {
1633
1676
  url.searchParams.append("sort", sort);
1634
1677
  }
@@ -1636,7 +1679,7 @@ async function getMergeRequestNotes(projectId, mergeRequestIid, sort, order_by)
1636
1679
  url.searchParams.append("order_by", order_by);
1637
1680
  }
1638
1681
  const response = await fetch(url.toString(), {
1639
- ...DEFAULT_FETCH_CONFIG,
1682
+ ...getFetchConfig(),
1640
1683
  method: "GET",
1641
1684
  });
1642
1685
  await handleGitLabError(response);
@@ -1645,7 +1688,7 @@ async function getMergeRequestNotes(projectId, mergeRequestIid, sort, order_by)
1645
1688
  }
1646
1689
  async function updateMergeRequestNote(projectId, mergeRequestIid, noteId, body) {
1647
1690
  projectId = decodeURIComponent(projectId); // Decode project ID
1648
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes/${noteId}`);
1691
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/notes/${noteId}`);
1649
1692
  const payload = {
1650
1693
  id: projectId,
1651
1694
  merge_request_iid: mergeRequestIid,
@@ -1653,7 +1696,7 @@ async function updateMergeRequestNote(projectId, mergeRequestIid, noteId, body)
1653
1696
  body,
1654
1697
  };
1655
1698
  const response = await fetch(url.toString(), {
1656
- ...DEFAULT_FETCH_CONFIG,
1699
+ ...getFetchConfig(),
1657
1700
  method: "PUT",
1658
1701
  body: JSON.stringify(payload),
1659
1702
  });
@@ -1676,7 +1719,7 @@ async function updateMergeRequestNote(projectId, mergeRequestIid, noteId, body)
1676
1719
  async function createOrUpdateFile(projectId, filePath, content, commitMessage, branch, previousPath, last_commit_id, commit_id) {
1677
1720
  projectId = decodeURIComponent(projectId); // Decode project ID
1678
1721
  const encodedPath = encodeURIComponent(filePath);
1679
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/files/${encodedPath}`);
1722
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/files/${encodedPath}`);
1680
1723
  const body = {
1681
1724
  branch,
1682
1725
  content,
@@ -1721,7 +1764,7 @@ async function createOrUpdateFile(projectId, filePath, content, commitMessage, b
1721
1764
  }
1722
1765
  }
1723
1766
  const response = await fetch(url.toString(), {
1724
- ...DEFAULT_FETCH_CONFIG,
1767
+ ...getFetchConfig(),
1725
1768
  method,
1726
1769
  body: JSON.stringify(body),
1727
1770
  });
@@ -1743,12 +1786,12 @@ async function createOrUpdateFile(projectId, filePath, content, commitMessage, b
1743
1786
  */
1744
1787
  async function createTree(projectId, files, ref) {
1745
1788
  projectId = decodeURIComponent(projectId); // Decode project ID
1746
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/tree`);
1789
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/tree`);
1747
1790
  if (ref) {
1748
1791
  url.searchParams.append("ref", ref);
1749
1792
  }
1750
1793
  const response = await fetch(url.toString(), {
1751
- ...DEFAULT_FETCH_CONFIG,
1794
+ ...getFetchConfig(),
1752
1795
  method: "POST",
1753
1796
  body: JSON.stringify({
1754
1797
  files: files.map(file => ({
@@ -1781,9 +1824,9 @@ async function createTree(projectId, files, ref) {
1781
1824
  */
1782
1825
  async function createCommit(projectId, message, branch, actions) {
1783
1826
  projectId = decodeURIComponent(projectId); // Decode project ID
1784
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits`);
1827
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits`);
1785
1828
  const response = await fetch(url.toString(), {
1786
- ...DEFAULT_FETCH_CONFIG,
1829
+ ...getFetchConfig(),
1787
1830
  method: "POST",
1788
1831
  body: JSON.stringify({
1789
1832
  branch,
@@ -1817,14 +1860,14 @@ async function createCommit(projectId, message, branch, actions) {
1817
1860
  * @returns {Promise<GitLabSearchResponse>} The search results
1818
1861
  */
1819
1862
  async function searchProjects(query, page = 1, perPage = 20) {
1820
- const url = new URL(`${GITLAB_API_URL}/projects`);
1863
+ const url = new URL(`${getEffectiveApiUrl()}/projects`);
1821
1864
  url.searchParams.append("search", query);
1822
1865
  url.searchParams.append("page", page.toString());
1823
1866
  url.searchParams.append("per_page", perPage.toString());
1824
1867
  url.searchParams.append("order_by", "id");
1825
1868
  url.searchParams.append("sort", "desc");
1826
1869
  const response = await fetch(url.toString(), {
1827
- ...DEFAULT_FETCH_CONFIG,
1870
+ ...getFetchConfig(),
1828
1871
  });
1829
1872
  if (!response.ok) {
1830
1873
  const errorBody = await response.text();
@@ -1850,8 +1893,8 @@ async function searchProjects(query, page = 1, perPage = 20) {
1850
1893
  * @returns {Promise<GitLabRepository>} The created repository
1851
1894
  */
1852
1895
  async function createRepository(options) {
1853
- const response = await fetch(`${GITLAB_API_URL}/projects`, {
1854
- ...DEFAULT_FETCH_CONFIG,
1896
+ const response = await fetch(`${getEffectiveApiUrl()}/projects`, {
1897
+ ...getFetchConfig(),
1855
1898
  method: "POST",
1856
1899
  body: JSON.stringify({
1857
1900
  name: options.name,
@@ -1882,16 +1925,16 @@ async function getMergeRequest(projectId, mergeRequestIid, branchName) {
1882
1925
  projectId = decodeURIComponent(projectId); // Decode project ID
1883
1926
  let url;
1884
1927
  if (mergeRequestIid) {
1885
- url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}`);
1928
+ url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}`);
1886
1929
  }
1887
1930
  else if (branchName) {
1888
- url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests?source_branch=${encodeURIComponent(branchName)}`);
1931
+ url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests?source_branch=${encodeURIComponent(branchName)}`);
1889
1932
  }
1890
1933
  else {
1891
1934
  throw new Error("Either mergeRequestIid or branchName must be provided");
1892
1935
  }
1893
1936
  const response = await fetch(url.toString(), {
1894
- ...DEFAULT_FETCH_CONFIG,
1937
+ ...getFetchConfig(),
1895
1938
  });
1896
1939
  await handleGitLabError(response);
1897
1940
  const data = await response.json();
@@ -1920,12 +1963,12 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view
1920
1963
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
1921
1964
  mergeRequestIid = mergeRequest.iid;
1922
1965
  }
1923
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/changes`);
1966
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/changes`);
1924
1967
  if (view) {
1925
1968
  url.searchParams.append("view", view);
1926
1969
  }
1927
1970
  const response = await fetch(url.toString(), {
1928
- ...DEFAULT_FETCH_CONFIG,
1971
+ ...getFetchConfig(),
1929
1972
  });
1930
1973
  await handleGitLabError(response);
1931
1974
  const data = (await response.json());
@@ -1950,7 +1993,7 @@ async function listMergeRequestDiffs(projectId, mergeRequestIid, branchName, pag
1950
1993
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
1951
1994
  mergeRequestIid = mergeRequest.iid;
1952
1995
  }
1953
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/diffs`);
1996
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/diffs`);
1954
1997
  if (page) {
1955
1998
  url.searchParams.append("page", page.toString());
1956
1999
  }
@@ -1961,7 +2004,7 @@ async function listMergeRequestDiffs(projectId, mergeRequestIid, branchName, pag
1961
2004
  url.searchParams.append("unidiff", "true");
1962
2005
  }
1963
2006
  const response = await fetch(url.toString(), {
1964
- ...DEFAULT_FETCH_CONFIG,
2007
+ ...getFetchConfig(),
1965
2008
  });
1966
2009
  await handleGitLabError(response);
1967
2010
  return await response.json(); // Return full response including commits, diff_refs, changes, etc.
@@ -1977,14 +2020,14 @@ async function listMergeRequestDiffs(projectId, mergeRequestIid, branchName, pag
1977
2020
  */
1978
2021
  async function getBranchDiffs(projectId, from, to, straight) {
1979
2022
  projectId = decodeURIComponent(projectId); // Decode project ID
1980
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/compare`);
2023
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/compare`);
1981
2024
  url.searchParams.append("from", from);
1982
2025
  url.searchParams.append("to", to);
1983
2026
  if (straight !== undefined) {
1984
2027
  url.searchParams.append("straight", straight.toString());
1985
2028
  }
1986
2029
  const response = await fetch(url.toString(), {
1987
- ...DEFAULT_FETCH_CONFIG,
2030
+ ...getFetchConfig(),
1988
2031
  });
1989
2032
  if (!response.ok) {
1990
2033
  const errorBody = await response.text();
@@ -2012,9 +2055,9 @@ async function updateMergeRequest(projectId, options, mergeRequestIid, branchNam
2012
2055
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
2013
2056
  mergeRequestIid = mergeRequest.iid;
2014
2057
  }
2015
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}`);
2058
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}`);
2016
2059
  const response = await fetch(url.toString(), {
2017
- ...DEFAULT_FETCH_CONFIG,
2060
+ ...getFetchConfig(),
2018
2061
  method: "PUT",
2019
2062
  body: JSON.stringify(options),
2020
2063
  });
@@ -2032,9 +2075,9 @@ async function updateMergeRequest(projectId, options, mergeRequestIid, branchNam
2032
2075
  */
2033
2076
  async function mergeMergeRequest(projectId, options, mergeRequestIid) {
2034
2077
  projectId = decodeURIComponent(projectId); // Decode project ID
2035
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/merge`);
2078
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/merge`);
2036
2079
  const response = await fetch(url.toString(), {
2037
- ...DEFAULT_FETCH_CONFIG,
2080
+ ...getFetchConfig(),
2038
2081
  method: "PUT",
2039
2082
  body: JSON.stringify(options),
2040
2083
  });
@@ -2056,10 +2099,10 @@ async function createNote(projectId, noteableType, // 'issue' 또는 'merge_requ
2056
2099
  noteableIid, body) {
2057
2100
  projectId = decodeURIComponent(projectId); // Decode project ID
2058
2101
  // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능
2059
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation
2102
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation
2060
2103
  );
2061
2104
  const response = await fetch(url.toString(), {
2062
- ...DEFAULT_FETCH_CONFIG,
2105
+ ...getFetchConfig(),
2063
2106
  method: "POST",
2064
2107
  body: JSON.stringify({ body }),
2065
2108
  });
@@ -2076,7 +2119,7 @@ noteableIid, body) {
2076
2119
  * @returns {Promise<GitLabDraftNote[]>} Array of draft notes
2077
2120
  */
2078
2121
  async function getDraftNote(project_id, merge_request_iid, draft_note_id) {
2079
- const response = await fetch(`/projects/${encodeURIComponent(project_id)}/merge_requests/${merge_request_iid}/draft_notes/${draft_note_id}`);
2122
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(project_id)}/merge_requests/${merge_request_iid}/draft_notes/${draft_note_id}`);
2080
2123
  if (!response.ok) {
2081
2124
  const errorText = await response.text();
2082
2125
  throw new Error(`GitLab API error: ${response.status} ${response.statusText}\n${errorText}`);
@@ -2086,9 +2129,9 @@ async function getDraftNote(project_id, merge_request_iid, draft_note_id) {
2086
2129
  }
2087
2130
  async function listDraftNotes(projectId, mergeRequestIid) {
2088
2131
  projectId = decodeURIComponent(projectId);
2089
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes`);
2132
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes`);
2090
2133
  const response = await fetch(url.toString(), {
2091
- ...DEFAULT_FETCH_CONFIG,
2134
+ ...getFetchConfig(),
2092
2135
  method: "GET",
2093
2136
  });
2094
2137
  if (!response.ok) {
@@ -2109,7 +2152,7 @@ async function listDraftNotes(projectId, mergeRequestIid) {
2109
2152
  */
2110
2153
  async function createDraftNote(projectId, mergeRequestIid, body, position, resolveDiscussion) {
2111
2154
  projectId = decodeURIComponent(projectId);
2112
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes`);
2155
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes`);
2113
2156
  const requestBody = { note: body };
2114
2157
  if (position) {
2115
2158
  requestBody.position = position;
@@ -2118,7 +2161,7 @@ async function createDraftNote(projectId, mergeRequestIid, body, position, resol
2118
2161
  requestBody.resolve_discussion = resolveDiscussion;
2119
2162
  }
2120
2163
  const response = await fetch(url.toString(), {
2121
- ...DEFAULT_FETCH_CONFIG,
2164
+ ...getFetchConfig(),
2122
2165
  method: "POST",
2123
2166
  body: JSON.stringify(requestBody),
2124
2167
  });
@@ -2141,7 +2184,7 @@ async function createDraftNote(projectId, mergeRequestIid, body, position, resol
2141
2184
  */
2142
2185
  async function updateDraftNote(projectId, mergeRequestIid, draftNoteId, body, position, resolveDiscussion) {
2143
2186
  projectId = decodeURIComponent(projectId);
2144
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes/${draftNoteId}`);
2187
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes/${draftNoteId}`);
2145
2188
  const requestBody = {};
2146
2189
  if (body !== undefined) {
2147
2190
  requestBody.note = body;
@@ -2153,7 +2196,7 @@ async function updateDraftNote(projectId, mergeRequestIid, draftNoteId, body, po
2153
2196
  requestBody.resolve_discussion = resolveDiscussion;
2154
2197
  }
2155
2198
  const response = await fetch(url.toString(), {
2156
- ...DEFAULT_FETCH_CONFIG,
2199
+ ...getFetchConfig(),
2157
2200
  method: "PUT",
2158
2201
  body: JSON.stringify(requestBody),
2159
2202
  });
@@ -2173,9 +2216,9 @@ async function updateDraftNote(projectId, mergeRequestIid, draftNoteId, body, po
2173
2216
  */
2174
2217
  async function deleteDraftNote(projectId, mergeRequestIid, draftNoteId) {
2175
2218
  projectId = decodeURIComponent(projectId);
2176
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes/${draftNoteId}`);
2219
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes/${draftNoteId}`);
2177
2220
  const response = await fetch(url.toString(), {
2178
- ...DEFAULT_FETCH_CONFIG,
2221
+ ...getFetchConfig(),
2179
2222
  method: "DELETE",
2180
2223
  });
2181
2224
  if (!response.ok) {
@@ -2192,9 +2235,9 @@ async function deleteDraftNote(projectId, mergeRequestIid, draftNoteId) {
2192
2235
  */
2193
2236
  async function publishDraftNote(projectId, mergeRequestIid, draftNoteId) {
2194
2237
  projectId = decodeURIComponent(projectId);
2195
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes/${draftNoteId}/publish`);
2238
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes/${draftNoteId}/publish`);
2196
2239
  const response = await fetch(url.toString(), {
2197
- ...DEFAULT_FETCH_CONFIG,
2240
+ ...getFetchConfig(),
2198
2241
  method: "PUT",
2199
2242
  });
2200
2243
  if (!response.ok) {
@@ -2244,9 +2287,9 @@ async function publishDraftNote(projectId, mergeRequestIid, draftNoteId) {
2244
2287
  */
2245
2288
  async function bulkPublishDraftNotes(projectId, mergeRequestIid) {
2246
2289
  projectId = decodeURIComponent(projectId);
2247
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes/bulk_publish`);
2290
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/draft_notes/bulk_publish`);
2248
2291
  const response = await fetch(url.toString(), {
2249
- ...DEFAULT_FETCH_CONFIG,
2292
+ ...getFetchConfig(),
2250
2293
  method: "POST", // Changed from PUT to POST
2251
2294
  body: JSON.stringify({}), // Send empty body for POST request
2252
2295
  });
@@ -2273,12 +2316,12 @@ async function bulkPublishDraftNotes(projectId, mergeRequestIid) {
2273
2316
  }
2274
2317
  async function resolveMergeRequestThread(projectId, mergeRequestIid, discussionId, resolved) {
2275
2318
  projectId = decodeURIComponent(projectId);
2276
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}`);
2319
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}`);
2277
2320
  if (resolved !== undefined) {
2278
2321
  url.searchParams.append("resolved", resolved ? "true" : "false");
2279
2322
  }
2280
2323
  const response = await fetch(url.toString(), {
2281
- ...DEFAULT_FETCH_CONFIG,
2324
+ ...getFetchConfig(),
2282
2325
  method: "PUT",
2283
2326
  });
2284
2327
  if (!response.ok) {
@@ -2305,7 +2348,7 @@ async function resolveMergeRequestThread(projectId, mergeRequestIid, discussionI
2305
2348
  */
2306
2349
  async function createMergeRequestThread(projectId, mergeRequestIid, body, position, createdAt) {
2307
2350
  projectId = decodeURIComponent(projectId); // Decode project ID
2308
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions`);
2351
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions`);
2309
2352
  const payload = { body };
2310
2353
  // Add optional parameters if provided
2311
2354
  if (position) {
@@ -2315,7 +2358,7 @@ async function createMergeRequestThread(projectId, mergeRequestIid, body, positi
2315
2358
  payload.created_at = createdAt;
2316
2359
  }
2317
2360
  const response = await fetch(url.toString(), {
2318
- ...DEFAULT_FETCH_CONFIG,
2361
+ ...getFetchConfig(),
2319
2362
  method: "POST",
2320
2363
  body: JSON.stringify(payload),
2321
2364
  });
@@ -2334,7 +2377,7 @@ async function createMergeRequestThread(projectId, mergeRequestIid, body, positi
2334
2377
  * @returns {Promise<GitLabNamespace[]>} List of namespaces
2335
2378
  */
2336
2379
  async function listNamespaces(options) {
2337
- const url = new URL(`${GITLAB_API_URL}/namespaces`);
2380
+ const url = new URL(`${getEffectiveApiUrl()}/namespaces`);
2338
2381
  if (options.search) {
2339
2382
  url.searchParams.append("search", options.search);
2340
2383
  }
@@ -2345,7 +2388,7 @@ async function listNamespaces(options) {
2345
2388
  url.searchParams.append("top_level_only", "true");
2346
2389
  }
2347
2390
  const response = await fetch(url.toString(), {
2348
- ...DEFAULT_FETCH_CONFIG,
2391
+ ...getFetchConfig(),
2349
2392
  });
2350
2393
  await handleGitLabError(response);
2351
2394
  const data = await response.json();
@@ -2359,9 +2402,9 @@ async function listNamespaces(options) {
2359
2402
  * @returns {Promise<GitLabNamespace>} The namespace details
2360
2403
  */
2361
2404
  async function getNamespace(id) {
2362
- const url = new URL(`${GITLAB_API_URL}/namespaces/${encodeURIComponent(id)}`);
2405
+ const url = new URL(`${getEffectiveApiUrl()}/namespaces/${encodeURIComponent(id)}`);
2363
2406
  const response = await fetch(url.toString(), {
2364
- ...DEFAULT_FETCH_CONFIG,
2407
+ ...getFetchConfig(),
2365
2408
  });
2366
2409
  await handleGitLabError(response);
2367
2410
  const data = await response.json();
@@ -2376,12 +2419,12 @@ async function getNamespace(id) {
2376
2419
  * @returns {Promise<GitLabNamespaceExistsResponse>} The verification result
2377
2420
  */
2378
2421
  async function verifyNamespaceExistence(namespacePath, parentId) {
2379
- const url = new URL(`${GITLAB_API_URL}/namespaces/${encodeURIComponent(namespacePath)}/exists`);
2422
+ const url = new URL(`${getEffectiveApiUrl()}/namespaces/${encodeURIComponent(namespacePath)}/exists`);
2380
2423
  if (parentId) {
2381
2424
  url.searchParams.append("parent_id", parentId.toString());
2382
2425
  }
2383
2426
  const response = await fetch(url.toString(), {
2384
- ...DEFAULT_FETCH_CONFIG,
2427
+ ...getFetchConfig(),
2385
2428
  });
2386
2429
  await handleGitLabError(response);
2387
2430
  const data = await response.json();
@@ -2400,7 +2443,7 @@ async function verifyNamespaceExistence(namespacePath, parentId) {
2400
2443
  */
2401
2444
  async function getProject(projectId, options = {}) {
2402
2445
  projectId = decodeURIComponent(projectId); // Decode project ID
2403
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}`);
2446
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}`);
2404
2447
  if (options.license) {
2405
2448
  url.searchParams.append("license", "true");
2406
2449
  }
@@ -2411,7 +2454,7 @@ async function getProject(projectId, options = {}) {
2411
2454
  url.searchParams.append("with_custom_attributes", "true");
2412
2455
  }
2413
2456
  const response = await fetch(url.toString(), {
2414
- ...DEFAULT_FETCH_CONFIG,
2457
+ ...getFetchConfig(),
2415
2458
  });
2416
2459
  await handleGitLabError(response);
2417
2460
  const data = await response.json();
@@ -2438,8 +2481,8 @@ async function listProjects(options = {}) {
2438
2481
  }
2439
2482
  }
2440
2483
  // Make the API request
2441
- const response = await fetch(`${GITLAB_API_URL}/projects?${params.toString()}`, {
2442
- ...DEFAULT_FETCH_CONFIG,
2484
+ const response = await fetch(`${getEffectiveApiUrl()}/projects?${params.toString()}`, {
2485
+ ...getFetchConfig(),
2443
2486
  });
2444
2487
  // Handle errors
2445
2488
  await handleGitLabError(response);
@@ -2457,7 +2500,7 @@ async function listProjects(options = {}) {
2457
2500
  async function listLabels(projectId, options = {}) {
2458
2501
  projectId = decodeURIComponent(projectId); // Decode project ID
2459
2502
  // Construct the URL with project path
2460
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels`);
2503
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels`);
2461
2504
  // Add query parameters
2462
2505
  Object.entries(options).forEach(([key, value]) => {
2463
2506
  if (value !== undefined) {
@@ -2471,7 +2514,7 @@ async function listLabels(projectId, options = {}) {
2471
2514
  });
2472
2515
  // Make the API request
2473
2516
  const response = await fetch(url.toString(), {
2474
- ...DEFAULT_FETCH_CONFIG,
2517
+ ...getFetchConfig(),
2475
2518
  });
2476
2519
  // Handle errors
2477
2520
  await handleGitLabError(response);
@@ -2489,14 +2532,14 @@ async function listLabels(projectId, options = {}) {
2489
2532
  */
2490
2533
  async function getLabel(projectId, labelId, includeAncestorGroups) {
2491
2534
  projectId = decodeURIComponent(projectId); // Decode project ID
2492
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`);
2535
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`);
2493
2536
  // Add query parameters
2494
2537
  if (includeAncestorGroups !== undefined) {
2495
2538
  url.searchParams.append("include_ancestor_groups", includeAncestorGroups ? "true" : "false");
2496
2539
  }
2497
2540
  // Make the API request
2498
2541
  const response = await fetch(url.toString(), {
2499
- ...DEFAULT_FETCH_CONFIG,
2542
+ ...getFetchConfig(),
2500
2543
  });
2501
2544
  // Handle errors
2502
2545
  await handleGitLabError(response);
@@ -2514,8 +2557,8 @@ async function getLabel(projectId, labelId, includeAncestorGroups) {
2514
2557
  async function createLabel(projectId, options) {
2515
2558
  projectId = decodeURIComponent(projectId); // Decode project ID
2516
2559
  // Make the API request
2517
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels`, {
2518
- ...DEFAULT_FETCH_CONFIG,
2560
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels`, {
2561
+ ...getFetchConfig(),
2519
2562
  method: "POST",
2520
2563
  body: JSON.stringify(options),
2521
2564
  });
@@ -2536,8 +2579,8 @@ async function createLabel(projectId, options) {
2536
2579
  async function updateLabel(projectId, labelId, options) {
2537
2580
  projectId = decodeURIComponent(projectId); // Decode project ID
2538
2581
  // Make the API request
2539
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`, {
2540
- ...DEFAULT_FETCH_CONFIG,
2582
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`, {
2583
+ ...getFetchConfig(),
2541
2584
  method: "PUT",
2542
2585
  body: JSON.stringify(options),
2543
2586
  });
@@ -2556,8 +2599,8 @@ async function updateLabel(projectId, labelId, options) {
2556
2599
  async function deleteLabel(projectId, labelId) {
2557
2600
  projectId = decodeURIComponent(projectId); // Decode project ID
2558
2601
  // Make the API request
2559
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`, {
2560
- ...DEFAULT_FETCH_CONFIG,
2602
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`, {
2603
+ ...getFetchConfig(),
2561
2604
  method: "DELETE",
2562
2605
  });
2563
2606
  // Handle errors
@@ -2570,7 +2613,7 @@ async function deleteLabel(projectId, labelId) {
2570
2613
  * @returns {Promise<GitLabProject[]>} Array of projects in the group
2571
2614
  */
2572
2615
  async function listGroupProjects(options) {
2573
- const url = new URL(`${GITLAB_API_URL}/groups/${encodeURIComponent(options.group_id)}/projects`);
2616
+ const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeURIComponent(options.group_id)}/projects`);
2574
2617
  // Add optional parameters to URL
2575
2618
  if (options.include_subgroups)
2576
2619
  url.searchParams.append("include_subgroups", "true");
@@ -2605,7 +2648,7 @@ async function listGroupProjects(options) {
2605
2648
  if (options.with_security_reports !== undefined)
2606
2649
  url.searchParams.append("with_security_reports", options.with_security_reports.toString());
2607
2650
  const response = await fetch(url.toString(), {
2608
- ...DEFAULT_FETCH_CONFIG,
2651
+ ...getFetchConfig(),
2609
2652
  });
2610
2653
  await handleGitLabError(response);
2611
2654
  const projects = await response.json();
@@ -2617,7 +2660,7 @@ async function listGroupProjects(options) {
2617
2660
  */
2618
2661
  async function listWikiPages(projectId, options = {}) {
2619
2662
  projectId = decodeURIComponent(projectId); // Decode project ID
2620
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis`);
2663
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis`);
2621
2664
  if (options.page)
2622
2665
  url.searchParams.append("page", options.page.toString());
2623
2666
  if (options.per_page)
@@ -2625,7 +2668,7 @@ async function listWikiPages(projectId, options = {}) {
2625
2668
  if (options.with_content)
2626
2669
  url.searchParams.append("with_content", options.with_content.toString());
2627
2670
  const response = await fetch(url.toString(), {
2628
- ...DEFAULT_FETCH_CONFIG,
2671
+ ...getFetchConfig(),
2629
2672
  });
2630
2673
  await handleGitLabError(response);
2631
2674
  const data = await response.json();
@@ -2636,7 +2679,7 @@ async function listWikiPages(projectId, options = {}) {
2636
2679
  */
2637
2680
  async function getWikiPage(projectId, slug) {
2638
2681
  projectId = decodeURIComponent(projectId); // Decode project ID
2639
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, { ...DEFAULT_FETCH_CONFIG });
2682
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, { ...getFetchConfig() });
2640
2683
  await handleGitLabError(response);
2641
2684
  const data = await response.json();
2642
2685
  return GitLabWikiPageSchema.parse(data);
@@ -2649,8 +2692,8 @@ async function createWikiPage(projectId, title, content, format) {
2649
2692
  const body = { title, content };
2650
2693
  if (format)
2651
2694
  body.format = format;
2652
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis`, {
2653
- ...DEFAULT_FETCH_CONFIG,
2695
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis`, {
2696
+ ...getFetchConfig(),
2654
2697
  method: "POST",
2655
2698
  body: JSON.stringify(body),
2656
2699
  });
@@ -2670,8 +2713,8 @@ async function updateWikiPage(projectId, slug, title, content, format) {
2670
2713
  body.content = content;
2671
2714
  if (format)
2672
2715
  body.format = format;
2673
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, {
2674
- ...DEFAULT_FETCH_CONFIG,
2716
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, {
2717
+ ...getFetchConfig(),
2675
2718
  method: "PUT",
2676
2719
  body: JSON.stringify(body),
2677
2720
  });
@@ -2684,8 +2727,8 @@ async function updateWikiPage(projectId, slug, title, content, format) {
2684
2727
  */
2685
2728
  async function deleteWikiPage(projectId, slug) {
2686
2729
  projectId = decodeURIComponent(projectId); // Decode project ID
2687
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, {
2688
- ...DEFAULT_FETCH_CONFIG,
2730
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, {
2731
+ ...getFetchConfig(),
2689
2732
  method: "DELETE",
2690
2733
  });
2691
2734
  await handleGitLabError(response);
@@ -2699,7 +2742,7 @@ async function deleteWikiPage(projectId, slug) {
2699
2742
  */
2700
2743
  async function listPipelines(projectId, options = {}) {
2701
2744
  projectId = decodeURIComponent(projectId); // Decode project ID
2702
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines`);
2745
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines`);
2703
2746
  // Add all query parameters
2704
2747
  Object.entries(options).forEach(([key, value]) => {
2705
2748
  if (value !== undefined) {
@@ -2707,7 +2750,7 @@ async function listPipelines(projectId, options = {}) {
2707
2750
  }
2708
2751
  });
2709
2752
  const response = await fetch(url.toString(), {
2710
- ...DEFAULT_FETCH_CONFIG,
2753
+ ...getFetchConfig(),
2711
2754
  });
2712
2755
  await handleGitLabError(response);
2713
2756
  const data = await response.json();
@@ -2722,9 +2765,9 @@ async function listPipelines(projectId, options = {}) {
2722
2765
  */
2723
2766
  async function getPipeline(projectId, pipelineId) {
2724
2767
  projectId = decodeURIComponent(projectId); // Decode project ID
2725
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}`);
2768
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}`);
2726
2769
  const response = await fetch(url.toString(), {
2727
- ...DEFAULT_FETCH_CONFIG,
2770
+ ...getFetchConfig(),
2728
2771
  });
2729
2772
  if (response.status === 404) {
2730
2773
  throw new Error(`Pipeline not found`);
@@ -2743,7 +2786,7 @@ async function getPipeline(projectId, pipelineId) {
2743
2786
  */
2744
2787
  async function listPipelineJobs(projectId, pipelineId, options = {}) {
2745
2788
  projectId = decodeURIComponent(projectId); // Decode project ID
2746
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/jobs`);
2789
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/jobs`);
2747
2790
  // Add all query parameters
2748
2791
  Object.entries(options).forEach(([key, value]) => {
2749
2792
  if (value !== undefined) {
@@ -2756,7 +2799,7 @@ async function listPipelineJobs(projectId, pipelineId, options = {}) {
2756
2799
  }
2757
2800
  });
2758
2801
  const response = await fetch(url.toString(), {
2759
- ...DEFAULT_FETCH_CONFIG,
2802
+ ...getFetchConfig(),
2760
2803
  });
2761
2804
  if (response.status === 404) {
2762
2805
  throw new Error(`Pipeline not found`);
@@ -2775,7 +2818,7 @@ async function listPipelineJobs(projectId, pipelineId, options = {}) {
2775
2818
  */
2776
2819
  async function listPipelineTriggerJobs(projectId, pipelineId, options = {}) {
2777
2820
  projectId = decodeURIComponent(projectId); // Decode project ID
2778
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/bridges`);
2821
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/bridges`);
2779
2822
  // Add all query parameters
2780
2823
  Object.entries(options).forEach(([key, value]) => {
2781
2824
  if (value !== undefined) {
@@ -2788,7 +2831,7 @@ async function listPipelineTriggerJobs(projectId, pipelineId, options = {}) {
2788
2831
  }
2789
2832
  });
2790
2833
  const response = await fetch(url.toString(), {
2791
- ...DEFAULT_FETCH_CONFIG,
2834
+ ...getFetchConfig(),
2792
2835
  });
2793
2836
  if (response.status === 404) {
2794
2837
  throw new Error(`Pipeline not found`);
@@ -2799,9 +2842,9 @@ async function listPipelineTriggerJobs(projectId, pipelineId, options = {}) {
2799
2842
  }
2800
2843
  async function getPipelineJob(projectId, jobId) {
2801
2844
  projectId = decodeURIComponent(projectId); // Decode project ID
2802
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}`);
2845
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}`);
2803
2846
  const response = await fetch(url.toString(), {
2804
- ...DEFAULT_FETCH_CONFIG,
2847
+ ...getFetchConfig(),
2805
2848
  });
2806
2849
  if (response.status === 404) {
2807
2850
  throw new Error(`Job not found`);
@@ -2821,9 +2864,9 @@ async function getPipelineJob(projectId, jobId) {
2821
2864
  */
2822
2865
  async function getPipelineJobOutput(projectId, jobId, limit, offset) {
2823
2866
  projectId = decodeURIComponent(projectId); // Decode project ID
2824
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/trace`);
2867
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/trace`);
2825
2868
  const response = await fetch(url.toString(), {
2826
- ...DEFAULT_FETCH_CONFIG,
2869
+ ...getFetchConfig(),
2827
2870
  headers: {
2828
2871
  ...BASE_HEADERS,
2829
2872
  ...buildAuthHeaders(),
@@ -2867,7 +2910,7 @@ async function getPipelineJobOutput(projectId, jobId, limit, offset) {
2867
2910
  */
2868
2911
  async function createPipeline(projectId, ref, variables) {
2869
2912
  projectId = decodeURIComponent(projectId); // Decode project ID
2870
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline`);
2913
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline`);
2871
2914
  const body = { ref };
2872
2915
  if (variables && variables.length > 0) {
2873
2916
  body.variables = variables;
@@ -2893,7 +2936,7 @@ async function createPipeline(projectId, ref, variables) {
2893
2936
  */
2894
2937
  async function retryPipeline(projectId, pipelineId) {
2895
2938
  projectId = decodeURIComponent(projectId); // Decode project ID
2896
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/retry`);
2939
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/retry`);
2897
2940
  const response = await fetch(url.toString(), {
2898
2941
  method: "POST",
2899
2942
  headers: {
@@ -2914,7 +2957,7 @@ async function retryPipeline(projectId, pipelineId) {
2914
2957
  */
2915
2958
  async function cancelPipeline(projectId, pipelineId) {
2916
2959
  projectId = decodeURIComponent(projectId); // Decode project ID
2917
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/cancel`);
2960
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/cancel`);
2918
2961
  const response = await fetch(url.toString(), {
2919
2962
  method: "POST",
2920
2963
  headers: {
@@ -2936,13 +2979,13 @@ async function cancelPipeline(projectId, pipelineId) {
2936
2979
  */
2937
2980
  async function playPipelineJob(projectId, jobId, variables) {
2938
2981
  projectId = decodeURIComponent(projectId);
2939
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/play`);
2982
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/play`);
2940
2983
  const body = {};
2941
2984
  if (variables && variables.length > 0) {
2942
2985
  body.job_variables_attributes = variables;
2943
2986
  }
2944
2987
  const response = await fetch(url.toString(), {
2945
- ...DEFAULT_FETCH_CONFIG,
2988
+ ...getFetchConfig(),
2946
2989
  method: "POST",
2947
2990
  body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,
2948
2991
  });
@@ -2959,9 +3002,9 @@ async function playPipelineJob(projectId, jobId, variables) {
2959
3002
  */
2960
3003
  async function retryPipelineJob(projectId, jobId) {
2961
3004
  projectId = decodeURIComponent(projectId);
2962
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/retry`);
3005
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/retry`);
2963
3006
  const response = await fetch(url.toString(), {
2964
- ...DEFAULT_FETCH_CONFIG,
3007
+ ...getFetchConfig(),
2965
3008
  method: "POST",
2966
3009
  });
2967
3010
  await handleGitLabError(response);
@@ -2978,12 +3021,12 @@ async function retryPipelineJob(projectId, jobId) {
2978
3021
  */
2979
3022
  async function cancelPipelineJob(projectId, jobId, force) {
2980
3023
  projectId = decodeURIComponent(projectId);
2981
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/cancel`);
3024
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/cancel`);
2982
3025
  if (force !== undefined) {
2983
3026
  url.searchParams.append('force', force.toString());
2984
3027
  }
2985
3028
  const response = await fetch(url.toString(), {
2986
- ...DEFAULT_FETCH_CONFIG,
3029
+ ...getFetchConfig(),
2987
3030
  method: "POST",
2988
3031
  });
2989
3032
  await handleGitLabError(response);
@@ -3015,7 +3058,7 @@ async function getRepositoryTree(options) {
3015
3058
  ...BASE_HEADERS,
3016
3059
  ...buildAuthHeaders(),
3017
3060
  };
3018
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(options.project_id))}/repository/tree?${queryParams.toString()}`, {
3061
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(options.project_id))}/repository/tree?${queryParams.toString()}`, {
3019
3062
  headers,
3020
3063
  });
3021
3064
  if (response.status === 404) {
@@ -3035,7 +3078,7 @@ async function getRepositoryTree(options) {
3035
3078
  */
3036
3079
  async function listProjectMilestones(projectId, options) {
3037
3080
  projectId = decodeURIComponent(projectId);
3038
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones`);
3081
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones`);
3039
3082
  Object.entries(options).forEach(([key, value]) => {
3040
3083
  if (value !== undefined) {
3041
3084
  if (key === "iids" && Array.isArray(value) && value.length > 0) {
@@ -3049,7 +3092,7 @@ async function listProjectMilestones(projectId, options) {
3049
3092
  }
3050
3093
  });
3051
3094
  const response = await fetch(url.toString(), {
3052
- ...DEFAULT_FETCH_CONFIG,
3095
+ ...getFetchConfig(),
3053
3096
  });
3054
3097
  await handleGitLabError(response);
3055
3098
  const data = await response.json();
@@ -3063,9 +3106,9 @@ async function listProjectMilestones(projectId, options) {
3063
3106
  */
3064
3107
  async function getProjectMilestone(projectId, milestoneId) {
3065
3108
  projectId = decodeURIComponent(projectId);
3066
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
3109
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
3067
3110
  const response = await fetch(url.toString(), {
3068
- ...DEFAULT_FETCH_CONFIG,
3111
+ ...getFetchConfig(),
3069
3112
  });
3070
3113
  await handleGitLabError(response);
3071
3114
  const data = await response.json();
@@ -3079,9 +3122,9 @@ async function getProjectMilestone(projectId, milestoneId) {
3079
3122
  */
3080
3123
  async function createProjectMilestone(projectId, options) {
3081
3124
  projectId = decodeURIComponent(projectId);
3082
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones`);
3125
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones`);
3083
3126
  const response = await fetch(url.toString(), {
3084
- ...DEFAULT_FETCH_CONFIG,
3127
+ ...getFetchConfig(),
3085
3128
  method: "POST",
3086
3129
  body: JSON.stringify(options),
3087
3130
  });
@@ -3098,9 +3141,9 @@ async function createProjectMilestone(projectId, options) {
3098
3141
  */
3099
3142
  async function editProjectMilestone(projectId, milestoneId, options) {
3100
3143
  projectId = decodeURIComponent(projectId);
3101
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
3144
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
3102
3145
  const response = await fetch(url.toString(), {
3103
- ...DEFAULT_FETCH_CONFIG,
3146
+ ...getFetchConfig(),
3104
3147
  method: "PUT",
3105
3148
  body: JSON.stringify(options),
3106
3149
  });
@@ -3116,9 +3159,9 @@ async function editProjectMilestone(projectId, milestoneId, options) {
3116
3159
  */
3117
3160
  async function deleteProjectMilestone(projectId, milestoneId) {
3118
3161
  projectId = decodeURIComponent(projectId);
3119
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
3162
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
3120
3163
  const response = await fetch(url.toString(), {
3121
- ...DEFAULT_FETCH_CONFIG,
3164
+ ...getFetchConfig(),
3122
3165
  method: "DELETE",
3123
3166
  });
3124
3167
  await handleGitLabError(response);
@@ -3131,9 +3174,9 @@ async function deleteProjectMilestone(projectId, milestoneId) {
3131
3174
  */
3132
3175
  async function getMilestoneIssues(projectId, milestoneId) {
3133
3176
  projectId = decodeURIComponent(projectId);
3134
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/issues`);
3177
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/issues`);
3135
3178
  const response = await fetch(url.toString(), {
3136
- ...DEFAULT_FETCH_CONFIG,
3179
+ ...getFetchConfig(),
3137
3180
  });
3138
3181
  await handleGitLabError(response);
3139
3182
  const data = await response.json();
@@ -3147,9 +3190,9 @@ async function getMilestoneIssues(projectId, milestoneId) {
3147
3190
  */
3148
3191
  async function getMilestoneMergeRequests(projectId, milestoneId) {
3149
3192
  projectId = decodeURIComponent(projectId);
3150
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/merge_requests`);
3193
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/merge_requests`);
3151
3194
  const response = await fetch(url.toString(), {
3152
- ...DEFAULT_FETCH_CONFIG,
3195
+ ...getFetchConfig(),
3153
3196
  });
3154
3197
  await handleGitLabError(response);
3155
3198
  const data = await response.json();
@@ -3163,9 +3206,9 @@ async function getMilestoneMergeRequests(projectId, milestoneId) {
3163
3206
  */
3164
3207
  async function promoteProjectMilestone(projectId, milestoneId) {
3165
3208
  projectId = decodeURIComponent(projectId);
3166
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/promote`);
3209
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/promote`);
3167
3210
  const response = await fetch(url.toString(), {
3168
- ...DEFAULT_FETCH_CONFIG,
3211
+ ...getFetchConfig(),
3169
3212
  method: "POST",
3170
3213
  });
3171
3214
  await handleGitLabError(response);
@@ -3180,9 +3223,9 @@ async function promoteProjectMilestone(projectId, milestoneId) {
3180
3223
  */
3181
3224
  async function getMilestoneBurndownEvents(projectId, milestoneId) {
3182
3225
  projectId = decodeURIComponent(projectId);
3183
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/burndown_events`);
3226
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/burndown_events`);
3184
3227
  const response = await fetch(url.toString(), {
3185
- ...DEFAULT_FETCH_CONFIG,
3228
+ ...getFetchConfig(),
3186
3229
  });
3187
3230
  await handleGitLabError(response);
3188
3231
  const data = await response.json();
@@ -3196,10 +3239,10 @@ async function getMilestoneBurndownEvents(projectId, milestoneId) {
3196
3239
  */
3197
3240
  async function getUser(username) {
3198
3241
  try {
3199
- const url = new URL(`${GITLAB_API_URL}/users`);
3242
+ const url = new URL(`${getEffectiveApiUrl()}/users`);
3200
3243
  url.searchParams.append("username", username);
3201
3244
  const response = await fetch(url.toString(), {
3202
- ...DEFAULT_FETCH_CONFIG,
3245
+ ...getFetchConfig(),
3203
3246
  });
3204
3247
  await handleGitLabError(response);
3205
3248
  const users = await response.json();
@@ -3250,7 +3293,7 @@ async function getUsers(usernames) {
3250
3293
  */
3251
3294
  async function listCommits(projectId, options = {}) {
3252
3295
  projectId = decodeURIComponent(projectId);
3253
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits`);
3296
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits`);
3254
3297
  // Add query parameters
3255
3298
  if (options.ref_name)
3256
3299
  url.searchParams.append("ref_name", options.ref_name);
@@ -3277,7 +3320,7 @@ async function listCommits(projectId, options = {}) {
3277
3320
  if (options.per_page)
3278
3321
  url.searchParams.append("per_page", options.per_page.toString());
3279
3322
  const response = await fetch(url.toString(), {
3280
- ...DEFAULT_FETCH_CONFIG,
3323
+ ...getFetchConfig(),
3281
3324
  });
3282
3325
  await handleGitLabError(response);
3283
3326
  const data = await response.json();
@@ -3294,12 +3337,12 @@ async function listCommits(projectId, options = {}) {
3294
3337
  */
3295
3338
  async function getCommit(projectId, sha, stats) {
3296
3339
  projectId = decodeURIComponent(projectId);
3297
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits/${encodeURIComponent(sha)}`);
3340
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits/${encodeURIComponent(sha)}`);
3298
3341
  if (stats) {
3299
3342
  url.searchParams.append("stats", "true");
3300
3343
  }
3301
3344
  const response = await fetch(url.toString(), {
3302
- ...DEFAULT_FETCH_CONFIG,
3345
+ ...getFetchConfig(),
3303
3346
  });
3304
3347
  await handleGitLabError(response);
3305
3348
  const data = await response.json();
@@ -3316,7 +3359,7 @@ async function getCommit(projectId, sha, stats) {
3316
3359
  */
3317
3360
  async function getCommitDiff(projectId, sha, full_diff) {
3318
3361
  projectId = decodeURIComponent(projectId);
3319
- const baseUrl = `${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits/${encodeURIComponent(sha)}/diff`;
3362
+ const baseUrl = `${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits/${encodeURIComponent(sha)}/diff`;
3320
3363
  let allDiffs = [];
3321
3364
  let page = 1;
3322
3365
  while (true) {
@@ -3325,7 +3368,7 @@ async function getCommitDiff(projectId, sha, full_diff) {
3325
3368
  url.searchParams.append("page", page.toString());
3326
3369
  }
3327
3370
  const response = await fetch(url.toString(), {
3328
- ...DEFAULT_FETCH_CONFIG,
3371
+ ...getFetchConfig(),
3329
3372
  });
3330
3373
  await handleGitLabError(response);
3331
3374
  const data = await response.json();
@@ -3348,7 +3391,7 @@ async function getCommitDiff(projectId, sha, full_diff) {
3348
3391
  * @returns {Promise<GitLabUser>} The current user
3349
3392
  */
3350
3393
  async function getCurrentUser() {
3351
- const response = await fetch(`${GITLAB_API_URL}/user`, DEFAULT_FETCH_CONFIG);
3394
+ const response = await fetch(`${getEffectiveApiUrl()}/user`, getFetchConfig());
3352
3395
  await handleGitLabError(response);
3353
3396
  const data = await response.json();
3354
3397
  return GitLabUserSchema.parse(data);
@@ -3397,7 +3440,7 @@ async function myIssues(options = {}) {
3397
3440
  async function listProjectMembers(projectId, options = {}) {
3398
3441
  projectId = decodeURIComponent(projectId);
3399
3442
  const effectiveProjectId = getEffectiveProjectId(projectId);
3400
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/members`);
3443
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/members`);
3401
3444
  // Add query parameters
3402
3445
  if (options.query)
3403
3446
  url.searchParams.append("query", options.query);
@@ -3411,7 +3454,7 @@ async function listProjectMembers(projectId, options = {}) {
3411
3454
  url.searchParams.append("per_page", options.per_page.toString());
3412
3455
  if (options.page)
3413
3456
  url.searchParams.append("page", options.page.toString());
3414
- const response = await fetch(url.toString(), DEFAULT_FETCH_CONFIG);
3457
+ const response = await fetch(url.toString(), getFetchConfig());
3415
3458
  await handleGitLabError(response);
3416
3459
  const data = await response.json();
3417
3460
  return z.array(GitLabProjectMemberSchema).parse(data);
@@ -3425,7 +3468,7 @@ async function listProjectMembers(projectId, options = {}) {
3425
3468
  */
3426
3469
  async function listGroupIterations(groupId, options = {}) {
3427
3470
  groupId = decodeURIComponent(groupId);
3428
- const url = new URL(`${GITLAB_API_URL}/groups/${encodeURIComponent(groupId)}/iterations`);
3471
+ const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeURIComponent(groupId)}/iterations`);
3429
3472
  // クエリパラメータの追加
3430
3473
  if (options.state)
3431
3474
  url.searchParams.append("state", options.state);
@@ -3445,7 +3488,7 @@ async function listGroupIterations(groupId, options = {}) {
3445
3488
  url.searchParams.append("page", options.page.toString());
3446
3489
  if (options.per_page)
3447
3490
  url.searchParams.append("per_page", options.per_page.toString());
3448
- const response = await fetch(url.toString(), DEFAULT_FETCH_CONFIG);
3491
+ const response = await fetch(url.toString(), getFetchConfig());
3449
3492
  if (!response.ok) {
3450
3493
  await handleGitLabError(response);
3451
3494
  }
@@ -3476,7 +3519,7 @@ async function markdownUpload(projectId, filePath) {
3476
3519
  filename: fileName,
3477
3520
  contentType: "application/octet-stream",
3478
3521
  });
3479
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/uploads`);
3522
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/uploads`);
3480
3523
  const response = await fetch(url.toString(), {
3481
3524
  method: "POST",
3482
3525
  headers: {
@@ -3495,7 +3538,7 @@ async function markdownUpload(projectId, filePath) {
3495
3538
  }
3496
3539
  async function downloadAttachment(projectId, secret, filename, localPath) {
3497
3540
  const effectiveProjectId = getEffectiveProjectId(projectId);
3498
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/uploads/${secret}/${filename}`);
3541
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/uploads/${secret}/${filename}`);
3499
3542
  const response = await fetch(url.toString(), {
3500
3543
  method: "GET",
3501
3544
  headers: {
@@ -3520,7 +3563,7 @@ async function downloadAttachment(projectId, secret, filename, localPath) {
3520
3563
  * @returns {Promise<GitLabEvent[]>} List of events
3521
3564
  */
3522
3565
  async function listEvents(options = {}) {
3523
- const url = new URL(`${GITLAB_API_URL}/events`);
3566
+ const url = new URL(`${getEffectiveApiUrl()}/events`);
3524
3567
  // Add all query parameters
3525
3568
  Object.entries(options).forEach(([key, value]) => {
3526
3569
  if (value !== undefined) {
@@ -3548,7 +3591,7 @@ async function listEvents(options = {}) {
3548
3591
  */
3549
3592
  async function getProjectEvents(projectId, options = {}) {
3550
3593
  const effectiveProjectId = getEffectiveProjectId(projectId);
3551
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/events`);
3594
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/events`);
3552
3595
  // Add all query parameters
3553
3596
  Object.entries(options).forEach(([key, value]) => {
3554
3597
  if (value !== undefined) {
@@ -3577,7 +3620,7 @@ async function getProjectEvents(projectId, options = {}) {
3577
3620
  */
3578
3621
  async function listReleases(projectId, options = {}) {
3579
3622
  const effectiveProjectId = getEffectiveProjectId(projectId);
3580
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/releases`);
3623
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/releases`);
3581
3624
  // Add query parameters
3582
3625
  Object.entries(options).forEach(([key, value]) => {
3583
3626
  if (value !== undefined) {
@@ -3585,7 +3628,7 @@ async function listReleases(projectId, options = {}) {
3585
3628
  }
3586
3629
  });
3587
3630
  const response = await fetch(url.toString(), {
3588
- ...DEFAULT_FETCH_CONFIG,
3631
+ ...getFetchConfig(),
3589
3632
  });
3590
3633
  await handleGitLabError(response);
3591
3634
  const data = await response.json();
@@ -3601,12 +3644,12 @@ async function listReleases(projectId, options = {}) {
3601
3644
  */
3602
3645
  async function getRelease(projectId, tagName, includeHtmlDescription) {
3603
3646
  const effectiveProjectId = getEffectiveProjectId(projectId);
3604
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}`);
3647
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}`);
3605
3648
  if (includeHtmlDescription !== undefined) {
3606
3649
  url.searchParams.append("include_html_description", includeHtmlDescription.toString());
3607
3650
  }
3608
3651
  const response = await fetch(url.toString(), {
3609
- ...DEFAULT_FETCH_CONFIG,
3652
+ ...getFetchConfig(),
3610
3653
  });
3611
3654
  await handleGitLabError(response);
3612
3655
  const data = await response.json();
@@ -3621,8 +3664,8 @@ async function getRelease(projectId, tagName, includeHtmlDescription) {
3621
3664
  */
3622
3665
  async function createRelease(projectId, options) {
3623
3666
  const effectiveProjectId = getEffectiveProjectId(projectId);
3624
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/releases`, {
3625
- ...DEFAULT_FETCH_CONFIG,
3667
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/releases`, {
3668
+ ...getFetchConfig(),
3626
3669
  method: "POST",
3627
3670
  body: JSON.stringify(options),
3628
3671
  });
@@ -3640,8 +3683,8 @@ async function createRelease(projectId, options) {
3640
3683
  */
3641
3684
  async function updateRelease(projectId, tagName, options) {
3642
3685
  const effectiveProjectId = getEffectiveProjectId(projectId);
3643
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}`, {
3644
- ...DEFAULT_FETCH_CONFIG,
3686
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}`, {
3687
+ ...getFetchConfig(),
3645
3688
  method: "PUT",
3646
3689
  body: JSON.stringify(options),
3647
3690
  });
@@ -3658,8 +3701,8 @@ async function updateRelease(projectId, tagName, options) {
3658
3701
  */
3659
3702
  async function deleteRelease(projectId, tagName) {
3660
3703
  const effectiveProjectId = getEffectiveProjectId(projectId);
3661
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}`, {
3662
- ...DEFAULT_FETCH_CONFIG,
3704
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}`, {
3705
+ ...getFetchConfig(),
3663
3706
  method: "DELETE",
3664
3707
  });
3665
3708
  await handleGitLabError(response);
@@ -3674,8 +3717,8 @@ async function deleteRelease(projectId, tagName) {
3674
3717
  */
3675
3718
  async function createReleaseEvidence(projectId, tagName) {
3676
3719
  const effectiveProjectId = getEffectiveProjectId(projectId);
3677
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}/evidence`, {
3678
- ...DEFAULT_FETCH_CONFIG,
3720
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}/evidence`, {
3721
+ ...getFetchConfig(),
3679
3722
  method: "POST",
3680
3723
  });
3681
3724
  await handleGitLabError(response);
@@ -3690,13 +3733,16 @@ async function createReleaseEvidence(projectId, tagName) {
3690
3733
  */
3691
3734
  async function downloadReleaseAsset(projectId, tagName, directAssetPath) {
3692
3735
  const effectiveProjectId = getEffectiveProjectId(projectId);
3693
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}/downloads/${directAssetPath}`, {
3694
- ...DEFAULT_FETCH_CONFIG,
3736
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/releases/${encodeURIComponent(tagName)}/downloads/${directAssetPath}`, {
3737
+ ...getFetchConfig(),
3695
3738
  });
3696
3739
  await handleGitLabError(response);
3697
3740
  return await response.text();
3698
3741
  }
3699
3742
  server.setRequestHandler(ListToolsRequestSchema, async () => {
3743
+ // In remote auth mode, retrieve session context from AsyncLocalStorage
3744
+ // This ensures the context is available even when called from SDK's async chains
3745
+ const sessionContext = REMOTE_AUTHORIZATION ? sessionAuthStore.getStore() : null;
3700
3746
  // Apply read-only filter first
3701
3747
  const tools0 = GITLAB_READ_ONLY_MODE
3702
3748
  ? allTools.filter(tool => readOnlyTools.includes(tool.name))
@@ -3733,19 +3779,38 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
3733
3779
  };
3734
3780
  });
3735
3781
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
3782
+ // Manually retrieve the session context using the session ID passed in the request.
3783
+ // This is a robust workaround for AsyncLocalStorage context loss.
3784
+ const sessionId = request.params.sessionId;
3785
+ if (REMOTE_AUTHORIZATION && sessionId && authBySession[sessionId]) {
3786
+ const authData = authBySession[sessionId];
3787
+ const sessionContext = {
3788
+ sessionId,
3789
+ header: authData.header,
3790
+ token: authData.token,
3791
+ lastUsed: authData.lastUsed,
3792
+ apiUrl: authData.apiUrl,
3793
+ };
3794
+ // Run the handler within the retrieved context
3795
+ return await sessionAuthStore.run(sessionContext, () => handleToolCall(request.params));
3796
+ }
3797
+ // Fallback for non-remote-auth mode or if session is not found
3798
+ return handleToolCall(request.params);
3799
+ });
3800
+ async function handleToolCall(params) {
3736
3801
  try {
3737
- if (!request.params.arguments) {
3802
+ if (!params.arguments) {
3738
3803
  throw new Error("Arguments are required");
3739
3804
  }
3740
3805
  // Ensure session is established for every request if cookie authentication is enabled
3741
3806
  if (GITLAB_AUTH_COOKIE_PATH) {
3742
3807
  await ensureSessionForRequest();
3743
3808
  }
3744
- logger.info(request.params.name);
3745
- switch (request.params.name) {
3809
+ logger.info(params.name);
3810
+ switch (params.name) {
3746
3811
  case "execute_graphql": {
3747
- const args = ExecuteGraphQLSchema.parse(request.params.arguments);
3748
- const apiUrl = new URL(GITLAB_API_URL);
3812
+ const args = ExecuteGraphQLSchema.parse(params.arguments);
3813
+ const apiUrl = new URL(getEffectiveApiUrl());
3749
3814
  // Build GraphQL endpoint preserving any instance subpath (e.g. /gitlab)
3750
3815
  const restPath = apiUrl.pathname || ""; // e.g. /api/v4 or /gitlab/api/v4
3751
3816
  const idx = restPath.lastIndexOf("/api/v4");
@@ -3758,7 +3823,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3758
3823
  logger.info({ endpoint: graphqlUrl }, "execute_graphql request");
3759
3824
  try {
3760
3825
  const response = await fetch(graphqlUrl, {
3761
- ...DEFAULT_FETCH_CONFIG,
3826
+ ...getFetchConfig(),
3762
3827
  method: "POST",
3763
3828
  headers: {
3764
3829
  ...BASE_HEADERS,
@@ -3794,7 +3859,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3794
3859
  if (GITLAB_PROJECT_ID) {
3795
3860
  throw new Error("Direct project ID is set. So fork_repository is not allowed");
3796
3861
  }
3797
- const forkArgs = ForkRepositorySchema.parse(request.params.arguments);
3862
+ const forkArgs = ForkRepositorySchema.parse(params.arguments);
3798
3863
  try {
3799
3864
  const forkedProject = await forkProject(forkArgs.project_id, forkArgs.namespace);
3800
3865
  return {
@@ -3818,7 +3883,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3818
3883
  }
3819
3884
  }
3820
3885
  case "create_branch": {
3821
- const args = CreateBranchSchema.parse(request.params.arguments);
3886
+ const args = CreateBranchSchema.parse(params.arguments);
3822
3887
  let ref = args.ref;
3823
3888
  if (!ref) {
3824
3889
  ref = await getDefaultBranchRef(args.project_id);
@@ -3832,7 +3897,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3832
3897
  };
3833
3898
  }
3834
3899
  case "get_branch_diffs": {
3835
- const args = GetBranchDiffsSchema.parse(request.params.arguments);
3900
+ const args = GetBranchDiffsSchema.parse(params.arguments);
3836
3901
  const diffResp = await getBranchDiffs(args.project_id, args.from, args.to, args.straight);
3837
3902
  if (args.excluded_file_patterns?.length) {
3838
3903
  const regexPatterns = args.excluded_file_patterns.map(pattern => new RegExp(pattern));
@@ -3850,7 +3915,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3850
3915
  };
3851
3916
  }
3852
3917
  case "search_repositories": {
3853
- const args = SearchRepositoriesSchema.parse(request.params.arguments);
3918
+ const args = SearchRepositoriesSchema.parse(params.arguments);
3854
3919
  const results = await searchProjects(args.search, args.page, args.per_page);
3855
3920
  return {
3856
3921
  content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
@@ -3860,35 +3925,35 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3860
3925
  if (GITLAB_PROJECT_ID) {
3861
3926
  throw new Error("Direct project ID is set. So fork_repository is not allowed");
3862
3927
  }
3863
- const args = CreateRepositorySchema.parse(request.params.arguments);
3928
+ const args = CreateRepositorySchema.parse(params.arguments);
3864
3929
  const repository = await createRepository(args);
3865
3930
  return {
3866
3931
  content: [{ type: "text", text: JSON.stringify(repository, null, 2) }],
3867
3932
  };
3868
3933
  }
3869
3934
  case "get_file_contents": {
3870
- const args = GetFileContentsSchema.parse(request.params.arguments);
3935
+ const args = GetFileContentsSchema.parse(params.arguments);
3871
3936
  const contents = await getFileContents(args.project_id, args.file_path, args.ref);
3872
3937
  return {
3873
3938
  content: [{ type: "text", text: JSON.stringify(contents, null, 2) }],
3874
3939
  };
3875
3940
  }
3876
3941
  case "create_or_update_file": {
3877
- const args = CreateOrUpdateFileSchema.parse(request.params.arguments);
3942
+ const args = CreateOrUpdateFileSchema.parse(params.arguments);
3878
3943
  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);
3879
3944
  return {
3880
3945
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
3881
3946
  };
3882
3947
  }
3883
3948
  case "push_files": {
3884
- const args = PushFilesSchema.parse(request.params.arguments);
3949
+ const args = PushFilesSchema.parse(params.arguments);
3885
3950
  const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map(f => ({ path: f.file_path, content: f.content })));
3886
3951
  return {
3887
3952
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
3888
3953
  };
3889
3954
  }
3890
3955
  case "create_issue": {
3891
- const args = CreateIssueSchema.parse(request.params.arguments);
3956
+ const args = CreateIssueSchema.parse(params.arguments);
3892
3957
  const { project_id, ...options } = args;
3893
3958
  const issue = await createIssue(project_id, options);
3894
3959
  return {
@@ -3896,7 +3961,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3896
3961
  };
3897
3962
  }
3898
3963
  case "create_merge_request": {
3899
- const args = CreateMergeRequestSchema.parse(request.params.arguments);
3964
+ const args = CreateMergeRequestSchema.parse(params.arguments);
3900
3965
  const { project_id, ...options } = args;
3901
3966
  const mergeRequest = await createMergeRequest(project_id, options);
3902
3967
  return {
@@ -3904,7 +3969,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3904
3969
  };
3905
3970
  }
3906
3971
  case "delete_merge_request_discussion_note": {
3907
- const args = DeleteMergeRequestDiscussionNoteSchema.parse(request.params.arguments);
3972
+ const args = DeleteMergeRequestDiscussionNoteSchema.parse(params.arguments);
3908
3973
  const { project_id, merge_request_iid, discussion_id, note_id } = args;
3909
3974
  await deleteMergeRequestDiscussionNote(project_id, merge_request_iid, discussion_id, note_id);
3910
3975
  return {
@@ -3912,7 +3977,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3912
3977
  };
3913
3978
  }
3914
3979
  case "update_merge_request_discussion_note": {
3915
- const args = UpdateMergeRequestDiscussionNoteSchema.parse(request.params.arguments);
3980
+ const args = UpdateMergeRequestDiscussionNoteSchema.parse(params.arguments);
3916
3981
  const note = await updateMergeRequestDiscussionNote(args.project_id, args.merge_request_iid, args.discussion_id, args.note_id, args.body, // Now optional
3917
3982
  args.resolved // Now one of body or resolved must be provided, not both
3918
3983
  );
@@ -3921,84 +3986,84 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3921
3986
  };
3922
3987
  }
3923
3988
  case "create_merge_request_discussion_note": {
3924
- const args = CreateMergeRequestDiscussionNoteSchema.parse(request.params.arguments);
3989
+ const args = CreateMergeRequestDiscussionNoteSchema.parse(params.arguments);
3925
3990
  const note = await createMergeRequestDiscussionNote(args.project_id, args.merge_request_iid, args.discussion_id, args.body, args.created_at);
3926
3991
  return {
3927
3992
  content: [{ type: "text", text: JSON.stringify(note, null, 2) }],
3928
3993
  };
3929
3994
  }
3930
3995
  case "create_merge_request_note": {
3931
- const args = CreateMergeRequestNoteSchema.parse(request.params.arguments);
3996
+ const args = CreateMergeRequestNoteSchema.parse(params.arguments);
3932
3997
  const note = await createMergeRequestNote(args.project_id, args.merge_request_iid, args.body);
3933
3998
  return {
3934
3999
  content: [{ type: "text", text: JSON.stringify(note, null, 2) }],
3935
4000
  };
3936
4001
  }
3937
4002
  case "delete_merge_request_note": {
3938
- const args = DeleteMergeRequestNoteSchema.parse(request.params.arguments);
4003
+ const args = DeleteMergeRequestNoteSchema.parse(params.arguments);
3939
4004
  await deleteMergeRequestNote(args.project_id, args.merge_request_iid, args.note_id);
3940
4005
  return {
3941
4006
  content: [{ type: "text", text: "Merge request note deleted successfully" }],
3942
4007
  };
3943
4008
  }
3944
4009
  case 'get_merge_request_note': {
3945
- const args = GetMergeRequestNoteSchema.parse(request.params.arguments);
4010
+ const args = GetMergeRequestNoteSchema.parse(params.arguments);
3946
4011
  const note = await getMergeRequestNote(args.project_id, args.merge_request_iid, args.note_id);
3947
4012
  return {
3948
4013
  content: [{ type: "text", text: JSON.stringify(note, null, 2) }],
3949
4014
  };
3950
4015
  }
3951
4016
  case 'get_merge_request_notes': {
3952
- const args = GetMergeRequestNotesSchema.parse(request.params.arguments);
4017
+ const args = GetMergeRequestNotesSchema.parse(params.arguments);
3953
4018
  const notes = await getMergeRequestNotes(args.project_id, args.merge_request_iid, args.sort, args.order_by);
3954
4019
  return {
3955
4020
  content: [{ type: "text", text: JSON.stringify(notes, null, 2) }],
3956
4021
  };
3957
4022
  }
3958
4023
  case 'update_merge_request_note': {
3959
- const args = UpdateMergeRequestNoteSchema.parse(request.params.arguments);
4024
+ const args = UpdateMergeRequestNoteSchema.parse(params.arguments);
3960
4025
  const note = await updateMergeRequestNote(args.project_id, args.merge_request_iid, args.note_id, args.body);
3961
4026
  return {
3962
4027
  content: [{ type: "text", text: JSON.stringify(note, null, 2) }],
3963
4028
  };
3964
4029
  }
3965
4030
  case "update_issue_note": {
3966
- const args = UpdateIssueNoteSchema.parse(request.params.arguments);
4031
+ const args = UpdateIssueNoteSchema.parse(params.arguments);
3967
4032
  const note = await updateIssueNote(args.project_id, args.issue_iid, args.discussion_id, args.note_id, args.body);
3968
4033
  return {
3969
4034
  content: [{ type: "text", text: JSON.stringify(note, null, 2) }],
3970
4035
  };
3971
4036
  }
3972
4037
  case "create_issue_note": {
3973
- const args = CreateIssueNoteSchema.parse(request.params.arguments);
4038
+ const args = CreateIssueNoteSchema.parse(params.arguments);
3974
4039
  const note = await createIssueNote(args.project_id, args.issue_iid, args.discussion_id, args.body, args.created_at);
3975
4040
  return {
3976
4041
  content: [{ type: "text", text: JSON.stringify(note, null, 2) }],
3977
4042
  };
3978
4043
  }
3979
4044
  case "get_merge_request": {
3980
- const args = GetMergeRequestSchema.parse(request.params.arguments);
4045
+ const args = GetMergeRequestSchema.parse(params.arguments);
3981
4046
  const mergeRequest = await getMergeRequest(args.project_id, args.merge_request_iid, args.source_branch);
3982
4047
  return {
3983
4048
  content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
3984
4049
  };
3985
4050
  }
3986
4051
  case "get_merge_request_diffs": {
3987
- const args = GetMergeRequestDiffsSchema.parse(request.params.arguments);
4052
+ const args = GetMergeRequestDiffsSchema.parse(params.arguments);
3988
4053
  const diffs = await getMergeRequestDiffs(args.project_id, args.merge_request_iid, args.source_branch, args.view);
3989
4054
  return {
3990
4055
  content: [{ type: "text", text: JSON.stringify(diffs, null, 2) }],
3991
4056
  };
3992
4057
  }
3993
4058
  case "list_merge_request_diffs": {
3994
- const args = ListMergeRequestDiffsSchema.parse(request.params.arguments);
4059
+ const args = ListMergeRequestDiffsSchema.parse(params.arguments);
3995
4060
  const changes = await listMergeRequestDiffs(args.project_id, args.merge_request_iid, args.source_branch, args.page, args.per_page, args.unidiff);
3996
4061
  return {
3997
4062
  content: [{ type: "text", text: JSON.stringify(changes, null, 2) }],
3998
4063
  };
3999
4064
  }
4000
4065
  case "update_merge_request": {
4001
- const args = UpdateMergeRequestSchema.parse(request.params.arguments);
4066
+ const args = UpdateMergeRequestSchema.parse(params.arguments);
4002
4067
  const { project_id, merge_request_iid, source_branch, ...options } = args;
4003
4068
  const mergeRequest = await updateMergeRequest(project_id, options, merge_request_iid, source_branch);
4004
4069
  return {
@@ -4006,7 +4071,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4006
4071
  };
4007
4072
  }
4008
4073
  case "merge_merge_request": {
4009
- const args = MergeMergeRequestSchema.parse(request.params.arguments);
4074
+ const args = MergeMergeRequestSchema.parse(params.arguments);
4010
4075
  const { project_id, merge_request_iid, ...options } = args;
4011
4076
  const mergeRequest = await mergeMergeRequest(project_id, options, merge_request_iid);
4012
4077
  return {
@@ -4014,7 +4079,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4014
4079
  };
4015
4080
  }
4016
4081
  case "mr_discussions": {
4017
- const args = ListMergeRequestDiscussionsSchema.parse(request.params.arguments);
4082
+ const args = ListMergeRequestDiscussionsSchema.parse(params.arguments);
4018
4083
  const { project_id, merge_request_iid, ...options } = args;
4019
4084
  const discussions = await listMergeRequestDiscussions(project_id, merge_request_iid, options);
4020
4085
  return {
@@ -4022,7 +4087,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4022
4087
  };
4023
4088
  }
4024
4089
  case "list_namespaces": {
4025
- const args = ListNamespacesSchema.parse(request.params.arguments);
4090
+ const args = ListNamespacesSchema.parse(params.arguments);
4026
4091
  const url = new URL(`${GITLAB_API_URL}/namespaces`);
4027
4092
  if (args.search) {
4028
4093
  url.searchParams.append("search", args.search);
@@ -4037,7 +4102,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4037
4102
  url.searchParams.append("owned", args.owned.toString());
4038
4103
  }
4039
4104
  const response = await fetch(url.toString(), {
4040
- ...DEFAULT_FETCH_CONFIG,
4105
+ ...getFetchConfig(),
4041
4106
  });
4042
4107
  await handleGitLabError(response);
4043
4108
  const data = await response.json();
@@ -4047,10 +4112,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4047
4112
  };
4048
4113
  }
4049
4114
  case "get_namespace": {
4050
- const args = GetNamespaceSchema.parse(request.params.arguments);
4115
+ const args = GetNamespaceSchema.parse(params.arguments);
4051
4116
  const url = new URL(`${GITLAB_API_URL}/namespaces/${encodeURIComponent(args.namespace_id)}`);
4052
4117
  const response = await fetch(url.toString(), {
4053
- ...DEFAULT_FETCH_CONFIG,
4118
+ ...getFetchConfig(),
4054
4119
  });
4055
4120
  await handleGitLabError(response);
4056
4121
  const data = await response.json();
@@ -4060,10 +4125,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4060
4125
  };
4061
4126
  }
4062
4127
  case "verify_namespace": {
4063
- const args = VerifyNamespaceSchema.parse(request.params.arguments);
4128
+ const args = VerifyNamespaceSchema.parse(params.arguments);
4064
4129
  const url = new URL(`${GITLAB_API_URL}/namespaces/${encodeURIComponent(args.path)}/exists`);
4065
4130
  const response = await fetch(url.toString(), {
4066
- ...DEFAULT_FETCH_CONFIG,
4131
+ ...getFetchConfig(),
4067
4132
  });
4068
4133
  await handleGitLabError(response);
4069
4134
  const data = await response.json();
@@ -4073,27 +4138,35 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4073
4138
  };
4074
4139
  }
4075
4140
  case "get_project": {
4076
- const args = GetProjectSchema.parse(request.params.arguments);
4077
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(args.project_id))}`);
4141
+ const args = GetProjectSchema.parse(params.arguments);
4142
+ const options = params.arguments;
4143
+ const effectiveProjectId = getEffectiveProjectId(args.project_id);
4144
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}`);
4145
+ if (options.license)
4146
+ url.searchParams.append("license", "true");
4147
+ if (options.statistics)
4148
+ url.searchParams.append("statistics", "true");
4149
+ if (options.with_custom_attributes)
4150
+ url.searchParams.append("with_custom_attributes", "true");
4078
4151
  const response = await fetch(url.toString(), {
4079
- ...DEFAULT_FETCH_CONFIG,
4152
+ ...getFetchConfig(),
4080
4153
  });
4081
4154
  await handleGitLabError(response);
4082
4155
  const data = await response.json();
4083
- const project = GitLabProjectSchema.parse(data);
4156
+ // Return raw data without parsing through our schema to avoid type mismatches in tests
4084
4157
  return {
4085
- content: [{ type: "text", text: JSON.stringify(project, null, 2) }],
4158
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
4086
4159
  };
4087
4160
  }
4088
4161
  case "list_projects": {
4089
- const args = ListProjectsSchema.parse(request.params.arguments);
4162
+ const args = ListProjectsSchema.parse(params.arguments);
4090
4163
  const projects = await listProjects(args);
4091
4164
  return {
4092
4165
  content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
4093
4166
  };
4094
4167
  }
4095
4168
  case "list_project_members": {
4096
- const args = ListProjectMembersSchema.parse(request.params.arguments);
4169
+ const args = ListProjectMembersSchema.parse(params.arguments);
4097
4170
  const { project_id, ...options } = args;
4098
4171
  const members = await listProjectMembers(project_id, options);
4099
4172
  return {
@@ -4101,14 +4174,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4101
4174
  };
4102
4175
  }
4103
4176
  case "get_users": {
4104
- const args = GetUsersSchema.parse(request.params.arguments);
4177
+ const args = GetUsersSchema.parse(params.arguments);
4105
4178
  const usersMap = await getUsers(args.usernames);
4106
4179
  return {
4107
4180
  content: [{ type: "text", text: JSON.stringify(usersMap, null, 2) }],
4108
4181
  };
4109
4182
  }
4110
4183
  case "create_note": {
4111
- const args = CreateNoteSchema.parse(request.params.arguments);
4184
+ const args = CreateNoteSchema.parse(params.arguments);
4112
4185
  const { project_id, noteable_type, noteable_iid, body } = args;
4113
4186
  const note = await createNote(project_id, noteable_type, noteable_iid, body);
4114
4187
  return {
@@ -4116,7 +4189,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4116
4189
  };
4117
4190
  }
4118
4191
  case "get_draft_note": {
4119
- const args = GetDraftNoteSchema.parse(request.params.arguments);
4192
+ const args = GetDraftNoteSchema.parse(params.arguments);
4120
4193
  const { project_id, merge_request_iid, draft_note_id } = args;
4121
4194
  const draftNote = await getDraftNote(project_id, merge_request_iid, draft_note_id);
4122
4195
  return {
@@ -4124,7 +4197,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4124
4197
  };
4125
4198
  }
4126
4199
  case "list_draft_notes": {
4127
- const args = ListDraftNotesSchema.parse(request.params.arguments);
4200
+ const args = ListDraftNotesSchema.parse(params.arguments);
4128
4201
  const { project_id, merge_request_iid } = args;
4129
4202
  const draftNotes = await listDraftNotes(project_id, merge_request_iid);
4130
4203
  return {
@@ -4132,7 +4205,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4132
4205
  };
4133
4206
  }
4134
4207
  case "create_draft_note": {
4135
- const args = CreateDraftNoteSchema.parse(request.params.arguments);
4208
+ const args = CreateDraftNoteSchema.parse(params.arguments);
4136
4209
  const { project_id, merge_request_iid, body, position, resolve_discussion } = args;
4137
4210
  const draftNote = await createDraftNote(project_id, merge_request_iid, body, position, resolve_discussion);
4138
4211
  return {
@@ -4140,7 +4213,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4140
4213
  };
4141
4214
  }
4142
4215
  case "update_draft_note": {
4143
- const args = UpdateDraftNoteSchema.parse(request.params.arguments);
4216
+ const args = UpdateDraftNoteSchema.parse(params.arguments);
4144
4217
  const { project_id, merge_request_iid, draft_note_id, body, position, resolve_discussion } = args;
4145
4218
  const draftNote = await updateDraftNote(project_id, merge_request_iid, draft_note_id, body, position, resolve_discussion);
4146
4219
  return {
@@ -4148,7 +4221,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4148
4221
  };
4149
4222
  }
4150
4223
  case "delete_draft_note": {
4151
- const args = DeleteDraftNoteSchema.parse(request.params.arguments);
4224
+ const args = DeleteDraftNoteSchema.parse(params.arguments);
4152
4225
  const { project_id, merge_request_iid, draft_note_id } = args;
4153
4226
  await deleteDraftNote(project_id, merge_request_iid, draft_note_id);
4154
4227
  return {
@@ -4156,7 +4229,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4156
4229
  };
4157
4230
  }
4158
4231
  case "publish_draft_note": {
4159
- const args = PublishDraftNoteSchema.parse(request.params.arguments);
4232
+ const args = PublishDraftNoteSchema.parse(params.arguments);
4160
4233
  const { project_id, merge_request_iid, draft_note_id } = args;
4161
4234
  const publishedNote = await publishDraftNote(project_id, merge_request_iid, draft_note_id);
4162
4235
  return {
@@ -4164,7 +4237,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4164
4237
  };
4165
4238
  }
4166
4239
  case "bulk_publish_draft_notes": {
4167
- const args = BulkPublishDraftNotesSchema.parse(request.params.arguments);
4240
+ const args = BulkPublishDraftNotesSchema.parse(params.arguments);
4168
4241
  const { project_id, merge_request_iid } = args;
4169
4242
  const publishedNotes = await bulkPublishDraftNotes(project_id, merge_request_iid);
4170
4243
  return {
@@ -4172,7 +4245,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4172
4245
  };
4173
4246
  }
4174
4247
  case "create_merge_request_thread": {
4175
- const args = CreateMergeRequestThreadSchema.parse(request.params.arguments);
4248
+ const args = CreateMergeRequestThreadSchema.parse(params.arguments);
4176
4249
  const { project_id, merge_request_iid, body, position, created_at } = args;
4177
4250
  const thread = await createMergeRequestThread(project_id, merge_request_iid, body, position, created_at);
4178
4251
  return {
@@ -4180,7 +4253,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4180
4253
  };
4181
4254
  }
4182
4255
  case "resolve_merge_request_thread": {
4183
- const args = ResolveMergeRequestThreadSchema.parse(request.params.arguments);
4256
+ const args = ResolveMergeRequestThreadSchema.parse(params.arguments);
4184
4257
  const { project_id, merge_request_iid, discussion_id, resolved } = args;
4185
4258
  await resolveMergeRequestThread(project_id, merge_request_iid, discussion_id, resolved);
4186
4259
  return {
@@ -4188,7 +4261,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4188
4261
  };
4189
4262
  }
4190
4263
  case "list_issues": {
4191
- const args = ListIssuesSchema.parse(request.params.arguments);
4264
+ const args = ListIssuesSchema.parse(params.arguments);
4192
4265
  const { project_id, ...options } = args;
4193
4266
  const issues = await listIssues(project_id, options);
4194
4267
  return {
@@ -4196,21 +4269,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4196
4269
  };
4197
4270
  }
4198
4271
  case "my_issues": {
4199
- const args = MyIssuesSchema.parse(request.params.arguments);
4272
+ const args = MyIssuesSchema.parse(params.arguments);
4200
4273
  const issues = await myIssues(args);
4201
4274
  return {
4202
4275
  content: [{ type: "text", text: JSON.stringify(issues, null, 2) }],
4203
4276
  };
4204
4277
  }
4205
4278
  case "get_issue": {
4206
- const args = GetIssueSchema.parse(request.params.arguments);
4279
+ const args = GetIssueSchema.parse(params.arguments);
4207
4280
  const issue = await getIssue(args.project_id, args.issue_iid);
4208
4281
  return {
4209
4282
  content: [{ type: "text", text: JSON.stringify(issue, null, 2) }],
4210
4283
  };
4211
4284
  }
4212
4285
  case "update_issue": {
4213
- const args = UpdateIssueSchema.parse(request.params.arguments);
4286
+ const args = UpdateIssueSchema.parse(params.arguments);
4214
4287
  const { project_id, issue_iid, ...options } = args;
4215
4288
  const issue = await updateIssue(project_id, issue_iid, options);
4216
4289
  return {
@@ -4218,7 +4291,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4218
4291
  };
4219
4292
  }
4220
4293
  case "delete_issue": {
4221
- const args = DeleteIssueSchema.parse(request.params.arguments);
4294
+ const args = DeleteIssueSchema.parse(params.arguments);
4222
4295
  await deleteIssue(args.project_id, args.issue_iid);
4223
4296
  return {
4224
4297
  content: [
@@ -4230,14 +4303,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4230
4303
  };
4231
4304
  }
4232
4305
  case "list_issue_links": {
4233
- const args = ListIssueLinksSchema.parse(request.params.arguments);
4306
+ const args = ListIssueLinksSchema.parse(params.arguments);
4234
4307
  const links = await listIssueLinks(args.project_id, args.issue_iid);
4235
4308
  return {
4236
4309
  content: [{ type: "text", text: JSON.stringify(links, null, 2) }],
4237
4310
  };
4238
4311
  }
4239
4312
  case "list_issue_discussions": {
4240
- const args = ListIssueDiscussionsSchema.parse(request.params.arguments);
4313
+ const args = ListIssueDiscussionsSchema.parse(params.arguments);
4241
4314
  const { project_id, issue_iid, ...options } = args;
4242
4315
  const discussions = await listIssueDiscussions(project_id, issue_iid, options);
4243
4316
  return {
@@ -4245,21 +4318,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4245
4318
  };
4246
4319
  }
4247
4320
  case "get_issue_link": {
4248
- const args = GetIssueLinkSchema.parse(request.params.arguments);
4321
+ const args = GetIssueLinkSchema.parse(params.arguments);
4249
4322
  const link = await getIssueLink(args.project_id, args.issue_iid, args.issue_link_id);
4250
4323
  return {
4251
4324
  content: [{ type: "text", text: JSON.stringify(link, null, 2) }],
4252
4325
  };
4253
4326
  }
4254
4327
  case "create_issue_link": {
4255
- const args = CreateIssueLinkSchema.parse(request.params.arguments);
4328
+ const args = CreateIssueLinkSchema.parse(params.arguments);
4256
4329
  const link = await createIssueLink(args.project_id, args.issue_iid, args.target_project_id, args.target_issue_iid, args.link_type);
4257
4330
  return {
4258
4331
  content: [{ type: "text", text: JSON.stringify(link, null, 2) }],
4259
4332
  };
4260
4333
  }
4261
4334
  case "delete_issue_link": {
4262
- const args = DeleteIssueLinkSchema.parse(request.params.arguments);
4335
+ const args = DeleteIssueLinkSchema.parse(params.arguments);
4263
4336
  await deleteIssueLink(args.project_id, args.issue_iid, args.issue_link_id);
4264
4337
  return {
4265
4338
  content: [
@@ -4274,28 +4347,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4274
4347
  };
4275
4348
  }
4276
4349
  case "list_labels": {
4277
- const args = ListLabelsSchema.parse(request.params.arguments);
4350
+ const args = ListLabelsSchema.parse(params.arguments);
4278
4351
  const labels = await listLabels(args.project_id, args);
4279
4352
  return {
4280
4353
  content: [{ type: "text", text: JSON.stringify(labels, null, 2) }],
4281
4354
  };
4282
4355
  }
4283
4356
  case "get_label": {
4284
- const args = GetLabelSchema.parse(request.params.arguments);
4357
+ const args = GetLabelSchema.parse(params.arguments);
4285
4358
  const label = await getLabel(args.project_id, args.label_id, args.include_ancestor_groups);
4286
4359
  return {
4287
4360
  content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
4288
4361
  };
4289
4362
  }
4290
4363
  case "create_label": {
4291
- const args = CreateLabelSchema.parse(request.params.arguments);
4364
+ const args = CreateLabelSchema.parse(params.arguments);
4292
4365
  const label = await createLabel(args.project_id, args);
4293
4366
  return {
4294
4367
  content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
4295
4368
  };
4296
4369
  }
4297
4370
  case "update_label": {
4298
- const args = UpdateLabelSchema.parse(request.params.arguments);
4371
+ const args = UpdateLabelSchema.parse(params.arguments);
4299
4372
  const { project_id, label_id, ...options } = args;
4300
4373
  const label = await updateLabel(project_id, label_id, options);
4301
4374
  return {
@@ -4303,7 +4376,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4303
4376
  };
4304
4377
  }
4305
4378
  case "delete_label": {
4306
- const args = DeleteLabelSchema.parse(request.params.arguments);
4379
+ const args = DeleteLabelSchema.parse(params.arguments);
4307
4380
  await deleteLabel(args.project_id, args.label_id);
4308
4381
  return {
4309
4382
  content: [
@@ -4315,14 +4388,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4315
4388
  };
4316
4389
  }
4317
4390
  case "list_group_projects": {
4318
- const args = ListGroupProjectsSchema.parse(request.params.arguments);
4391
+ const args = ListGroupProjectsSchema.parse(params.arguments);
4319
4392
  const projects = await listGroupProjects(args);
4320
4393
  return {
4321
4394
  content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
4322
4395
  };
4323
4396
  }
4324
4397
  case "list_wiki_pages": {
4325
- const { project_id, page, per_page, with_content } = ListWikiPagesSchema.parse(request.params.arguments);
4398
+ const { project_id, page, per_page, with_content } = ListWikiPagesSchema.parse(params.arguments);
4326
4399
  const wikiPages = await listWikiPages(project_id, {
4327
4400
  page,
4328
4401
  per_page,
@@ -4333,28 +4406,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4333
4406
  };
4334
4407
  }
4335
4408
  case "get_wiki_page": {
4336
- const { project_id, slug } = GetWikiPageSchema.parse(request.params.arguments);
4409
+ const { project_id, slug } = GetWikiPageSchema.parse(params.arguments);
4337
4410
  const wikiPage = await getWikiPage(project_id, slug);
4338
4411
  return {
4339
4412
  content: [{ type: "text", text: JSON.stringify(wikiPage, null, 2) }],
4340
4413
  };
4341
4414
  }
4342
4415
  case "create_wiki_page": {
4343
- const { project_id, title, content, format } = CreateWikiPageSchema.parse(request.params.arguments);
4416
+ const { project_id, title, content, format } = CreateWikiPageSchema.parse(params.arguments);
4344
4417
  const wikiPage = await createWikiPage(project_id, title, content, format);
4345
4418
  return {
4346
4419
  content: [{ type: "text", text: JSON.stringify(wikiPage, null, 2) }],
4347
4420
  };
4348
4421
  }
4349
4422
  case "update_wiki_page": {
4350
- const { project_id, slug, title, content, format } = UpdateWikiPageSchema.parse(request.params.arguments);
4423
+ const { project_id, slug, title, content, format } = UpdateWikiPageSchema.parse(params.arguments);
4351
4424
  const wikiPage = await updateWikiPage(project_id, slug, title, content, format);
4352
4425
  return {
4353
4426
  content: [{ type: "text", text: JSON.stringify(wikiPage, null, 2) }],
4354
4427
  };
4355
4428
  }
4356
4429
  case "delete_wiki_page": {
4357
- const { project_id, slug } = DeleteWikiPageSchema.parse(request.params.arguments);
4430
+ const { project_id, slug } = DeleteWikiPageSchema.parse(params.arguments);
4358
4431
  await deleteWikiPage(project_id, slug);
4359
4432
  return {
4360
4433
  content: [
@@ -4369,14 +4442,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4369
4442
  };
4370
4443
  }
4371
4444
  case "get_repository_tree": {
4372
- const args = GetRepositoryTreeSchema.parse(request.params.arguments);
4445
+ const args = GetRepositoryTreeSchema.parse(params.arguments);
4373
4446
  const tree = await getRepositoryTree(args);
4374
4447
  return {
4375
4448
  content: [{ type: "text", text: JSON.stringify(tree, null, 2) }],
4376
4449
  };
4377
4450
  }
4378
4451
  case "list_pipelines": {
4379
- const args = ListPipelinesSchema.parse(request.params.arguments);
4452
+ const args = ListPipelinesSchema.parse(params.arguments);
4380
4453
  const { project_id, ...options } = args;
4381
4454
  const pipelines = await listPipelines(project_id, options);
4382
4455
  return {
@@ -4384,7 +4457,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4384
4457
  };
4385
4458
  }
4386
4459
  case "get_pipeline": {
4387
- const { project_id, pipeline_id } = GetPipelineSchema.parse(request.params.arguments);
4460
+ const { project_id, pipeline_id } = GetPipelineSchema.parse(params.arguments);
4388
4461
  const pipeline = await getPipeline(project_id, pipeline_id);
4389
4462
  return {
4390
4463
  content: [
@@ -4396,7 +4469,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4396
4469
  };
4397
4470
  }
4398
4471
  case "list_pipeline_jobs": {
4399
- const { project_id, pipeline_id, ...options } = ListPipelineJobsSchema.parse(request.params.arguments);
4472
+ const { project_id, pipeline_id, ...options } = ListPipelineJobsSchema.parse(params.arguments);
4400
4473
  const jobs = await listPipelineJobs(project_id, pipeline_id, options);
4401
4474
  return {
4402
4475
  content: [
@@ -4408,7 +4481,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4408
4481
  };
4409
4482
  }
4410
4483
  case "list_pipeline_trigger_jobs": {
4411
- const { project_id, pipeline_id, ...options } = ListPipelineTriggerJobsSchema.parse(request.params.arguments);
4484
+ const { project_id, pipeline_id, ...options } = ListPipelineTriggerJobsSchema.parse(params.arguments);
4412
4485
  const triggerJobs = await listPipelineTriggerJobs(project_id, pipeline_id, options);
4413
4486
  return {
4414
4487
  content: [
@@ -4420,7 +4493,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4420
4493
  };
4421
4494
  }
4422
4495
  case "get_pipeline_job": {
4423
- const { project_id, job_id } = GetPipelineJobOutputSchema.parse(request.params.arguments);
4496
+ const { project_id, job_id } = GetPipelineJobOutputSchema.parse(params.arguments);
4424
4497
  const jobDetails = await getPipelineJob(project_id, job_id);
4425
4498
  return {
4426
4499
  content: [
@@ -4432,7 +4505,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4432
4505
  };
4433
4506
  }
4434
4507
  case "get_pipeline_job_output": {
4435
- const { project_id, job_id, limit, offset } = GetPipelineJobOutputSchema.parse(request.params.arguments);
4508
+ const { project_id, job_id, limit, offset } = GetPipelineJobOutputSchema.parse(params.arguments);
4436
4509
  const jobOutput = await getPipelineJobOutput(project_id, job_id, limit, offset);
4437
4510
  return {
4438
4511
  content: [
@@ -4444,7 +4517,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4444
4517
  };
4445
4518
  }
4446
4519
  case "create_pipeline": {
4447
- const { project_id, ref, variables } = CreatePipelineSchema.parse(request.params.arguments);
4520
+ const { project_id, ref, variables } = CreatePipelineSchema.parse(params.arguments);
4448
4521
  const pipeline = await createPipeline(project_id, ref, variables);
4449
4522
  return {
4450
4523
  content: [
@@ -4456,7 +4529,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4456
4529
  };
4457
4530
  }
4458
4531
  case "retry_pipeline": {
4459
- const { project_id, pipeline_id } = RetryPipelineSchema.parse(request.params.arguments);
4532
+ const { project_id, pipeline_id } = RetryPipelineSchema.parse(params.arguments);
4460
4533
  const pipeline = await retryPipeline(project_id, pipeline_id);
4461
4534
  return {
4462
4535
  content: [
@@ -4468,7 +4541,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4468
4541
  };
4469
4542
  }
4470
4543
  case "cancel_pipeline": {
4471
- const { project_id, pipeline_id } = CancelPipelineSchema.parse(request.params.arguments);
4544
+ const { project_id, pipeline_id } = CancelPipelineSchema.parse(params.arguments);
4472
4545
  const pipeline = await cancelPipeline(project_id, pipeline_id);
4473
4546
  return {
4474
4547
  content: [
@@ -4480,7 +4553,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4480
4553
  };
4481
4554
  }
4482
4555
  case "play_pipeline_job": {
4483
- const { project_id, job_id, job_variables_attributes } = PlayPipelineJobSchema.parse(request.params.arguments);
4556
+ const { project_id, job_id, job_variables_attributes } = PlayPipelineJobSchema.parse(params.arguments);
4484
4557
  const job = await playPipelineJob(project_id, job_id, job_variables_attributes);
4485
4558
  return {
4486
4559
  content: [
@@ -4492,7 +4565,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4492
4565
  };
4493
4566
  }
4494
4567
  case "retry_pipeline_job": {
4495
- const { project_id, job_id } = RetryPipelineJobSchema.parse(request.params.arguments);
4568
+ const { project_id, job_id } = RetryPipelineJobSchema.parse(params.arguments);
4496
4569
  const job = await retryPipelineJob(project_id, job_id);
4497
4570
  return {
4498
4571
  content: [
@@ -4504,7 +4577,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4504
4577
  };
4505
4578
  }
4506
4579
  case "cancel_pipeline_job": {
4507
- const { project_id, job_id, force } = CancelPipelineJobSchema.parse(request.params.arguments);
4580
+ const { project_id, job_id, force } = CancelPipelineJobSchema.parse(params.arguments);
4508
4581
  const job = await cancelPipelineJob(project_id, job_id, force);
4509
4582
  return {
4510
4583
  content: [
@@ -4516,14 +4589,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4516
4589
  };
4517
4590
  }
4518
4591
  case "list_merge_requests": {
4519
- const args = ListMergeRequestsSchema.parse(request.params.arguments);
4592
+ const args = ListMergeRequestsSchema.parse(params.arguments);
4520
4593
  const mergeRequests = await listMergeRequests(args.project_id, args);
4521
4594
  return {
4522
4595
  content: [{ type: "text", text: JSON.stringify(mergeRequests, null, 2) }],
4523
4596
  };
4524
4597
  }
4525
4598
  case "list_milestones": {
4526
- const { project_id, ...options } = ListProjectMilestonesSchema.parse(request.params.arguments);
4599
+ const { project_id, ...options } = ListProjectMilestonesSchema.parse(params.arguments);
4527
4600
  const milestones = await listProjectMilestones(project_id, options);
4528
4601
  return {
4529
4602
  content: [
@@ -4535,7 +4608,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4535
4608
  };
4536
4609
  }
4537
4610
  case "get_milestone": {
4538
- const { project_id, milestone_id } = GetProjectMilestoneSchema.parse(request.params.arguments);
4611
+ const { project_id, milestone_id } = GetProjectMilestoneSchema.parse(params.arguments);
4539
4612
  const milestone = await getProjectMilestone(project_id, milestone_id);
4540
4613
  return {
4541
4614
  content: [
@@ -4547,7 +4620,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4547
4620
  };
4548
4621
  }
4549
4622
  case "create_milestone": {
4550
- const { project_id, ...options } = CreateProjectMilestoneSchema.parse(request.params.arguments);
4623
+ const { project_id, ...options } = CreateProjectMilestoneSchema.parse(params.arguments);
4551
4624
  const milestone = await createProjectMilestone(project_id, options);
4552
4625
  return {
4553
4626
  content: [
@@ -4559,7 +4632,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4559
4632
  };
4560
4633
  }
4561
4634
  case "edit_milestone": {
4562
- const { project_id, milestone_id, ...options } = EditProjectMilestoneSchema.parse(request.params.arguments);
4635
+ const { project_id, milestone_id, ...options } = EditProjectMilestoneSchema.parse(params.arguments);
4563
4636
  const milestone = await editProjectMilestone(project_id, milestone_id, options);
4564
4637
  return {
4565
4638
  content: [
@@ -4571,7 +4644,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4571
4644
  };
4572
4645
  }
4573
4646
  case "delete_milestone": {
4574
- const { project_id, milestone_id } = DeleteProjectMilestoneSchema.parse(request.params.arguments);
4647
+ const { project_id, milestone_id } = DeleteProjectMilestoneSchema.parse(params.arguments);
4575
4648
  await deleteProjectMilestone(project_id, milestone_id);
4576
4649
  return {
4577
4650
  content: [
@@ -4586,7 +4659,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4586
4659
  };
4587
4660
  }
4588
4661
  case "get_milestone_issue": {
4589
- const { project_id, milestone_id } = GetMilestoneIssuesSchema.parse(request.params.arguments);
4662
+ const { project_id, milestone_id } = GetMilestoneIssuesSchema.parse(params.arguments);
4590
4663
  const issues = await getMilestoneIssues(project_id, milestone_id);
4591
4664
  return {
4592
4665
  content: [
@@ -4598,7 +4671,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4598
4671
  };
4599
4672
  }
4600
4673
  case "get_milestone_merge_requests": {
4601
- const { project_id, milestone_id } = GetMilestoneMergeRequestsSchema.parse(request.params.arguments);
4674
+ const { project_id, milestone_id } = GetMilestoneMergeRequestsSchema.parse(params.arguments);
4602
4675
  const mergeRequests = await getMilestoneMergeRequests(project_id, milestone_id);
4603
4676
  return {
4604
4677
  content: [
@@ -4610,7 +4683,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4610
4683
  };
4611
4684
  }
4612
4685
  case "promote_milestone": {
4613
- const { project_id, milestone_id } = PromoteProjectMilestoneSchema.parse(request.params.arguments);
4686
+ const { project_id, milestone_id } = PromoteProjectMilestoneSchema.parse(params.arguments);
4614
4687
  const milestone = await promoteProjectMilestone(project_id, milestone_id);
4615
4688
  return {
4616
4689
  content: [
@@ -4622,7 +4695,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4622
4695
  };
4623
4696
  }
4624
4697
  case "get_milestone_burndown_events": {
4625
- const { project_id, milestone_id } = GetMilestoneBurndownEventsSchema.parse(request.params.arguments);
4698
+ const { project_id, milestone_id } = GetMilestoneBurndownEventsSchema.parse(params.arguments);
4626
4699
  const events = await getMilestoneBurndownEvents(project_id, milestone_id);
4627
4700
  return {
4628
4701
  content: [
@@ -4634,56 +4707,56 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4634
4707
  };
4635
4708
  }
4636
4709
  case "list_commits": {
4637
- const args = ListCommitsSchema.parse(request.params.arguments);
4710
+ const args = ListCommitsSchema.parse(params.arguments);
4638
4711
  const commits = await listCommits(args.project_id, args);
4639
4712
  return {
4640
4713
  content: [{ type: "text", text: JSON.stringify(commits, null, 2) }],
4641
4714
  };
4642
4715
  }
4643
4716
  case "get_commit": {
4644
- const args = GetCommitSchema.parse(request.params.arguments);
4717
+ const args = GetCommitSchema.parse(params.arguments);
4645
4718
  const commit = await getCommit(args.project_id, args.sha, args.stats);
4646
4719
  return {
4647
4720
  content: [{ type: "text", text: JSON.stringify(commit, null, 2) }],
4648
4721
  };
4649
4722
  }
4650
4723
  case "get_commit_diff": {
4651
- const args = GetCommitDiffSchema.parse(request.params.arguments);
4724
+ const args = GetCommitDiffSchema.parse(params.arguments);
4652
4725
  const diff = await getCommitDiff(args.project_id, args.sha, args.full_diff);
4653
4726
  return {
4654
4727
  content: [{ type: "text", text: JSON.stringify(diff, null, 2) }],
4655
4728
  };
4656
4729
  }
4657
4730
  case "list_group_iterations": {
4658
- const args = ListGroupIterationsSchema.parse(request.params.arguments);
4731
+ const args = ListGroupIterationsSchema.parse(params.arguments);
4659
4732
  const iterations = await listGroupIterations(args.group_id, args);
4660
4733
  return {
4661
4734
  content: [{ type: "text", text: JSON.stringify(iterations, null, 2) }],
4662
4735
  };
4663
4736
  }
4664
4737
  case "upload_markdown": {
4665
- const args = MarkdownUploadSchema.parse(request.params.arguments);
4738
+ const args = MarkdownUploadSchema.parse(params.arguments);
4666
4739
  const upload = await markdownUpload(args.project_id, args.file_path);
4667
4740
  return {
4668
4741
  content: [{ type: "text", text: JSON.stringify(upload, null, 2) }],
4669
4742
  };
4670
4743
  }
4671
4744
  case "download_attachment": {
4672
- const args = DownloadAttachmentSchema.parse(request.params.arguments);
4745
+ const args = DownloadAttachmentSchema.parse(params.arguments);
4673
4746
  const filePath = await downloadAttachment(args.project_id, args.secret, args.filename, args.local_path);
4674
4747
  return {
4675
4748
  content: [{ type: "text", text: JSON.stringify({ success: true, file_path: filePath }, null, 2) }],
4676
4749
  };
4677
4750
  }
4678
4751
  case "list_events": {
4679
- const args = ListEventsSchema.parse(request.params.arguments);
4752
+ const args = ListEventsSchema.parse(params.arguments);
4680
4753
  const events = await listEvents(args);
4681
4754
  return {
4682
4755
  content: [{ type: "text", text: JSON.stringify(events, null, 2) }],
4683
4756
  };
4684
4757
  }
4685
4758
  case "get_project_events": {
4686
- const args = GetProjectEventsSchema.parse(request.params.arguments);
4759
+ const args = GetProjectEventsSchema.parse(params.arguments);
4687
4760
  const { project_id, ...options } = args;
4688
4761
  const events = await getProjectEvents(project_id, options);
4689
4762
  return {
@@ -4691,7 +4764,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4691
4764
  };
4692
4765
  }
4693
4766
  case "list_releases": {
4694
- const args = ListReleasesSchema.parse(request.params.arguments);
4767
+ const args = ListReleasesSchema.parse(params.arguments);
4695
4768
  const { project_id, ...options } = args;
4696
4769
  const releases = await listReleases(project_id, options);
4697
4770
  return {
@@ -4699,14 +4772,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4699
4772
  };
4700
4773
  }
4701
4774
  case "get_release": {
4702
- const args = GetReleaseSchema.parse(request.params.arguments);
4775
+ const args = GetReleaseSchema.parse(params.arguments);
4703
4776
  const release = await getRelease(args.project_id, args.tag_name, args.include_html_description);
4704
4777
  return {
4705
4778
  content: [{ type: "text", text: JSON.stringify(release, null, 2) }],
4706
4779
  };
4707
4780
  }
4708
4781
  case "create_release": {
4709
- const args = CreateReleaseSchema.parse(request.params.arguments);
4782
+ const args = CreateReleaseSchema.parse(params.arguments);
4710
4783
  const { project_id, ...options } = args;
4711
4784
  const release = await createRelease(project_id, options);
4712
4785
  return {
@@ -4714,7 +4787,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4714
4787
  };
4715
4788
  }
4716
4789
  case "update_release": {
4717
- const args = UpdateReleaseSchema.parse(request.params.arguments);
4790
+ const args = UpdateReleaseSchema.parse(params.arguments);
4718
4791
  const { project_id, tag_name, ...options } = args;
4719
4792
  const release = await updateRelease(project_id, tag_name, options);
4720
4793
  return {
@@ -4722,7 +4795,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4722
4795
  };
4723
4796
  }
4724
4797
  case "delete_release": {
4725
- const args = DeleteReleaseSchema.parse(request.params.arguments);
4798
+ const args = DeleteReleaseSchema.parse(params.arguments);
4726
4799
  const release = await deleteRelease(args.project_id, args.tag_name);
4727
4800
  return {
4728
4801
  content: [
@@ -4734,7 +4807,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4734
4807
  };
4735
4808
  }
4736
4809
  case "create_release_evidence": {
4737
- const args = CreateReleaseEvidenceSchema.parse(request.params.arguments);
4810
+ const args = CreateReleaseEvidenceSchema.parse(params.arguments);
4738
4811
  await createReleaseEvidence(args.project_id, args.tag_name);
4739
4812
  return {
4740
4813
  content: [
@@ -4746,18 +4819,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4746
4819
  };
4747
4820
  }
4748
4821
  case "download_release_asset": {
4749
- const args = DownloadReleaseAssetSchema.parse(request.params.arguments);
4822
+ const args = DownloadReleaseAssetSchema.parse(params.arguments);
4750
4823
  const assetContent = await downloadReleaseAsset(args.project_id, args.tag_name, args.direct_asset_path);
4751
4824
  return {
4752
4825
  content: [{ type: "text", text: assetContent }],
4753
4826
  };
4754
4827
  }
4755
4828
  default:
4756
- throw new Error(`Unknown tool: ${request.params.name}`);
4829
+ throw new Error(`Unknown tool: ${params.name}`);
4757
4830
  }
4758
4831
  }
4759
4832
  catch (error) {
4760
- logger.debug(request.params);
4833
+ logger.debug(params);
4761
4834
  if (error instanceof z.ZodError) {
4762
4835
  throw new Error(`Invalid arguments: ${error.errors
4763
4836
  .map(e => `${e.path.join(".")}: ${e.message}`)
@@ -4765,7 +4838,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4765
4838
  }
4766
4839
  throw error;
4767
4840
  }
4768
- });
4841
+ }
4769
4842
  /**
4770
4843
  * Color constants for terminal output
4771
4844
  */
@@ -4842,8 +4915,6 @@ async function startSSEServer() {
4842
4915
  async function startStreamableHTTPServer() {
4843
4916
  const app = express();
4844
4917
  const streamableTransports = {};
4845
- // Session-based auth mapping for remote authorization
4846
- const authBySession = {};
4847
4918
  const authTimeouts = {};
4848
4919
  // Configuration and limits
4849
4920
  const MAX_SESSIONS = parseInt(process.env.MAX_SESSIONS || '1000');
@@ -4894,18 +4965,36 @@ async function startStreamableHTTPServer() {
4894
4965
  const parseAuthHeaders = (req) => {
4895
4966
  const authHeader = req.headers['authorization'] || '';
4896
4967
  const privateToken = req.headers['private-token'] || '';
4968
+ const dynamicApiUrl = req.headers['x-gitlab-api-url']?.trim();
4969
+ let apiUrl = GITLAB_API_URL; // Default API URL
4970
+ // Only process dynamic URL if the feature is enabled
4971
+ if (ENABLE_DYNAMIC_API_URL && dynamicApiUrl) {
4972
+ try {
4973
+ new URL(dynamicApiUrl); // Ensure it's a valid URL format
4974
+ apiUrl = normalizeGitLabApiUrl(dynamicApiUrl);
4975
+ }
4976
+ catch (e) {
4977
+ logger.warn(`Invalid X-GitLab-API-URL provided: ${dynamicApiUrl}. Auth will fail.`);
4978
+ return null; // Reject if URL is malformed
4979
+ }
4980
+ }
4981
+ // Extract token
4982
+ let token = null;
4983
+ let header = null;
4897
4984
  if (privateToken) {
4898
- const token = privateToken.trim();
4899
- if (!token || !validateToken(token))
4900
- return null;
4901
- return { header: 'Private-Token', token, lastUsed: Date.now() };
4985
+ token = privateToken.trim();
4986
+ header = 'Private-Token';
4902
4987
  }
4903
- if (authHeader) {
4988
+ else if (authHeader) {
4904
4989
  const match = authHeader.match(/^Bearer\s+(.+)$/i);
4905
- const token = match ? match[1].trim() : '';
4906
- if (!token || !validateToken(token))
4907
- return null;
4908
- return { header: 'Authorization', token, lastUsed: Date.now() };
4990
+ if (match) {
4991
+ token = match[1].trim();
4992
+ header = 'Authorization';
4993
+ }
4994
+ }
4995
+ // Validate token and return AuthData object
4996
+ if (token && header && validateToken(token)) {
4997
+ return { header, token, lastUsed: Date.now(), apiUrl };
4909
4998
  }
4910
4999
  return null;
4911
5000
  };
@@ -5002,8 +5091,8 @@ async function startStreamableHTTPServer() {
5002
5091
  // First request without session - will fail in initialization
5003
5092
  }
5004
5093
  }
5005
- // Wrap request handling in AsyncLocalStorage context
5006
- const handleRequestWithAuth = async () => {
5094
+ // Handle request with proper AsyncLocalStorage context
5095
+ const handleRequest = async () => {
5007
5096
  try {
5008
5097
  let transport;
5009
5098
  if (sessionId && streamableTransports[sessionId]) {
@@ -5045,8 +5134,9 @@ async function startStreamableHTTPServer() {
5045
5134
  }
5046
5135
  }
5047
5136
  };
5048
- // Connect transport to MCP server before handling the request
5137
+ // Connect transport to MCP server
5049
5138
  await server.connect(transport);
5139
+ // Handle the request - context is already set up in the outer handleRequest wrapper
5050
5140
  await transport.handleRequest(req, res, req.body);
5051
5141
  }
5052
5142
  }
@@ -5065,13 +5155,15 @@ async function startStreamableHTTPServer() {
5065
5155
  sessionId,
5066
5156
  header: authData.header,
5067
5157
  token: authData.token,
5068
- lastUsed: authData.lastUsed
5158
+ lastUsed: authData.lastUsed,
5159
+ apiUrl: authData.apiUrl
5069
5160
  };
5070
- await sessionAuthStore.run(ctx, handleRequestWithAuth);
5161
+ // Run the entire request handling within AsyncLocalStorage context
5162
+ await sessionAuthStore.run(ctx, handleRequest);
5071
5163
  }
5072
5164
  else {
5073
5165
  // Standard execution (no remote auth or no session yet)
5074
- await handleRequestWithAuth();
5166
+ await handleRequest();
5075
5167
  }
5076
5168
  });
5077
5169
  // Metrics endpoint
@@ -5219,6 +5311,8 @@ async function runServer() {
5219
5311
  }
5220
5312
  const transportMode = determineTransportMode();
5221
5313
  await initializeServerByTransportMode(transportMode);
5314
+ logger.info(`Configured GitLab API URLs: ${GITLAB_API_URLS.join(", ")}`);
5315
+ logger.info(`Default GitLab API URL: ${GITLAB_API_URL}`);
5222
5316
  }
5223
5317
  catch (error) {
5224
5318
  logger.error("Error initializing server:", error);