@zereight/mcp-gitlab 2.1.29 → 2.1.38

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.
Files changed (71) hide show
  1. package/README.ko.md +72 -20
  2. package/README.md +120 -82
  3. package/README.zh-CN.md +72 -20
  4. package/build/config.js +13 -0
  5. package/build/downloads/proxy.js +199 -0
  6. package/build/index.js +850 -490
  7. package/build/schemas.js +115 -28
  8. package/build/scripts/check-skill-sync.js +137 -0
  9. package/build/scripts/generate-tool-docs.js +8 -3
  10. package/build/server/metrics.js +84 -0
  11. package/build/server/transport-mode.js +25 -0
  12. package/build/test/callback-proxy-tests.js +1 -1
  13. package/build/test/client-pool-test.js +1 -1
  14. package/build/test/dynamic-api-url-allowlist.test.js +2 -2
  15. package/build/test/dynamic-api-url-test.js +4 -4
  16. package/build/test/dynamic-routing-tests.js +4 -4
  17. package/build/test/group-milestone-schema.test.js +45 -0
  18. package/build/test/mcp-oauth-tests.js +29 -4
  19. package/build/test/mcp-server-name.test.js +87 -0
  20. package/build/test/multi-server-test.js +3 -3
  21. package/build/test/no-proxy-integration-test.js +1 -1
  22. package/build/test/nullable-gitlab-response-fields.test.js +22 -0
  23. package/build/test/path-segment-encoding.test.js +11 -0
  24. package/build/test/remote-auth-simple-test.js +226 -199
  25. package/build/test/server/metrics.test.js +47 -0
  26. package/build/test/sse-auth-guard.test.js +2 -2
  27. package/build/test/stateless/session-id-integration.test.js +3 -3
  28. package/build/test/streamable-http-concurrent-session.test.js +64 -1
  29. package/build/test/streamable-http-dns-rebinding.test.js +185 -0
  30. package/build/test/streamable-http-static-token-auth.test.js +25 -22
  31. package/build/test/streamable-http-unauthenticated-discovery.test.js +28 -15
  32. package/build/test/test-ci-catalog.js +1 -1
  33. package/build/test/test-ci-lint.js +1 -1
  34. package/build/test/test-ci-variables.js +8 -5
  35. package/build/test/test-dependency-proxy.js +11 -7
  36. package/build/test/test-deployment-tools.js +16 -2
  37. package/build/test/test-download-attachment.js +1 -1
  38. package/build/test/test-get-file-blame.js +1 -1
  39. package/build/test/test-geteffectiveprojectid.js +221 -8
  40. package/build/test/test-issue-description-patch.js +1 -1
  41. package/build/test/test-job-artifacts.js +1 -1
  42. package/build/test/test-list-issues.js +1 -1
  43. package/build/test/test-list-merge-requests.js +1 -1
  44. package/build/test/test-list-project-members.js +1 -1
  45. package/build/test/test-merge-request-approval-state-tools.js +1 -1
  46. package/build/test/test-merge-request-pipelines.js +1 -1
  47. package/build/test/test-mr-diffs-filter.js +1 -1
  48. package/build/test/test-mr-file-diffs.js +2 -2
  49. package/build/test/test-oauth-proxy-rate-limit.js +1 -1
  50. package/build/test/test-permission-mode.js +216 -0
  51. package/build/test/test-protected-branches.js +23 -4
  52. package/build/test/test-remote-downloads.js +2 -2
  53. package/build/test/test-search-code.js +1 -1
  54. package/build/test/test-tags.js +1 -1
  55. package/build/test/test-todos.js +1 -1
  56. package/build/test/test-token-optimizations.js +3 -3
  57. package/build/test/test-toolset-filtering.js +3 -3
  58. package/build/test/test-update-issue-slim.js +141 -0
  59. package/build/test/test-upload-markdown.js +1 -1
  60. package/build/test/utils/download-token.test.js +35 -0
  61. package/build/test/utils/forwarded-public-base-url.test.js +9 -1
  62. package/build/test/utils/graphql-query.test.js +64 -1
  63. package/build/test/utils/mock-gitlab-server.js +24 -31
  64. package/build/test/utils/server-launcher.js +1 -2
  65. package/build/test/utils/version-check.test.js +52 -0
  66. package/build/tools/registry.js +93 -5
  67. package/build/utils/download-token.js +71 -0
  68. package/build/utils/forwarded-public-base-url.js +22 -0
  69. package/build/utils/graphql-query.js +109 -12
  70. package/build/utils/version-check.js +40 -0
  71. package/package.json +5 -4
