gitlab-mcp-agent-server 0.2.5 → 0.2.7

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.
@@ -54,6 +54,13 @@ class GitLabOAuthManager {
54
54
  try {
55
55
  return await this.loginInteractively();
56
56
  }
57
+ catch (error) {
58
+ if (error instanceof OAuthCallbackPortBusyError) {
59
+ console.error(error.message);
60
+ return this.waitForTokenFromOtherProcess(lockFilePath);
61
+ }
62
+ throw error;
63
+ }
57
64
  finally {
58
65
  lock.release();
59
66
  }
@@ -190,6 +197,11 @@ class GitLabOAuthManager {
190
197
  });
191
198
  server.on('error', (error) => {
192
199
  settled = true;
200
+ const errno = error;
201
+ if (errno.code === 'EADDRINUSE') {
202
+ reject(new OAuthCallbackPortBusyError(`OAuth callback port is busy (${redirect.hostname}:${resolvePort(redirect)}). If another OAuth flow is active, finish it and retry.`));
203
+ return;
204
+ }
193
205
  reject(new Error(`OAuth callback server failed on ${redirect.hostname}:${resolvePort(redirect)}: ${error.message}`));
194
206
  });
195
207
  server.listen(resolvePort(redirect), redirect.hostname, () => {
@@ -201,7 +213,7 @@ class GitLabOAuthManager {
201
213
  console.error(authorizeUrl.toString());
202
214
  }
203
215
  });
204
- const timeoutMs = 20_000;
216
+ const timeoutMs = this.options.callbackTimeoutMs ?? 180_000;
205
217
  setTimeout(() => {
206
218
  if (settled) {
207
219
  return;
@@ -315,6 +327,12 @@ class OAuthRefreshError extends Error {
315
327
  this.name = 'OAuthRefreshError';
316
328
  }
317
329
  }
330
+ class OAuthCallbackPortBusyError extends Error {
331
+ constructor(message) {
332
+ super(message);
333
+ this.name = 'OAuthCallbackPortBusyError';
334
+ }
335
+ }
318
336
  function shouldReloginOnRefreshFailure(error) {
319
337
  if (!(error instanceof OAuthRefreshError)) {
320
338
  return false;
@@ -375,6 +393,15 @@ function isStaleLock(lockFilePath) {
375
393
  try {
376
394
  const raw = (0, node_fs_1.readFileSync)(lockFilePath, 'utf8');
377
395
  const parsed = JSON.parse(raw);
396
+ if (parsed.startedAt) {
397
+ const startedAtMs = new Date(parsed.startedAt).getTime();
398
+ if (!Number.isNaN(startedAtMs)) {
399
+ const ageMs = Date.now() - startedAtMs;
400
+ if (ageMs > 10 * 60 * 1000) {
401
+ return true;
402
+ }
403
+ }
404
+ }
378
405
  if (!parsed.pid || !Number.isInteger(parsed.pid)) {
379
406
  return true;
380
407
  }
@@ -28,6 +28,7 @@ function createMcpServer() {
28
28
  scopes: config.gitlab.oauth.scopes,
29
29
  bootstrapAccessToken: config.gitlab.accessToken,
30
30
  tokenStorePath: config.gitlab.oauth.tokenStorePath,
31
+ callbackTimeoutMs: config.gitlab.oauth.callbackTimeoutMs,
31
32
  autoLogin: config.gitlab.oauth.autoLogin,
32
33
  openBrowser: config.gitlab.oauth.openBrowser
33
34
  })
@@ -15,6 +15,7 @@ const EnvSchema = zod_1.z.object({
15
15
  GITLAB_OAUTH_REDIRECT_URI: zod_1.z.string().optional(),
16
16
  GITLAB_OAUTH_SCOPES: zod_1.z.string().default('api'),
17
17
  GITLAB_OAUTH_TOKEN_STORE_PATH: zod_1.z.string().optional(),
18
+ GITLAB_OAUTH_CALLBACK_TIMEOUT_MS: zod_1.z.coerce.number().int().positive().optional(),
18
19
  GITLAB_OAUTH_AUTO_LOGIN: zod_1.z
19
20
  .enum(['true', 'false'])
20
21
  .optional()
@@ -68,6 +69,7 @@ function loadConfig() {
68
69
  redirectUri: env.GITLAB_OAUTH_REDIRECT_URI,
69
70
  scopes: splitCsv(env.GITLAB_OAUTH_SCOPES),
70
71
  tokenStorePath: defaultTokenStorePath,
72
+ callbackTimeoutMs: env.GITLAB_OAUTH_CALLBACK_TIMEOUT_MS ?? 180_000,
71
73
  autoLogin: env.GITLAB_OAUTH_AUTO_LOGIN,
72
74
  openBrowser: env.GITLAB_OAUTH_OPEN_BROWSER
73
75
  },
@@ -60,6 +60,7 @@ export GITLAB_AUTH_MODE="oauth";
60
60
  export GITLAB_OAUTH_CLIENT_ID="<GITLAB_COM_APP_ID>";
61
61
  export GITLAB_OAUTH_CLIENT_SECRET="<GITLAB_COM_APP_SECRET>";
62
62
  export GITLAB_OAUTH_REDIRECT_URI="http://127.0.0.1:8787/oauth/callback";
63
+ export GITLAB_OAUTH_CALLBACK_TIMEOUT_MS="180000";
63
64
  export GITLAB_OAUTH_AUTO_LOGIN="true";
64
65
  export GITLAB_OAUTH_OPEN_BROWSER="true";
65
66
 
@@ -77,6 +78,7 @@ export GITLAB_AUTH_MODE="oauth";
77
78
  export GITLAB_OAUTH_CLIENT_ID="<WORK_APP_ID>";
78
79
  export GITLAB_OAUTH_CLIENT_SECRET="<WORK_APP_SECRET>";
79
80
  export GITLAB_OAUTH_REDIRECT_URI="http://127.0.0.1:8788/oauth/callback";
81
+ export GITLAB_OAUTH_CALLBACK_TIMEOUT_MS="180000";
80
82
  export GITLAB_OAUTH_AUTO_LOGIN="true";
81
83
  export GITLAB_OAUTH_OPEN_BROWSER="true";
82
84
 
@@ -121,6 +123,8 @@ npx -y gitlab-mcp-agent-server
121
123
  2. Ограничь права файла:
122
124
  - `chmod 600 /home/<user>/.config/gitlab-mcp/gitlab.com/token.json`
123
125
  3. Для headless окружений ставь `GITLAB_OAUTH_OPEN_BROWSER=false`.
126
+ 4. При медленном интерактивном flow увеличь окно ожидания callback:
127
+ - `GITLAB_OAUTH_CALLBACK_TIMEOUT_MS=180000` (или выше).
124
128
 
125
129
  ## 7. Сценарий пользовательского запроса
126
130
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitlab-mcp-agent-server",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "MCP server for GitLab integration via OAuth",
5
5
  "main": "build/src/index.js",
6
6
  "bin": {