@zereight/mcp-gitlab 2.1.55 → 2.1.56

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.ko.md CHANGED
@@ -98,7 +98,7 @@ npm install -g @zereight/mcp-gitlab
98
98
 
99
99
  예시는 기존 `mcp-gitlab`보다 충돌 가능성이 낮은 `zereight-mcp-gitlab` 별칭을 사용합니다. MCP 클라이언트가 찾지 못하면 `which zereight-mcp-gitlab`의 절대 경로를 사용하세요.
100
100
 
101
- 전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.1.54`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
101
+ 전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.1.55`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
102
102
 
103
103
  #### CLI 인자 사용하기(환경 변수 문제가 있는 클라이언트용)
104
104
 
@@ -288,7 +288,7 @@ MCP 클라이언트 설정:
288
288
  | `STREAMABLE_HTTP` | 예 | 반드시 `true` |
289
289
  | `ENABLE_DYNAMIC_API_URL` | 선택 | 요청별 `X-GitLab-API-URL` 헤더 허용 |
290
290
  | `GITLAB_ALLOWED_HOSTS` | 선택 | 허용할 `X-GitLab-API-URL` 호스트의 쉼표 구분 목록; `GITLAB_API_URL` 호스트는 항상 허용 |
291
- | `GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY` | 선택 | 인증 없이 `initialize`, `notifications/initialized`, `tools/list`만 허용(도구 호출은 여전히 인증 필요) |
291
+ | `GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY` | 선택 | 인증 없이 `initialize`, `notifications/initialized`, `tools/list`, `server/discover`만 허용(도구 호출은 여전히 인증 필요) |
292
292
  | `MCP_SERVER_URL` / `MCP_ALLOWED_HOSTS` / `MCP_ALLOWED_ORIGINS` | 선택 | DNS rebinding 방지를 위한 허용 `/mcp` 호스트/오리진 값 |
293
293
  | `MCP_TRUST_PROXY` | 선택 | 리버스 프록시 뒤에서 `Forwarded` / `X-Forwarded-*` 헤더 신뢰(다운로드 URL, Express `req.ip`, `/mcp` IP rate limit, OAuth rate limit) |
294
294
 
package/README.md CHANGED
@@ -103,7 +103,7 @@ npm install -g @zereight/mcp-gitlab
103
103
 
104
104
  The examples use `zereight-mcp-gitlab`, a less collision-prone alias for the legacy `mcp-gitlab` binary. If your MCP client cannot find it, use the absolute path from `which zereight-mcp-gitlab`.
105
105
 
106
- No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.1.54`. If you always want the newest release, use `npx -y @zereight/mcp-gitlab@latest` instead. The server prints a notice to stderr on startup when a newer version is available (disable with `GITLAB_DISABLE_VERSION_CHECK=true`).
106
+ No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.1.55`. If you always want the newest release, use `npx -y @zereight/mcp-gitlab@latest` instead. The server prints a notice to stderr on startup when a newer version is available (disable with `GITLAB_DISABLE_VERSION_CHECK=true`).
107
107
 
108
108
  #### Using CLI Arguments (for clients with env var issues)
109
109
 
@@ -316,7 +316,7 @@ the token to GitLab on behalf of the caller.
316
316
  | `STREAMABLE_HTTP` | ✅ | Must be `true` |
317
317
  | `ENABLE_DYNAMIC_API_URL` | optional | Allow per-request GitLab URL via `X-GitLab-API-URL` header |
318
318
  | `GITLAB_ALLOWED_HOSTS` | optional | Comma-separated allowed `X-GitLab-API-URL` hosts; `GITLAB_API_URL` hosts are always allowed |
319
- | `GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY` | optional | Allow unauthenticated `initialize`, `notifications/initialized`, and `tools/list` only (tool calls still require auth) |
319
+ | `GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY` | optional | Allow unauthenticated `initialize`, `notifications/initialized`, `tools/list`, and `server/discover` only (tool calls still require auth) |
320
320
  | `MCP_SERVER_URL` / `MCP_ALLOWED_HOSTS` / `MCP_ALLOWED_ORIGINS` | optional | Allowed public `/mcp` host/origin values for DNS rebinding protection |