@@ -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, GetProtectedBranchSchema, ListProtectedBranchesSchema, ProtectBranchSchema, UnprotectBranchSchema, UpdateDefaultBranchSchema, DeleteDraftNoteSchema, DeleteGroupWikiPageSchema, DeleteIssueLinkSchema, DeleteIssueSchema, DeleteIssueEmojiReactionSchema, DeleteIssueNoteEmojiReactionSchema, DeleteLabelSchema, DeleteMergeRequestDiscussionNoteSchema, DeleteMergeRequestNoteSchema, DeleteMergeRequestEmojiReactionSchema, DeleteMergeRequestNoteEmojiReactionSchema, DeleteProjectMilestoneSchema, DeleteReleaseSchema, DeleteTagSchema, DeleteWikiPageSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, DownloadAttachmentSchema, DownloadAttachmentRemoteSchema, DownloadJobArtifactsSchema, DownloadJobArtifactsRemoteSchema, DownloadReleaseAssetSchema, EditProjectMilestoneSchema, ExecuteGraphQLSchema, ForkRepositorySchema, HealthCheckSchema, GetBranchSchema, GetBranchDiffsSchema, GetCommitDiffSchema, GetCommitSchema, GetFileBlameSchema, GetDeploymentSchema, GetDraftNoteSchema, GetEnvironmentSchema, GetFileContentsSchema, GetGroupWikiPageSchema, GetIssueLinkSchema, GetIssueSchema, GetJobArtifactFileSchema, GetLabelSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GetMergeRequestDiffsSchema, GetMergeRequestFileDiffSchema, GetMergeRequestNoteSchema, GetMergeRequestNotesSchema, GetMergeRequestSchema, GetMergeRequestVersionSchema, GetMilestoneBurndownEventsSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, GetNamespaceSchema, GetPipelineJobOutputSchema, GetPipelineSchema, GetProjectEventsSchema, GetProjectMilestoneSchema, GetProjectSchema, GetReleaseSchema, GetRepositoryTreeSchema, GetTagSchema, GetTagSignatureSchema, GetTimelineEventsSchema, GetUsersSchema, GetUserSchema, WhoAmISchema, GetWebhookEventSchema, GetWikiPageSchema, GetWorkItemSchema, ListBranchesSchema, ListCommitsSchema, ListCommitStatusesSchema, ListCustomFieldDefinitionsSchema, ListDeploymentsSchema, ListDraftNotesSchema, ListEnvironmentsSchema, ListEventsSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, ListGroupWikiPagesSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListJobArtifactsSchema, ListLabelsSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiffsSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestVersionsSchema, ListMergeRequestsSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListCiCatalogResourcesSchema, GetCiCatalogResourceSchema, 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, UpdateProjectSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestSchema, UpdateReleaseSchema, UpdateWikiPageSchema, UpdateWorkItemSchema, VerifyNamespaceSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, GetDependencyProxySettingsSchema, UpdateDependencyProxySettingsSchema, ListDependencyProxyBlobsSchema, PurgeDependencyProxyCacheSchema, } 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, CreateGroupMilestoneSchema, CreateReleaseEvidenceSchema, CreateReleaseSchema, CreateRepositorySchema, CreateTagSchema, CreateTimelineEventSchema, CreateWikiPageSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, CreateWorkItemSchema, DeleteBranchSchema, GetProtectedBranchSchema, ListProtectedBranchesSchema, ProtectBranchSchema, UnprotectBranchSchema, UpdateDefaultBranchSchema, DeleteDraftNoteSchema, DeleteGroupMilestoneSchema, DeleteGroupWikiPageSchema, DeleteIssueLinkSchema, DeleteIssueSchema, DeleteIssueEmojiReactionSchema, DeleteIssueNoteEmojiReactionSchema, DeleteLabelSchema, DeleteMergeRequestDiscussionNoteSchema, DeleteMergeRequestNoteSchema, DeleteMergeRequestEmojiReactionSchema, DeleteMergeRequestNoteEmojiReactionSchema, DeleteProjectMilestoneSchema, DeleteReleaseSchema, DeleteTagSchema, DeleteWikiPageSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, DownloadAttachmentSchema, DownloadAttachmentRemoteSchema, DownloadJobArtifactsSchema, DownloadJobArtifactsRemoteSchema, DownloadReleaseAssetSchema, EditProjectMilestoneSchema, EditGroupMilestoneSchema, ExecuteGraphQLSchema, ForkRepositorySchema, HealthCheckSchema, GetBranchSchema, GetBranchDiffsSchema, GetCommitDiffSchema, GetCommitSchema, GetFileBlameSchema, GetDeploymentSchema, GetDraftNoteSchema, GetEnvironmentSchema, GetFileContentsSchema, GetGroupWikiPageSchema, GetIssueLinkSchema, GetIssueSchema, GetJobArtifactFileSchema, GetLabelSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GetMergeRequestDiffsSchema, GetMergeRequestFileDiffSchema, GetMergeRequestNoteSchema, GetMergeRequestNotesSchema, GetMergeRequestSchema, GetMergeRequestVersionSchema, GetMilestoneBurndownEventsSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, GetGroupMilestoneSchema, GetGroupMilestoneIssuesSchema, GetGroupMilestoneMergeRequestsSchema, GetGroupMilestoneBurndownEventsSchema, GetNamespaceSchema, GetPipelineJobOutputSchema, PipelineJobControlSchema, GetPipelineSchema, GetProjectEventsSchema, GetProjectMilestoneSchema, GetProjectSchema, GetReleaseSchema, GetRepositoryTreeSchema, GetTagSchema, GetTagSignatureSchema, GetTimelineEventsSchema, GetUsersSchema, GetUserSchema, WhoAmISchema, GetWebhookEventSchema, GetWikiPageSchema, GetWorkItemSchema, ListBranchesSchema, ListCommitsSchema, ListCommitStatusesSchema, ListCustomFieldDefinitionsSchema, ListDeploymentsSchema, ListDraftNotesSchema, ListEnvironmentsSchema, ListEventsSchema, ListGroupIterationsSchema, ListGroupMilestonesSchema, ListGroupProjectsSchema, ListGroupWikiPagesSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListJobArtifactsSchema, ListLabelsSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiffsSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestVersionsSchema, ListMergeRequestsSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListCiCatalogResourcesSchema, GetCiCatalogResourceSchema, ListPipelinesSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListReleasesSchema, ListTagsSchema, ListWebhookEventsSchema, ListWebhooksSchema, ListWikiPagesSchema, ListWorkItemNotesSchema, ListWorkItemStatusesSchema, ListWorkItemsSchema, MarkdownUploadSchema, MarkdownUploadRemoteSchema, MergeMergeRequestSchema, MoveWorkItemSchema, MyIssuesSchema, PlayPipelineJobSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PushFilesSchema, ResolveMergeRequestThreadSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UnapproveMergeRequestSchema, UpdateDraftNoteSchema, UpdateGroupWikiPageSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateIssueDescriptionPatchSchema, UpdateLabelSchema, UpdateProjectSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestSchema, UpdateReleaseSchema, UpdateWikiPageSchema, UpdateWorkItemSchema, VerifyNamespaceSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, GetDependencyProxySettingsSchema, UpdateDependencyProxySettingsSchema, ListDependencyProxyBlobsSchema, PurgeDependencyProxyCacheSchema, } from "../schemas.js";
5
5
  const IS_REMOTE = SSE || STREAMABLE_HTTP;
