@zereight/mcp-gitlab 1.0.63 → 1.0.64

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
@@ -111,6 +111,7 @@ $ sh scripts/image_push.sh docker_user_name
111
111
  - `USE_GITLAB_WIKI`: When set to 'true', enables the wiki-related tools (list_wiki_pages, get_wiki_page, create_wiki_page, update_wiki_page, delete_wiki_page). By default, wiki features are disabled.
112
112
  - `USE_MILESTONE`: When set to 'true', enables the milestone-related tools (list_milestones, get_milestone, create_milestone, edit_milestone, delete_milestone, get_milestone_issue, get_milestone_merge_requests, promote_milestone, get_milestone_burndown_events). By default, milestone features are disabled.
113
113
  - `USE_PIPELINE`: When set to 'true', enables the pipeline-related tools (list_pipelines, get_pipeline, list_pipeline_jobs, get_pipeline_job, get_pipeline_job_output, create_pipeline, retry_pipeline, cancel_pipeline). By default, pipeline features are disabled.
114
+ - `GITLAB_AUTH_COOKIE_PATH`: Path to an authentication cookie file for GitLab instances that require cookie-based authentication. When provided, the cookie will be included in all GitLab API requests.
114
115
 
115
116
  ## Tools 🛠️
116
117
 
package/build/index.js CHANGED
@@ -3,7 +3,9 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
5
5
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
6
- import fetch from "node-fetch";
6
+ import nodeFetch from "node-fetch";
7
+ import fetchCookie from "fetch-cookie";
8
+ import { CookieJar, parse as parseCookie } from "tough-cookie";
7
9
  import { SocksProxyAgent } from "socks-proxy-agent";
8
10
  import { HttpsProxyAgent } from "https-proxy-agent";
9
11
  import { HttpProxyAgent } from "http-proxy-agent";
@@ -51,6 +53,7 @@ const server = new Server({
51
53
  },
52
54
  });
53
55
  const GITLAB_PERSONAL_ACCESS_TOKEN = process.env.GITLAB_PERSONAL_ACCESS_TOKEN;
56
+ const GITLAB_AUTH_COOKIE_PATH = process.env.GITLAB_AUTH_COOKIE_PATH;
54
57
  const IS_OLD = process.env.GITLAB_IS_OLD === "true";
55
58
  const GITLAB_READ_ONLY_MODE = process.env.GITLAB_READ_ONLY_MODE === "true";
56
59
  const USE_GITLAB_WIKI = process.env.USE_GITLAB_WIKI === "true";
@@ -91,6 +94,76 @@ if (HTTPS_PROXY) {
91
94
  }
92
95
  httpsAgent = httpsAgent || new HttpsAgent(sslOptions);
93
96
  httpAgent = httpAgent || new Agent();
