@zereight/mcp-gitlab 2.2.5 → 2.2.6
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 +1 -1
- package/README.md +1 -1
- package/README.pt-BR.md +1 -1
- package/README.zh-CN.md +1 -1
- package/build/index.js +54 -4
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -138,7 +138,7 @@ command = lib.getExe inputs.gitlab-mcp.packages.${system}.default;
|
|
|
138
138
|
|
|
139
139
|
예시는 기존 `mcp-gitlab`보다 충돌 가능성이 낮은 `zereight-mcp-gitlab` 별칭을 사용합니다. MCP 클라이언트가 찾지 못하면 `which zereight-mcp-gitlab`의 절대 경로를 사용하세요.
|
|
140
140
|
|
|
141
|
-
전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.2.
|
|
141
|
+
전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.2.5`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
|
|
142
142
|
|
|
143
143
|
#### CLI 인자 사용하기(환경 변수 문제가 있는 클라이언트용)
|
|
144
144
|
|
package/README.md
CHANGED
|
@@ -143,7 +143,7 @@ The store path is pinned by your lock file; update it with `nix flake update git
|
|
|
143
143
|
|
|
144
144
|
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`.
|
|
145
145
|
|
|
146
|
-
No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.2.
|
|
146
|
+
No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.2.5`. 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`).
|
|
147
147
|
|
|
148
148
|
#### Using CLI Arguments (for clients with env var issues)
|
|
149
149
|
|
package/README.pt-BR.md
CHANGED
|
@@ -138,7 +138,7 @@ O caminho no store é fixado pelo seu lock file; atualize-o com `nix flake updat
|
|
|
138
138
|
|
|
139
139
|
Os exemplos usam `zereight-mcp-gitlab`, um alias com menor chance de conflito que o binário legado `mcp-gitlab`. Se o seu cliente MCP não o encontrar, use o caminho absoluto retornado por `which zereight-mcp-gitlab`.
|
|
140
140
|
|
|
141
|
-
Não quer instalar globalmente? Fixe o `npx` na versão estável anterior (a versão recomendada por esta documentação), por exemplo `npx -y @zereight/mcp-gitlab@2.2.
|
|
141
|
+
Não quer instalar globalmente? Fixe o `npx` na versão estável anterior (a versão recomendada por esta documentação), por exemplo `npx -y @zereight/mcp-gitlab@2.2.5`. Se quiser sempre a versão mais recente, use `npx -y @zereight/mcp-gitlab@latest`. O servidor exibe um aviso no stderr durante a inicialização quando existe uma versão mais nova disponível (desative com `GITLAB_DISABLE_VERSION_CHECK=true`).
|
|
142
142
|
|
|
143
143
|
#### Usando argumentos de CLI (para clientes com problemas com variáveis de ambiente)
|
|
144
144
|
|
package/README.zh-CN.md
CHANGED
|
@@ -138,7 +138,7 @@ command = lib.getExe inputs.gitlab-mcp.packages.${system}.default;
|
|
|
138
138
|
|
|
139
139
|
示例使用 `zereight-mcp-gitlab`,这是比旧的 `mcp-gitlab` 更不容易冲突的别名。如果 MCP 客户端找不到它,请使用 `which zereight-mcp-gitlab` 输出的绝对路径。
|
|
140
140
|
|
|
141
|
-
如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.2.
|
|
141
|
+
如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.2.5`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
|
|
142
142
|
|
|
143
143
|
#### 使用 CLI 参数(适用于环境变量有问题的客户端)
|
|
144
144
|
|
package/build/index.js
CHANGED
|
@@ -1186,6 +1186,27 @@ function getEffectiveAllowedProjectIds() {
|
|
|
1186
1186
|
}
|
|
1187
1187
|
return GITLAB_ALLOWED_PROJECT_IDS;
|
|
1188
1188
|
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Keep a dynamic project scope bound to its session
|
|
1191
|
+
* A request that omits X-GitLab-Allowed-Project-Ids keeps the session scope, and a
|
|
1192
|
+
* request naming a different scope is refused (null). An unscoped session may be
|
|
1193
|
+
* narrowed. Re-initialize to change the scope.
|
|
1194
|
+
*/
|
|
1195
|
+
function bindSessionProjectScope(requested, sessionScope) {
|
|
1196
|
+
if (!sessionScope || sessionScope.length === 0) {
|
|
1197
|
+
return requested;
|
|
1198
|
+
}
|
|
1199
|
+
if (requested === undefined) {
|
|
1200
|
+
return sessionScope;
|
|
1201
|
+
}
|
|
1202
|
+
const want = new Set(requested);
|
|
1203
|
+
const have = new Set(sessionScope);
|
|
1204
|
+
return want.size === have.size && [...want].every(id => have.has(id)) ? sessionScope : null;
|
|
1205
|
+
}
|
|
1206
|
+
const SESSION_PROJECT_SCOPE_MISMATCH = {
|
|
1207
|
+
error: "Project scope mismatch",
|
|
1208
|
+
message: "X-GitLab-Allowed-Project-Ids does not match the project scope of this session. Re-initialize to change it.",
|
|
1209
|
+
};
|
|
1189
1210
|
/**
|
|
1190
1211
|
* Reject tools that cannot be limited to allowed projects when
|
|
1191
1212
|
* ENABLE_STRICT_PROJECT_SCOPE is set and an allowlist is in effect
|
|
@@ -11694,6 +11715,9 @@ async function startStreamableHTTPServer() {
|
|
|
11694
11715
|
const incomingSid = readMcpSessionIdHeader(req);
|
|
11695
11716
|
const isInit = isInitializationRequestBody(req.body);
|
|
11696
11717
|
const headerAuth = parseAuthHeaders(req);
|
|
11718
|
+
const opened = incomingSid && looksLikeStatelessSessionId(incomingSid)
|
|
11719
|
+
? openSessionId(material, incomingSid, sessionTtlSeconds)
|
|
11720
|
+
: null;
|
|
11697
11721
|
// In GITLAB_MCP_OAUTH mode, req.auth may be populated by requireBearerAuth.
|
|
11698
11722
|
// Use it when headerAuth is null (no Private-Token / JOB-TOKEN / Authorization).
|
|
11699
11723
|
let effective = null;
|
|
@@ -11701,7 +11725,6 @@ async function startStreamableHTTPServer() {
|
|
|
11701
11725
|
if (headerAuth) {
|
|
11702
11726
|
let needsUpstreamValidation = isInit || !incomingSid;
|
|
11703
11727
|
if (!needsUpstreamValidation && incomingSid && looksLikeStatelessSessionId(incomingSid)) {
|
|
11704
|
-
const opened = openSessionId(material, incomingSid, sessionTtlSeconds);
|
|
11705
11728
|
needsUpstreamValidation =
|
|
11706
11729
|
!opened ||
|
|
11707
11730
|
opened.h !== headerAuth.header ||
|
|
@@ -11748,7 +11771,6 @@ async function startStreamableHTTPServer() {
|
|
|
11748
11771
|
let sidPresentedButInvalid = false;
|
|
11749
11772
|
if (!effective && incomingSid) {
|
|
11750
11773
|
if (looksLikeStatelessSessionId(incomingSid)) {
|
|
11751
|
-
const opened = openSessionId(material, incomingSid, sessionTtlSeconds);
|
|
11752
11774
|
if (opened) {
|
|
11753
11775
|
effective = {
|
|
11754
11776
|
header: opened.h,
|
|
@@ -11791,6 +11813,18 @@ async function startStreamableHTTPServer() {
|
|
|
11791
11813
|
});
|
|
11792
11814
|
return;
|
|
11793
11815
|
}
|
|
11816
|
+
// The project scope sealed into the sid belongs to the session: live headers
|
|
11817
|
+
// may refresh the token but cannot drop or replace the scope.
|
|
11818
|
+
if (ENABLE_DYNAMIC_PROJECT_SCOPE && freshAuthPresent && !isInit && opened) {
|
|
11819
|
+
const allowedProjectIds = bindSessionProjectScope(effective.allowedProjectIds, opened.p);
|
|
11820
|
+
if (allowedProjectIds === null) {
|
|
11821
|
+
metrics.authFailures++;
|
|
11822
|
+
metrics.statelessAuthFailures++;
|
|
11823
|
+
res.status(401).json(SESSION_PROJECT_SCOPE_MISMATCH);
|
|
11824
|
+
return;
|
|
11825
|
+
}
|
|
11826
|
+
effective.allowedProjectIds = allowedProjectIds;
|
|
11827
|
+
}
|
|
11794
11828
|
// Always mint a fresh sid so the embedded iat advances on every request.
|
|
11795
11829
|
// This makes OAUTH_STATELESS_SESSION_TTL_SECONDS behave as an inactivity
|
|
11796
11830
|
// timeout rather than an absolute-age cap — matching the legacy
|
|
@@ -12180,7 +12214,13 @@ async function startStreamableHTTPServer() {
|
|
|
12180
12214
|
}
|
|
12181
12215
|
}
|
|
12182
12216
|
else if (sessionId && authData) {
|
|
12183
|
-
const
|
|
12217
|
+
const allowedProjectIds = bindSessionProjectScope(authData.allowedProjectIds, authBySession[sessionId].allowedProjectIds);
|
|
12218
|
+
if (allowedProjectIds === null) {
|
|
12219
|
+
metrics.authFailures++;
|
|
12220
|
+
res.status(401).json(SESSION_PROJECT_SCOPE_MISMATCH);
|
|
12221
|
+
return;
|
|
12222
|
+
}
|
|
12223
|
+
const result = await storeValidatedSessionAuth(sessionId, { ...authData, allowedProjectIds }, publicBaseUrl, authBySession[sessionId], { skipIfUnchanged: true });
|
|
12184
12224
|
if (result === "invalid") {
|
|
12185
12225
|
metrics.authFailures++;
|
|
12186
12226
|
res.status(401).json({
|
|
@@ -12193,6 +12233,10 @@ async function startStreamableHTTPServer() {
|
|
|
12193
12233
|
if (result === "stored") {
|
|
12194
12234
|
logger.debug(`Session ${sessionId}: updated ${authData.header} header`);
|
|
12195
12235
|
}
|
|
12236
|
+
else {
|
|
12237
|
+
// Same token: keep the stored auth but let an unscoped session narrow.
|
|
12238
|
+
authBySession[sessionId].allowedProjectIds = allowedProjectIds;
|
|
12239
|
+
}
|
|
12196
12240
|
}
|
|
12197
12241
|
else if (sessionId && authBySession[sessionId]) {
|
|
12198
12242
|
// Existing session with stored auth: update last used time and reset timeout
|
|
@@ -12218,7 +12262,13 @@ async function startStreamableHTTPServer() {
|
|
|
12218
12262
|
setAuthTimeout(sessionId);
|
|
12219
12263
|
}
|
|
12220
12264
|
else {
|
|
12221
|
-
|
|
12265
|
+
const allowedProjectIds = bindSessionProjectScope(headerAuthData.allowedProjectIds, authBySession[sessionId].allowedProjectIds);
|
|
12266
|
+
if (allowedProjectIds === null) {
|
|
12267
|
+
metrics.authFailures++;
|
|
12268
|
+
res.status(401).json(SESSION_PROJECT_SCOPE_MISMATCH);
|
|
12269
|
+
return;
|
|
12270
|
+
}
|
|
12271
|
+
authBySession[sessionId] = withPublicBaseUrl({ ...headerAuthData, allowedProjectIds }, publicBaseUrl, authBySession[sessionId]);
|
|
12222
12272
|
setAuthTimeout(sessionId);
|
|
12223
12273
|
}
|
|
12224
12274
|
}
|
package/package.json
CHANGED