@zereight/mcp-gitlab 2.1.17 → 2.1.19

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.
@@ -508,4 +508,64 @@ describe('getEffectiveProjectId', { concurrency: 1 }, () => {
508
508
  assert.ok(result.content, 'Should have content');
509
509
  });
510
510
  });
511
+ describe('GITLAB_PROJECT_ID guards dependency proxy tools', () => {
512
+ let mcpUrl;
513
+ let mockGitLab;
514
+ let servers = [];
515
+ let client;
516
+ before(async () => {
517
+ const mockPort = await findMockServerPort(9500);
518
+ mockGitLab = new MockGitLabServer({
519
+ port: mockPort,
520
+ validTokens: [MOCK_TOKEN],
521
+ });
522
+ await mockGitLab.start();
523
+ const mockGitLabUrl = mockGitLab.getUrl();
524
+ const mcpPort = await findAvailablePort(3500);
525
+ const server = await launchServer({
526
+ mode: TransportMode.STREAMABLE_HTTP,
527
+ port: mcpPort,
528
+ timeout: 5000,
529
+ env: {
530
+ REMOTE_AUTHORIZATION: 'true',
531
+ GITLAB_API_URL: `${mockGitLabUrl}/api/v4`,
532
+ GITLAB_PROJECT_ID: DEFAULT_PROJECT_ID,
533
+ GITLAB_TOOLSETS: 'dependency_proxy',
534
+ },
535
+ });
536
+ servers.push(server);
537
+ mcpUrl = `http://${HOST}:${mcpPort}/mcp`;
538
+ client = new CustomHeaderClient({
539
+ authorization: `Bearer ${MOCK_TOKEN}`,
540
+ });
541
+ await client.connect(mcpUrl);
542
+ });
543
+ after(async () => {
544
+ if (client)
545
+ await client.disconnect();
546
+ cleanupServers(servers);
547
+ if (mockGitLab)
548
+ await mockGitLab.stop();
549
+ });
550
+ test('should reject get_dependency_proxy_settings when GITLAB_PROJECT_ID is set', async () => {
551
+ try {
552
+ await client.callTool('get_dependency_proxy_settings', { group_id: 'my-group' });
553
+ assert.fail('Should have rejected get_dependency_proxy_settings');
554
+ }
555
+ catch (error) {
556
+ assert.ok(error instanceof Error);
557
+ assert.ok(error.message.includes('get_dependency_proxy_settings is not allowed'), 'Should mention get_dependency_proxy_settings');
558
+ }
559
+ });
560
+ test('should reject purge_dependency_proxy_cache when GITLAB_PROJECT_ID is set', async () => {
561
+ try {
562
+ await client.callTool('purge_dependency_proxy_cache', { group_id: 'my-group' });
563
+ assert.fail('Should have rejected purge_dependency_proxy_cache');
564
+ }
565
+ catch (error) {
566
+ assert.ok(error instanceof Error);
567
+ assert.ok(error.message.includes('purge_dependency_proxy_cache is not allowed'), 'Should mention purge_dependency_proxy_cache');
568
+ }
569
+ });
570
+ });
511
571
  }); // end wrapper describe
