@zereight/mcp-gitlab 2.1.17 → 2.1.18

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.md CHANGED
@@ -657,6 +657,10 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
657
657
  164. `create_group_variable` - Create a new CI/CD variable in a group
658
658
  165. `update_group_variable` - Update an existing CI/CD variable in a group, with optional filter to disambiguate by environment scope
659
659
  166. `delete_group_variable` - Delete a CI/CD variable from a group, with optional filter to disambiguate by environment scope
660
+ 167. `get_dependency_proxy_settings` - Get dependency proxy settings for a group (enabled status, blob count, total size, image prefix, TTL policy)
661
+ 168. `update_dependency_proxy_settings` - Update dependency proxy settings for a group (enable/disable, credentials for authenticated Docker Hub pulls)
662
+ 169. `list_dependency_proxy_blobs` - List cached dependency proxy blobs for a group with cursor-based pagination
663
+ 170. `purge_dependency_proxy_cache` - Schedule purge of all cached dependency proxy blobs for a group
660
664
 
661
665
  <!-- TOOLS-END -->
662
666
 
package/build/index.js CHANGED
@@ -139,7 +139,7 @@ CreateMergeRequestNoteSchema, CreateMergeRequestDiscussionNoteSchema, CreateMerg
139
139
  GitLabDiscussionNoteSchema, // Added
140
140
  GitLabDiscussionSchema,
141
141
  // Draft Notes Schemas
142
- GitLabDraftNoteSchema, GitLabForkSchema, GitLabBranchSchema, GitLabGroupSchema, GitLabIssueLinkSchema, GitLabIssueSchema, GitLabIssueWithLinkDetailsSchema, GitLabMarkdownUploadSchema, GitLabMergeRequestPipelineSchema, GitLabMergeRequestSchema, GitLabMilestonesSchema, GitLabNamespaceExistsResponseSchema, GitLabNamespaceSchema, GitLabPipelineJobSchema, GitLabDeploymentSchema, GitLabEnvironmentSchema, GitLabPipelineSchema, GitLabPipelineTriggerJobSchema, GitLabProjectMemberSchema, GitLabProjectSchema, GitLabTodoSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabSearchBlobResultSchema, GitLabSearchResponseSchema, GitLabTreeItemSchema, GitLabUserSchema, GitLabUsersResponseSchema, GitLabWikiPageSchema, GroupIteration, ListCommitStatusesSchema, ListBranchesSchema, ListCommitsSchema, ListDraftNotesSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, GitLabCiVariableSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListTodosSchema, ListLabelsSchema, ListMergeRequestDiffsSchema, // Added
142
+ GitLabDraftNoteSchema, GitLabForkSchema, GitLabBranchSchema, GitLabGroupSchema, GitLabIssueLinkSchema, GitLabIssueSchema, GitLabIssueWithLinkDetailsSchema, GitLabMarkdownUploadSchema, GitLabMergeRequestPipelineSchema, GitLabMergeRequestSchema, GitLabMilestonesSchema, GitLabNamespaceExistsResponseSchema, GitLabNamespaceSchema, GitLabPipelineJobSchema, GitLabDeploymentSchema, GitLabEnvironmentSchema, GitLabPipelineSchema, GitLabPipelineTriggerJobSchema, GitLabProjectMemberSchema, GitLabProjectSchema, GitLabTodoSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabSearchBlobResultSchema, GitLabSearchResponseSchema, GitLabTreeItemSchema, GitLabUserSchema, GitLabUsersResponseSchema, GitLabWikiPageSchema, GroupIteration, ListCommitStatusesSchema, ListBranchesSchema, ListCommitsSchema, ListDraftNotesSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, GitLabCiVariableSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, GitLabDependencyProxySchema, GitLabDependencyProxyBlobSchema, GetDependencyProxySettingsSchema, UpdateDependencyProxySettingsSchema, ListDependencyProxyBlobsSchema, PurgeDependencyProxyCacheSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListTodosSchema, ListLabelsSchema, ListMergeRequestDiffsSchema, // Added
143
143
  GetMergeRequestFileDiffSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestsSchema, ListMergeRequestVersionsSchema, GetMergeRequestVersionSchema, GitLabMergeRequestVersionSchema, GitLabMergeRequestVersionDetailSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListDeploymentsSchema, ListEnvironmentsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, GetGroupWikiPageSchema, ListGroupWikiPagesSchema, UpdateGroupWikiPageSchema, MarkdownUploadSchema, MarkdownUploadRemoteSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, GetJobArtifactFileSchema, GitLabArtifactEntrySchema, ListJobArtifactsSchema, MergeMergeRequestSchema, ApproveMergeRequestSchema, UnapproveMergeRequestSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GitLabMergeRequestApprovalsResponseSchema, GitLabMergeRequestApprovalStateSchema, MyIssuesSchema, MarkAllTodosDoneSchema, MarkTodoDoneSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PlayPipelineJobSchema, PushFilesSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateIssueDescriptionPatchSchema, UpdateLabelSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestSchema, UpdateWikiPageSchema, VerifyNamespaceSchema, GitLabEventSchema, ListEventsSchema, GetProjectEventsSchema, ExecuteGraphQLSchema, GitLabReleaseSchema, ListReleasesSchema, GetReleaseSchema, CreateReleaseSchema, UpdateReleaseSchema, DeleteReleaseSchema, CreateReleaseEvidenceSchema, DownloadReleaseAssetSchema, ListTagsSchema, GetTagSchema, CreateTagSchema, DeleteTagSchema, GetTagSignatureSchema, GitLabTagSchema, GitLabTagSignatureSchema, GetMergeRequestNotesSchema, GetMergeRequestNoteSchema, DeleteMergeRequestDiscussionNoteSchema, ResolveMergeRequestThreadSchema, GetWorkItemSchema, ListWorkItemsSchema, CreateWorkItemSchema, UpdateWorkItemSchema, ConvertWorkItemTypeSchema, ListWorkItemStatusesSchema, ListWorkItemNotesSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, MoveWorkItemSchema, ListCustomFieldDefinitionsSchema, GetTimelineEventsSchema, CreateTimelineEventSchema, ListWebhooksSchema, ListWebhookEventsSchema, GetWebhookEventSchema, HealthCheckSchema, } from "./schemas.js";
