@zereight/mcp-gitlab 2.1.50 → 2.1.52
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 +34 -10
- package/README.md +34 -10
- package/README.zh-CN.md +35 -10
- package/build/auth-cli.js +95 -0
- package/build/cli-command.js +33 -0
- package/build/index.js +40 -20
- package/build/oauth-device-flow.js +158 -0
- package/build/oauth.js +20 -0
- package/build/schemas.js +53 -10
- package/build/scripts/generate-tool-docs.js +1 -1
- package/build/test/create-commit-actions.test.js +102 -0
- package/build/test/gitlab-artifact-entry-schema.test.js +44 -0
- package/build/test/oauth-device-flow-tests.js +440 -0
- package/build/test/schema-tests.js +117 -3
- package/build/test/test-job-artifacts.js +35 -1
- package/build/test/test-permission-mode.js +28 -0
- package/build/tools/tool-descriptions.js +2 -2
- package/build/utils/gitlab-commit-actions.js +42 -0
- package/package.json +4 -3
|
@@ -140,6 +140,34 @@ describe("Permission Mode", { concurrency: 1 }, () => {
|
|
|
140
140
|
await client.disconnect();
|
|
141
141
|
}
|
|
142
142
|
});
|
|
143
|
+
test("rejects push_files delete actions", async () => {
|
|
144
|
+
const client = await connectClient(server);
|
|
145
|
+
try {
|
|
146
|
+
await assert.rejects(() => client.callTool("push_files", {
|
|
147
|
+
project_id: "1",
|
|
148
|
+
branch: "main",
|
|
149
|
+
commit_message: "m",
|
|
150
|
+
files: [{ file_path: "a.txt", action: "delete" }],
|
|
151
|
+
}), (error) => error.message.includes("delete or move actions in modify mode"), "push_files delete should be rejected in modify mode");
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
await client.disconnect();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
test("rejects push_files move actions", async () => {
|
|
158
|
+
const client = await connectClient(server);
|
|
159
|
+
try {
|
|
160
|
+
await assert.rejects(() => client.callTool("push_files", {
|
|
161
|
+
project_id: "1",
|
|
162
|
+
branch: "main",
|
|
163
|
+
commit_message: "m",
|
|
164
|
+
files: [{ file_path: "b.txt", action: "move", previous_path: "a.txt" }],
|
|
165
|
+
}), (error) => error.message.includes("delete or move actions in modify mode"), "push_files move should be rejected in modify mode");
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
await client.disconnect();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
143
171
|
test("does not apply the modify-mode guard to non-delete mutations", async () => {
|
|
144
172
|
const client = await connectClient(server);
|
|
145
173
|
try {
|
|
@@ -5,8 +5,8 @@ const TOOL_GUIDANCE = {
|
|
|
5
5
|
get_merge_request_approval_state: "Use this to inspect approval rules and approvers before deciding whether a merge request can be merged; use `approve_merge_request` to change approval state. It is read-only and returns the approval-state response, while missing requests, unsupported GitLab versions, and permission failures are reported as errors.",
|
|
6
6
|
get_merge_request_conflicts: "Use this to inspect merge conflicts before attempting `merge_merge_request`; it reports conflicts and does not resolve them. It is read-only, requires access to the project and merge request, and returns GitLab's conflict data or an error when the request cannot be evaluated.",
|
|
7
7
|
list_merge_request_pipelines: "Use this to inspect pipelines associated with one merge request; use `list_pipelines` for project-wide pipeline filtering. It is read-only and paginated, requires project access, and returns pipeline records or GitLab errors for invalid identifiers, missing resources, or rate limits.",
|
|
8
|
-
create_or_update_file: "Use this for a single repository file when you know whether the target path is new or already exists; use `push_files` for a multi-file commit. The operation creates or updates remote content in a commit, requires repository write permission, and returns the commit result or a conflict/validation error.",
|
|
9
|
-
push_files: "Use this to commit several file changes atomically; use `create_or_update_file` when only one path is involved. The operation writes repository history on the selected branch, requires repository write permission, and returns the commit result or a validation, conflict, or protected-branch error.",
|
|
8
|
+
create_or_update_file: "Use this for a single repository file when you know whether the target path is new or already exists; use `push_files` for a multi-file commit. Optional `encoding` (`text` or `base64`) defaults to `GITLAB_REPO_FILE_ENCODING` so existing callers stay unchanged. The operation creates or updates remote content in a commit, requires repository write permission, and returns the commit result or a conflict/validation error.",
|
|
9
|
+
push_files: "Use this to commit several file changes atomically; use `create_or_update_file` when only one path is involved. Each file defaults to action `create`; optional per-file `action` (create/update/delete/move) and `encoding` (text/base64) are additive. `GITLAB_PERMISSION_MODE=modify` rejects `delete` and `move`. The operation writes repository history on the selected branch, requires repository write permission, and returns the commit result or a validation, conflict, or protected-branch error.",
|
|
10
10
|
create_issue: "Use this to open a new issue; use `update_issue` for an existing issue and `create_issue_note` to add discussion without changing issue fields. The operation creates remote project data, requires issue creation permission, and returns the new issue or a validation, permission, or duplicate-related error.",
|
|
11
11
|
create_merge_request: "Use this to open a new merge request from an existing source branch to a target branch; use `update_merge_request` after it exists. The operation creates remote review state, requires project access, and returns the new merge request or a validation, permission, branch, or duplicate-related error.",
|
|
12
12
|
fork_repository: "Use this to create a copy of an existing project in the current user's namespace or a permitted namespace; use `search_repositories` or `get_project` to inspect projects without copying them. The operation creates a new project, requires fork permission, and returns the forked project or a namespace/permission error.",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function fileOperationsIncludeDeleteOrMove(files) {
|
|
2
|
+
return files.some(file => file.action === "delete" || file.action === "move");
|
|
3
|
+
}
|
|
4
|
+
export function encodeRepoFilePayloadContent(content, globalEncoding) {
|
|
5
|
+
if (globalEncoding === "base64") {
|
|
6
|
+
return Buffer.from(content).toString("base64");
|
|
7
|
+
}
|
|
8
|
+
return content;
|
|
9
|
+
}
|
|
10
|
+
function applyContentAndEncoding(entry, content, encoding, globalEncoding) {
|
|
11
|
+
if (encoding !== undefined) {
|
|
12
|
+
entry.content = content;
|
|
13
|
+
entry.encoding = encoding;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
entry.content = encodeRepoFilePayloadContent(content, globalEncoding);
|
|
17
|
+
entry.encoding = globalEncoding;
|
|
18
|
+
}
|
|
19
|
+
export function toGitLabCommitActions(actions, globalEncoding) {
|
|
20
|
+
return actions.map(action => {
|
|
21
|
+
const act = action.action ?? "create";
|
|
22
|
+
const entry = { action: act, file_path: action.path };
|
|
23
|
+
if (act === "delete") {
|
|
24
|
+
return entry;
|
|
25
|
+
}
|
|
26
|
+
if (act === "move") {
|
|
27
|
+
if (action.previous_path) {
|
|
28
|
+
entry.previous_path = action.previous_path;
|
|
29
|
+
}
|
|
30
|
+
if (action.content === undefined) {
|
|
31
|
+
return entry;
|
|
32
|
+
}
|
|
33
|
+
applyContentAndEncoding(entry, action.content, action.encoding, globalEncoding);
|
|
34
|
+
return entry;
|
|
35
|
+
}
|
|
36
|
+
if (action.content === undefined) {
|
|
37
|
+
throw new Error(`content is required when action is '${act}'`);
|
|
38
|
+
}
|
|
39
|
+
applyContentAndEncoding(entry, action.content, action.encoding, globalEncoding);
|
|
40
|
+
return entry;
|
|
41
|
+
});
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zereight/mcp-gitlab",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.52",
|
|
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": [
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"test:live": "node test/validate-api.js",
|
|
59
59
|
"test:consumer-smoke": "bash scripts/consumer-install-smoke.sh",
|
|
60
60
|
"test:remote-auth": "npm run build && node --import tsx/esm --test --experimental-test-isolation=none test/remote-auth-simple-test.ts",
|
|
61
|
-
"test:schema": "tsx test/schema-tests.ts && tsx test/test-json-schema.ts",
|
|
62
|
-
"test:oauth": "tsx test/oauth-tests.ts",
|
|
61
|
+
"test:schema": "tsx test/schema-tests.ts && tsx test/test-json-schema.ts && node --import tsx/esm --test --experimental-test-isolation=none --test-concurrency=1 test/*-schema.test.ts",
|
|
62
|
+
"test:oauth": "tsx test/oauth-tests.ts && node --import tsx/esm --test --experimental-test-isolation=none test/oauth-device-flow-tests.ts",
|
|
63
63
|
"test:list-merge-requests": "npm run build && tsx test/test-list-merge-requests.ts",
|
|
64
64
|
"test:approvals": "npm run build && tsx test/test-merge-request-approvals.ts",
|
|
65
65
|
"lint": "eslint . --ext .ts",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"lint:fix": "eslint . --ext .ts --fix",
|
|
69
69
|
"release": "bash scripts/release.sh",
|
|
70
70
|
"release:mcp-registry": "bash scripts/publish_mcp_registry.sh",
|
|
71
|
+
"sync:alias-package": "node scripts/sync-alias-package-version.mjs",
|
|
71
72
|
"format": "prettier --write \"**/*.{js,ts,json,md}\"",
|
|
72
73
|
"format:check": "prettier --check \"**/*.{js,ts,json,md}\""
|
|
73
74
|
},
|