@zereight/mcp-gitlab 1.0.55 → 1.0.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,8 +36,8 @@ When using with the Claude App, you need to set up your API key and URLs directl
36
36
  ```
37
37
 
38
38
  #### Docker
39
-
40
- ```json
39
+ - stdio
40
+ ```mcp.json
41
41
  {
42
42
  "mcpServers": {
43
43
  "GitLab communication server": {
@@ -73,6 +73,30 @@ When using with the Claude App, you need to set up your API key and URLs directl
73
73
  }
74
74
  ```
75
75
 
76
+ - sse
77
+ ```shell
78
+ docker run -i --rm \
79
+ -e GITLAB_PERSONAL_ACCESS_TOKEN=your_gitlab_token \
80
+ -e GITLAB_API_URL= "https://gitlab.com/api/v4"\
81
+ -e GITLAB_READ_ONLY_MODE=true \
82
+ -e USE_GITLAB_WIKI=true \
83
+ -e USE_MILESTONE=true \
84
+ -e USE_PIPELINE=true \
85
+ -e SSE=true \
86
+ -p 3333:3002 \
87
+ gitlab-mcp
88
+ ```
89
+
90
+ ```json
91
+ {
92
+ "mcpServers": {
93
+ "GitLab communication server": {
94
+ "url": "http://localhost:3333/sse"
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
76
100
  #### Docker Image Push
77
101
 
78
102
  ```shell
package/build/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
4
5
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
6
  import fetch from "node-fetch";
6
7
  import { SocksProxyAgent } from "socks-proxy-agent";
@@ -12,6 +13,7 @@ import { fileURLToPath } from "url";
12
13
  import { dirname } from "path";
13
14
  import fs from "fs";
14
15
  import path from "path";
16
+ import express from "express";
15
17
  // Add type imports for proxy agents
16
18
  import { Agent } from "http";
17
19
  import { Agent as HttpsAgent } from 'https';
@@ -21,7 +23,7 @@ import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLab
21
23
  GetPipelineJobOutputSchema, GitLabPipelineJobSchema,
22
24
  // Discussion Schemas
23
25
  GitLabDiscussionNoteSchema, // Added
24
- GitLabDiscussionSchema, UpdateMergeRequestNoteSchema, // Added
26
+ GitLabDiscussionSchema, PaginatedDiscussionsResponseSchema, UpdateMergeRequestNoteSchema, // Added
25
27
  CreateMergeRequestNoteSchema, // Added
26
28
  ListMergeRequestDiscussionsSchema, UpdateIssueNoteSchema, CreateIssueNoteSchema, ListMergeRequestsSchema, GitLabMilestonesSchema, ListProjectMilestonesSchema, GetProjectMilestoneSchema, CreateProjectMilestoneSchema, EditProjectMilestoneSchema, DeleteProjectMilestoneSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, PromoteProjectMilestoneSchema, GetMilestoneBurndownEventsSchema, GitLabCompareResultSchema, GetBranchDiffsSchema, } from "./schemas.js";