144
144
  import { randomUUID, createCipheriv, createDecipheriv, randomBytes, createHash } from "node:crypto";
145
145
  import { pino } from "pino";
@@ -5875,6 +5875,84 @@ async function deleteGroupVariable(groupId, key, filter) {
5875
5875
  const response = await fetch(url.toString(), { ...getFetchConfig(), method: "DELETE" });
5876
5876
  await handleGitLabError(response);
5877
5877
  }
5878
+ // --- Dependency Proxy ---
5879
+ async function resolveGroupFullPath(groupId) {
5880
+ const decoded = decodeURIComponent(groupId);
5881
+ if (/^\d+$/.test(decoded)) {
5882
+ const response = await fetch(`${getEffectiveApiUrl()}/groups/${decoded}`, getFetchConfig());
5883
+ await handleGitLabError(response);
5884
+ const data = z.object({ full_path: z.string() }).parse(await response.json());
5885
+ return data.full_path;
5886
+ }
5887
+ return decoded;
5888
+ }
5889
+ async function getDependencyProxySettings(groupPath) {
5890
+ const fullPath = await resolveGroupFullPath(groupPath);
5891
+ const data = await executeGraphQL(`query($fullPath: ID!) {
5892
+ group(fullPath: $fullPath) {
5893
+ dependencyProxySetting { enabled }
5894
+ dependencyProxyBlobCount
5895
+ dependencyProxyTotalSize
5896
+ dependencyProxyImagePrefix
5897
+ dependencyProxyImageTtlPolicy { enabled ttl }
5898
+ }
5899
+ }`, { fullPath });
5900
+ const g = data.group;
5901
+ if (!g)
5902
+ throw new Error(`Group not found: ${fullPath}`);
5903
+ return GitLabDependencyProxySchema.parse({
5904
+ enabled: g.dependencyProxySetting?.enabled ?? false,
5905
+ blob_count: g.dependencyProxyBlobCount,
5906
+ total_size: g.dependencyProxyTotalSize,
5907
+ image_prefix: g.dependencyProxyImagePrefix,
5908
+ ttl_policy: g.dependencyProxyImageTtlPolicy,
5909
+ });
5910
+ }
5911
+ async function updateDependencyProxySettings(groupPath, options) {
5912
+ if (options.enabled === undefined && options.identity === undefined && options.secret === undefined) {
5913
+ throw new Error("At least one of enabled, identity, or secret must be provided");
5914
+ }
5915
+ const fullPath = await resolveGroupFullPath(groupPath);
5916
+ const input = { groupPath: fullPath };
5917
+ if (options.enabled !== undefined)
5918
+ input["enabled"] = options.enabled;
5919
+ if (options.identity !== undefined)
5920
+ input["identity"] = options.identity;
5921
+ if (options.secret !== undefined)
5922
+ input["secret"] = options.secret;
5923
+ const mutationResult = await executeGraphQL(`mutation($input: UpdateDependencyProxySettingsInput!) {
5924
+ updateDependencyProxySettings(input: $input) { errors }
5925
+ }`, { input });
5926
+ const errors = mutationResult.updateDependencyProxySettings?.errors;
5927
+ if (errors && errors.length > 0) {
5928
+ throw new Error(`Failed to update dependency proxy settings: ${errors.join(", ")}`);
5929
+ }
5930
+ return getDependencyProxySettings(fullPath);
5931
+ }
5932
+ async function listDependencyProxyBlobs(groupPath, options = {}) {
5933
+ const fullPath = await resolveGroupFullPath(groupPath);
5934
+ const data = await executeGraphQL(`query($fullPath: ID!, $first: Int, $after: String) {
5935
+ group(fullPath: $fullPath) {
5936
+ dependencyProxyBlobs(first: $first, after: $after) {
5937
+ nodes { fileName size createdAt }
5938
+ pageInfo { hasNextPage endCursor }
5939
+ }
5940
+ }
5941
+ }`, { fullPath, first: options.first ?? 20, after: options.after });
5942
+ const conn = data.group?.dependencyProxyBlobs;
5943
+ if (!conn)
5944
+ throw new Error(`Group not found or dependency proxy not enabled: ${fullPath}`);
5945
+ return {
5946
+ blobs: conn.nodes.map(n => GitLabDependencyProxyBlobSchema.parse({ file_name: n.fileName, size: n.size, created_at: n.createdAt })),
5947
+ pageInfo: conn.pageInfo,
5948
+ };
5949
+ }
5950
+ async function purgeDependencyProxyCache(groupId) {
5951
+ const encoded = encodeURIComponent(decodeURIComponent(groupId));
5952
+ const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/dependency_proxy/cache`);
5953
+ const response = await fetch(url.toString(), { ...getFetchConfig(), method: "DELETE" });
5954
+ await handleGitLabError(response);
5955
+ }
5878
5956
  /**
5879
5957
  * Upload a file to a GitLab project for use in markdown content.
5880
5958
  *
@@ -7921,6 +7999,45 @@ async function handleToolCall(params) {
7921
7999
  ],
7922
8000
  };
7923
8001
  }
8002
+ case "get_dependency_proxy_settings": {
8003
+ rejectIfProjectScopedDeployment("get_dependency_proxy_settings");
8004
+ const args = GetDependencyProxySettingsSchema.parse(params.arguments);
8005
+ const settings = await getDependencyProxySettings(args.group_id);
8006
+ return {
8007
+ content: [{ type: "text", text: JSON.stringify(settings, null, 2) }],
8008
+ };
8009
+ }
8010
+ case "update_dependency_proxy_settings": {
8011
+ rejectIfProjectScopedDeployment("update_dependency_proxy_settings");
8012
+ const args = UpdateDependencyProxySettingsSchema.parse(params.arguments);
8013
+ const { group_id, ...options } = args;
8014
+ const settings = await updateDependencyProxySettings(group_id, options);
8015
+ return {
8016
+ content: [{ type: "text", text: JSON.stringify(settings, null, 2) }],
8017
+ };
8018
+ }
8019
+ case "list_dependency_proxy_blobs": {
8020
+ rejectIfProjectScopedDeployment("list_dependency_proxy_blobs");
8021
+ const args = ListDependencyProxyBlobsSchema.parse(params.arguments);
8022
+ const { group_id, ...options } = args;
8023
+ const result = await listDependencyProxyBlobs(group_id, options);
8024
+ return {
8025
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
8026
+ };
8027
+ }
8028
+ case "purge_dependency_proxy_cache": {
8029
+ rejectIfProjectScopedDeployment("purge_dependency_proxy_cache");
8030
+ const args = PurgeDependencyProxyCacheSchema.parse(params.arguments);
8031
+ await purgeDependencyProxyCache(args.group_id);
8032
+ return {
8033
+ content: [
8034
+ {
8035
+ type: "text",
8036
+ text: JSON.stringify({ status: "success", message: "Dependency proxy cache purge scheduled" }, null, 2),
8037
+ },
8038
+ ],
8039
+ };
8040
+ }
7924
8041
  case "upload_markdown": {
7925
8042
  if (IS_REMOTE) {
7926
8043
  const args = MarkdownUploadRemoteSchema.parse(params.arguments);
package/build/schemas.js CHANGED
@@ -3503,3 +3503,39 @@ export const DeleteGroupVariableSchema = z.object({
3503
3503
  .optional()
3504
3504
  .describe("Filter by environment scope to disambiguate when multiple variables share the same key"),
3505
3505
  });
3506
+ // --- Dependency Proxy types ---
3507
+ export const GitLabDependencyProxySchema = z.object({
3508
+ enabled: z.boolean(),
3509
+ blob_count: z.number().nullable().optional(),
3510
+ total_size: z.string().nullable().optional(),
3511
+ image_prefix: z.string().nullable().optional(),
3512
+ ttl_policy: z
3513
+ .object({
3514
+ enabled: z.boolean(),
3515
+ ttl: z.number().nullable().optional(),
3516
+ })
3517
+ .nullable()
3518
+ .optional(),
3519
+ });
3520
+ export const GitLabDependencyProxyBlobSchema = z.object({
3521
+ file_name: z.string(),
3522
+ size: z.string(),
3523
+ created_at: z.string().nullable().optional(),
3524
+ });
3525
+ export const GetDependencyProxySettingsSchema = z.object({
3526
+ group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
3527
+ });
3528
+ export const UpdateDependencyProxySettingsSchema = z.object({
3529
+ group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
3530
+ enabled: z.boolean().optional().describe("Enable or disable the dependency proxy"),
3531
+ identity: z.string().optional().describe("Proxy username for authenticated Docker Hub pulls (Premium/Ultimate)"),
3532
+ secret: z.string().optional().describe("Proxy password / access token for authenticated pulls"),
3533
+ });
3534
+ export const ListDependencyProxyBlobsSchema = z.object({
3535
+ group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
3536
+ first: z.number().int().optional().describe("Number of blobs to return (default: 20)"),
3537
+ after: z.string().optional().describe("Cursor for pagination (from previous response pageInfo.endCursor)"),
3538
+ });
3539
+ export const PurgeDependencyProxyCacheSchema = z.object({
3540
+ group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
3541
+ });
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ts-node
2
- import { GetFileContentsSchema, GitLabFileContentSchema, GitLabRepositorySchema, CreatePipelineSchema, CreateCommitStatusSchema, ListCommitStatusesSchema, CreateIssueNoteSchema, CreateMergeRequestEmojiReactionSchema, CreateIssueEmojiReactionSchema, DeleteMergeRequestEmojiReactionSchema, DeleteIssueEmojiReactionSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, CreateIssueSchema, ListIssuesSchema, ListMergeRequestsSchema, ListLabelsSchema, GitLabMergeRequestSchema, GitLabTreeItemSchema, GetMergeRequestSchema, ListMergeRequestPipelinesSchema, GetRepositoryTreeSchema, GitLabUserFullSchema, GitLabMarkdownUploadSchema, } from '../schemas.js';
2
+ import { GetFileContentsSchema, GitLabFileContentSchema, GitLabRepositorySchema, CreatePipelineSchema, CreateCommitStatusSchema, ListCommitStatusesSchema, CreateIssueNoteSchema, CreateMergeRequestEmojiReactionSchema, CreateIssueEmojiReactionSchema, DeleteMergeRequestEmojiReactionSchema, DeleteIssueEmojiReactionSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, CreateIssueSchema, ListIssuesSchema, ListMergeRequestsSchema, ListLabelsSchema, GitLabMergeRequestSchema, GitLabTreeItemSchema, GetMergeRequestSchema, ListMergeRequestPipelinesSchema, GetRepositoryTreeSchema, GitLabUserFullSchema, GitLabMarkdownUploadSchema, GitLabDependencyProxySchema, GitLabDependencyProxyBlobSchema, } from '../schemas.js';
3
3
  function runGetFileContentsSchemaTests() {
4
4
  console.log('🧪 Testing GetFileContentsSchema...');
5
5
  const cases = [
@@ -1385,6 +1385,106 @@ function runGitLabUserFullSchemaTests() {
1385
1385
  console.log(`\nResults: ${passed} passed, ${failed} failed`);
1386
1386
  return { passed, failed };
1387
1387
  }
1388
+ function runGitLabDependencyProxySchemaTests() {
1389
+ console.log('🧪 Testing GitLabDependencyProxySchema...');
1390
+ const cases = [
1391
+ {
1392
+ name: 'schema:dependency_proxy:minimal',
1393
+ input: { enabled: true, blob_count: 0, total_size: '0' },
1394
+ shouldFail: false,
1395
+ },
1396
+ {
1397
+ name: 'schema:dependency_proxy:full',
1398
+ input: {
1399
+ enabled: true,
1400
+ blob_count: 42,
1401
+ total_size: '1234567890',
1402
+ image_prefix: 'gitlab.example.com:5050/my-group/dependency_proxy/containers',
1403
+ ttl_policy: { enabled: true, ttl: 90 },
1404
+ },
1405
+ shouldFail: false,
1406
+ },
1407
+ {
1408
+ name: 'schema:dependency_proxy:nulls-allowed',
1409
+ input: { enabled: false, blob_count: null, total_size: null, image_prefix: null, ttl_policy: null },
1410
+ shouldFail: false,
1411
+ },
1412
+ ];
1413
+ let passed = 0;
1414
+ let failed = 0;
1415
+ cases.forEach(testCase => {
1416
+ const result = { name: testCase.name, status: 'failed' };
1417
+ const parsed = GitLabDependencyProxySchema.safeParse(testCase.input);
1418
+ if (testCase.shouldFail) {
1419
+ result.status = parsed.success ? 'failed' : 'passed';
1420
+ if (parsed.success)
1421
+ result.error = 'Expected schema validation to fail';
1422
+ }
1423
+ else if (parsed.success) {
1424
+ result.status = 'passed';
1425
+ }
1426
+ else {
1427
+ result.error = parsed.error?.message || 'Schema validation failed';
1428
+ }
1429
+ if (result.status === 'passed') {
1430
+ passed++;
1431
+ console.log(`✅ ${result.name}`);
1432
+ }
1433
+ else {
1434
+ failed++;
1435
+ console.log(`❌ ${result.name}: ${result.error}`);
1436
+ }
1437
+ });
1438
+ console.log(`\nResults: ${passed} passed, ${failed} failed`);
1439
+ return { passed, failed };
1440
+ }
1441
+ function runGitLabDependencyProxyBlobSchemaTests() {
1442
+ console.log('🧪 Testing GitLabDependencyProxyBlobSchema...');
1443
+ const cases = [
1444
+ {
1445
+ name: 'schema:dependency_proxy_blob:basic',
1446
+ input: { file_name: 'sha256:abc123', size: '2.5 MiB', created_at: '2026-01-01T00:00:00Z' },
1447
+ shouldFail: false,
1448
+ },
1449
+ {
1450
+ name: 'schema:dependency_proxy_blob:no-created-at',
1451
+ input: { file_name: 'sha256:def456', size: '512 KiB' },
1452
+ shouldFail: false,
1453
+ },
1454
+ {
1455
+ name: 'schema:dependency_proxy_blob:size-must-be-string',
1456
+ input: { file_name: 'sha256:ghi789', size: 12345 },
1457
+ shouldFail: true,
1458
+ },
1459
+ ];
1460
+ let passed = 0;
1461
+ let failed = 0;
1462
+ cases.forEach(testCase => {
1463
+ const result = { name: testCase.name, status: 'failed' };
1464
+ const parsed = GitLabDependencyProxyBlobSchema.safeParse(testCase.input);
1465
+ if (testCase.shouldFail) {
1466
+ result.status = parsed.success ? 'failed' : 'passed';
1467
+ if (parsed.success)
1468
+ result.error = 'Expected schema validation to fail';
1469
+ }
1470
+ else if (parsed.success) {
1471
+ result.status = 'passed';
1472
+ }
1473
+ else {
1474
+ result.error = parsed.error?.message || 'Schema validation failed';
1475
+ }
1476
+ if (result.status === 'passed') {
1477
+ passed++;
1478
+ console.log(`✅ ${result.name}`);
1479
+ }
1480
+ else {
1481
+ failed++;
1482
+ console.log(`❌ ${result.name}: ${result.error}`);
1483
+ }
1484
+ });
1485
+ console.log(`\nResults: ${passed} passed, ${failed} failed`);
1486
+ return { passed, failed };
1487
+ }
1388
1488
  if (import.meta.url === `file://${process.argv[1]}`) {
1389
1489
  const getFileContentsResult = runGetFileContentsSchemaTests();
1390
1490
  const fileContentResult = runGitLabFileContentSchemaTests();
@@ -1403,8 +1503,10 @@ if (import.meta.url === `file://${process.argv[1]}`) {
1403
1503
  const repositoryTreeResult = runGetRepositoryTreeSchemaTests();
1404
1504
  const gitLabUserFullResult = runGitLabUserFullSchemaTests();
1405
1505
  const gitLabMarkdownUploadResult = runGitLabMarkdownUploadSchemaTests();
1406
- const totalPassed = getFileContentsResult.passed + fileContentResult.passed + createPipelineResult.passed + commitStatusResult.passed + createIssueNoteResult.passed + getMergeRequestResult.passed + listMergeRequestPipelinesResult.passed + gitLabMergeRequestResult.passed + emojiReactionResult.passed + repositorySchemaResult.passed + labelsCoercionResult.passed + approvedByUsernamesResult.passed + listLabelsResult.passed + treeItemResult.passed + repositoryTreeResult.passed + gitLabUserFullResult.passed + gitLabMarkdownUploadResult.passed;
1407
- const totalFailed = getFileContentsResult.failed + fileContentResult.failed + createPipelineResult.failed + commitStatusResult.failed + createIssueNoteResult.failed + getMergeRequestResult.failed + listMergeRequestPipelinesResult.failed + gitLabMergeRequestResult.failed + emojiReactionResult.failed + repositorySchemaResult.failed + labelsCoercionResult.failed + approvedByUsernamesResult.failed + listLabelsResult.failed + treeItemResult.failed + repositoryTreeResult.failed + gitLabUserFullResult.failed + gitLabMarkdownUploadResult.failed;
1506
+ const dependencyProxyResult = runGitLabDependencyProxySchemaTests();
1507
+ const dependencyProxyBlobResult = runGitLabDependencyProxyBlobSchemaTests();
1508
+ const totalPassed = getFileContentsResult.passed + fileContentResult.passed + createPipelineResult.passed + commitStatusResult.passed + createIssueNoteResult.passed + getMergeRequestResult.passed + listMergeRequestPipelinesResult.passed + gitLabMergeRequestResult.passed + emojiReactionResult.passed + repositorySchemaResult.passed + labelsCoercionResult.passed + approvedByUsernamesResult.passed + listLabelsResult.passed + treeItemResult.passed + repositoryTreeResult.passed + gitLabUserFullResult.passed + gitLabMarkdownUploadResult.passed + dependencyProxyResult.passed + dependencyProxyBlobResult.passed;
1509
+ const totalFailed = getFileContentsResult.failed + fileContentResult.failed + createPipelineResult.failed + commitStatusResult.failed + createIssueNoteResult.failed + getMergeRequestResult.failed + listMergeRequestPipelinesResult.failed + gitLabMergeRequestResult.failed + emojiReactionResult.failed + repositorySchemaResult.failed + labelsCoercionResult.failed + approvedByUsernamesResult.failed + listLabelsResult.failed + treeItemResult.failed + repositoryTreeResult.failed + gitLabUserFullResult.failed + gitLabMarkdownUploadResult.failed + dependencyProxyResult.failed + dependencyProxyBlobResult.failed;
1408
1510
  console.log(`\nTotal Results: ${totalPassed} passed, ${totalFailed} failed`);
1409
1511
  if (totalFailed > 0) {
1410
1512
  process.exit(1);
@@ -0,0 +1,191 @@
1
+ import { describe, test, before, after } 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-dependency-proxy";
6
+ const TEST_GROUP_PATH = "my-group";
7
+ async function callTool(toolName, args, env) {
8
+ return new Promise((resolve, reject) => {
9
+ const proc = spawn("node", ["build/index.js"], {
10
+ stdio: ["pipe", "pipe", "pipe"],
11
+ env: { ...process.env, ...env },
12
+ });
13
+ let output = "";
14
+ let errorOutput = "";
15
+ proc.stdout?.on("data", (d) => (output += d));
16
+ proc.stderr?.on("data", (d) => (errorOutput += d));
17
+ proc.on("close", code => {
18
+ if (code !== 0) {
19
+ return reject(new Error(`Process exited with code ${code}: ${errorOutput}`));
20
+ }
21
+ const line = output.split("\n").find(l => l.startsWith("{"));
22
+ if (!line)
23
+ return reject(new Error("No JSON output found"));
24
+ try {
25
+ const response = JSON.parse(line);
26
+ if (response.error) {
27
+ reject(new Error(response.error?.message ?? String(response.error)));
28
+ }
29
+ else {
30
+ const content = response.result?.content?.[0]?.text;
31
+ resolve(content ? JSON.parse(content) : response.result);
32
+ }
33
+ }
34
+ catch (e) {
35
+ reject(e);
36
+ }
37
+ });
38
+ proc.stdin?.end(JSON.stringify({
39
+ jsonrpc: "2.0",
40
+ id: 1,
41
+ method: "tools/call",
42
+ params: { name: toolName, arguments: args },
43
+ }) + "\n");
44
+ });
45
+ }
46
+ describe("dependency proxy tools", () => {
47
+ let mockServer;
48
+ let mockPort;
49
+ let baseEnv;
50
+ before(async () => {
51
+ mockPort = await findMockServerPort();
52
+ mockServer = new MockGitLabServer({ port: mockPort, validTokens: [MOCK_TOKEN] });
53
+ // Mock GraphQL endpoint for get/list operations
54
+ mockServer.addRootHandler("post", "/api/graphql", (req, res) => {
55
+ const { query } = req.body;
56
+ if (query.includes("updateDependencyProxySettings")) {
57
+ res.json({ data: { updateDependencyProxySettings: { errors: [] } } });
58
+ }
59
+ else if (query.includes("dependencyProxySetting")) {
60
+ res.json({
61
+ data: {
62
+ group: {
63
+ dependencyProxySetting: { enabled: true },
64
+ dependencyProxyBlobCount: 3,
65
+ dependencyProxyTotalSize: "15728640",
66
+ dependencyProxyImagePrefix: `localhost:${mockPort}/my-group/dependency_proxy/containers`,
67
+ dependencyProxyImageTtlPolicy: { enabled: true, ttl: 90 },
68
+ },
69
+ },
70
+ });
71
+ }
72
+ else if (query.includes("dependencyProxyBlobs")) {
73
+ res.json({
74
+ data: {
75
+ group: {
76
+ dependencyProxyBlobs: {
77
+ nodes: [
78
+ { fileName: "sha256:abc123", size: "5 MiB", createdAt: "2026-01-01T00:00:00Z" },
79
+ { fileName: "sha256:def456", size: "10 MiB", createdAt: "2026-01-02T00:00:00Z" },
80
+ ],
81
+ pageInfo: { hasNextPage: false, endCursor: null },
82
+ },
83
+ },
84
+ },
85
+ });
86
+ }
87
+ else {
88
+ res.status(400).json({ errors: [{ message: "Unexpected GraphQL query" }] });
89
+ }
90
+ });
91
+ // Mock REST endpoint for purge
92
+ mockServer.addMockHandler("delete", `/groups/${TEST_GROUP_PATH}/dependency_proxy/cache`, (_req, res) => {
93
+ res.status(202).send();
94
+ });
95
+ await mockServer.start();
96
+ baseEnv = {
97
+ GITLAB_PERSONAL_ACCESS_TOKEN: MOCK_TOKEN,
98
+ GITLAB_API_URL: `http://localhost:${mockPort}/api/v4`,
99
+ GITLAB_TOOLSETS: "dependency_proxy",
100
+ };
101
+ });
102
+ after(async () => {
103
+ await mockServer.stop();
104
+ });
105
+ test("get_dependency_proxy_settings returns enabled status and blob info", async () => {
106
+ const result = await callTool("get_dependency_proxy_settings", { group_id: TEST_GROUP_PATH }, baseEnv);
107
+ assert.strictEqual(result.enabled, true);
108
+ assert.strictEqual(result.blob_count, 3);
109
+ assert.strictEqual(result.total_size, "15728640");
110
+ assert.ok(result.ttl_policy?.enabled);
111
+ });
112
+ test("list_dependency_proxy_blobs returns blobs with string sizes", async () => {
113
+ const result = await callTool("list_dependency_proxy_blobs", { group_id: TEST_GROUP_PATH }, baseEnv);
114
+ assert.ok(Array.isArray(result.blobs));
115
+ assert.strictEqual(result.blobs.length, 2);
116
+ assert.strictEqual(typeof result.blobs[0].size, "string");
117
+ assert.strictEqual(result.blobs[0].file_name, "sha256:abc123");
118
+ });
119
+ test("purge_dependency_proxy_cache returns scheduled status", async () => {
120
+ const result = await callTool("purge_dependency_proxy_cache", { group_id: TEST_GROUP_PATH }, baseEnv);
121
+ assert.strictEqual(result.status, "success");
122
+ assert.ok(result.message.includes("scheduled"));
123
+ });
124
+ test("update_dependency_proxy_settings enables the proxy and returns settings", async () => {
125
+ const result = await callTool("update_dependency_proxy_settings", { group_id: TEST_GROUP_PATH, enabled: true }, baseEnv);
126
+ assert.strictEqual(result.enabled, true);
127
+ });
128
+ test("update_dependency_proxy_settings rejects empty options", async () => {
129
+ await assert.rejects(() => callTool("update_dependency_proxy_settings", { group_id: TEST_GROUP_PATH }, baseEnv), /At least one of enabled, identity, or secret must be provided/);
130
+ });
131
+ test("dependency_proxy tools are absent when toolset is not activated", async () => {
132
+ return new Promise((resolve, reject) => {
133
+ const proc = spawn("node", ["build/index.js"], {
134
+ stdio: ["pipe", "pipe", "pipe"],
135
+ env: {
136
+ ...process.env,
137
+ GITLAB_PERSONAL_ACCESS_TOKEN: MOCK_TOKEN,
138
+ GITLAB_API_URL: `http://localhost:${mockPort}/api/v4`,
139
+ // No GITLAB_TOOLSETS — default toolsets only
140
+ },
141
+ });
142
+ let output = "";
143
+ proc.stdout?.on("data", (d) => (output += d));
144
+ proc.on("close", () => {
145
+ try {
146
+ const line = output.split("\n").find(l => l.startsWith("{"));
147
+ if (!line)
148
+ return reject(new Error("No JSON output found"));
149
+ const response = JSON.parse(line);
150
+ const tools = response.result?.tools ?? [];
151
+ const names = tools.map(t => t.name);
152
+ assert.ok(!names.includes("get_dependency_proxy_settings"), "tool should not be in default toolset");
153
+ assert.ok(!names.includes("purge_dependency_proxy_cache"), "tool should not be in default toolset");
154
+ resolve();
155
+ }
156
+ catch (e) {
157
+ reject(e);
158
+ }
159
+ });
160
+ proc.stdin?.end(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list", params: {} }) + "\n");
161
+ });
162
+ });
163
+ test("write tools are absent from tools/list in read-only mode", async () => {
164
+ return new Promise((resolve, reject) => {
165
+ const proc = spawn("node", ["build/index.js"], {
166
+ stdio: ["pipe", "pipe", "pipe"],
167
+ env: { ...process.env, ...baseEnv, GITLAB_READ_ONLY_MODE: "true" },
168
+ });
169
+ let output = "";
170
+ proc.stdout?.on("data", (d) => (output += d));
171
+ proc.on("close", () => {
172
+ try {
173
+ const line = output.split("\n").find(l => l.startsWith("{"));
174
+ if (!line)
175
+ return reject(new Error("No JSON output found"));
176
+ const response = JSON.parse(line);
177
+ const names = (response.result?.tools ?? []).map((t) => t.name);
178
+ assert.ok(!names.includes("purge_dependency_proxy_cache"), "purge should be absent in read-only mode");
179
+ assert.ok(!names.includes("update_dependency_proxy_settings"), "update should be absent in read-only mode");
180
+ assert.ok(names.includes("get_dependency_proxy_settings"), "get should be present in read-only mode");
181
+ assert.ok(names.includes("list_dependency_proxy_blobs"), "list should be present in read-only mode");
182
+ resolve();
183
+ }
184
+ catch (e) {
185
+ reject(e);
186
+ }
187
+ });
188
+ proc.stdin?.end(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list", params: {} }) + "\n");
189
+ });
190
+ });
191
+ });
@@ -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
@@ -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, 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 = [
@@ -982,6 +982,27 @@ export const allTools = [
982
982
  description: "Delete a CI/CD variable from a group",
983
983
  inputSchema: toJSONSchema(DeleteGroupVariableSchema),
984
984
  },
985
+ // --- Dependency proxy tools ---
986
+ {
987
+ name: "get_dependency_proxy_settings",
988
+ description: "Get dependency proxy settings for a group",
989
+ inputSchema: toJSONSchema(GetDependencyProxySettingsSchema),
990
+ },
991
+ {
992
+ name: "update_dependency_proxy_settings",
993
+ description: "Update dependency proxy settings for a group (enable/disable, credentials for authenticated Docker Hub pulls)",
994
+ inputSchema: toJSONSchema(UpdateDependencyProxySettingsSchema),
995
+ },
996
+ {
997
+ name: "list_dependency_proxy_blobs",
998
+ description: "List cached dependency proxy blobs for a group",
999
+ inputSchema: toJSONSchema(ListDependencyProxyBlobsSchema),
1000
+ },
1001
+ {
1002
+ name: "purge_dependency_proxy_cache",
1003
+ description: "Schedule purge of all cached dependency proxy blobs for a group",
1004
+ inputSchema: toJSONSchema(PurgeDependencyProxyCacheSchema),
1005
+ },
985
1006
  // --- Meta tool: Dynamic tool discovery ---
986
1007
  {
987
1008
  name: "discover_tools",
@@ -1105,6 +1126,8 @@ export const readOnlyTools = new Set([
1105
1126
  "get_project_variable",
1106
1127
  "list_group_variables",
1107
1128
  "get_group_variable",
1129
+ "get_dependency_proxy_settings",
1130
+ "list_dependency_proxy_blobs",
1108
1131
  ]);
1109
1132
  // Define which tools are destructive (data loss potential)
1110
1133
  export const destructiveTools = new Set([
@@ -1130,6 +1153,7 @@ export const destructiveTools = new Set([
1130
1153
  "push_files",
1131
1154
  "delete_project_variable",
1132
1155
  "delete_group_variable",
1156
+ "purge_dependency_proxy_cache",
1133
1157
  ]);
1134
1158
  // Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
1135
1159
  export const wikiToolNames = new Set([
@@ -1473,6 +1497,16 @@ export const TOOLSET_DEFINITIONS = [
1473
1497
  "delete_group_variable",
1474
1498
  ]),
1475
1499
  },
1500
+ {
1501
+ id: "dependency_proxy",
1502
+ isDefault: false,
1503
+ tools: new Set([
1504
+ "get_dependency_proxy_settings",
1505
+ "update_dependency_proxy_settings",
1506
+ "list_dependency_proxy_blobs",
1507
+ "purge_dependency_proxy_cache",
1508
+ ]),
1509
+ },
1476
1510
  ];
1477
1511
  // Derived lookup: tool name → toolset ID
1478
1512
  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.18",
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-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",