@@ -0,0 +1,155 @@
1
+ import { after, before, describe, test } from "node:test";
2
+ import assert from "node:assert";
3
+ import { spawn } from "child_process";
4
+ import { MockGitLabServer, findMockServerPort } from "./utils/mock-gitlab-server.js";
5
+ const MOCK_TOKEN = "glpat-mock-token-protected-branches";
6
+ const TEST_PROJECT_ID = "123";
7
+ const TEST_BRANCH = "main";
8
+ function buildProtectedBranch(overrides = {}) {
9
+ return {
10
+ id: 1,
11
+ name: TEST_BRANCH,
12
+ push_access_levels: [
13
+ { access_level: 30, access_level_description: "Developers + Maintainers" },
14
+ ],
15
+ merge_access_levels: [{ access_level: 40, access_level_description: "Maintainers" }],
16
+ unprotect_access_levels: [{ access_level: 60, access_level_description: "Administrators" }],
17
+ allow_force_push: false,
18
+ code_owner_approval_required: false,
19
+ ...overrides,
20
+ };
21
+ }
22
+ async function callTool(toolName, args, env) {
23
+ return new Promise((resolve, reject) => {
24
+ const proc = spawn("node", ["build/index.js"], {
25
+ stdio: ["pipe", "pipe", "pipe"],
26
+ env: {
27
+ ...process.env,
28
+ ...env,
29
+ },
30
+ });
31
+ let output = "";
32
+ let errorOutput = "";
33
+ proc.stdout?.on("data", (d) => (output += d));
34
+ proc.stderr?.on("data", (d) => (errorOutput += d));
35
+ proc.on("close", code => {
36
+ if (code !== 0) {
37
+ reject(new Error(`Process exited with code ${code}: ${errorOutput}`));
38
+ return;
39
+ }
40
+ const line = output.split("\n").find(l => l.startsWith("{"));
41
+ if (!line) {
42
+ reject(new Error("No JSON output found"));
43
+ return;
44
+ }
45
+ const response = JSON.parse(line);
46
+ if (response.error) {
47
+ reject(new Error(response.error.message ?? JSON.stringify(response.error)));
48
+ return;
49
+ }
50
+ const content = response.result?.content?.[0]?.text;
51
+ if (!content) {
52
+ resolve(response.result);
53
+ return;
54
+ }
55
+ try {
56
+ resolve(JSON.parse(content));
57
+ }
58
+ catch {
59
+ resolve(content);
60
+ }
61
+ });
62
+ proc.stdin?.end(JSON.stringify({
63
+ jsonrpc: "2.0",
64
+ id: 1,
65
+ method: "tools/call",
66
+ params: { name: toolName, arguments: args },
67
+ }) + "\n");
68
+ });
69
+ }
70
+ describe("protected branch tools", () => {
71
+ let mockGitLab;
72
+ let mockGitLabUrl;
73
+ before(async () => {
74
+ const mockPort = await findMockServerPort(20500, 50);
75
+ mockGitLab = new MockGitLabServer({
76
+ port: mockPort,
77
+ validTokens: [MOCK_TOKEN],
78
+ });
79
+ mockGitLab.addMockHandler("get", `/projects/${TEST_PROJECT_ID}/protected_branches`, (req, res) => {
80
+ assert.strictEqual(req.query.search, "main");
81
+ assert.strictEqual(req.query.page, "2");
82
+ assert.strictEqual(req.query.per_page, "10");
83
+ res.json([buildProtectedBranch()]);
84
+ });
85
+ mockGitLab.addMockHandler("get", `/projects/${TEST_PROJECT_ID}/protected_branches/${TEST_BRANCH}`, (_req, res) => {
86
+ res.json(buildProtectedBranch());
87
+ });
88
+ mockGitLab.addMockHandler("post", `/projects/${TEST_PROJECT_ID}/protected_branches`, (req, res) => {
89
+ assert.deepStrictEqual(req.body, {
90
+ name: TEST_BRANCH,
91
+ push_access_level: 30,
92
+ merge_access_level: 40,
93
+ unprotect_access_level: 60,
94
+ allow_force_push: false,
95
+ code_owner_approval_required: false,
96
+ });
97
+ res.status(201).json(buildProtectedBranch());
98
+ });
99
+ mockGitLab.addMockHandler("delete", `/projects/${TEST_PROJECT_ID}/protected_branches/${TEST_BRANCH}`, (_req, res) => {
100
+ res.status(204).send();
101
+ });
102
+ mockGitLab.addMockHandler("put", `/projects/${TEST_PROJECT_ID}`, (req, res) => {
103
+ assert.deepStrictEqual(req.body, { default_branch: TEST_BRANCH });
104
+ res.json({ id: Number(TEST_PROJECT_ID), default_branch: TEST_BRANCH });
105
+ });
106
+ await mockGitLab.start();
107
+ mockGitLabUrl = mockGitLab.getUrl();
108
+ });
109
+ after(async () => {
110
+ await mockGitLab.stop();
111
+ });
112
+ const env = () => ({
113
+ GITLAB_API_URL: `${mockGitLabUrl}/api/v4`,
114
+ GITLAB_PERSONAL_ACCESS_TOKEN: MOCK_TOKEN,
115
+ GITLAB_TOOLSETS: "branches",
116
+ });
117
+ test("list_protected_branches forwards search and pagination", async () => {
118
+ const result = await callTool("list_protected_branches", { project_id: TEST_PROJECT_ID, search: "main", page: 2, per_page: 10 }, env());
119
+ assert.ok(Array.isArray(result));
120
+ assert.strictEqual(result[0].name, TEST_BRANCH);
121
+ });
122
+ test("get_protected_branch parses a single protected branch", async () => {
123
+ const result = await callTool("get_protected_branch", { project_id: TEST_PROJECT_ID, branch_name: TEST_BRANCH }, env());
124
+ assert.strictEqual(result.name, TEST_BRANCH);
125
+ assert.strictEqual(result.allow_force_push, false);
126
+ });
127
+ test("protect_branch posts normalized branch name, access levels, and false booleans", async () => {
128
+ const result = await callTool("protect_branch", {
129
+ project_id: TEST_PROJECT_ID,
130
+ branch_name: TEST_BRANCH,
131
+ push_access_level: "30",
132
+ merge_access_level: 40,
133
+ unprotect_access_level: 60,
134
+ allow_force_push: "false",
135
+ code_owner_approval_required: "false",
136
+ }, env());
137
+ assert.strictEqual(result.name, TEST_BRANCH);
138
+ });
139
+ test("protect_branch rejects invalid access levels before calling GitLab", async () => {
140
+ await assert.rejects(() => callTool("protect_branch", {
141
+ project_id: TEST_PROJECT_ID,
142
+ branch_name: TEST_BRANCH,
143
+ push_access_level: 99,
144
+ }, env()), /Access level must be one of/);
145
+ });
146
+ test("unprotect_branch sends DELETE and returns status", async () => {
147
+ const result = await callTool("unprotect_branch", { project_id: TEST_PROJECT_ID, branch_name: TEST_BRANCH }, env());
148
+ assert.deepStrictEqual(result, { status: "unprotected", branch: TEST_BRANCH });
149
+ });
150
+ test("update_default_branch sends the expected PUT body", async () => {
151
+ const result = await callTool("update_default_branch", { project_id: TEST_PROJECT_ID, default_branch: TEST_BRANCH }, env());
152
+ assert.strictEqual(result.status, "updated");
153
+ assert.strictEqual(result.default_branch, TEST_BRANCH);
154
+ });
155
+ });
@@ -19,7 +19,7 @@ const TOOLSET_TOOL_COUNTS = {
19
19
  merge_requests: 41,
20
20
  issues: 24,
21
21
  repositories: 7,
22
- branches: 10,
22
+ branches: 15,
23
23
  projects: 9,
24
24
  labels: 5,
25
25
  ci: 2,
@@ -34,6 +34,7 @@ const TOOLSET_TOOL_COUNTS = {
34
34
  webhooks: 3,
35
35
  groups: 1,
36
36
  variables: 10,
37
+ dependency_proxy: 4,
37
38
  };
38
39
  const LEGACY_PIPELINE_TOOL_COUNT = TOOLSET_TOOL_COUNTS.pipelines + TOOLSET_TOOL_COUNTS.ci;
39
40
  const DEFAULT_TOOLSETS = [
@@ -57,6 +58,7 @@ const NON_DEFAULT_TOOLSETS = [
57
58
  "webhooks",
58
59
  "search",
59
60
  "variables",
61
+ "dependency_proxy",
60
62
  ];
61
63
  // discover_tools meta-tool is always force-injected (Step 5.5)
62
64
  const DISCOVER_TOOLS_COUNT = 1;
@@ -81,6 +83,7 @@ const TOOLSET_SAMPLE_TOOLS = {
81
83
  webhooks: ["list_webhooks", "list_webhook_events", "get_webhook_event"],
82
84
  groups: ["create_group"],
83
85
  variables: ["list_project_variables", "create_project_variable", "delete_project_variable", "list_group_variables", "create_group_variable", "delete_group_variable"],
86
+ dependency_proxy: ["get_dependency_proxy_settings", "list_dependency_proxy_blobs", "purge_dependency_proxy_cache"],
84
87
  };
85
88
  // --- Helpers ---
86
89
  async function launchMcpServer(mockGitLabUrl, mcpPort, extraEnv = {}) {
@@ -1,7 +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 { 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, CreateReleaseEvidenceSchema, CreateReleaseSchema, CreateRepositorySchema, CreateTagSchema, CreateTimelineEventSchema, CreateWikiPageSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, CreateWorkItemSchema, DeleteBranchSchema, DeleteDraftNoteSchema, DeleteGroupWikiPageSchema, DeleteIssueLinkSchema, DeleteIssueSchema, DeleteIssueEmojiReactionSchema, DeleteIssueNoteEmojiReactionSchema, DeleteLabelSchema, DeleteMergeRequestDiscussionNoteSchema, DeleteMergeRequestNoteSchema, DeleteMergeRequestEmojiReactionSchema, DeleteMergeRequestNoteEmojiReactionSchema, DeleteProjectMilestoneSchema, DeleteReleaseSchema, DeleteTagSchema, DeleteWikiPageSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, DownloadAttachmentSchema, DownloadAttachmentRemoteSchema, DownloadJobArtifactsSchema, DownloadJobArtifactsRemoteSchema, DownloadReleaseAssetSchema, EditProjectMilestoneSchema, 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, GetNamespaceSchema, GetPipelineJobOutputSchema, GetPipelineSchema, GetProjectEventsSchema, GetProjectMilestoneSchema, GetProjectSchema, GetReleaseSchema, GetRepositoryTreeSchema, GetTagSchema, GetTagSignatureSchema, GetTimelineEventsSchema, GetUsersSchema, GetUserSchema, WhoAmISchema, GetWebhookEventSchema, GetWikiPageSchema, GetWorkItemSchema, ListBranchesSchema, ListCommitsSchema, ListCommitStatusesSchema, ListCustomFieldDefinitionsSchema, ListDeploymentsSchema, ListDraftNotesSchema, ListEnvironmentsSchema, ListEventsSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, ListGroupWikiPagesSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListJobArtifactsSchema, ListLabelsSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiffsSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestVersionsSchema, ListMergeRequestsSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListPipelinesSchema, 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, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestSchema, UpdateReleaseSchema, UpdateWikiPageSchema, UpdateWorkItemSchema, VerifyNamespaceSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, } from "../schemas.js";
4
+ 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, CreateReleaseEvidenceSchema, CreateReleaseSchema, CreateRepositorySchema, CreateTagSchema, CreateTimelineEventSchema, CreateWikiPageSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, CreateWorkItemSchema, DeleteBranchSchema, GetProtectedBranchSchema, ListProtectedBranchesSchema, ProtectBranchSchema, UnprotectBranchSchema, UpdateDefaultBranchSchema, DeleteDraftNoteSchema, DeleteGroupWikiPageSchema, DeleteIssueLinkSchema, DeleteIssueSchema, DeleteIssueEmojiReactionSchema, DeleteIssueNoteEmojiReactionSchema, DeleteLabelSchema, DeleteMergeRequestDiscussionNoteSchema, DeleteMergeRequestNoteSchema, DeleteMergeRequestEmojiReactionSchema, DeleteMergeRequestNoteEmojiReactionSchema, DeleteProjectMilestoneSchema, DeleteReleaseSchema, DeleteTagSchema, DeleteWikiPageSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, DownloadAttachmentSchema, DownloadAttachmentRemoteSchema, DownloadJobArtifactsSchema, DownloadJobArtifactsRemoteSchema, DownloadReleaseAssetSchema, EditProjectMilestoneSchema, 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, GetNamespaceSchema, GetPipelineJobOutputSchema, GetPipelineSchema, GetProjectEventsSchema, GetProjectMilestoneSchema, GetProjectSchema, GetReleaseSchema, GetRepositoryTreeSchema, GetTagSchema, GetTagSignatureSchema, GetTimelineEventsSchema, GetUsersSchema, GetUserSchema, WhoAmISchema, GetWebhookEventSchema, GetWikiPageSchema, GetWorkItemSchema, ListBranchesSchema, ListCommitsSchema, ListCommitStatusesSchema, ListCustomFieldDefinitionsSchema, ListDeploymentsSchema, ListDraftNotesSchema, ListEnvironmentsSchema, ListEventsSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, ListGroupWikiPagesSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListJobArtifactsSchema, ListLabelsSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiffsSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestVersionsSchema, ListMergeRequestsSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListPipelinesSchema, 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, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestSchema, UpdateReleaseSchema, UpdateWikiPageSchema, UpdateWorkItemSchema, VerifyNamespaceSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, GetDependencyProxySettingsSchema, UpdateDependencyProxySettingsSchema, ListDependencyProxyBlobsSchema, PurgeDependencyProxyCacheSchema, } from "../schemas.js";
5
5
  const IS_REMOTE = SSE || STREAMABLE_HTTP;
6
6
  // Define all available tools
7
7
  export const allTools = [
@@ -105,6 +105,31 @@ export const allTools = [
105
105
  description: "Delete branch from project",
106
106
  inputSchema: toJSONSchema(DeleteBranchSchema),
107
107
  },
108
+ {
109
+ name: "list_protected_branches",
110
+ description: "List protected branches in a project, supports search filter",
111
+ inputSchema: toJSONSchema(ListProtectedBranchesSchema),
112
+ },
113
+ {
114
+ name: "get_protected_branch",
115
+ description: "Get details of a single protected branch (access levels, force push settings)",
116
+ inputSchema: toJSONSchema(GetProtectedBranchSchema),
117
+ },
118
+ {
119
+ name: "protect_branch",
120
+ description: "Protect a repository branch (set push/merge/unprotect access levels)",
121
+ inputSchema: toJSONSchema(ProtectBranchSchema),
122
+ },
123
+ {
124
+ name: "unprotect_branch",
125
+ description: "Remove protection from a previously protected branch",
126
+ inputSchema: toJSONSchema(UnprotectBranchSchema),
127
+ },
128
+ {
129
+ name: "update_default_branch",
130
+ description: "Change the default branch of a project",
131
+ inputSchema: toJSONSchema(UpdateDefaultBranchSchema),
132
+ },
108
133
  {
109
134
  name: "get_merge_request",
110
135
  description: "Get details of a merge request (mergeRequestIid or branchName required)",
@@ -982,6 +1007,27 @@ export const allTools = [
982
1007
  description: "Delete a CI/CD variable from a group",
983
1008
  inputSchema: toJSONSchema(DeleteGroupVariableSchema),
984
1009
  },
1010
+ // --- Dependency proxy tools ---
1011
+ {
1012
+ name: "get_dependency_proxy_settings",
1013
+ description: "Get dependency proxy settings for a group",
1014
+ inputSchema: toJSONSchema(GetDependencyProxySettingsSchema),
1015
+ },
1016
+ {
1017
+ name: "update_dependency_proxy_settings",
1018
+ description: "Update dependency proxy settings for a group (enable/disable, credentials for authenticated Docker Hub pulls)",
1019
+ inputSchema: toJSONSchema(UpdateDependencyProxySettingsSchema),
1020
+ },
1021
+ {
1022
+ name: "list_dependency_proxy_blobs",
1023
+ description: "List cached dependency proxy blobs for a group",
1024
+ inputSchema: toJSONSchema(ListDependencyProxyBlobsSchema),
1025
+ },
1026
+ {
1027
+ name: "purge_dependency_proxy_cache",
1028
+ description: "Schedule purge of all cached dependency proxy blobs for a group",
1029
+ inputSchema: toJSONSchema(PurgeDependencyProxyCacheSchema),
1030
+ },
985
1031
  // --- Meta tool: Dynamic tool discovery ---
986
1032
  {
987
1033
  name: "discover_tools",
@@ -1017,6 +1063,8 @@ export const readOnlyTools = new Set([
1017
1063
  "get_branch",
1018
1064
  "list_branches",
1019
1065
  "get_branch_diffs",
1066
+ "list_protected_branches",
1067
+ "get_protected_branch",
1020
1068
  "list_merge_request_pipelines",
1021
1069
  "get_merge_request_note",
1022
1070
  "get_merge_request_notes",
@@ -1105,6 +1153,8 @@ export const readOnlyTools = new Set([
1105
1153
  "get_project_variable",
1106
1154
  "list_group_variables",
1107
1155
  "get_group_variable",
1156
+ "get_dependency_proxy_settings",
1157
+ "list_dependency_proxy_blobs",
1108
1158
  ]);
1109
1159
  // Define which tools are destructive (data loss potential)
1110
1160
  export const destructiveTools = new Set([
@@ -1126,10 +1176,14 @@ export const destructiveTools = new Set([
1126
1176
  "delete_work_item_emoji_reaction",
1127
1177
  "delete_work_item_note_emoji_reaction",
1128
1178
  "delete_branch",
1179
+ "unprotect_branch",
1180
+ "protect_branch",
1181
+ "update_default_branch",
1129
1182
  "merge_merge_request",
1130
1183
  "push_files",
1131
1184
  "delete_project_variable",
1132
1185
  "delete_group_variable",
1186
+ "purge_dependency_proxy_cache",
1133
1187
  ]);
1134
1188
  // Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
1135
1189
  export const wikiToolNames = new Set([
@@ -1282,6 +1336,11 @@ export const TOOLSET_DEFINITIONS = [
1282
1336
  "get_branch",
1283
1337
  "list_branches",
1284
1338
  "delete_branch",
1339
+ "list_protected_branches",
1340
+ "get_protected_branch",
1341
+ "protect_branch",
1342
+ "unprotect_branch",
1343
+ "update_default_branch",
1285
1344
  "list_commits",
1286
1345
  "get_commit",
1287
1346
  "get_commit_diff",
@@ -1473,6 +1532,16 @@ export const TOOLSET_DEFINITIONS = [
1473
1532
  "delete_group_variable",
1474
1533
  ]),
1475
1534
  },
1535
+ {
1536
+ id: "dependency_proxy",
1537
+ isDefault: false,
1538
+ tools: new Set([
1539
+ "get_dependency_proxy_settings",
1540
+ "update_dependency_proxy_settings",
1541
+ "list_dependency_proxy_blobs",
1542
+ "purge_dependency_proxy_cache",
1543
+ ]),
1544
+ },
1476
1545
  ];
1477
1546
  // Derived lookup: tool name → toolset ID
1478
1547
  export const TOOLSET_BY_TOOL_NAME = new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "2.1.17",
3
+ "version": "2.1.19",
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": [
@@ -51,7 +51,7 @@
51
51
  "changelog": "auto-changelog -p",
52
52
  "test": "npm run test:all",
53
53
  "test:all": "npm run build && npm run test:mock && npm run test:live",
54
- "test:mock": "node --import tsx/esm --test test/remote-auth-simple-test.ts && node --import tsx/esm --test test/mcp-oauth-tests.ts && node --import tsx/esm --test test/streamable-http-static-token-auth.test.ts && tsx test/oauth-tests.ts && tsx test/test-list-merge-requests.ts && node --import tsx/esm --test test/test-merge-request-pipelines.ts && tsx test/test-list-project-members.ts && tsx test/test-download-attachment.ts && node --import tsx/esm --test test/test-upload-markdown.ts && node --import tsx/esm --test test/test-job-artifacts.ts && node --import tsx/esm --test test/test-deployment-tools.ts && node --import tsx/esm --test test/test-merge-request-approval-state-tools.ts && node --import tsx/esm --test test/test-search-code.ts && node --import tsx/esm --test test/test-tags.ts && node --import tsx/esm --test test/test-toolset-filtering.ts && node --import tsx/esm --test test/test-ci-lint.ts && node --import tsx/esm --test test/test-todos.ts && node --import tsx/esm --test test/test-auth-retry.ts && node --import tsx/esm --test test/test-issue-description-patch.ts && node --import tsx/esm --test test/test-geteffectiveprojectid.ts && node --import tsx/esm --test test/test-get-file-blame.ts && node --import tsx/esm --test test/stateless/codec.test.ts test/stateless/client-id.test.ts test/stateless/callback-proxy.test.ts test/stateless/session-id.test.ts test/stateless/session-id-integration.test.ts test/stateless/config-ttl.test.ts && node --import tsx/esm --test test/utils/tool-args.test.ts && node --import tsx/esm --test test/utils/merge-request-position.test.ts && node --import tsx/esm --test test/nullish-tool-arguments-schema.test.ts && node --import tsx/esm --test test/test-ci-variables.ts",
54
+ "test:mock": "node --import tsx/esm --test test/remote-auth-simple-test.ts && node --import tsx/esm --test test/mcp-oauth-tests.ts && node --import tsx/esm --test test/streamable-http-static-token-auth.test.ts && tsx test/oauth-tests.ts && tsx test/test-list-merge-requests.ts && node --import tsx/esm --test test/test-merge-request-pipelines.ts && tsx test/test-list-project-members.ts && tsx test/test-download-attachment.ts && node --import tsx/esm --test test/test-upload-markdown.ts && node --import tsx/esm --test test/test-job-artifacts.ts && node --import tsx/esm --test test/test-deployment-tools.ts && node --import tsx/esm --test test/test-merge-request-approval-state-tools.ts && node --import tsx/esm --test test/test-search-code.ts && node --import tsx/esm --test test/test-tags.ts && node --import tsx/esm --test test/test-protected-branches.ts && node --import tsx/esm --test test/test-toolset-filtering.ts && node --import tsx/esm --test test/test-ci-lint.ts && node --import tsx/esm --test test/test-todos.ts && node --import tsx/esm --test test/test-auth-retry.ts && node --import tsx/esm --test test/test-issue-description-patch.ts && node --import tsx/esm --test test/test-geteffectiveprojectid.ts && node --import tsx/esm --test test/test-get-file-blame.ts && node --import tsx/esm --test test/stateless/codec.test.ts test/stateless/client-id.test.ts test/stateless/callback-proxy.test.ts test/stateless/session-id.test.ts test/stateless/session-id-integration.test.ts test/stateless/config-ttl.test.ts && node --import tsx/esm --test test/utils/tool-args.test.ts && node --import tsx/esm --test test/utils/merge-request-position.test.ts && node --import tsx/esm --test test/nullish-tool-arguments-schema.test.ts && node --import tsx/esm --test test/test-ci-variables.ts && node --import tsx/esm --test test/test-dependency-proxy.ts",
55
55
  "test:stateless": "npm run build && node --import tsx/esm --test test/stateless/codec.test.ts test/stateless/client-id.test.ts test/stateless/callback-proxy.test.ts test/stateless/session-id.test.ts test/stateless/session-id-integration.test.ts test/stateless/config-ttl.test.ts",
56
56
  "test:mcp-oauth": "npm run build && node --import tsx/esm --test test/mcp-oauth-tests.ts",
57
57
  "test:live": "node test/validate-api.js",