27
29
  /**
@@ -53,6 +55,7 @@ const GITLAB_READ_ONLY_MODE = process.env.GITLAB_READ_ONLY_MODE === "true";
53
55
  const USE_GITLAB_WIKI = process.env.USE_GITLAB_WIKI === "true";
54
56
  const USE_MILESTONE = process.env.USE_MILESTONE === "true";
55
57
  const USE_PIPELINE = process.env.USE_PIPELINE === "true";
58
+ const SSE = process.env.SSE === "true";
56
59
  // Add proxy configuration
57
60
  const HTTP_PROXY = process.env.HTTP_PROXY;
58
61
  const HTTPS_PROXY = process.env.HTTPS_PROXY;
@@ -692,12 +695,14 @@ async function listIssues(projectId, options = {}) {
692
695
  // Add all query parameters
693
696
  Object.entries(options).forEach(([key, value]) => {
694
697
  if (value !== undefined) {
695
- if (key === "label_name" && Array.isArray(value)) {
698
+ if (key === "labels" && Array.isArray(value)) {
696
699
  // Handle array of labels
697
- url.searchParams.append(key, value.join(","));
700
+ value.forEach(label => {
701
+ url.searchParams.append("labels[]", label.toString());
702
+ });
698
703
  }
699
704
  else {
700
- url.searchParams.append(key, value.toString());
705
+ url.searchParams.append("labels[]", value.toString());
701
706
  }
702
707
  }
703
708
  });
@@ -919,55 +924,66 @@ async function createMergeRequest(projectId, options) {
919
924
  return GitLabMergeRequestSchema.parse(data);
920
925
  }
921
926
  /**
922
- * List merge request discussion items
923
- * 병합 요청 토론 목록 조회
927
+ * Shared helper function for listing discussions
928
+ * 토론 목록 조회를 위한 공유 헬퍼 함수
924
929
  *
925
930
  * @param {string} projectId - The ID or URL-encoded path of the project
926
- * @param {number} mergeRequestIid - The IID of a merge request
927
- * @returns {Promise<GitLabDiscussion[]>} List of discussions
931
+ * @param {"issues" | "merge_requests"} resourceType - The type of resource (issues or merge_requests)
932
+ * @param {number} resourceIid - The IID of the issue or merge request
933
+ * @param {PaginationOptions} options - Pagination and sorting options
934
+ * @returns {Promise<PaginatedDiscussionsResponse>} Paginated list of discussions
928
935
  */
929
- async function listMergeRequestDiscussions(projectId, mergeRequestIid) {
936
+ async function listDiscussions(projectId, resourceType, resourceIid, options = {}) {
930
937
  projectId = decodeURIComponent(projectId); // Decode project ID
931
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions`);
938
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/${resourceType}/${resourceIid}/discussions`);
939
+ // Add query parameters for pagination and sorting
940
+ if (options.page) {
941
+ url.searchParams.append("page", options.page.toString());
942
+ }
943
+ if (options.per_page) {
944
+ url.searchParams.append("per_page", options.per_page.toString());
945
+ }
932
946
  const response = await fetch(url.toString(), {
933
947
  ...DEFAULT_FETCH_CONFIG,
934
948
  });
935
949
  await handleGitLabError(response);
936
- const data = await response.json();
937
- // Ensure the response is parsed as an array of discussions
938
- return z.array(GitLabDiscussionSchema).parse(data);
950
+ const discussions = await response.json();
951
+ // Extract pagination headers
952
+ const pagination = {
953
+ x_next_page: response.headers.get("x-next-page") ? parseInt(response.headers.get("x-next-page")) : null,
954
+ x_page: response.headers.get("x-page") ? parseInt(response.headers.get("x-page")) : undefined,
955
+ x_per_page: response.headers.get("x-per-page") ? parseInt(response.headers.get("x-per-page")) : undefined,
956
+ x_prev_page: response.headers.get("x-prev-page") ? parseInt(response.headers.get("x-prev-page")) : null,
957
+ x_total: response.headers.get("x-total") ? parseInt(response.headers.get("x-total")) : null,
958
+ x_total_pages: response.headers.get("x-total-pages") ? parseInt(response.headers.get("x-total-pages")) : null,
959
+ };
960
+ return PaginatedDiscussionsResponseSchema.parse({
961
+ items: discussions,
962
+ pagination: pagination,
963
+ });
964
+ }
965
+ /**
966
+ * List merge request discussion items
967
+ * 병합 요청 토론 목록 조회
968
+ *
969
+ * @param {string} projectId - The ID or URL-encoded path of the project
970
+ * @param {number} mergeRequestIid - The IID of a merge request
971
+ * @param {DiscussionPaginationOptions} options - Pagination and sorting options
972
+ * @returns {Promise<GitLabDiscussion[]>} List of discussions
973
+ */
974
+ async function listMergeRequestDiscussions(projectId, mergeRequestIid, options = {}) {
975
+ return listDiscussions(projectId, "merge_requests", mergeRequestIid, options);
939
976
  }
