gitlab-mcp-agent-server 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -5,11 +5,7 @@ MCP server for GitLab integration (TypeScript + Node.js).
|
|
|
5
5
|
Полный пользовательский сценарий подключения к ИИ-агенту:
|
|
6
6
|
- `docs/USER_GUIDE.md`
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
npx -y gitlab-mcp-agent-server
|
|
12
|
-
```
|
|
8
|
+
Основной сценарий: добавить сервер в `~/.codex/config.toml` (готовый блок есть в `docs/USER_GUIDE.md`).
|
|
13
9
|
|
|
14
10
|
Для конечного пользователя обычно достаточно:
|
|
15
11
|
1. Зарегистрировать GitLab OAuth application.
|
|
@@ -64,15 +64,28 @@ class GitLabOAuthManager {
|
|
|
64
64
|
this.assertRedirectUri();
|
|
65
65
|
const redirect = new URL(this.options.redirectUri);
|
|
66
66
|
const state = (0, node_crypto_1.randomBytes)(16).toString('hex');
|
|
67
|
+
const authorizeUrl = new URL(`${this.oauthBaseUrl}/oauth/authorize`);
|
|
68
|
+
authorizeUrl.searchParams.set('client_id', this.options.clientId);
|
|
69
|
+
authorizeUrl.searchParams.set('redirect_uri', this.options.redirectUri);
|
|
70
|
+
authorizeUrl.searchParams.set('response_type', 'code');
|
|
71
|
+
authorizeUrl.searchParams.set('scope', this.options.scopes.join(' '));
|
|
72
|
+
authorizeUrl.searchParams.set('state', state);
|
|
73
|
+
const localEntryUrl = `${redirect.protocol}//${redirect.host}/`;
|
|
67
74
|
const code = await new Promise((resolve, reject) => {
|
|
68
75
|
const server = (0, node_http_1.createServer)((req, res) => {
|
|
69
76
|
if (!req.url) {
|
|
70
77
|
return;
|
|
71
78
|
}
|
|
72
79
|
const url = new URL(req.url, `${redirect.protocol}//${redirect.host}`);
|
|
80
|
+
if (url.pathname === '/') {
|
|
81
|
+
res.statusCode = 200;
|
|
82
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
83
|
+
res.end(renderOAuthEntryPage(authorizeUrl.toString()));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
73
86
|
if (url.pathname !== redirect.pathname) {
|
|
74
87
|
res.statusCode = 404;
|
|
75
|
-
res.end('Not found');
|
|
88
|
+
res.end('Not found. Open "/" to start OAuth authorization.');
|
|
76
89
|
return;
|
|
77
90
|
}
|
|
78
91
|
const responseState = url.searchParams.get('state');
|
|
@@ -99,17 +112,12 @@ class GitLabOAuthManager {
|
|
|
99
112
|
resolve(authCode);
|
|
100
113
|
});
|
|
101
114
|
server.listen(resolvePort(redirect), redirect.hostname, () => {
|
|
102
|
-
const
|
|
103
|
-
authorizeUrl.searchParams.set('client_id', this.options.clientId);
|
|
104
|
-
authorizeUrl.searchParams.set('redirect_uri', this.options.redirectUri);
|
|
105
|
-
authorizeUrl.searchParams.set('response_type', 'code');
|
|
106
|
-
authorizeUrl.searchParams.set('scope', this.options.scopes.join(' '));
|
|
107
|
-
authorizeUrl.searchParams.set('state', state);
|
|
108
|
-
const authorizeUrlText = authorizeUrl.toString();
|
|
109
|
-
const opened = this.options.openBrowser && openInBrowser(authorizeUrlText);
|
|
115
|
+
const opened = this.options.openBrowser && openInBrowser(localEntryUrl);
|
|
110
116
|
if (!opened) {
|
|
111
|
-
console.error('Open this URL to
|
|
112
|
-
console.error(
|
|
117
|
+
console.error('Open this local URL to start OAuth authorization:');
|
|
118
|
+
console.error(localEntryUrl);
|
|
119
|
+
console.error('If local redirect does not work, use direct GitLab OAuth URL:');
|
|
120
|
+
console.error(authorizeUrl.toString());
|
|
113
121
|
}
|
|
114
122
|
});
|
|
115
123
|
});
|
|
@@ -208,3 +216,35 @@ function hasOpenCommand(platform) {
|
|
|
208
216
|
return false;
|
|
209
217
|
}
|
|
210
218
|
}
|
|
219
|
+
function renderOAuthEntryPage(authorizeUrl) {
|
|
220
|
+
const escapedUrl = authorizeUrl.replace(/&/g, '&').replace(/"/g, '"');
|
|
221
|
+
return `<!doctype html>
|
|
222
|
+
<html lang="en">
|
|
223
|
+
<head>
|
|
224
|
+
<meta charset="utf-8" />
|
|
225
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
226
|
+
<title>GitLab OAuth</title>
|
|
227
|
+
<style>
|
|
228
|
+
body { font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif; margin: 0; padding: 24px; background: #0b1220; color: #e5e7eb; }
|
|
229
|
+
.card { max-width: 640px; margin: 48px auto; background: #121a2b; border: 1px solid #1f2a44; border-radius: 12px; padding: 24px; }
|
|
230
|
+
h1 { margin: 0 0 12px; font-size: 20px; }
|
|
231
|
+
p { margin: 0 0 14px; color: #cbd5e1; line-height: 1.5; }
|
|
232
|
+
a.btn { display: inline-block; background: #2563eb; color: white; text-decoration: none; padding: 10px 14px; border-radius: 8px; font-weight: 600; }
|
|
233
|
+
.hint { margin-top: 12px; font-size: 13px; color: #94a3b8; }
|
|
234
|
+
</style>
|
|
235
|
+
</head>
|
|
236
|
+
<body>
|
|
237
|
+
<div class="card">
|
|
238
|
+
<h1>Authorize GitLab Access</h1>
|
|
239
|
+
<p>Click the button below to continue OAuth authorization.</p>
|
|
240
|
+
<a class="btn" href="${escapedUrl}">Authorize with GitLab</a>
|
|
241
|
+
<p class="hint">You will be redirected automatically in 3 seconds if no action is taken.</p>
|
|
242
|
+
</div>
|
|
243
|
+
<script>
|
|
244
|
+
setTimeout(function () {
|
|
245
|
+
window.location.href = ${JSON.stringify(authorizeUrl)};
|
|
246
|
+
}, 3000);
|
|
247
|
+
</script>
|
|
248
|
+
</body>
|
|
249
|
+
</html>`;
|
|
250
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OAuthTokenStore = void 0;
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
5
6
|
class OAuthTokenStore {
|
|
6
7
|
filePath;
|
|
7
8
|
constructor(filePath) {
|
|
@@ -19,6 +20,7 @@ class OAuthTokenStore {
|
|
|
19
20
|
return parsed;
|
|
20
21
|
}
|
|
21
22
|
write(token) {
|
|
23
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.filePath), { recursive: true });
|
|
22
24
|
(0, node_fs_1.writeFileSync)(this.filePath, JSON.stringify(token, null, 2), 'utf8');
|
|
23
25
|
(0, node_fs_1.chmodSync)(this.filePath, 0o600);
|
|
24
26
|
}
|
package/docs/USER_GUIDE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# User Guide
|
|
2
2
|
|
|
3
|
-
Этот гайд для конечного пользователя: как подключить `gitlab-mcp-agent-server` в
|
|
3
|
+
Этот гайд для конечного пользователя: как подключить `gitlab-mcp-agent-server` в **Codex** и работать через OAuth с авто-рефрешем токена.
|
|
4
4
|
|
|
5
5
|
## 1. Подготовка GitLab OAuth Application
|
|
6
6
|
|
|
@@ -23,60 +23,61 @@
|
|
|
23
23
|
|
|
24
24
|
Остальные параметры имеют дефолты.
|
|
25
25
|
|
|
26
|
-
## 3.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
}
|
|
26
|
+
## 3. Конфиг Codex (`~/.codex/config.toml`)
|
|
27
|
+
|
|
28
|
+
Минимальный рабочий блок:
|
|
29
|
+
|
|
30
|
+
```toml
|
|
31
|
+
[mcp_servers.gitlab]
|
|
32
|
+
command = "bash"
|
|
33
|
+
args = ["-lc", """
|
|
34
|
+
export NVM_DIR="$HOME/.nvm";
|
|
35
|
+
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh";
|
|
36
|
+
|
|
37
|
+
export GITLAB_OAUTH_CLIENT_ID="<APPLICATION_ID>";
|
|
38
|
+
export GITLAB_OAUTH_CLIENT_SECRET="<SECRET>";
|
|
39
|
+
|
|
40
|
+
npx -y gitlab-mcp-agent-server
|
|
41
|
+
"""]
|
|
44
42
|
```
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
44
|
+
Рекомендованный расширенный блок:
|
|
45
|
+
|
|
46
|
+
```toml
|
|
47
|
+
[mcp_servers.gitlab]
|
|
48
|
+
command = "bash"
|
|
49
|
+
args = ["-lc", """
|
|
50
|
+
export NVM_DIR="$HOME/.nvm";
|
|
51
|
+
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh";
|
|
52
|
+
|
|
53
|
+
export GITLAB_API_URL="https://gitlab.com/api/v4";
|
|
54
|
+
export GITLAB_AUTH_MODE="oauth";
|
|
55
|
+
export GITLAB_OAUTH_CLIENT_ID="<APPLICATION_ID>";
|
|
56
|
+
export GITLAB_OAUTH_CLIENT_SECRET="<SECRET>";
|
|
57
|
+
export GITLAB_OAUTH_REDIRECT_URI="http://127.0.0.1:8787/oauth/callback";
|
|
58
|
+
export GITLAB_OAUTH_SCOPES="api";
|
|
59
|
+
export GITLAB_OAUTH_TOKEN_STORE_PATH="$HOME/.config/gitlab-mcp/token.json";
|
|
60
|
+
export GITLAB_OAUTH_AUTO_LOGIN="true";
|
|
61
|
+
export GITLAB_OAUTH_OPEN_BROWSER="false";
|
|
62
|
+
|
|
63
|
+
# optional
|
|
64
|
+
export GITLAB_DEFAULT_PROJECT="group/repo";
|
|
65
|
+
export GITLAB_AUTO_RESOLVE_PROJECT_FROM_GIT="true";
|
|
66
|
+
|
|
67
|
+
npx -y gitlab-mcp-agent-server
|
|
68
|
+
"""]
|
|
71
69
|
```
|
|
72
70
|
|
|
71
|
+
После изменения `config.toml` перезапусти Codex.
|
|
72
|
+
|
|
73
73
|
## 4. Что происходит при первом запуске
|
|
74
74
|
|
|
75
75
|
1. Агент вызывает любой GitLab tool (например `gitlab_list_labels`).
|
|
76
76
|
2. Если токена нет, сервер запускает OAuth flow.
|
|
77
77
|
3. Если `GITLAB_OAUTH_OPEN_BROWSER=true` и окружение GUI доступно, браузер откроется автоматически.
|
|
78
|
-
4.
|
|
79
|
-
5.
|
|
78
|
+
4. Локальный URL `http://127.0.0.1:8787/` автоматически редиректит на GitLab OAuth.
|
|
79
|
+
5. Если браузер не может быть открыт, сервер печатает URL авторизации в лог.
|
|
80
|
+
6. После подтверждения в GitLab и callback на `http://127.0.0.1:8787/oauth/callback` токены сохраняются в `GITLAB_OAUTH_TOKEN_STORE_PATH`.
|
|
80
81
|
|
|
81
82
|
Если `GITLAB_DEFAULT_PROJECT` не указан:
|
|
82
83
|
1. сервер пытается автоматически определить проект из `git remote origin` в `cwd`;
|