321
321
  | `MCP_TRUST_PROXY` | optional | Trust `Forwarded` / `X-Forwarded-*` headers behind a reverse proxy (download URLs, Express `req.ip`, `/mcp` IP rate limits, OAuth rate limits) |
322
322
 
package/README.zh-CN.md CHANGED
@@ -98,7 +98,7 @@ npm install -g @zereight/mcp-gitlab
98
98
 
99
99
  示例使用 `zereight-mcp-gitlab`,这是比旧的 `mcp-gitlab` 更不容易冲突的别名。如果 MCP 客户端找不到它,请使用 `which zereight-mcp-gitlab` 输出的绝对路径。
100
100
 
101
- 如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.1.54`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
101
+ 如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.1.55`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
102
102
 
103
103
  #### 使用 CLI 参数(适用于环境变量有问题的客户端)
104
104
 
@@ -289,7 +289,7 @@ MCP 客户端配置:
289
289
  | `STREAMABLE_HTTP` | 是 | 必须为 `true` |
290
290
  | `ENABLE_DYNAMIC_API_URL` | 可选 | 允许按请求通过 `X-GitLab-API-URL` 请求头指定 GitLab URL |
291
291
  | `GITLAB_ALLOWED_HOSTS` | 可选 | 允许的 `X-GitLab-API-URL` 主机逗号分隔列表;`GITLAB_API_URL` 中的主机始终允许 |
292
- | `GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY` | 可选 | 仅允许未认证的 `initialize`、`notifications/initialized`、`tools/list`(工具调用仍需认证) |
292
+ | `GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY` | 可选 | 仅允许未认证的 `initialize`、`notifications/initialized`、`tools/list`、`server/discover`(工具调用仍需认证) |
293
293
  | `MCP_SERVER_URL` / `MCP_ALLOWED_HOSTS` / `MCP_ALLOWED_ORIGINS` | 可选 | 用于 DNS rebinding 防护的允许 `/mcp` 主机/来源值 |
294
294
  | `MCP_TRUST_PROXY` | 可选 | 在反向代理后信任 `Forwarded` / `X-Forwarded-*` 请求头(下载 URL、Express `req.ip`、`/mcp` IP 速率限制、OAuth 速率限制) |
295
295
 
@@ -34,7 +34,10 @@ export function isUnauthenticatedDiscoveryRequestBody(body) {
34
34
  if (typeof m !== "object" || m === null)
35
35
  return false;
36
36
  const method = m.method;
37
- return (method === "initialize" || method === "notifications/initialized" || method === "tools/list");
37
+ return (method === "initialize" ||
38
+ method === "notifications/initialized" ||
39
+ method === "tools/list" ||
40
+ method === "server/discover");
38
41
  };
39
42
  if (Array.isArray(body))
40
43
  return body.every(isDiscoveryMethod);
@@ -16,12 +16,22 @@ describe("streamable request helpers", () => {
16
16
  });
17
17
  test("allows only discovery methods in unauthenticated discovery bodies", () => {
18
18
  assert.strictEqual(isUnauthenticatedDiscoveryRequestBody({ method: "tools/list" }), true);
19
+ assert.strictEqual(isUnauthenticatedDiscoveryRequestBody({ method: "server/discover" }), true);
19
20
  assert.strictEqual(isUnauthenticatedDiscoveryRequestBody([
20
21
  { method: "initialize" },
21
22
  { method: "notifications/initialized" },
22
23
  { method: "tools/list" },
24
+ { method: "server/discover" },
23
25
  ]), true);
24
- assert.strictEqual(isUnauthenticatedDiscoveryRequestBody([{ method: "tools/list" }, { method: "call_tool" }]), false);
26
+ assert.strictEqual(isUnauthenticatedDiscoveryRequestBody([
27
+ { method: "tools/list" },
28
+ { method: "call_tool" },
29
+ { method: "tools/call" },
30
+ ]), false);
31
+ assert.strictEqual(isUnauthenticatedDiscoveryRequestBody([
32
+ { method: "server/discover" },
33
+ { method: "tools/call" },
34
+ ]), false);
25
35
  assert.strictEqual(isUnauthenticatedDiscoveryRequestBody(undefined), false);
26
36
  });
