@zereight/mcp-gitlab 2.1.48 → 2.1.50
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 +2 -2
- package/README.zh-CN.md +1 -1
- package/build/schemas.js +6 -1
- package/build/test/gitlab-tag-schema.test.js +51 -0
- package/build/test/merge-merge-request-schema.test.js +75 -0
- package/build/test/test-tags.js +14 -0
- package/build/test/tool-description-quality.test.js +25 -0
- package/build/tools/registry.js +4 -0
- package/build/tools/tool-descriptions.js +87 -0
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -80,7 +80,7 @@ npm install -g @zereight/mcp-gitlab
|
|
|
80
80
|
|
|
81
81
|
예시는 기존 `mcp-gitlab`보다 충돌 가능성이 낮은 `zereight-mcp-gitlab` 별칭을 사용합니다. MCP 클라이언트가 찾지 못하면 `which zereight-mcp-gitlab`의 절대 경로를 사용하세요.
|
|
82
82
|
|
|
83
|
-
전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.1.
|
|
83
|
+
전역 설치를 쓰지 않으려면 `npx -y @zereight/mcp-gitlab@2.1.49`처럼 직전 안정 버전(문서가 권장하는 버전)으로 고정하세요. 항상 최신 버전을 원하면 `npx -y @zereight/mcp-gitlab@latest`를 사용하세요. 새 버전이 나오면 서버가 시작 시 stderr로 알려줍니다(`GITLAB_DISABLE_VERSION_CHECK=true`로 비활성화 가능).
|
|
84
84
|
|
|
85
85
|
#### CLI 인자 사용하기(환경 변수 문제가 있는 클라이언트용)
|
|
86
86
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GitLab MCP Server
|
|
2
2
|
|
|
3
|
-
[](https://mcptoplist.com/server/io.github.zereight%2Fgitlab-mcp)
|
|
3
|
+
[](https://mcptoplist.com/server/io.github.zereight%2Fgitlab-mcp) [](https://mcpindex.ai/server/io-github-zereight-gitlab-mcp)
|
|
4
4
|
|
|
5
5
|
[English](./README.md) | [한국어](./README.ko.md) | [简体中文](./README.zh-CN.md)
|
|
6
6
|
|
|
@@ -82,7 +82,7 @@ npm install -g @zereight/mcp-gitlab
|
|
|
82
82
|
|
|
83
83
|
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`.
|
|
84
84
|
|
|
85
|
-
No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.1.
|
|
85
|
+
No global install? Pin `npx` to the previous stable release (the version these docs recommend), for example `npx -y @zereight/mcp-gitlab@2.1.49`. 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`).
|
|
86
86
|
|
|
87
87
|
#### Using CLI Arguments (for clients with env var issues)
|
|
88
88
|
|
package/README.zh-CN.md
CHANGED
|
@@ -80,7 +80,7 @@ npm install -g @zereight/mcp-gitlab
|
|
|
80
80
|
|
|
81
81
|
示例使用 `zereight-mcp-gitlab`,这是比旧的 `mcp-gitlab` 更不容易冲突的别名。如果 MCP 客户端找不到它,请使用 `which zereight-mcp-gitlab` 输出的绝对路径。
|
|
82
82
|
|
|
83
|
-
如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.1.
|
|
83
|
+
如果不想全局安装,请将 `npx` 固定到上一个稳定版本(即文档推荐的版本),例如 `npx -y @zereight/mcp-gitlab@2.1.49`。如果始终想使用最新版本,请改用 `npx -y @zereight/mcp-gitlab@latest`。有新版本发布时,服务器会在启动时通过 stderr 提示(可用 `GITLAB_DISABLE_VERSION_CHECK=true` 关闭)。
|
|
84
84
|
|
|
85
85
|
#### 使用 CLI 参数(适用于环境变量有问题的客户端)
|
|
86
86
|
|
package/build/schemas.js
CHANGED
|
@@ -1160,6 +1160,7 @@ export const GitLabMergeRequestSchema = z.object({
|
|
|
1160
1160
|
updated_at: z.string(),
|
|
1161
1161
|
merged_at: z.string().nullable(),
|
|
1162
1162
|
closed_at: z.string().nullable(),
|
|
1163
|
+
sha: z.string().optional().describe("SHA of the head commit in the source branch"),
|
|
1163
1164
|
merge_commit_sha: z.string().nullable(),
|
|
1164
1165
|
merge_user: GitLabUserSchema.nullable()
|
|
1165
1166
|
.optional()
|
|
@@ -1773,6 +1774,10 @@ export const UpdateMergeRequestSchema = MergeRequestParamsSchema.extend({
|
|
|
1773
1774
|
});
|
|
1774
1775
|
export const MergeMergeRequestSchema = ProjectParamsSchema.extend({
|
|
1775
1776
|
merge_request_iid: z.coerce.string().optional().describe("The IID of a merge request"),
|
|
1777
|
+
sha: z
|
|
1778
|
+
.string()
|
|
1779
|
+
.optional()
|
|
1780
|
+
.describe("SHA of the source-branch HEAD from get_merge_request (`sha` or `diff_refs.head_sha`). If provided, GitLab merges only when HEAD still matches. GitLab 19.2+ groups may require this (Require a commit SHA on the merge requests API)."),
|
|
1776
1781
|
auto_merge: z.coerce
|
|
1777
1782
|
.boolean()
|
|
1778
1783
|
.optional()
|
|
@@ -3417,7 +3422,7 @@ export const GitLabTagSchema = z.object({
|
|
|
3417
3422
|
tag_name: z.string(),
|
|
3418
3423
|
description: z.string(),
|
|
3419
3424
|
})
|
|
3420
|
-
.
|
|
3425
|
+
.nullish(),
|
|
3421
3426
|
protected: z.boolean(),
|
|
3422
3427
|
created_at: z.string().nullable(),
|
|
3423
3428
|
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { GitLabTagSchema } from "../schemas.js";
|
|
4
|
+
function validTag(overrides = {}) {
|
|
5
|
+
return {
|
|
6
|
+
name: "v1.0.0",
|
|
7
|
+
message: "tag message",
|
|
8
|
+
target: "abc123def456",
|
|
9
|
+
commit: {
|
|
10
|
+
id: "abc123def456",
|
|
11
|
+
short_id: "abc123de",
|
|
12
|
+
title: "Release",
|
|
13
|
+
created_at: "2026-03-13T10:00:00.000Z",
|
|
14
|
+
parent_ids: ["1111111111111111"],
|
|
15
|
+
message: "Release",
|
|
16
|
+
author_name: "Test User",
|
|
17
|
+
author_email: "test@example.com",
|
|
18
|
+
authored_date: "2026-03-13T09:55:00.000Z",
|
|
19
|
+
committer_name: "Test User",
|
|
20
|
+
committer_email: "test@example.com",
|
|
21
|
+
committed_date: "2026-03-13T10:00:00.000Z",
|
|
22
|
+
},
|
|
23
|
+
protected: false,
|
|
24
|
+
created_at: "2026-03-13T10:00:00.000Z",
|
|
25
|
+
...overrides,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
describe("When parsing a GitLab tag response", () => {
|
|
29
|
+
describe("with a missing release key", () => {
|
|
30
|
+
it("should accept the tag instead of requiring release", () => {
|
|
31
|
+
const parsed = GitLabTagSchema.parse(validTag());
|
|
32
|
+
assert.strictEqual(parsed.name, "v1.0.0");
|
|
33
|
+
assert.strictEqual(parsed.release, undefined);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("with release set to null", () => {
|
|
37
|
+
it("should accept a null release", () => {
|
|
38
|
+
const parsed = GitLabTagSchema.parse(validTag({ release: null }));
|
|
39
|
+
assert.strictEqual(parsed.release, null);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe("with a release object", () => {
|
|
43
|
+
it("should keep tag_name and description", () => {
|
|
44
|
+
const parsed = GitLabTagSchema.parse(validTag({
|
|
45
|
+
release: { tag_name: "v1.0.0", description: "notes" },
|
|
46
|
+
}));
|
|
47
|
+
assert.strictEqual(parsed.release?.tag_name, "v1.0.0");
|
|
48
|
+
assert.strictEqual(parsed.release?.description, "notes");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { GitLabMergeRequestSchema, MergeMergeRequestSchema } from "../schemas.js";
|
|
4
|
+
const HEAD_SHA = "e82eb4a098e32c796079ca3915e07487fc4db24c";
|
|
5
|
+
const PROJECT_ID = "group/project";
|
|
6
|
+
const MERGE_REQUEST_IID = "42";
|
|
7
|
+
function mergeArgs(overrides = {}) {
|
|
8
|
+
return {
|
|
9
|
+
project_id: PROJECT_ID,
|
|
10
|
+
merge_request_iid: MERGE_REQUEST_IID,
|
|
11
|
+
...overrides,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function mergeRequest(overrides = {}) {
|
|
15
|
+
return {
|
|
16
|
+
id: "1001",
|
|
17
|
+
iid: MERGE_REQUEST_IID,
|
|
18
|
+
project_id: "123",
|
|
19
|
+
title: "Add milestone exposure",
|
|
20
|
+
description: "Expose MR milestone",
|
|
21
|
+
state: "opened",
|
|
22
|
+
author: {
|
|
23
|
+
id: "1",
|
|
24
|
+
username: "octocat",
|
|
25
|
+
name: "Octo Cat",
|
|
26
|
+
avatar_url: null,
|
|
27
|
+
web_url: "https://gitlab.example.com/octocat",
|
|
28
|
+
},
|
|
29
|
+
assignees: [],
|
|
30
|
+
reviewers: [],
|
|
31
|
+
source_branch: "feature/milestone",
|
|
32
|
+
target_branch: "main",
|
|
33
|
+
web_url: "https://gitlab.example.com/group/project/-/merge_requests/42",
|
|
34
|
+
created_at: "2026-05-07T00:00:00.000Z",
|
|
35
|
+
updated_at: "2026-05-07T00:00:00.000Z",
|
|
36
|
+
merged_at: null,
|
|
37
|
+
closed_at: null,
|
|
38
|
+
merge_commit_sha: null,
|
|
39
|
+
...overrides,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
describe("When parsing merge_merge_request arguments", () => {
|
|
43
|
+
describe("with sha provided", () => {
|
|
44
|
+
it("should keep sha on the parsed object", () => {
|
|
45
|
+
const parsed = MergeMergeRequestSchema.parse(mergeArgs({ sha: HEAD_SHA }));
|
|
46
|
+
assert.strictEqual(parsed.sha, HEAD_SHA);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe("with sha omitted", () => {
|
|
50
|
+
it("should parse and leave sha undefined", () => {
|
|
51
|
+
const parsed = MergeMergeRequestSchema.parse(mergeArgs());
|
|
52
|
+
assert.strictEqual(parsed.sha, undefined);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("with an unknown extra field", () => {
|
|
56
|
+
it("should strip the unknown field", () => {
|
|
57
|
+
const parsed = MergeMergeRequestSchema.parse(mergeArgs({ not_a_schema_field: "drop-me" }));
|
|
58
|
+
assert.equal("not_a_schema_field" in parsed, false);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe("When parsing a GitLab merge request response", () => {
|
|
63
|
+
describe("with sha present", () => {
|
|
64
|
+
it("should keep sha instead of stripping it", () => {
|
|
65
|
+
const parsed = GitLabMergeRequestSchema.parse(mergeRequest({ sha: HEAD_SHA }));
|
|
66
|
+
assert.strictEqual(parsed.sha, HEAD_SHA);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("with sha omitted", () => {
|
|
70
|
+
it("should accept the payload", () => {
|
|
71
|
+
const parsed = GitLabMergeRequestSchema.parse(mergeRequest());
|
|
72
|
+
assert.strictEqual(parsed.sha, undefined);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
package/build/test/test-tags.js
CHANGED
|
@@ -105,6 +105,11 @@ describe("tag tools", () => {
|
|
|
105
105
|
res.json(buildTag());
|
|
106
106
|
});
|
|
107
107
|
mockGitLab.addMockHandler("post", `/projects/${TEST_PROJECT_ID}/repository/tags`, (req, res) => {
|
|
108
|
+
if (req.body.tag_name === "v-no-release") {
|
|
109
|
+
const tag = buildTag({ name: "v-no-release", message: null });
|
|
110
|
+
res.json(Object.fromEntries(Object.entries(tag).filter(([key]) => key !== "release")));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
108
113
|
assert.deepStrictEqual(req.body, {
|
|
109
114
|
tag_name: TEST_TAG_NAME,
|
|
110
115
|
ref: "main",
|
|
@@ -175,6 +180,15 @@ describe("tag tools", () => {
|
|
|
175
180
|
assert.strictEqual(result.release.description, "Release notes");
|
|
176
181
|
assert.strictEqual(result.created_at, null);
|
|
177
182
|
});
|
|
183
|
+
test("create_tag succeeds when GitLab omits the release field", async () => {
|
|
184
|
+
const result = await callTool("create_tag", {
|
|
185
|
+
project_id: TEST_PROJECT_ID,
|
|
186
|
+
tag_name: "v-no-release",
|
|
187
|
+
ref: "main",
|
|
188
|
+
}, env());
|
|
189
|
+
assert.strictEqual(result.name, "v-no-release");
|
|
190
|
+
assert.strictEqual(result.release, undefined);
|
|
191
|
+
});
|
|
178
192
|
test("delete_tag returns a success payload", async () => {
|
|
179
193
|
const result = await callTool("delete_tag", { project_id: TEST_PROJECT_ID, tag_name: TEST_TAG_NAME }, env());
|
|
180
194
|
assert.deepStrictEqual(result, {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { allTools, TOOLSET_DEFINITIONS } from "../tools/registry.js";
|
|
4
|
+
function getDefaultToolNames() {
|
|
5
|
+
return new Set(TOOLSET_DEFINITIONS.filter(definition => definition.isDefault).flatMap(definition => [
|
|
6
|
+
...definition.tools,
|
|
7
|
+
]));
|
|
8
|
+
}
|
|
9
|
+
describe("When MCP tool descriptions are exposed", () => {
|
|
10
|
+
describe("with the default toolsets", () => {
|
|
11
|
+
it("should provide enough context for first-attempt tool selection", () => {
|
|
12
|
+
const defaultToolNames = getDefaultToolNames();
|
|
13
|
+
const defaultTools = allTools.filter(tool => defaultToolNames.has(tool.name));
|
|
14
|
+
assert.ok(defaultTools.length > 0);
|
|
15
|
+
assert.ok(defaultTools.every(tool => tool.description.length >= 120));
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe("with create_branch", () => {
|
|
19
|
+
it("should explain its source revision and neighboring branch operations", () => {
|
|
20
|
+
const tool = allTools.find(candidate => candidate.name === "create_branch");
|
|
21
|
+
assert.ok(tool);
|
|
22
|
+
assert.match(tool.description, /source branch|tag|commit[\s\S]*protect_branch[\s\S]*already-exists/i);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
package/build/tools/registry.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
2
2
|
import { toJSONSchema } from "../utils/schema.js";
|
|
3
3
|
import { USE_GITLAB_WIKI, USE_MILESTONE, USE_PIPELINE, SSE, STREAMABLE_HTTP, } from "../config.js";
|
|
4
|
+
import { getToolDescription } from "./tool-descriptions.js";
|
|
4
5
|
import { ApproveMergeRequestSchema, BulkPublishDraftNotesSchema, CancelPipelineJobSchema, CancelPipelineSchema, ConvertWorkItemTypeSchema, CreateBranchSchema, CreateDraftNoteSchema, CreateGroupSchema, CreateGroupWikiPageSchema, CreateIssueLinkSchema, CreateIssueNoteSchema, CreateIssueSchema, CreateIssueEmojiReactionSchema, CreateIssueNoteEmojiReactionSchema, ListIssueEmojiReactionsSchema, ListIssueNoteEmojiReactionsSchema, CreateLabelSchema, MarkAllTodosDoneSchema, ListTodosSchema, MarkTodoDoneSchema, CreateMergeRequestDiscussionNoteSchema, CreateMergeRequestEmojiReactionSchema, ListMergeRequestEmojiReactionsSchema, ListMergeRequestNoteEmojiReactionsSchema, CreateMergeRequestNoteSchema, CreateMergeRequestNoteEmojiReactionSchema, CreateMergeRequestSchema, CreateMergeRequestThreadSchema, CreateNoteSchema, CreateCommitStatusSchema, CreateOrUpdateFileSchema, CreatePipelineSchema, CreateProjectMilestoneSchema, CreateGroupMilestoneSchema, CreateReleaseEvidenceSchema, CreateReleaseSchema, CreateRepositorySchema, CreateTagSchema, CreateTimelineEventSchema, CreateWikiPageSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, CreateWorkItemSchema, DeleteBranchSchema, GetProtectedBranchSchema, ListProtectedBranchesSchema, ProtectBranchSchema, UnprotectBranchSchema, UpdateDefaultBranchSchema, DeleteDraftNoteSchema, DeleteGroupMilestoneSchema, DeleteGroupWikiPageSchema, DeleteIssueLinkSchema, DeleteIssueSchema, DeleteIssueEmojiReactionSchema, DeleteIssueNoteEmojiReactionSchema, DeleteLabelSchema, DeleteMergeRequestDiscussionNoteSchema, DeleteMergeRequestNoteSchema, DeleteMergeRequestEmojiReactionSchema, DeleteMergeRequestNoteEmojiReactionSchema, DeleteProjectMilestoneSchema, DeleteReleaseSchema, DeleteTagSchema, DeleteWikiPageSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, DownloadAttachmentSchema, DownloadAttachmentRemoteSchema, DownloadJobArtifactsSchema, DownloadJobArtifactsRemoteSchema, DownloadReleaseAssetSchema, EditProjectMilestoneSchema, EditGroupMilestoneSchema, ExecuteGraphQLSchema, ForkRepositorySchema, HealthCheckSchema, GetBranchSchema, GetBranchDiffsSchema, GetCommitDiffSchema, GetCommitSchema, GetFileBlameSchema, GetDeploymentSchema, GetDraftNoteSchema, GetEnvironmentSchema, GetFileContentsSchema, GetGroupWikiPageSchema, GetIssueLinkSchema, GetIssueSchema, GetJobArtifactFileSchema, GetLabelSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GetMergeRequestDiffsSchema, GetMergeRequestFileDiffSchema, GetMergeRequestNoteSchema, GetMergeRequestNotesSchema, GetMergeRequestSchema, GetMergeRequestVersionSchema, GetMilestoneBurndownEventsSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, GetGroupMilestoneSchema, GetGroupMilestoneIssuesSchema, GetGroupMilestoneMergeRequestsSchema, GetGroupMilestoneBurndownEventsSchema, GetNamespaceSchema, GetPipelineJobOutputSchema, PipelineJobControlSchema, GetPipelineSchema, GetProjectEventsSchema, GetProjectMilestoneSchema, GetProjectSchema, GetReleaseSchema, GetRepositoryTreeSchema, GetTagSchema, GetTagSignatureSchema, GetTimelineEventsSchema, GetUsersSchema, GetUserSchema, WhoAmISchema, GetWebhookEventSchema, GetWikiPageSchema, GetWorkItemSchema, ListBranchesSchema, ListCommitsSchema, ListCommitStatusesSchema, ListCustomFieldDefinitionsSchema, ListDeploymentsSchema, ListDraftNotesSchema, ListEnvironmentsSchema, ListEventsSchema, ListGroupIterationsSchema, ListGroupMilestonesSchema, ListGroupProjectsSchema, ListGroupWikiPagesSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListJobArtifactsSchema, ListLabelsSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiffsSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestVersionsSchema, ListMergeRequestsSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListCiCatalogResourcesSchema, GetCiCatalogResourceSchema, ListPipelinesSchema, ListGroupMembersSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListReleasesSchema, ListTagsSchema, ListWebhookEventsSchema, ListWebhooksSchema, ListWikiPagesSchema, ListWorkItemNotesSchema, ListWorkItemStatusesSchema, ListWorkItemsSchema, MarkdownUploadSchema, MarkdownUploadRemoteSchema, MergeMergeRequestSchema, MoveWorkItemSchema, MyIssuesSchema, PlayPipelineJobSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PushFilesSchema, ResolveMergeRequestThreadSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UnapproveMergeRequestSchema, UpdateDraftNoteSchema, UpdateGroupWikiPageSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateIssueDescriptionPatchSchema, UpdateLabelSchema, UpdateProjectSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestSchema, UpdateReleaseSchema, UpdateWikiPageSchema, UpdateWorkItemSchema, VerifyNamespaceSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, GetDependencyProxySettingsSchema, UpdateDependencyProxySettingsSchema, ListDependencyProxyBlobsSchema, PurgeDependencyProxyCacheSchema, ListProjectVulnerabilitiesSchema, GetVulnerabilitySchema, DismissVulnerabilitySchema, ConfirmVulnerabilitySchema, } from "../schemas.js";
|
|
5
6
|
const IS_REMOTE = SSE || STREAMABLE_HTTP;
|
|
6
7
|
// Define all available tools
|
|
@@ -1711,6 +1712,9 @@ const discoverTool = allTools.find(t => t.name === "discover_tools");
|
|
|
1711
1712
|
if (discoverTool) {
|
|
1712
1713
|
discoverTool.description = `Discover and activate additional tool categories for this session. Available categories: ${[...ALL_TOOLSET_IDS].join(", ")}. Already-active categories are listed in the response.`;
|
|
1713
1714
|
}
|
|
1715
|
+
for (const tool of allTools) {
|
|
1716
|
+
tool.description = getToolDescription(tool.name, tool.description, readOnlyTools.has(tool.name), destructiveTools.has(tool.name));
|
|
1717
|
+
}
|
|
1714
1718
|
export function parseEnabledToolsets(raw) {
|
|
1715
1719
|
if (!raw || raw.trim() === "") {
|
|
1716
1720
|
return DEFAULT_TOOLSET_IDS;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const TOOL_GUIDANCE = {
|
|
2
|
+
merge_merge_request: "Use this only after checking the merge request approval, conflict, and pipeline state; use `approve_merge_request` to approve rather than merge. The operation changes repository state and may squash commits, schedule auto-merge, or delete the source branch, so it requires merge permission and returns GitLab's merge result or a mergeability error. Pass `sha` from `get_merge_request` (`sha` or `diff_refs.head_sha`); GitLab 19.2+ groups may reject merges without it.",
|
|
3
|
+
approve_merge_request: "Use this to record an approval on an existing merge request; it does not merge the request or change its source branch. The operation changes review state, may require re-authentication or approval permission, and returns the updated approval result or a permission/state error.",
|
|
4
|
+
unapprove_merge_request: "Use this to remove the current user's approval from an existing merge request; use `merge_merge_request` only when you intend to merge. The operation changes review state and requires approval permission, and GitLab returns the updated result or an error when the request or approval is unavailable.",
|
|
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
|
+
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
|
+
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.",
|
|
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
|
+
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
|
+
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.",
|
|
13
|
+
create_branch: "Use this to create a branch from a branch, tag, or commit; use `get_branch` or `list_branches` to inspect branches and `protect_branch` to configure protection afterward. The operation changes remote repository state, requires branch-creation permission, and returns the new branch or a validation, missing-ref, protected-project, or already-exists error. `project_id` accepts a numeric ID or URL-encoded path, `branch` is the new name, and `ref` selects its starting revision.",
|
|
14
|
+
delete_branch: "Use this only after confirming the branch name and intended data loss; use `get_branch` or `list_branches` before deletion and never use it to remove branch protection. The operation permanently removes a remote branch, requires branch-delete permission, and returns the deletion result or a protected-branch, missing-resource, or permission error.",
|
|
15
|
+
protect_branch: "Use this to create or update protection rules for a branch or wildcard; use `get_protected_branch` to inspect existing rules first. The operation changes who may push, merge, or unprotect, may enable force-push or code-owner settings, requires maintainer-level permission, and returns the protection rule or a validation/permission error.",
|
|
16
|
+
unprotect_branch: "Use this to remove protection from an existing branch; use `protect_branch` to change access levels without removing the rule. The operation changes repository security controls, requires permission to manage protected branches, and returns the result or an error when the branch is missing or policy forbids the change.",
|
|
17
|
+
update_default_branch: "Use this to change which branch GitLab treats as the project's default; use `create_branch` to create a branch rather than changing project defaults. The operation changes project settings and may affect clone, merge request, and CI defaults, requires project-maintainer permission, and returns the updated project or a validation/permission error.",
|
|
18
|
+
create_note: "Use this for a top-level comment on an issue or merge request when no typed discussion operation is needed; use `create_merge_request_thread` or `create_issue_note` for threaded replies. The operation creates remote discussion content, requires note permission, and returns the created note or a target/permission/validation error.",
|
|
19
|
+
create_merge_request_thread: "Use this to start a review thread on a merge request; use `create_merge_request_note` for an unthreaded note and `create_merge_request_discussion_note` to reply to an existing thread. The operation creates remote review content, requires note permission, and returns the discussion or a position/permission/validation error.",
|
|
20
|
+
resolve_merge_request_thread: "Use this to mark an existing merge request review thread resolved; use `update_merge_request_discussion_note` when the note text itself must change. The operation changes review state, requires permission to resolve discussions, and returns the updated discussion or a missing-thread/permission error.",
|
|
21
|
+
mr_discussions: "Use this to list complete discussion threads for a merge request; use `get_merge_request_notes` when only flat notes are needed. It is read-only and returns threaded discussion items, while invalid merge request identifiers, missing resources, and permission failures are reported as errors.",
|
|
22
|
+
create_merge_request_discussion_note: "Use this to reply inside an existing merge request discussion; use `create_merge_request_thread` to start a new thread and `create_merge_request_note` for a top-level note. The operation creates remote review content, requires note permission, and returns the new note or a missing-discussion/position/permission error.",
|
|
23
|
+
get_merge_request_note: "Use this to fetch one known merge request note by note identifier; use `get_merge_request_notes` for a collection and `mr_discussions` for threaded context. It is read-only and returns the note object or an error for an invalid identifier, missing note, or insufficient permission.",
|
|
24
|
+
get_merge_request_notes: "Use this to list flat notes on a merge request; use `mr_discussions` when thread structure and resolution state are required. It is read-only and returns note records, while invalid identifiers, missing resources, and pagination or permission errors are reported by GitLab.",
|
|
25
|
+
create_issue_note: "Use this to add a note to an existing issue, optionally as a reply to a discussion; use `update_issue` for issue fields and `create_note` only when the generic endpoint is required. The operation creates remote discussion content, requires note permission, and returns the note or a missing-issue/thread/permission error.",
|
|
26
|
+
list_issue_discussions: "Use this to inspect threaded discussions for an issue; use `list_issues` for issue records and `get_issue` for one issue's fields. It is read-only and returns discussion items, while invalid identifiers, missing issues, and permission failures are reported as errors.",
|
|
27
|
+
update_issue: "Use this to change fields on an existing issue; use `update_issue_description_patch` for a targeted description edit that avoids sending the full body, and use `create_issue_note` for discussion. The operation mutates issue state, requires issue-edit permission, and returns the updated issue or a validation/permission/conflict error.",
|
|
28
|
+
update_issue_description_patch: "Use this for a targeted search/replace or unified-diff change to an issue description; use `dry_run` before applying an uncertain patch and `create_note` when an audit summary is wanted. It changes the issue description when not dry-running, requires issue-edit permission, and returns the patch result or a mismatch/validation/permission error.",
|
|
29
|
+
delete_issue: "Use this only after confirming the issue and intended permanent removal; use `update_issue` to close or edit an issue without deleting it. The operation permanently removes issue data, requires delete permission, and returns the deletion result or a missing-resource, permission, or policy error.",
|
|
30
|
+
delete_issue_link: "Use this to remove an existing relationship between two issues; use `list_issue_links` or `get_issue_link` to verify the link first. The operation changes issue relationships, requires issue-edit permission, and returns the result or an error when the link is missing or access is denied.",
|
|
31
|
+
download_job_artifacts: "Use this to retrieve a pipeline job's artifact archive; remote HTTP mode returns a download URL while local mode saves the archive to a local path. It is read-only but may create a local file in stdio mode, requires job/project access, and returns the download result or an artifact/permission error.",
|
|
32
|
+
download_attachment: "Use this to retrieve a previously uploaded project attachment; remote mode returns inline base64 for images or a download URL, while local mode can save to a path. It is read-only with respect to GitLab, requires project access, and returns the file content or an attachment/permission error.",
|
|
33
|
+
execute_graphql: "Use this only when a supported GitLab REST tool does not cover the requested operation; prefer a typed tool when one exists. The query is sent directly to GitLab and can include mutations when permission allows, so callers must treat it as potentially state-changing and handle GraphQL errors in the returned response.",
|
|
34
|
+
discover_tools: "Use this when a needed opt-in category is not currently exposed; omit `category` to inspect available categories, then call it with a category to activate that group for the current session. It changes only the session's tool registry, returns the active-tool summary, and does not change GitLab data.",
|
|
35
|
+
health_check: "Use this to verify server connectivity and authentication before making GitLab requests; use `whoami` when the authenticated user's identity is the goal. It does not mutate GitLab state and returns server/authentication status plus GitLab version details when available.",
|
|
36
|
+
whoami: "Use this to identify the authenticated GitLab user; use `get_user` or `get_users` when looking up another user. It is read-only and returns the current user profile, while missing credentials or GitLab permission failures are reported as errors.",
|
|
37
|
+
};
|
|
38
|
+
const PARAMETER_GUIDANCE = "When `project_id` or `group_id` is accepted, provide the numeric ID or complete URL-encoded path described by the schema; use required identifiers and pagination fields exactly as documented.";
|
|
39
|
+
function ensureSentence(text) {
|
|
40
|
+
const trimmed = text.trim();
|
|
41
|
+
return /[.!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
|
|
42
|
+
}
|
|
43
|
+
function getLifecycleGuidance(name) {
|
|
44
|
+
const verb = name.split("_")[0] ?? "use";
|
|
45
|
+
if (verb === "list") {
|
|
46
|
+
return "Use this for a collection of resources; choose the corresponding get tool when you already know the single resource to inspect.";
|
|
47
|
+
}
|
|
48
|
+
if (verb === "get") {
|
|
49
|
+
return "Use this for a known resource or result; choose the corresponding list or search tool when you need to discover multiple resources.";
|
|
50
|
+
}
|
|
51
|
+
if (verb === "create") {
|
|
52
|
+
return "Use this for a new resource or action; choose the corresponding update or edit tool when the resource already exists.";
|
|
53
|
+
}
|
|
54
|
+
if (verb === "update" || verb === "edit") {
|
|
55
|
+
return "Use this for an existing resource; choose the corresponding create tool for a new resource and a note tool for discussion-only text.";
|
|
56
|
+
}
|
|
57
|
+
if (verb === "delete" || verb === "remove") {
|
|
58
|
+
return "Use this only after verifying the target; choose a get or list tool first when you need to inspect state without changing it.";
|
|
59
|
+
}
|
|
60
|
+
if (verb === "search") {
|
|
61
|
+
return "Use this to discover matching content; choose a typed get or list tool when the target identifier is already known.";
|
|
62
|
+
}
|
|
63
|
+
if (verb === "validate") {
|
|
64
|
+
return "Use this to check configuration without applying it; choose a create or update tool only after validation succeeds.";
|
|
65
|
+
}
|
|
66
|
+
return "Use this for the specific operation described; choose a sibling tool when you need a different resource or lifecycle action.";
|
|
67
|
+
}
|
|
68
|
+
function getBehaviorGuidance(readOnly, destructive) {
|
|
69
|
+
if (readOnly) {
|
|
70
|
+
return "It is read-only and does not mutate GitLab data; missing resources, invalid identifiers, insufficient permission, and rate limits are returned as errors.";
|
|
71
|
+
}
|
|
72
|
+
if (destructive) {
|
|
73
|
+
return "It changes or removes remote GitLab data and may be irreversible; it requires the necessary project or group permission and returns validation, conflict, permission, or rate-limit errors.";
|
|
74
|
+
}
|
|
75
|
+
return "It changes remote GitLab state and requires the necessary project or group permission; GitLab returns validation, conflict, permission, or rate-limit errors instead of silently applying an invalid request.";
|
|
76
|
+
}
|
|
77
|
+
export function getToolDescription(name, baseDescription, readOnly, destructive) {
|
|
78
|
+
const explicitGuidance = TOOL_GUIDANCE[name];
|
|
79
|
+
if (explicitGuidance) {
|
|
80
|
+
return `${ensureSentence(baseDescription)} ${explicitGuidance}`;
|
|
81
|
+
}
|
|
82
|
+
const normalizedDescription = ensureSentence(baseDescription);
|
|
83
|
+
if (normalizedDescription.length >= 180) {
|
|
84
|
+
return normalizedDescription;
|
|
85
|
+
}
|
|
86
|
+
return `${normalizedDescription} ${getLifecycleGuidance(name)} ${getBehaviorGuidance(readOnly, destructive)} ${PARAMETER_GUIDANCE}`;
|
|
87
|
+
}
|
package/package.json
CHANGED