@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 +4 -0
- package/build/index.js +118 -1
- package/build/schemas.js +36 -0
- package/build/test/schema-tests.js +105 -3
- package/build/test/test-dependency-proxy.js +191 -0
- package/build/test/test-geteffectiveprojectid.js +60 -0
- package/build/test/test-toolset-filtering.js +3 -0
- package/build/tools/registry.js +35 -1
- package/package.json +2 -2
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
|
|
1407
|
-
const
|
|
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 = {}) {
|
package/build/tools/registry.js
CHANGED
|
@@ -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.
|
|
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",
|