@zereight/mcp-gitlab 1.0.56 → 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/build/index.js CHANGED
@@ -23,7 +23,7 @@ import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLab
23
23
  GetPipelineJobOutputSchema, GitLabPipelineJobSchema,
24
24
  // Discussion Schemas
25
25
  GitLabDiscussionNoteSchema, // Added
26
- GitLabDiscussionSchema, UpdateMergeRequestNoteSchema, // Added
26
+ GitLabDiscussionSchema, PaginatedDiscussionsResponseSchema, UpdateMergeRequestNoteSchema, // Added
27
27
  CreateMergeRequestNoteSchema, // Added
28
28
  ListMergeRequestDiscussionsSchema, UpdateIssueNoteSchema, CreateIssueNoteSchema, ListMergeRequestsSchema, GitLabMilestonesSchema, ListProjectMilestonesSchema, GetProjectMilestoneSchema, CreateProjectMilestoneSchema, EditProjectMilestoneSchema, DeleteProjectMilestoneSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, PromoteProjectMilestoneSchema, GetMilestoneBurndownEventsSchema, GitLabCompareResultSchema, GetBranchDiffsSchema, } from "./schemas.js";
29
29
  /**
@@ -924,55 +924,66 @@ async function createMergeRequest(projectId, options) {
924
924
  return GitLabMergeRequestSchema.parse(data);
925
925
  }
926
926
  /**
927
- * List merge request discussion items
928
- * 병합 요청 토론 목록 조회
927
+ * Shared helper function for listing discussions
928
+ * 토론 목록 조회를 위한 공유 헬퍼 함수
929
929
  *
930
930
  * @param {string} projectId - The ID or URL-encoded path of the project
931
- * @param {number} mergeRequestIid - The IID of a merge request
932
- * @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
933
935
  */
934
- async function listMergeRequestDiscussions(projectId, mergeRequestIid) {
936
+ async function listDiscussions(projectId, resourceType, resourceIid, options = {}) {
935
937
  projectId = decodeURIComponent(projectId); // Decode project ID
936
- 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
+ }
937
946
  const response = await fetch(url.toString(), {
938
947
  ...DEFAULT_FETCH_CONFIG,
939
948
  });
940
949
  await handleGitLabError(response);
941
- const data = await response.json();
942
- // Ensure the response is parsed as an array of discussions
943
- 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);
944
976
  }
945
977
  /**
946
978
  * List discussions for an issue
947
979
  *
948
980
  * @param {string} projectId - The ID or URL-encoded path of the project
949
981
  * @param {number} issueIid - The internal ID of the project issue
950
- * @param {Object} options - Pagination and sorting options
982
+ * @param {DiscussionPaginationOptions} options - Pagination and sorting options
951
983
  * @returns {Promise<GitLabDiscussion[]>} List of issue discussions
952
984
  */
953
985
  async function listIssueDiscussions(projectId, issueIid, options = {}) {
954
- projectId = decodeURIComponent(projectId); // Decode project ID
955
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/discussions`);
956
- // Add query parameters for pagination and sorting
957
- if (options.page) {
958
- url.searchParams.append("page", options.page.toString());
959
- }
960
- if (options.per_page) {
961
- url.searchParams.append("per_page", options.per_page.toString());
962
- }
963
- if (options.sort) {
964
- url.searchParams.append("sort", options.sort);
965
- }
966
- if (options.order_by) {
967
- url.searchParams.append("order_by", options.order_by);
968
- }
969
- const response = await fetch(url.toString(), {
970
- ...DEFAULT_FETCH_CONFIG,
971
- });
972
- await handleGitLabError(response);
973
- const data = await response.json();
974
- // Parse the response as an array of discussions
975
- return z.array(GitLabDiscussionSchema).parse(data);
986
+ return listDiscussions(projectId, "issues", issueIid, options);
976
987
  }
977
988
  /**
978
989
  * Modify an existing merge request thread note
@@ -2462,7 +2473,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2462
2473
  }
2463
2474
  case "mr_discussions": {
2464
2475
  const args = ListMergeRequestDiscussionsSchema.parse(request.params.arguments);
2465
- 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);
2466
2478
  return {
2467
2479
  content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
2468
2480
  };
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"),
@@ -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.56",
3
+ "version": "1.0.57",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",