97
+ // Create cookie jar with clean Netscape file parsing
98
+ const createCookieJar = () => {
99
+ if (!GITLAB_AUTH_COOKIE_PATH)
100
+ return null;
101
+ try {
102
+ const cookiePath = GITLAB_AUTH_COOKIE_PATH.startsWith("~/")
103
+ ? path.join(process.env.HOME || "", GITLAB_AUTH_COOKIE_PATH.slice(2))
104
+ : GITLAB_AUTH_COOKIE_PATH;
105
+ const jar = new CookieJar();
106
+ const cookieContent = fs.readFileSync(cookiePath, "utf8");
107
+ cookieContent.split("\n").forEach(line => {
108
+ // Handle #HttpOnly_ prefix
109
+ if (line.startsWith("#HttpOnly_")) {
110
+ line = line.slice(10);
111
+ }
112
+ // Skip comments and empty lines
113
+ if (line.startsWith("#") || !line.trim()) {
114
+ return;
115
+ }
116
+ // Parse Netscape format: domain, flag, path, secure, expires, name, value
117
+ const parts = line.split("\t");
118
+ if (parts.length >= 7) {
119
+ const [domain, , path, secure, expires, name, value] = parts;
120
+ // Build cookie string in standard format
121
+ const cookieStr = `${name}=${value}; Domain=${domain}; Path=${path}${secure === "TRUE" ? "; Secure" : ""}${expires !== "0" ? `; Expires=${new Date(parseInt(expires) * 1000).toUTCString()}` : ""}`;
122
+ // Use tough-cookie's parse function for robust parsing
123
+ const cookie = parseCookie(cookieStr);
124
+ if (cookie) {
125
+ const url = `${secure === "TRUE" ? "https" : "http"}://${domain.startsWith(".") ? domain.slice(1) : domain}`;
126
+ jar.setCookieSync(cookie, url);
127
+ }
128
+ }
129
+ });
130
+ return jar;
131
+ }
132
+ catch (error) {
133
+ console.error("Error loading cookie file:", error);
134
+ return null;
135
+ }
136
+ };
137
+ // Initialize cookie jar and fetch
138
+ const cookieJar = createCookieJar();
139
+ const fetch = cookieJar ? fetchCookie(nodeFetch, cookieJar) : nodeFetch;
140
+ // Ensure session is established for the current request
141
+ async function ensureSessionForRequest() {
142
+ if (!cookieJar || !GITLAB_AUTH_COOKIE_PATH)
143
+ return;
144
+ // Extract the base URL from GITLAB_API_URL
145
+ const apiUrl = new URL(GITLAB_API_URL);
146
+ const baseUrl = `${apiUrl.protocol}//${apiUrl.hostname}`;
147
+ // Check if we already have GitLab session cookies
148
+ const gitlabCookies = cookieJar.getCookiesSync(baseUrl);
149
+ const hasSessionCookie = gitlabCookies.some(cookie => cookie.key === '_gitlab_session' || cookie.key === 'remember_user_token');
150
+ if (!hasSessionCookie) {
151
+ try {
152
+ // Establish session with a lightweight request
153
+ await fetch(`${GITLAB_API_URL}/user`, {
154
+ ...DEFAULT_FETCH_CONFIG,
155
+ redirect: 'follow'
156
+ }).catch(() => {
157
+ // Ignore errors - the important thing is that cookies get set during redirects
158
+ });
159
+ // Small delay to ensure cookies are fully processed
160
+ await new Promise(resolve => setTimeout(resolve, 100));
161
+ }
162
+ catch (error) {
163
+ // Ignore session establishment errors
164
+ }
165
+ }
166
+ }
94
167
  // Modify DEFAULT_HEADERS to include agent configuration
95
168
  const DEFAULT_HEADERS = {
96
169
  Accept: "application/json",
@@ -2362,6 +2435,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2362
2435
  if (!request.params.arguments) {
2363
2436
  throw new Error("Arguments are required");
2364
2437
  }
2438
+ // Ensure session is established for every request if cookie authentication is enabled
2439
+ if (GITLAB_AUTH_COOKIE_PATH) {
2440
+ await ensureSessionForRequest();
2441
+ }
2365
2442
  switch (request.params.name) {
2366
2443
  case "fork_repository": {
2367
2444
  const forkArgs = ForkRepositorySchema.parse(request.params.arguments);
package/build/schemas.js CHANGED
@@ -274,14 +274,14 @@ export const GitLabRepositorySchema = z.object({
274
274
  project_access: z
275
275
  .object({
276
276
  access_level: z.number(),
277
- notification_level: z.number().optional(),
277
+ notification_level: z.number().nullable().optional(),
278
278
  })
279
279
  .optional()
280
280
  .nullable(),
281
281
  group_access: z
282
282
  .object({
283
283
  access_level: z.number(),
284
- notification_level: z.number().optional(),
284
+ notification_level: z.number().nullable().optional(),
285
285
  })
286
286
  .optional()
287
287
  .nullable(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "1.0.63",
3
+ "version": "1.0.64",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",
@@ -33,11 +33,13 @@
33
33
  "@modelcontextprotocol/sdk": "1.8.0",
34
34
  "@types/node-fetch": "^2.6.12",
35
35
  "express": "^5.1.0",
36
+ "fetch-cookie": "^3.1.0",
36
37
  "form-data": "^4.0.0",
37
38
  "http-proxy-agent": "^7.0.2",
38
39
  "https-proxy-agent": "^7.0.6",
39
40
  "node-fetch": "^3.3.2",
40
41
  "socks-proxy-agent": "^8.0.5",
42
+ "tough-cookie": "^5.1.2",
41
43
  "zod-to-json-schema": "^3.23.5"
42
44
  },
43
45
  "devDependencies": {