940
977
  /**
941
978
  * List discussions for an issue
942
979
  *
943
980
  * @param {string} projectId - The ID or URL-encoded path of the project
944
981
  * @param {number} issueIid - The internal ID of the project issue
945
- * @param {Object} options - Pagination and sorting options
982
+ * @param {DiscussionPaginationOptions} options - Pagination and sorting options
946
983
  * @returns {Promise<GitLabDiscussion[]>} List of issue discussions
947
984
  */
948
985
  async function listIssueDiscussions(projectId, issueIid, options = {}) {
949
- projectId = decodeURIComponent(projectId); // Decode project ID
950
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/discussions`);
951
- // Add query parameters for pagination and sorting
952
- if (options.page) {
953
- url.searchParams.append("page", options.page.toString());
954
- }
955
- if (options.per_page) {
956
- url.searchParams.append("per_page", options.per_page.toString());
957
- }
958
- if (options.sort) {
959
- url.searchParams.append("sort", options.sort);
960
- }
961
- if (options.order_by) {
962
- url.searchParams.append("order_by", options.order_by);
963
- }
964
- const response = await fetch(url.toString(), {
965
- ...DEFAULT_FETCH_CONFIG,
966
- });
967
- await handleGitLabError(response);
968
- const data = await response.json();
969
- // Parse the response as an array of discussions
970
- return z.array(GitLabDiscussionSchema).parse(data);
986
+ return listDiscussions(projectId, "issues", issueIid, options);
971
987
  }
972
988
  /**
973
989
  * Modify an existing merge request thread note
@@ -2457,7 +2473,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2457
2473
  }
2458
2474
  case "mr_discussions": {
2459
2475
  const args = ListMergeRequestDiscussionsSchema.parse(request.params.arguments);
2460
- const discussions = await listMergeRequestDiscussions(args.project_id, args.merge_request_iid);
2476
+ const { project_id, merge_request_iid, ...options } = args;
2477
+ const discussions = await listMergeRequestDiscussions(project_id, merge_request_iid, options);
2461
2478
  return {
2462
2479
  content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
2463
2480
  };
@@ -2966,8 +2983,36 @@ async function runServer() {
2966
2983
  console.error(`GitLab MCP Server v${SERVER_VERSION}`);
2967
2984
  console.error(`API URL: ${GITLAB_API_URL}`);
2968
2985
  console.error("========================");
2969
- const transport = new StdioServerTransport();
2970
- await server.connect(transport);
2986
+ if (!SSE) {
2987
+ const transport = new StdioServerTransport();
2988
+ await server.connect(transport);
2989
+ }
2990
+ else {
2991
+ const app = express();
2992
+ const transports = {};
2993
+ app.get("/sse", async (_, res) => {
2994
+ const transport = new SSEServerTransport("/messages", res);
2995
+ transports[transport.sessionId] = transport;
2996
+ res.on("close", () => {
2997
+ delete transports[transport.sessionId];
2998
+ });
2999
+ await server.connect(transport);
3000
+ });
3001
+ app.post("/messages", async (req, res) => {
3002
+ const sessionId = req.query.sessionId;
3003
+ const transport = transports[sessionId];
3004
+ if (transport) {
3005
+ await transport.handlePostMessage(req, res);
3006
+ }
3007
+ else {
3008
+ res.status(400).send("No transport found for sessionId");
3009
+ }
3010
+ });
3011
+ const PORT = process.env.PORT || 3002;
3012
+ app.listen(PORT, () => {
3013
+ console.log(`Server is running on port ${PORT}`);
3014
+ });
3015
+ }
2971
3016
  console.error("GitLab MCP Server running on stdio");
2972
3017
  }
2973
3018
  catch (error) {
package/build/schemas.js CHANGED
@@ -90,6 +90,12 @@ export const GitLabPipelineJobSchema = z.object({
90
90
  .optional(),
91
91
  web_url: z.string().optional(),
92
92
  });
93
+ // Shared base schema for various pagination options
94
+ // See https://docs.gitlab.com/api/rest/#pagination
95
+ export const PaginationOptionsSchema = z.object({
96
+ page: z.number().optional().describe("Page number for pagination (default: 1)"),
97
+ per_page: z.number().optional().describe("Number of items per page (max: 100, default: 20)"),
98
+ });
93
99
  // Schema for listing pipelines
94
100
  export const ListPipelinesSchema = z.object({
95
101
  project_id: z.string().describe("Project ID or URL-encoded path"),
@@ -130,9 +136,7 @@ export const ListPipelinesSchema = z.object({
130
136
  .optional()
131
137
  .describe("Order pipelines by"),
132
138
  sort: z.enum(["asc", "desc"]).optional().describe("Sort pipelines"),
133
- page: z.number().optional().describe("Page number for pagination"),
134
- per_page: z.number().optional().describe("Number of items per page (max 100)"),
135
- });
139
+ }).merge(PaginationOptionsSchema);
136
140
  // Schema for getting a specific pipeline
137
141
  export const GetPipelineSchema = z.object({
138
142
  project_id: z.string().describe("Project ID or URL-encoded path"),
@@ -147,9 +151,7 @@ export const ListPipelineJobsSchema = z.object({
147
151
  .optional()
148
152
  .describe("The scope of jobs to show"),
149
153
  include_retried: z.boolean().optional().describe("Whether to include retried jobs"),
150
- page: z.number().optional().describe("Page number for pagination"),
151
- per_page: z.number().optional().describe("Number of items per page (max 100)"),
152
- });
154
+ }).merge(PaginationOptionsSchema);
153
155
  // Schema for creating a new pipeline
154
156
  export const CreatePipelineSchema = z.object({
155
157
  project_id: z.string().describe("Project ID or URL-encoded path"),
@@ -287,7 +289,7 @@ export const GitLabRepositorySchema = z.object({
287
289
  container_registry_access_level: z.string().optional(),
288
290
  issues_enabled: z.boolean().optional(),
289
291
  merge_requests_enabled: z.boolean().optional(),
290
- merge_requests_template: z.string().optional(),
292
+ merge_requests_template: z.string().nullable().optional(),
291
293
  wiki_enabled: z.boolean().optional(),
292
294
  jobs_enabled: z.boolean().optional(),
293
295
  snippets_enabled: z.boolean().optional(),
@@ -643,15 +645,39 @@ export const GitLabDiscussionNoteSchema = z.object({
643
645
  })
644
646
  .optional(),
645
647
  });
648
+ // Reusable pagination schema for GitLab API responses.
649
+ // See https://docs.gitlab.com/api/rest/#pagination
650
+ export const GitLabPaginationSchema = z.object({
651
+ x_next_page: z.number().nullable().optional(),
652
+ x_page: z.number().optional(),
653
+ x_per_page: z.number().optional(),
654
+ x_prev_page: z.number().nullable().optional(),
655
+ x_total: z.number().nullable().optional(),
656
+ x_total_pages: z.number().nullable().optional(),
657
+ });
658
+ // Base paginated response schema that can be extended.
659
+ // See https://docs.gitlab.com/api/rest/#pagination
660
+ export const PaginatedResponseSchema = z.object({
661
+ pagination: GitLabPaginationSchema.optional(),
662
+ });
646
663
  export const GitLabDiscussionSchema = z.object({
647
664
  id: z.string(),
648
665
  individual_note: z.boolean(),
649
666
  notes: z.array(GitLabDiscussionNoteSchema),
650
667
  });
668
+ // Create a schema for paginated discussions response
669
+ export const PaginatedDiscussionsResponseSchema = z.object({
670
+ items: z.array(GitLabDiscussionSchema),
671
+ pagination: GitLabPaginationSchema,
672
+ });
673
+ export const ListIssueDiscussionsSchema = z.object({
674
+ project_id: z.string().describe("Project ID or URL-encoded path"),
675
+ issue_iid: z.number().describe("The internal ID of the project issue"),
676
+ }).merge(PaginationOptionsSchema);
651
677
  // Input schema for listing merge request discussions
652
678
  export const ListMergeRequestDiscussionsSchema = ProjectParamsSchema.extend({
653
679
  merge_request_iid: z.number().describe("The IID of a merge request"),
654
- });
680
+ }).merge(PaginationOptionsSchema);
655
681
  // Input schema for updating a merge request discussion note
656
682
  export const UpdateMergeRequestNoteSchema = ProjectParamsSchema.extend({
657
683
  merge_request_iid: z.number().describe("The IID of a merge request"),
@@ -699,9 +725,7 @@ export const CreateOrUpdateFileSchema = ProjectParamsSchema.extend({
699
725
  });
700
726
  export const SearchRepositoriesSchema = z.object({
701
727
  search: z.string().describe("Search query"), // Changed from query to match GitLab API
702
- page: z.number().optional().describe("Page number for pagination (default: 1)"),
703
- per_page: z.number().optional().describe("Number of results per page (default: 20)"),
704
- });
728
+ }).merge(PaginationOptionsSchema);
705
729
  export const CreateRepositorySchema = z.object({
706
730
  name: z.string().describe("Repository name"),
707
731
  description: z.string().optional().describe("Repository description"),
@@ -809,10 +833,10 @@ export const ListIssuesSchema = z.object({
809
833
  created_after: z.string().optional().describe("Return issues created after the given time"),
810
834
  created_before: z.string().optional().describe("Return issues created before the given time"),
811
835
  due_date: z.string().optional().describe("Return issues that have the due date"),
812
- label_name: z.array(z.string()).optional().describe("Array of label names"),
836
+ labels: z.array(z.string()).optional().describe("Array of label names"),
813
837
  milestone: z.string().optional().describe("Milestone title"),
814
838
  scope: z
815
- .enum(["created-by-me", "assigned-to-me", "all"])
839
+ .enum(["created_by_me", "assigned_to_me", "all"])
816
840
  .optional()
817
841
  .describe("Return issues from a specific scope"),
818
842
  search: z.string().optional().describe("Search for specific terms"),
@@ -823,9 +847,7 @@ export const ListIssuesSchema = z.object({
823
847
  updated_after: z.string().optional().describe("Return issues updated after the given time"),
824
848
  updated_before: z.string().optional().describe("Return issues updated before the given time"),
825
849
  with_labels_details: z.boolean().optional().describe("Return more details for each label"),
826
- page: z.number().optional().describe("Page number for pagination"),
827
- per_page: z.number().optional().describe("Number of items per page"),
828
- });
850
+ }).merge(PaginationOptionsSchema);
829
851
  // Merge Requests API operation schemas
830
852
  export const ListMergeRequestsSchema = z.object({
831
853
  project_id: z.string().describe("Project ID or URL-encoded path"),
@@ -895,9 +917,7 @@ export const ListMergeRequestsSchema = z.object({
895
917
  .describe("Return merge requests from a specific source branch"),
896
918
  wip: z.enum(["yes", "no"]).optional().describe("Filter merge requests against their wip status"),
897
919
  with_labels_details: z.boolean().optional().describe("Return more details for each label"),
898
- page: z.number().optional().describe("Page number for pagination"),
899
- per_page: z.number().optional().describe("Number of items per page"),
900
- });
920
+ }).merge(PaginationOptionsSchema);
901
921
  export const GetIssueSchema = z.object({
902
922
  project_id: z.string().describe("Project ID or URL-encoded path"),
903
923
  issue_iid: z.number().describe("The internal ID of the project issue"),
@@ -930,20 +950,6 @@ export const ListIssueLinksSchema = z.object({
930
950
  project_id: z.string().describe("Project ID or URL-encoded path"),
931
951
  issue_iid: z.number().describe("The internal ID of a project's issue"),
932
952
  });
933
- export const ListIssueDiscussionsSchema = z.object({
934
- project_id: z.string().describe("Project ID or URL-encoded path"),
935
- issue_iid: z.number().describe("The internal ID of the project issue"),
936
- page: z.number().optional().describe("Page number for pagination"),
937
- per_page: z.number().optional().describe("Number of items per page"),
938
- sort: z
939
- .enum(["asc", "desc"])
940
- .optional()
941
- .describe("Return issue discussions sorted in ascending or descending order"),
942
- order_by: z
943
- .enum(["created_at", "updated_at"])
944
- .optional()
945
- .describe("Return issue discussions ordered by created_at or updated_at fields"),
946
- });
947
953
  export const GetIssueLinkSchema = z.object({
948
954
  project_id: z.string().describe("Project ID or URL-encoded path"),
949
955
  issue_iid: z.number().describe("The internal ID of a project's issue"),
@@ -967,10 +973,8 @@ export const DeleteIssueLinkSchema = z.object({
967
973
  // Namespace API operation schemas
968
974
  export const ListNamespacesSchema = z.object({
969
975
  search: z.string().optional().describe("Search term for namespaces"),
970
- page: z.number().optional().describe("Page number for pagination"),
971
- per_page: z.number().optional().describe("Number of items per page"),
972
976
  owned: z.boolean().optional().describe("Filter for namespaces owned by current user"),
973
- });
977
+ }).merge(PaginationOptionsSchema);
974
978
  export const GetNamespaceSchema = z.object({
975
979
  namespace_id: z.string().describe("Namespace ID or full path"),
976
980
  });
@@ -983,8 +987,6 @@ export const GetProjectSchema = z.object({
983
987
  });
984
988
  export const ListProjectsSchema = z.object({
985
989
  search: z.string().optional().describe("Search term for projects"),
986
- page: z.number().optional().describe("Page number for pagination"),
987
- per_page: z.number().optional().describe("Number of items per page"),
988
990
  search_namespaces: z.boolean().optional().describe("Needs to be true if search is full path"),
989
991
  owned: z.boolean().optional().describe("Filter for projects owned by current user"),
990
992
  membership: z.boolean().optional().describe("Filter for projects where current user is a member"),
@@ -1011,7 +1013,7 @@ export const ListProjectsSchema = z.object({
1011
1013
  .optional()
1012
1014
  .describe("Filter projects with merge requests feature enabled"),
1013
1015
  min_access_level: z.number().optional().describe("Filter by minimum access level"),
1014
- });
1016
+ }).merge(PaginationOptionsSchema);
1015
1017
  // Label operation schemas
1016
1018
  export const ListLabelsSchema = z.object({
1017
1019
  project_id: z.string().describe("Project ID or URL-encoded path"),
@@ -1061,8 +1063,6 @@ export const ListGroupProjectsSchema = z.object({
1061
1063
  .optional()
1062
1064
  .describe("Field to sort by"),
1063
1065
  sort: z.enum(["asc", "desc"]).optional().describe("Sort direction"),
1064
- page: z.number().optional().describe("Page number"),
1065
- per_page: z.number().optional().describe("Number of results per page"),
1066
1066
  archived: z.boolean().optional().describe("Filter for archived projects"),
1067
1067
  visibility: z
1068
1068
  .enum(["public", "internal", "private"])
@@ -1082,13 +1082,11 @@ export const ListGroupProjectsSchema = z.object({
1082
1082
  statistics: z.boolean().optional().describe("Include project statistics"),
1083
1083
  with_custom_attributes: z.boolean().optional().describe("Include custom attributes"),
1084
1084
  with_security_reports: z.boolean().optional().describe("Include security reports"),
1085
- });
1085
+ }).merge(PaginationOptionsSchema);
1086
1086
  // Add wiki operation schemas
1087
1087
  export const ListWikiPagesSchema = z.object({
1088
1088
  project_id: z.string().describe("Project ID or URL-encoded path"),
1089
- page: z.number().optional().describe("Page number for pagination"),
1090
- per_page: z.number().optional().describe("Number of items per page"),
1091
- });
1089
+ }).merge(PaginationOptionsSchema);
1092
1090
  export const GetWikiPageSchema = z.object({
1093
1091
  project_id: z.string().describe("Project ID or URL-encoded path"),
1094
1092
  slug: z.string().describe("URL-encoded slug of the wiki page"),
@@ -1166,9 +1164,7 @@ export const ListProjectMilestonesSchema = ProjectParamsSchema.extend({
1166
1164
  .string()
1167
1165
  .optional()
1168
1166
  .describe("Return milestones updated after the specified date (ISO 8601 format)"),
1169
- page: z.number().optional().describe("Page number for pagination"),
1170
- per_page: z.number().optional().describe("Number of items per page (max 100)"),
1171
- });
1167
+ }).merge(PaginationOptionsSchema);
1172
1168
  // Schema for getting a single milestone
1173
1169
  export const GetProjectMilestoneSchema = ProjectParamsSchema.extend({
1174
1170
  milestone_id: z.number().describe("The ID of a project milestone"),
@@ -1196,14 +1192,8 @@ export const DeleteProjectMilestoneSchema = GetProjectMilestoneSchema;
1196
1192
  // Schema for getting issues assigned to a milestone
1197
1193
  export const GetMilestoneIssuesSchema = GetProjectMilestoneSchema;
1198
1194
  // Schema for getting merge requests assigned to a milestone
1199
- export const GetMilestoneMergeRequestsSchema = GetProjectMilestoneSchema.extend({
1200
- page: z.number().optional().describe("Page number for pagination"),
1201
- per_page: z.number().optional().describe("Number of items per page (max 100)"),
1202
- });
1195
+ export const GetMilestoneMergeRequestsSchema = GetProjectMilestoneSchema.merge(PaginationOptionsSchema);
1203
1196
  // Schema for promoting a project milestone to a group milestone
1204
1197
  export const PromoteProjectMilestoneSchema = GetProjectMilestoneSchema;
1205
1198
  // Schema for getting burndown chart events for a milestone
1206
- export const GetMilestoneBurndownEventsSchema = GetProjectMilestoneSchema.extend({
1207
- page: z.number().optional().describe("Page number for pagination"),
1208
- per_page: z.number().optional().describe("Number of items per page (max 100)"),
1209
- });
1199
+ export const GetMilestoneBurndownEventsSchema = GetProjectMilestoneSchema.merge(PaginationOptionsSchema);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",
@@ -30,8 +30,9 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@modelcontextprotocol/sdk": "1.8.0",
33
- "form-data": "^4.0.0",
34
33
  "@types/node-fetch": "^2.6.12",
34
+ "express": "^5.1.0",
35
+ "form-data": "^4.0.0",
35
36
  "http-proxy-agent": "^7.0.2",
36
37
  "https-proxy-agent": "^7.0.6",
37
38
  "node-fetch": "^3.3.2",
@@ -39,13 +40,14 @@
39
40
  "zod-to-json-schema": "^3.23.5"
40
41
  },
41
42
  "devDependencies": {
43
+ "@types/express": "^5.0.2",
42
44
  "@types/node": "^22.13.10",
43
- "typescript": "^5.8.2",
44
- "zod": "^3.24.2",
45
45
  "@typescript-eslint/eslint-plugin": "^8.21.0",
46
46
  "@typescript-eslint/parser": "^8.21.0",
47
47
  "eslint": "^9.18.0",
48
48
  "prettier": "^3.4.2",
49
- "ts-node": "^10.9.2"
49
+ "ts-node": "^10.9.2",
50
+ "typescript": "^5.8.2",
51
+ "zod": "^3.24.2"
50
52
  }
51
53
  }