@zereight/mcp-gitlab 1.0.69 → 1.0.70
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 +37 -28
- package/build/utils.js +9 -0
- package/package.json +6 -4
package/build/index.js
CHANGED
|
@@ -29,7 +29,19 @@ GitLabDiscussionNoteSchema, // Added
|
|
|
29
29
|
GitLabDiscussionSchema, PaginatedDiscussionsResponseSchema, UpdateMergeRequestNoteSchema, // Added
|
|
30
30
|
CreateMergeRequestNoteSchema, // Added
|
|
31
31
|
ListMergeRequestDiscussionsSchema, UpdateIssueNoteSchema, CreateIssueNoteSchema, ListMergeRequestsSchema, GitLabMilestonesSchema, ListProjectMilestonesSchema, GetProjectMilestoneSchema, CreateProjectMilestoneSchema, EditProjectMilestoneSchema, DeleteProjectMilestoneSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, PromoteProjectMilestoneSchema, GetMilestoneBurndownEventsSchema, GitLabCompareResultSchema, GetBranchDiffsSchema, ListCommitsSchema, GetCommitSchema, GetCommitDiffSchema, ListMergeRequestDiffsSchema, } from "./schemas.js";
|
|
32
|
+
import { formatBoolean } from "./utils.js";
|
|
32
33
|
import { randomUUID } from "crypto";
|
|
34
|
+
import { pino } from 'pino';
|
|
35
|
+
const logger = pino({
|
|
36
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
37
|
+
transport: {
|
|
38
|
+
target: 'pino-pretty',
|
|
39
|
+
options: {
|
|
40
|
+
colorize: true,
|
|
41
|
+
levelFirst: true,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
33
45
|
/**
|
|
34
46
|
* Available transport modes for MCP server
|
|
35
47
|
*/
|
|
@@ -72,7 +84,7 @@ const USE_MILESTONE = process.env.USE_MILESTONE === "true";
|
|
|
72
84
|
const USE_PIPELINE = process.env.USE_PIPELINE === "true";
|
|
73
85
|
const SSE = process.env.SSE === "true";
|
|
74
86
|
const STREAMABLE_HTTP = process.env.STREAMABLE_HTTP === "true";
|
|
75
|
-
const HOST = process.env.HOST || '
|
|
87
|
+
const HOST = process.env.HOST || '0.0.0.0';
|
|
76
88
|
const PORT = process.env.PORT || 3002;
|
|
77
89
|
// Add proxy configuration
|
|
78
90
|
const HTTP_PROXY = process.env.HTTP_PROXY;
|
|
@@ -144,7 +156,7 @@ const createCookieJar = () => {
|
|
|
144
156
|
return jar;
|
|
145
157
|
}
|
|
146
158
|
catch (error) {
|
|
147
|
-
|
|
159
|
+
logger.error("Error loading cookie file:", error);
|
|
148
160
|
return null;
|
|
149
161
|
}
|
|
150
162
|
};
|
|
@@ -642,7 +654,7 @@ function normalizeGitLabApiUrl(url) {
|
|
|
642
654
|
const GITLAB_API_URL = normalizeGitLabApiUrl(process.env.GITLAB_API_URL || "");
|
|
643
655
|
const GITLAB_PROJECT_ID = process.env.GITLAB_PROJECT_ID;
|
|
644
656
|
if (!GITLAB_PERSONAL_ACCESS_TOKEN) {
|
|
645
|
-
|
|
657
|
+
logger.error("GITLAB_PERSONAL_ACCESS_TOKEN environment variable is not set");
|
|
646
658
|
process.exit(1);
|
|
647
659
|
}
|
|
648
660
|
/**
|
|
@@ -657,8 +669,8 @@ async function handleGitLabError(response) {
|
|
|
657
669
|
const errorBody = await response.text();
|
|
658
670
|
// Check specifically for Rate Limit error
|
|
659
671
|
if (response.status === 403 && errorBody.includes("User API Key Rate limit exceeded")) {
|
|
660
|
-
|
|
661
|
-
|
|
672
|
+
logger.error("GitLab API Rate Limit Exceeded:", errorBody);
|
|
673
|
+
logger.error("User API Key Rate limit exceeded. Please try again later.");
|
|
662
674
|
throw new Error(`GitLab API Rate Limit Exceeded: ${errorBody}`);
|
|
663
675
|
}
|
|
664
676
|
else {
|
|
@@ -1046,7 +1058,7 @@ async function createMergeRequest(projectId, options) {
|
|
|
1046
1058
|
labels: options.labels?.join(","),
|
|
1047
1059
|
allow_collaboration: options.allow_collaboration,
|
|
1048
1060
|
draft: options.draft,
|
|
1049
|
-
remove_source_branch: options.remove_source_branch,
|
|
1061
|
+
remove_source_branch: formatBoolean(options.remove_source_branch),
|
|
1050
1062
|
squash: options.squash,
|
|
1051
1063
|
}),
|
|
1052
1064
|
});
|
|
@@ -2176,10 +2188,7 @@ async function createPipeline(projectId, ref, variables) {
|
|
|
2176
2188
|
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline`);
|
|
2177
2189
|
const body = { ref };
|
|
2178
2190
|
if (variables && variables.length > 0) {
|
|
2179
|
-
body.variables = variables
|
|
2180
|
-
acc[key] = value;
|
|
2181
|
-
return acc;
|
|
2182
|
-
}, {});
|
|
2191
|
+
body.variables = variables;
|
|
2183
2192
|
}
|
|
2184
2193
|
const response = await fetch(url.toString(), {
|
|
2185
2194
|
method: "POST",
|
|
@@ -2256,7 +2265,7 @@ async function getRepositoryTree(options) {
|
|
|
2256
2265
|
else {
|
|
2257
2266
|
headers["Authorization"] = `Bearer ${GITLAB_PERSONAL_ACCESS_TOKEN}`;
|
|
2258
2267
|
}
|
|
2259
|
-
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(options.project_id)}/repository/tree?${queryParams.toString()}`, {
|
|
2268
|
+
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(options.project_id))}/repository/tree?${queryParams.toString()}`, {
|
|
2260
2269
|
headers,
|
|
2261
2270
|
});
|
|
2262
2271
|
if (response.status === 404) {
|
|
@@ -2456,7 +2465,7 @@ async function getUser(username) {
|
|
|
2456
2465
|
return null;
|
|
2457
2466
|
}
|
|
2458
2467
|
catch (error) {
|
|
2459
|
-
|
|
2468
|
+
logger.error(`Error fetching user by username '${username}':`, error);
|
|
2460
2469
|
return null;
|
|
2461
2470
|
}
|
|
2462
2471
|
}
|
|
@@ -2475,7 +2484,7 @@ async function getUsers(usernames) {
|
|
|
2475
2484
|
users[username] = user;
|
|
2476
2485
|
}
|
|
2477
2486
|
catch (error) {
|
|
2478
|
-
|
|
2487
|
+
logger.error(`Error processing username '${username}':`, error);
|
|
2479
2488
|
users[username] = null;
|
|
2480
2489
|
}
|
|
2481
2490
|
}
|
|
@@ -2621,7 +2630,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2621
2630
|
};
|
|
2622
2631
|
}
|
|
2623
2632
|
catch (forkError) {
|
|
2624
|
-
|
|
2633
|
+
logger.error("Error forking repository:", forkError);
|
|
2625
2634
|
let forkErrorMessage = "Failed to fork repository";
|
|
2626
2635
|
if (forkError instanceof Error) {
|
|
2627
2636
|
forkErrorMessage = `${forkErrorMessage}: ${forkError.message}`;
|
|
@@ -2842,7 +2851,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2842
2851
|
}
|
|
2843
2852
|
case "get_project": {
|
|
2844
2853
|
const args = GetProjectSchema.parse(request.params.arguments);
|
|
2845
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(args.project_id)}`);
|
|
2854
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(args.project_id))}`);
|
|
2846
2855
|
const response = await fetch(url.toString(), {
|
|
2847
2856
|
...DEFAULT_FETCH_CONFIG,
|
|
2848
2857
|
});
|
|
@@ -3368,10 +3377,10 @@ async function startSSEServer() {
|
|
|
3368
3377
|
});
|
|
3369
3378
|
});
|
|
3370
3379
|
app.listen(Number(PORT), HOST, () => {
|
|
3371
|
-
|
|
3380
|
+
logger.info(`GitLab MCP Server running with SSE transport`);
|
|
3372
3381
|
const colorGreen = "\x1b[32m";
|
|
3373
3382
|
const colorReset = "\x1b[0m";
|
|
3374
|
-
|
|
3383
|
+
logger.info(`${colorGreen}Endpoint: http://${HOST}:${PORT}/sse${colorReset}`);
|
|
3375
3384
|
});
|
|
3376
3385
|
}
|
|
3377
3386
|
/**
|
|
@@ -3398,14 +3407,14 @@ async function startStreamableHTTPServer() {
|
|
|
3398
3407
|
sessionIdGenerator: () => randomUUID(),
|
|
3399
3408
|
onsessioninitialized: (newSessionId) => {
|
|
3400
3409
|
streamableTransports[newSessionId] = transport;
|
|
3401
|
-
|
|
3410
|
+
logger.warn(`Streamable HTTP session initialized: ${newSessionId}`);
|
|
3402
3411
|
}
|
|
3403
3412
|
});
|
|
3404
3413
|
// Set up cleanup handler when transport closes
|
|
3405
3414
|
transport.onclose = () => {
|
|
3406
3415
|
const sid = transport.sessionId;
|
|
3407
3416
|
if (sid && streamableTransports[sid]) {
|
|
3408
|
-
|
|
3417
|
+
logger.warn(`Streamable HTTP transport closed for session ${sid}, cleaning up`);
|
|
3409
3418
|
delete streamableTransports[sid];
|
|
3410
3419
|
}
|
|
3411
3420
|
};
|
|
@@ -3415,7 +3424,7 @@ async function startStreamableHTTPServer() {
|
|
|
3415
3424
|
}
|
|
3416
3425
|
}
|
|
3417
3426
|
catch (error) {
|
|
3418
|
-
|
|
3427
|
+
logger.error('Streamable HTTP error:', error);
|
|
3419
3428
|
res.status(500).json({
|
|
3420
3429
|
error: 'Internal server error',
|
|
3421
3430
|
message: error instanceof Error ? error.message : 'Unknown error'
|
|
@@ -3433,8 +3442,8 @@ async function startStreamableHTTPServer() {
|
|
|
3433
3442
|
});
|
|
3434
3443
|
// Start server
|
|
3435
3444
|
app.listen(Number(PORT), HOST, () => {
|
|
3436
|
-
|
|
3437
|
-
|
|
3445
|
+
logger.info(`GitLab MCP Server running with Streamable HTTP transport`);
|
|
3446
|
+
logger.info(`${colorGreen}Endpoint: http://${HOST}:${PORT}/mcp${colorReset}`);
|
|
3438
3447
|
});
|
|
3439
3448
|
}
|
|
3440
3449
|
/**
|
|
@@ -3442,18 +3451,18 @@ async function startStreamableHTTPServer() {
|
|
|
3442
3451
|
* Handle transport-specific initialization logic
|
|
3443
3452
|
*/
|
|
3444
3453
|
async function initializeServerByTransportMode(mode) {
|
|
3445
|
-
|
|
3454
|
+
logger.info('Initializing server with transport mode:', mode);
|
|
3446
3455
|
switch (mode) {
|
|
3447
3456
|
case TransportMode.STDIO:
|
|
3448
|
-
|
|
3457
|
+
logger.warn('Starting GitLab MCP Server with stdio transport');
|
|
3449
3458
|
await startStdioServer();
|
|
3450
3459
|
break;
|
|
3451
3460
|
case TransportMode.SSE:
|
|
3452
|
-
|
|
3461
|
+
logger.warn('Starting GitLab MCP Server with SSE transport');
|
|
3453
3462
|
await startSSEServer();
|
|
3454
3463
|
break;
|
|
3455
3464
|
case TransportMode.STREAMABLE_HTTP:
|
|
3456
|
-
|
|
3465
|
+
logger.warn('Starting GitLab MCP Server with Streamable HTTP transport');
|
|
3457
3466
|
await startStreamableHTTPServer();
|
|
3458
3467
|
break;
|
|
3459
3468
|
default:
|
|
@@ -3472,11 +3481,11 @@ async function runServer() {
|
|
|
3472
3481
|
await initializeServerByTransportMode(transportMode);
|
|
3473
3482
|
}
|
|
3474
3483
|
catch (error) {
|
|
3475
|
-
|
|
3484
|
+
logger.error("Error initializing server:", error);
|
|
3476
3485
|
process.exit(1);
|
|
3477
3486
|
}
|
|
3478
3487
|
}
|
|
3479
3488
|
runServer().catch(error => {
|
|
3480
|
-
|
|
3489
|
+
logger.error("Fatal error in main():", error);
|
|
3481
3490
|
process.exit(1);
|
|
3482
3491
|
});
|
package/build/utils.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zereight/mcp-gitlab",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.70",
|
|
4
4
|
"description": "MCP server for using the GitLab API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "zereight",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"deploy": "npm publish --access public",
|
|
24
24
|
"generate-tools": "npx ts-node scripts/generate-tools-readme.ts",
|
|
25
25
|
"changelog": "auto-changelog -p",
|
|
26
|
-
"test": "node test/validate-api.js
|
|
26
|
+
"test": "node test/validate-api.js",
|
|
27
27
|
"test:integration": "node test/validate-api.js",
|
|
28
28
|
"test:server": "npm run build && node build/test/test-all-transport-server.js",
|
|
29
29
|
"lint": "eslint . --ext .ts",
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
"http-proxy-agent": "^7.0.2",
|
|
41
41
|
"https-proxy-agent": "^7.0.6",
|
|
42
42
|
"node-fetch": "^3.3.2",
|
|
43
|
+
"pino": "^9.7.0",
|
|
44
|
+
"pino-pretty": "^13.0.0",
|
|
43
45
|
"socks-proxy-agent": "^8.0.5",
|
|
44
46
|
"tough-cookie": "^5.1.2",
|
|
45
47
|
"zod-to-json-schema": "^3.23.5"
|
|
@@ -49,11 +51,11 @@
|
|
|
49
51
|
"@types/node": "^22.13.10",
|
|
50
52
|
"@typescript-eslint/eslint-plugin": "^8.21.0",
|
|
51
53
|
"@typescript-eslint/parser": "^8.21.0",
|
|
54
|
+
"auto-changelog": "^2.4.0",
|
|
52
55
|
"eslint": "^9.18.0",
|
|
53
56
|
"prettier": "^3.4.2",
|
|
54
57
|
"ts-node": "^10.9.2",
|
|
55
58
|
"typescript": "^5.8.2",
|
|
56
|
-
"zod": "^3.24.2"
|
|
57
|
-
"auto-changelog": "^2.4.0"
|
|
59
|
+
"zod": "^3.24.2"
|
|
58
60
|
}
|
|
59
61
|
}
|