6
6
  // Define all available tools
7
7
  export const allTools = [
@@ -132,7 +132,7 @@ export const allTools = [
132
132
  },
133
133
  {
134
134
  name: "get_merge_request",
135
- description: "Get details of a merge request (mergeRequestIid or branchName required)",
135
+ description: "Get details of a merge request (mergeRequestIid or branchName required). Set include_summaries=true for deployment/commit/approval summaries",
136
136
  inputSchema: toJSONSchema(GetMergeRequestSchema),
137
137
  },
138
138
  {
@@ -354,12 +354,12 @@ export const allTools = [
354
354
  },
355
355
  {
356
356
  name: "get_issue",
357
- description: "Get details of a specific issue",
357
+ description: "Get details of a specific issue. Returns a slim milestone by default; set full_response=true for the complete milestone object",
358
358
  inputSchema: toJSONSchema(GetIssueSchema),
359
359
  },
360
360
  {
361
361
  name: "update_issue",
362
- description: "Update an issue",
362
+ description: "Update an issue. Returns a slim confirmation by default; set full_response=true for the complete updated issue object",
363
363
  inputSchema: toJSONSchema(UpdateIssueSchema),
364
364
  },
365
365
  {
@@ -577,7 +577,7 @@ export const allTools = [
577
577
  {
578
578
  name: "get_pipeline_job",
579
579
  description: "Get details of a GitLab pipeline job number",
580
- inputSchema: toJSONSchema(GetPipelineJobOutputSchema),
580
+ inputSchema: toJSONSchema(PipelineJobControlSchema),
581
581
  },
582
582
  {
583
583
  name: "get_pipeline_job_output",
@@ -703,6 +703,46 @@ export const allTools = [
703
703
  description: "Get burndown events for a specific milestone",
704
704
  inputSchema: toJSONSchema(GetMilestoneBurndownEventsSchema),
705
705
  },
706
+ {
707
+ name: "list_group_milestones",
708
+ description: "List group milestones with filtering options",
709
+ inputSchema: toJSONSchema(ListGroupMilestonesSchema),
710
+ },
711
+ {
712
+ name: "get_group_milestone",
713
+ description: "Get details of a specific group milestone",
714
+ inputSchema: toJSONSchema(GetGroupMilestoneSchema),
715
+ },
716
+ {
717
+ name: "create_group_milestone",
718
+ description: "Create a new group milestone",
719
+ inputSchema: toJSONSchema(CreateGroupMilestoneSchema),
720
+ },
721
+ {
722
+ name: "edit_group_milestone",
723
+ description: "Edit an existing group milestone",
724
+ inputSchema: toJSONSchema(EditGroupMilestoneSchema),
725
+ },
726
+ {
727
+ name: "delete_group_milestone",
728
+ description: "Delete a group milestone",
729
+ inputSchema: toJSONSchema(DeleteGroupMilestoneSchema),
730
+ },
731
+ {
732
+ name: "get_group_milestone_issue",
733
+ description: "Get issues associated with a specific group milestone",
734
+ inputSchema: toJSONSchema(GetGroupMilestoneIssuesSchema),
735
+ },
736
+ {
737
+ name: "get_group_milestone_merge_requests",
738
+ description: "Get merge requests associated with a specific group milestone",
739
+ inputSchema: toJSONSchema(GetGroupMilestoneMergeRequestsSchema),
740
+ },
741
+ {
742
+ name: "get_group_milestone_burndown_events",
743
+ description: "Get burndown events for a specific group milestone",
744
+ inputSchema: toJSONSchema(GetGroupMilestoneBurndownEventsSchema),
745
+ },
706
746
  {
707
747
  name: "get_users",
708
748
  description: "Get GitLab user details by usernames",
@@ -1126,6 +1166,11 @@ export const readOnlyTools = new Set([
1126
1166
  "get_milestone_issue",
1127
1167
  "get_milestone_merge_requests",
1128
1168
  "get_milestone_burndown_events",
1169
+ "list_group_milestones",
1170
+ "get_group_milestone",
1171
+ "get_group_milestone_issue",
1172
+ "get_group_milestone_merge_requests",
1173
+ "get_group_milestone_burndown_events",
1129
1174
  "list_wiki_pages",
1130
1175
  "get_wiki_page",
1131
1176
  "list_group_wiki_pages",
@@ -1181,6 +1226,7 @@ export const destructiveTools = new Set([
1181
1226
  "delete_wiki_page",
1182
1227
  "delete_group_wiki_page",
1183
1228
  "delete_milestone",
1229
+ "delete_group_milestone",
1184
1230
  "delete_release",
1185
1231
  "delete_tag",
1186
1232
  "delete_merge_request_note",
@@ -1202,6 +1248,32 @@ export const destructiveTools = new Set([
1202
1248
  "delete_group_variable",
1203
1249
  "purge_dependency_proxy_cache",
1204
1250
  ]);
1251
+ // Tools that permanently delete resources — blocked in "modify" permission mode.
1252
+ // Narrower than destructiveTools: merge/protect/push are modifications, not deletions.
1253
+ export const deleteTools = new Set([
1254
+ "delete_branch",
1255
+ "delete_draft_note",
1256
+ "delete_group_variable",
1257
+ "delete_group_wiki_page",
1258
+ "delete_group_milestone",
1259
+ "delete_issue",
1260
+ "delete_issue_emoji_reaction",
1261
+ "delete_issue_link",
1262
+ "delete_issue_note_emoji_reaction",
1263
+ "delete_label",
1264
+ "delete_merge_request_discussion_note",
1265
+ "delete_merge_request_emoji_reaction",
1266
+ "delete_merge_request_note",
1267
+ "delete_merge_request_note_emoji_reaction",
1268
+ "delete_milestone",
1269
+ "delete_project_variable",
1270
+ "delete_release",
1271
+ "delete_tag",
1272
+ "delete_wiki_page",
1273
+ "delete_work_item_emoji_reaction",
1274
+ "delete_work_item_note_emoji_reaction",
1275
+ "purge_dependency_proxy_cache",
1276
+ ]);
1205
1277
  // Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
1206
1278
  export const wikiToolNames = new Set([
1207
1279
  "list_wiki_pages",
@@ -1227,6 +1299,14 @@ export const milestoneToolNames = new Set([
1227
1299
  "get_milestone_merge_requests",
1228
1300
  "promote_milestone",
1229
1301
  "get_milestone_burndown_events",
1302
+ "list_group_milestones",
1303
+ "get_group_milestone",
1304
+ "create_group_milestone",
1305
+ "edit_group_milestone",
1306
+ "delete_group_milestone",
1307
+ "get_group_milestone_issue",
1308
+ "get_group_milestone_merge_requests",
1309
+ "get_group_milestone_burndown_events",
1230
1310
  ]);
1231
1311
  // Define which tools are related to pipelines and can be toggled by USE_PIPELINE
1232
1312
  export const pipelineToolNames = new Set([
@@ -1446,6 +1526,14 @@ export const TOOLSET_DEFINITIONS = [
1446
1526
  "get_milestone_merge_requests",
1447
1527
  "promote_milestone",
1448
1528
  "get_milestone_burndown_events",
1529
+ "list_group_milestones",
1530
+ "get_group_milestone",
1531
+ "create_group_milestone",
1532
+ "edit_group_milestone",
1533
+ "delete_group_milestone",
1534
+ "get_group_milestone_issue",
1535
+ "get_group_milestone_merge_requests",
1536
+ "get_group_milestone_burndown_events",
1449
1537
  ]),
1450
1538
  },
1451
1539
  {
@@ -0,0 +1,71 @@
1
+ import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
2
+ /**
3
+ * Encryption key for download tokens. When DOWNLOAD_TOKEN_SECRET is set
4
+ * (recommended for HA deployments behind a load balancer) the key is
5
+ * derived from that value so all replicas share the same key. Otherwise
6
+ * a random key is generated per process (tokens are not portable across
7
+ * restarts or replicas).
8
+ */
9
+ const DOWNLOAD_TOKEN_KEY = (() => {
10
+ const secret = process.env.DOWNLOAD_TOKEN_SECRET;
11
+ if (secret) {
12
+ return createHash("sha256").update(secret).digest();
13
+ }
14
+ return randomBytes(32);
15
+ })();
16
+ /** Download token TTL in seconds (default 5 minutes). */
17
+ const DEFAULT_DOWNLOAD_TOKEN_TTL = 300;
18
+ const parsedDownloadTokenTtl = Number.parseInt(process.env.DOWNLOAD_TOKEN_TTL || "", 10);
19
+ const DOWNLOAD_TOKEN_TTL = Number.isFinite(parsedDownloadTokenTtl) && parsedDownloadTokenTtl > 0
20
+ ? parsedDownloadTokenTtl
21
+ : DEFAULT_DOWNLOAD_TOKEN_TTL;
22
+ /**
23
+ * Create a self-contained encrypted download token for a specific GitLab resource.
24
+ */
25
+ export function createDownloadToken(header, token, apiUrl, resource) {
26
+ const iv = randomBytes(12);
27
+ const cipher = createCipheriv("aes-256-gcm", DOWNLOAD_TOKEN_KEY, iv);
28
+ const payload = JSON.stringify({
29
+ h: header,
30
+ t: token,
31
+ e: Math.floor(Date.now() / 1000) + DOWNLOAD_TOKEN_TTL,
32
+ ...(apiUrl ? { u: apiUrl } : {}),
33
+ ...(resource ? { r: resource.type, p: resource.params } : {}),
34
+ });
35
+ const encrypted = Buffer.concat([cipher.update(payload, "utf8"), cipher.final()]);
36
+ const tag = cipher.getAuthTag();
37
+ return Buffer.concat([iv, tag, encrypted]).toString("base64url");
38
+ }
39
+ /**
40
+ * Decrypt and validate a download token, returning null when it is invalid or expired.
41
+ */
42
+ export function decryptDownloadToken(tokenStr) {
43
+ try {
44
+ const buf = Buffer.from(tokenStr, "base64url");
45
+ if (buf.length < 29)
46
+ return null;
47
+ const iv = buf.subarray(0, 12);
48
+ const tag = buf.subarray(12, 28);
49
+ const encrypted = buf.subarray(28);
50
+ const decipher = createDecipheriv("aes-256-gcm", DOWNLOAD_TOKEN_KEY, iv);
51
+ decipher.setAuthTag(tag);
52
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
53
+ const payload = JSON.parse(decrypted.toString("utf8"));
54
+ if (payload.e && Math.floor(Date.now() / 1000) > payload.e) {
55
+ return null;
56
+ }
57
+ if (typeof payload.h !== "string" || payload.h.length === 0)
58
+ return null;
59
+ if (typeof payload.t !== "string" || payload.t.length === 0)
60
+ return null;
61
+ return {
62
+ header: payload.h,
63
+ token: payload.t,
64
+ ...(payload.u ? { apiUrl: payload.u } : {}),
65
+ ...(payload.r ? { resourceType: payload.r, resourceParams: payload.p } : {}),
66
+ };
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
@@ -1,3 +1,25 @@
1
+ export function getForwardedRequestHost(req, trustProxy) {
2
+ if (!trustProxy)
3
+ return undefined;
4
+ const forwarded = getLastHeaderValue(req.headers.forwarded);
5
+ if (forwarded) {
6
+ for (const part of forwarded.split(";")) {
7
+ const separator = part.indexOf("=");
8
+ if (separator <= 0)
9
+ continue;
10
+ const key = part.slice(0, separator).trim().toLowerCase();
11
+ const value = unquoteHeaderValue(part.slice(separator + 1).trim());
12
+ if (key === "host" && value && !/[\s/\\]/.test(value)) {
13
+ return value;
14
+ }
15
+ }
16
+ }
17
+ const host = getLastHeaderValue(req.headers["x-forwarded-host"]);
18
+ if (host && !/[\s/\\]/.test(host)) {
19
+ return host;
20
+ }
21
+ return undefined;
22
+ }
1
23
  function getLastHeaderValue(value) {
2
24
  const raw = Array.isArray(value) ? value[value.length - 1] : value;
3
25
  if (!raw)
@@ -10,6 +10,21 @@ function stripGraphQLCommentsAndStrings(source) {
10
10
  result += " ";
11
11
  continue;
12
12
  }
13
+ if (source.slice(i, i + 3) === '"""') {
14
+ i += 3;
15
+ while (i < source.length && source.slice(i, i + 3) !== '"""') {
16
+ if (source[i] === "\\" && source.slice(i, i + 4) === '\\"""') {
17
+ i += 4;
18
+ continue;
19
+ }
20
+ i++;
21
+ }
22
+ if (i < source.length) {
23
+ i += 3;
24
+ }
25
+ result += " ";
26
+ continue;
27
+ }
13
28
  if (ch === '"' || ch === "'") {
14
29
  const quote = ch;
15
30
  i++;
@@ -27,17 +42,6 @@ function stripGraphQLCommentsAndStrings(source) {
27
42
  result += " ";
28
43
  continue;
29
44
  }
30
- if (source.slice(i, i + 3) === '"""') {
31
- i += 3;
32
- while (i < source.length && source.slice(i, i + 3) !== '"""') {
33
- i++;
34
- }
35
- if (i < source.length) {
36
- i += 3;
37
- }
38
- result += " ";
39
- continue;
40
- }
41
45
  result += ch;
42
46
  i++;
43
47
  }
@@ -48,5 +52,98 @@ export function graphqlQueryContainsWriteOperation(query) {
48
52
  if (!normalized) {
49
53
  return false;
50
54
  }
51
- return /(?:^|[};]\s*)(mutation|subscription)\b/.test(normalized);
55
+ const operationTypePattern = /\s*(?:,\s*)?(query|mutation|subscription)\b/y;
56
+ let depth = 0;
57
+ let expectingOperationType = true;
58
+ for (let i = 0; i < normalized.length; i++) {
59
+ const ch = normalized[i];
60
+ if (ch === "{") {
61
+ depth++;
62
+ expectingOperationType = false;
63
+ continue;
64
+ }
65
+ if (ch === "}") {
66
+ depth = Math.max(0, depth - 1);
67
+ if (depth === 0) {
68
+ expectingOperationType = true;
69
+ }
70
+ continue;
71
+ }
72
+ if (depth === 0 && (ch === ";" || ch === ",")) {
73
+ expectingOperationType = true;
74
+ continue;
75
+ }
76
+ if (depth === 0 && expectingOperationType) {
77
+ operationTypePattern.lastIndex = i;
78
+ const operationMatch = operationTypePattern.exec(normalized);
79
+ if (operationMatch) {
80
+ const operationType = operationMatch[1];
81
+ expectingOperationType = false;
82
+ if (operationType === "mutation" || operationType === "subscription") {
83
+ return true;
84
+ }
85
+ i = operationTypePattern.lastIndex - 1;
86
+ }
87
+ }
88
+ }
89
+ return false;
90
+ }
91
+ const DELETE_FIELD_PATTERN = /delete|destroy|remove|prune|purge/i;
92
+ // Collects top-level selection field names (and aliases) of every mutation operation.
93
+ // Content inside parentheses (arguments) is skipped so argument names like
94
+ // removeSourceBranch do not count as delete fields. Returns null when a top-level
95
+ // fragment spread is present, since the spread could hide a delete field.
96
+ function extractTopLevelMutationFields(normalized) {
97
+ const fields = [];
98
+ const mutationRegex = /(?:^|[};]\s*)mutation\b[^({]*(?:\([^)]*\))?\s*\{/g;
99
+ let match;
100
+ while ((match = mutationRegex.exec(normalized)) !== null) {
101
+ let i = mutationRegex.lastIndex;
102
+ let braceDepth = 1;
103
+ let parenDepth = 0;
104
+ let current = "";
105
+ while (i < normalized.length && braceDepth > 0) {
106
+ const ch = normalized[i];
107
+ if (ch === "(")
108
+ parenDepth++;
109
+ else if (ch === ")")
110
+ parenDepth = Math.max(0, parenDepth - 1);
111
+ else if (ch === "{")
112
+ braceDepth++;
113
+ else if (ch === "}")
114
+ braceDepth--;
115
+ if (braceDepth === 1 && parenDepth === 0) {
116
+ if (normalized.startsWith("...", i)) {
117
+ return null;
118
+ }
119
+ if (/[A-Za-z0-9_]/.test(ch)) {
120
+ current += ch;
121
+ i++;
122
+ continue;
123
+ }
124
+ }
125
+ if (current) {
126
+ fields.push(current);
127
+ current = "";
128
+ }
129
+ i++;
130
+ }
131
+ if (current) {
132
+ fields.push(current);
133
+ }
134
+ }
135
+ return fields;
136
+ }
137
+ export function graphqlQueryContainsDeleteOperation(query) {
138
+ const normalized = stripGraphQLCommentsAndStrings(query).trim();
139
+ if (!normalized || !/(?:^|[};]\s*)mutation\b/.test(normalized)) {
140
+ return false;
141
+ }
142
+ const fields = extractTopLevelMutationFields(normalized);
143
+ if (fields === null || fields.length === 0) {
144
+ // Fragment spread at mutation top level, or a mutation whose selection set
145
+ // could not be located (exotic syntax): be conservative and treat as delete
146
+ return true;
147
+ }
148
+ return fields.some(field => DELETE_FIELD_PATTERN.test(field));
52
149
  }
@@ -0,0 +1,40 @@
1
+ const NPM_REGISTRY_LATEST_URL = "https://registry.npmjs.org/@zereight/mcp-gitlab/latest";
2
+ const RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
3
+ export function isNewerVersion(candidate, current) {
4
+ const parse = (version) => version.split(".").map(part => Number.parseInt(part, 10));
5
+ const a = parse(candidate);
6
+ const b = parse(current);
7
+ for (let i = 0; i < 3; i++) {
8
+ if (!Number.isFinite(a[i]) || !Number.isFinite(b[i]))
9
+ return false;
10
+ if (a[i] !== b[i])
11
+ return a[i] > b[i];
12
+ }
13
+ return false;
14
+ }
15
+ export async function fetchLatestVersion(fetchFn = fetch, timeoutMs = 3000) {
16
+ try {
17
+ const response = await fetchFn(NPM_REGISTRY_LATEST_URL, {
18
+ signal: AbortSignal.timeout(timeoutMs),
19
+ headers: { accept: "application/json" },
20
+ });
21
+ if (!response.ok)
22
+ return null;
23
+ const data = (await response.json());
24
+ return typeof data.version === "string" ? data.version : null;
25
+ }
26
+ catch {
27
+ // Fail silent: the update check must never break or delay server startup
28
+ // (offline machines, air-gapped networks, registry outages).
29
+ return null;
30
+ }
31
+ }
32
+ /** Returns the latest published version when it is newer than `currentVersion`, otherwise null. */
33
+ export async function checkForNewVersion(currentVersion, fetchFn = fetch) {
34
+ if (!RELEASE_VERSION_PATTERN.test(currentVersion))
35
+ return null;
36
+ const latest = await fetchLatestVersion(fetchFn);
37
+ if (!latest || !RELEASE_VERSION_PATTERN.test(latest))
38
+ return null;
39
+ return isNewerVersion(latest, currentVersion) ? latest : null;
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "2.1.29",
3
+ "version": "2.1.38",
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": [
@@ -52,18 +52,19 @@
52
52
  "changelog": "auto-changelog -p",
53
53
  "test": "npm run test:all",
54
54
  "test:all": "npm run build && npm run test:mock && npm run test:live",
55
- "test:mock": "node --import tsx/esm --test test/path-segment-encoding.test.ts && node --import tsx/esm --test test/remote-auth-simple-test.ts && node --import tsx/esm --test test/dynamic-api-url-allowlist.test.ts && node --import tsx/esm --test test/mcp-oauth-tests.ts && node --import tsx/esm --test test/test-oauth-proxy-rate-limit.ts && node --import tsx/esm --test test/streamable-http-static-token-auth.test.ts && node --import tsx/esm --test test/sse-auth-guard.test.ts && node --import tsx/esm --test test/streamable-http-concurrent-session.test.ts && node --import tsx/esm --test test/streamable-http-unauthenticated-discovery.test.ts && tsx test/oauth-tests.ts && tsx test/test-list-merge-requests.ts && tsx test/test-list-issues.ts && node --import tsx/esm --test test/test-create-repository.ts && node --import tsx/esm --test test/test-update-project.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-remote-downloads.ts && node --import tsx/esm --test test/test-deployment-tools.ts && node --import tsx/esm --test test/test-merge-request-approval-state-tools.ts && node --import tsx/esm --test test/test-search-code.ts && node --import tsx/esm --test test/test-tags.ts && node --import tsx/esm --test test/test-protected-branches.ts && node --import tsx/esm --test test/test-toolset-filtering.ts && node --import tsx/esm --test test/test-ci-lint.ts && node --import tsx/esm --test test/test-ci-catalog.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/proxy-client-ip.test.ts && node --import tsx/esm --test test/utils/forwarded-public-base-url.test.ts && node --import tsx/esm --test test/utils/graphql-query.test.ts && node --import tsx/esm --test test/utils/wiki-title.test.ts && node --import tsx/esm --test test/utils/merge-request-position.test.ts && node --import tsx/esm --test test/utils/redact-sensitive.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",
56
- "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",
55
+ "test:mock": "bash scripts/run-mock-tests.sh",
56
+ "test:stateless": "npm run build && node --import tsx/esm --test --experimental-test-isolation=none 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",
57
57
  "test:mcp-oauth": "npm run build && node --import tsx/esm --test test/mcp-oauth-tests.ts",
58
58
  "test:live": "node test/validate-api.js",
59
59
  "test:consumer-smoke": "bash scripts/consumer-install-smoke.sh",
60
- "test:remote-auth": "npm run build && node --import tsx/esm --test test/remote-auth-simple-test.ts",
60
+ "test:remote-auth": "npm run build && node --import tsx/esm --test --experimental-test-isolation=none test/remote-auth-simple-test.ts",
61
61
  "test:schema": "tsx test/schema-tests.ts && tsx test/test-json-schema.ts",
62
62
  "test:oauth": "tsx test/oauth-tests.ts",
63
63
  "test:list-merge-requests": "npm run build && tsx test/test-list-merge-requests.ts",
64
64
  "test:approvals": "npm run build && tsx test/test-merge-request-approvals.ts",
65
65
  "lint": "eslint . --ext .ts",
66
66
  "check:runtime-deps": "node scripts/check-runtime-deps.mjs",
67
+ "check:skill-sync": "tsx scripts/check-skill-sync.ts",
67
68
  "lint:fix": "eslint . --ext .ts --fix",
68
69
  "release": "bash scripts/release.sh",
69
70
  "release:mcp-registry": "bash scripts/publish_mcp_registry.sh",