27
37
  test("normalizes Mcp-Session-Id headers", () => {
@@ -106,6 +106,54 @@ describe("Streamable HTTP unauthenticated tool discovery", { timeout: 20_000 },
106
106
  assert.ok(Array.isArray(listResponse.data.result?.tools), "tools/list should return tools");
107
107
  assert.ok(listResponse.data.result.tools.length > 0, "tools/list should not be empty");
108
108
  });
109
+ test("allows unauthenticated server/discover in individual and batch requests", async () => {
110
+ const { server, mcpUrl } = await launchRemoteAuthServer({
111
+ GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY: "true",
112
+ });
113
+ servers.push(server);
114
+ const preInitDiscover = await rawMcpRequest(mcpUrl, {
115
+ jsonrpc: "2.0",
116
+ id: 10,
117
+ method: "server/discover",
118
+ params: {},
119
+ });
120
+ assert.strictEqual(preInitDiscover.status, 400, preInitDiscover.text);
121
+ assert.match(preInitDiscover.data?.error?.message ?? "", /Server not initialized/i, "server/discover should pass the auth gate before MCP initialization errors");
122
+ const sessionId = await initialize(mcpUrl);
123
+ const initialized = await rawMcpRequest(mcpUrl, { jsonrpc: "2.0", method: "notifications/initialized" }, { "mcp-session-id": sessionId });
124
+ assert.ok([200, 202, 204].includes(initialized.status), initialized.text);
125
+ const discoverResponse = await rawMcpRequest(mcpUrl, { jsonrpc: "2.0", id: 11, method: "server/discover", params: {} }, { "mcp-session-id": sessionId });
126
+ assert.strictEqual(discoverResponse.status, 200, discoverResponse.text);
127
+ assert.strictEqual(discoverResponse.data?.id, 11);
128
+ assert.strictEqual(discoverResponse.data?.error?.code, -32601);
129
+ assert.match(discoverResponse.data?.error?.message ?? "", /Method not found/i);
130
+ const batchResponse = await rawMcpRequest(mcpUrl, [
131
+ { jsonrpc: "2.0", id: 12, method: "server/discover", params: {} },
132
+ { jsonrpc: "2.0", id: 13, method: "tools/list", params: {} },
133
+ ], { "mcp-session-id": sessionId });
134
+ assert.strictEqual(batchResponse.status, 200, batchResponse.text);
135
+ assert.ok(Array.isArray(batchResponse.data.result?.tools), "batch tools/list should return tools");
136
+ assert.ok(batchResponse.data.result.tools.length > 0, "batch tools/list should not be empty");
137
+ });
138
+ test("rejects unauthenticated discovery batch when tools/call is included", async () => {
139
+ const { server, mcpUrl } = await launchRemoteAuthServer({
140
+ GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY: "true",
141
+ });
142
+ servers.push(server);
143
+ const response = await rawMcpRequest(mcpUrl, [
144
+ { jsonrpc: "2.0", id: 20, method: "server/discover", params: {} },
145
+ {
146
+ jsonrpc: "2.0",
147
+ id: 21,
148
+ method: "tools/call",
149
+ params: {
150
+ name: "get_project",
151
+ arguments: { project_id: "123" },
152
+ },
153
+ },
154
+ ]);
155
+ assert.strictEqual(response.status, 401, response.text);
156
+ });
109
157
  test("expires unauthenticated discovery sessions and frees capacity", async () => {
110
158
  const { server, mcpUrl } = await launchRemoteAuthServer({
111
159
  GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY: "true",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "2.1.55",
3
+ "version": "2.1.56",
4
4
  "mcpName": "io.github.zereight/gitlab-mcp",
5
5
  "description": "GitLab MCP server for projects, merge requests, issues, pipelines, wiki, releases, and more",
6
6
  "keywords": [