@zereight/mcp-gitlab 1.0.49 → 1.0.51

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
@@ -26,7 +26,8 @@ When using with the Claude App, you need to set up your API key and URLs directl
26
26
  "GITLAB_PERSONAL_ACCESS_TOKEN": "your_gitlab_token",
27
27
  "GITLAB_API_URL": "your_gitlab_api_url",
28
28
  "GITLAB_READ_ONLY_MODE": "false",
29
- "USE_GITLAB_WIKI": "true"
29
+ "USE_GITLAB_WIKI": "false",
30
+ "USE_MILESTONE": "false"
30
31
  }
31
32
  }
32
33
  }
@@ -52,13 +53,16 @@ When using with the Claude App, you need to set up your API key and URLs directl
52
53
  "GITLAB_READ_ONLY_MODE",
53
54
  "-e",
54
55
  "USE_GITLAB_WIKI",
56
+ "-e",
57
+ "USE_MILESTONE",
55
58
  "iwakitakuma/gitlab-mcp"
56
59
  ],
57
60
  "env": {
58
61
  "GITLAB_PERSONAL_ACCESS_TOKEN": "your_gitlab_token",
59
62
  "GITLAB_API_URL": "https://gitlab.com/api/v4", // Optional, for self-hosted GitLab
60
63
  "GITLAB_READ_ONLY_MODE": "false",
61
- "USE_GITLAB_WIKI": "true"
64
+ "USE_GITLAB_WIKI": "true",
65
+ "USE_MILESTONE": "true"
62
66
  }
63
67
  }
64
68
  }
@@ -77,10 +81,12 @@ $ sh scripts/image_push.sh docker_user_name
77
81
  - `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`)
78
82
  - `GITLAB_READ_ONLY_MODE`: When set to 'true', restricts the server to only expose read-only operations. Useful for enhanced security or when write access is not needed. Also useful for using with Cursor and it's 40 tool limit.
79
83
  - `USE_GITLAB_WIKI`: When set to 'true', enables the wiki-related tools (list_wiki_pages, get_wiki_page, create_wiki_page, update_wiki_page, delete_wiki_page). By default, wiki features are disabled.
84
+ - `USE_MILESTONE`: When set to 'true', enables the milestone-related tools (list_milestones, get_milestone, create_milestone, edit_milestone, delete_milestone, get_milestone_issue, get_milestone_merge_requests, promote_milestone, get_milestone_burndown_events). By default, milestone features are disabled.
80
85
 
81
86
  ## Tools 🛠️
82
87
 
83
88
  +<!-- TOOLS-START -->
89
+
84
90
  1. `create_or_update_file` - Create or update a single file in a GitLab project
85
91
  2. `search_repositories` - Search for GitLab projects
86
92
  3. `create_repository` - Create a new GitLab project
@@ -132,4 +138,13 @@ $ sh scripts/image_push.sh docker_user_name
132
138
  49. `get_pipeline_job` - Get details of a GitLab pipeline job number
133
139
  50. `get_pipeline_job_output` - Get the output/trace of a GitLab pipeline job number
134
140
  51. `list_merge_requests` - List merge requests in a GitLab project with filtering options
141
+ 52. `list_milestones` - List milestones in a GitLab project with filtering options
142
+ 53. `get_milestone` - Get details of a specific milestone
143
+ 54. `create_milestone` - Create a new milestone in a GitLab project
144
+ 55. `edit_milestone ` - Edit an existing milestone in a GitLab project
145
+ 56. `delete_milestone` - Delete a milestone from a GitLab project
146
+ 57. `get_milestone_issue` - Get issues associated with a specific milestone
147
+ 58. `get_milestone_merge_requests` - Get merge requests associated with a specific milestone
148
+ 59. `promote_milestone` - Promote a milestone to the next stage
149
+ 60. `get_milestone_burndown_events` - Get burndown events for a specific milestone
135
150
  <!-- TOOLS-END -->
package/build/index.js CHANGED
@@ -1,7 +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 { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
5
  import fetch from "node-fetch";
6
6
  import { SocksProxyAgent } from "socks-proxy-agent";
7
7
  import { HttpsProxyAgent } from "https-proxy-agent";
@@ -20,7 +20,7 @@ GetPipelineJobOutputSchema, GitLabPipelineJobSchema,
20
20
  GitLabDiscussionNoteSchema, // Added
21
21
  GitLabDiscussionSchema, UpdateMergeRequestNoteSchema, // Added
22
22
  CreateMergeRequestNoteSchema, // Added
23
- ListMergeRequestDiscussionsSchema, UpdateIssueNoteSchema, CreateIssueNoteSchema, ListMergeRequestsSchema, } from "./schemas.js";
23
+ ListMergeRequestDiscussionsSchema, UpdateIssueNoteSchema, CreateIssueNoteSchema, ListMergeRequestsSchema, GitLabMilestonesSchema, ListProjectMilestonesSchema, GetProjectMilestoneSchema, CreateProjectMilestoneSchema, EditProjectMilestoneSchema, DeleteProjectMilestoneSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, PromoteProjectMilestoneSchema, GetMilestoneBurndownEventsSchema, } from "./schemas.js";
24
24
  /**
25
25
  * Read version from package.json
26
26
  */
@@ -48,6 +48,7 @@ const server = new Server({
48
48
  const GITLAB_PERSONAL_ACCESS_TOKEN = process.env.GITLAB_PERSONAL_ACCESS_TOKEN;
49
49
  const GITLAB_READ_ONLY_MODE = process.env.GITLAB_READ_ONLY_MODE === "true";
50
50
  const USE_GITLAB_WIKI = process.env.USE_GITLAB_WIKI === "true";
51
+ const USE_MILESTONE = process.env.USE_MILESTONE === "true";
51
52
  // Add proxy configuration
52
53
  const HTTP_PROXY = process.env.HTTP_PROXY;
53
54
  const HTTPS_PROXY = process.env.HTTPS_PROXY;
@@ -343,6 +344,51 @@ const allTools = [
343
344
  description: "List merge requests in a GitLab project with filtering options",
344
345
  inputSchema: zodToJsonSchema(ListMergeRequestsSchema),
345
346
  },
347
+ {
348
+ name: "list_milestones",
349
+ description: "List milestones in a GitLab project with filtering options",
350
+ inputSchema: zodToJsonSchema(ListProjectMilestonesSchema),
351
+ },
352
+ {
353
+ name: "get_milestone",
354
+ description: "Get details of a specific milestone",
355
+ inputSchema: zodToJsonSchema(GetProjectMilestoneSchema),
356
+ },
357
+ {
358
+ name: "create_milestone",
359
+ description: "Create a new milestone in a GitLab project",
360
+ inputSchema: zodToJsonSchema(CreateProjectMilestoneSchema),
361
+ },
362
+ {
363
+ name: "edit_milestone",
364
+ description: "Edit an existing milestone in a GitLab project",
365
+ inputSchema: zodToJsonSchema(EditProjectMilestoneSchema),
366
+ },
367
+ {
368
+ name: "delete_milestone",
369
+ description: "Delete a milestone from a GitLab project",
370
+ inputSchema: zodToJsonSchema(DeleteProjectMilestoneSchema),
371
+ },
372
+ {
373
+ name: "get_milestone_issue",
374
+ description: "Get issues associated with a specific milestone",
375
+ inputSchema: zodToJsonSchema(GetMilestoneIssuesSchema),
376
+ },
377
+ {
378
+ name: "get_milestone_merge_requests",
379
+ description: "Get merge requests associated with a specific milestone",
380
+ inputSchema: zodToJsonSchema(GetMilestoneMergeRequestsSchema),
381
+ },
382
+ {
383
+ name: "promote_milestone",
384
+ description: "Promote a milestone to the next stage",
385
+ inputSchema: zodToJsonSchema(PromoteProjectMilestoneSchema),
386
+ },
387
+ {
388
+ name: "get_milestone_burndown_events",
389
+ description: "Get burndown events for a specific milestone",
390
+ inputSchema: zodToJsonSchema(GetMilestoneBurndownEventsSchema),
391
+ },
346
392
  ];
347
393
  // Define which tools are read-only
348
394
  const readOnlyTools = [
@@ -371,6 +417,13 @@ const readOnlyTools = [
371
417
  "get_label",
372
418
  "list_group_projects",
373
419
  "get_repository_tree",
420
+ "list_milestones",
421
+ "get_milestone",
422
+ "get_milestone_issue",
423
+ "get_milestone_merge_requests",
424
+ "get_milestone_burndown_events",
425
+ "list_wiki_pages",
426
+ "get_wiki_page",
374
427
  ];
375
428
  // Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
376
429
  const wikiToolNames = [
@@ -381,6 +434,18 @@ const wikiToolNames = [
381
434
  "delete_wiki_page",
382
435
  "upload_wiki_attachment",
383
436
  ];
437
+ // Define which tools are related to milestones and can be toggled by USE_MILESTONE
438
+ const milestoneToolNames = [
439
+ "list_milestones",
440
+ "get_milestone",
441
+ "create_milestone",
442
+ "edit_milestone",
443
+ "delete_milestone",
444
+ "get_milestone_issue",
445
+ "get_milestone_merge_requests",
446
+ "promote_milestone",
447
+ "get_milestone_burndown_events",
448
+ ];
384
449
  /**
385
450
  * Smart URL handling for GitLab API
386
451
  *
@@ -394,8 +459,7 @@ function normalizeGitLabApiUrl(url) {
394
459
  // Remove trailing slash if present
395
460
  let normalizedUrl = url.endsWith("/") ? url.slice(0, -1) : url;
396
461
  // Check if URL already has /api/v4
397
- if (!normalizedUrl.endsWith("/api/v4") &&
398
- !normalizedUrl.endsWith("/api/v4/")) {
462
+ if (!normalizedUrl.endsWith("/api/v4") && !normalizedUrl.endsWith("/api/v4/")) {
399
463
  // Append /api/v4 if not already present
400
464
  normalizedUrl = `${normalizedUrl}/api/v4`;
401
465
  }
@@ -418,8 +482,7 @@ async function handleGitLabError(response) {
418
482
  if (!response.ok) {
419
483
  const errorBody = await response.text();
420
484
  // Check specifically for Rate Limit error
421
- if (response.status === 403 &&
422
- errorBody.includes("User API Key Rate limit exceeded")) {
485
+ if (response.status === 403 && errorBody.includes("User API Key Rate limit exceeded")) {
423
486
  console.error("GitLab API Rate Limit Exceeded:", errorBody);
424
487
  console.log("User API Key Rate limit exceeded. Please try again later.");
425
488
  throw new Error(`GitLab API Rate Limit Exceeded: ${errorBody}`);
@@ -1045,7 +1108,7 @@ async function createTree(projectId, files, ref) {
1045
1108
  ...DEFAULT_FETCH_CONFIG,
1046
1109
  method: "POST",
1047
1110
  body: JSON.stringify({
1048
- files: files.map((file) => ({
1111
+ files: files.map(file => ({
1049
1112
  file_path: file.path,
1050
1113
  content: file.content,
1051
1114
  encoding: "text",
@@ -1082,7 +1145,7 @@ async function createCommit(projectId, message, branch, actions) {
1082
1145
  body: JSON.stringify({
1083
1146
  branch,
1084
1147
  commit_message: message,
1085
- actions: actions.map((action) => ({
1148
+ actions: actions.map(action => ({
1086
1149
  action: "create",
1087
1150
  file_path: action.path,
1088
1151
  content: action.content,
@@ -1830,21 +1893,184 @@ async function getRepositoryTree(options) {
1830
1893
  const data = await response.json();
1831
1894
  return z.array(GitLabTreeItemSchema).parse(data);
1832
1895
  }
1896
+ /**
1897
+ * List project milestones in a GitLab project
1898
+ * @param {string} projectId - The ID or URL-encoded path of the project
1899
+ * @param {Object} options - Options for listing milestones
1900
+ * @returns {Promise<GitLabMilestones[]>} List of milestones
1901
+ */
1902
+ async function listProjectMilestones(projectId, options) {
1903
+ projectId = decodeURIComponent(projectId);
1904
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones`);
1905
+ Object.entries(options).forEach(([key, value]) => {
1906
+ if (value !== undefined) {
1907
+ if (key === "iids" && Array.isArray(value) && value.length > 0) {
1908
+ value.forEach(iid => {
1909
+ url.searchParams.append("iids[]", iid.toString());
1910
+ });
1911
+ }
1912
+ else if (value !== undefined) {
1913
+ url.searchParams.append(key, value.toString());
1914
+ }
1915
+ }
1916
+ });
1917
+ const response = await fetch(url.toString(), {
1918
+ ...DEFAULT_FETCH_CONFIG,
1919
+ });
1920
+ await handleGitLabError(response);
1921
+ const data = await response.json();
1922
+ return z.array(GitLabMilestonesSchema).parse(data);
1923
+ }
1924
+ /**
1925
+ * Get a single milestone in a GitLab project
1926
+ * @param {string} projectId - The ID or URL-encoded path of the project
1927
+ * @param {number} milestoneId - The ID of the milestone
1928
+ * @returns {Promise<GitLabMilestones>} Milestone details
1929
+ */
1930
+ async function getProjectMilestone(projectId, milestoneId) {
1931
+ projectId = decodeURIComponent(projectId);
1932
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}`);
1933
+ const response = await fetch(url.toString(), {
1934
+ ...DEFAULT_FETCH_CONFIG,
1935
+ });
1936
+ await handleGitLabError(response);
1937
+ const data = await response.json();
1938
+ return GitLabMilestonesSchema.parse(data);
1939
+ }
1940
+ /**
1941
+ * Create a new milestone in a GitLab project
1942
+ * @param {string} projectId - The ID or URL-encoded path of the project
1943
+ * @param {Object} options - Options for creating a milestone
1944
+ * @returns {Promise<GitLabMilestones>} Created milestone
1945
+ */
1946
+ async function createProjectMilestone(projectId, options) {
1947
+ projectId = decodeURIComponent(projectId);
1948
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones`);
1949
+ const response = await fetch(url.toString(), {
1950
+ ...DEFAULT_FETCH_CONFIG,
1951
+ method: "POST",
1952
+ body: JSON.stringify(options),
1953
+ });
1954
+ await handleGitLabError(response);
1955
+ const data = await response.json();
1956
+ return GitLabMilestonesSchema.parse(data);
1957
+ }
1958
+ /**
1959
+ * Edit an existing milestone in a GitLab project
1960
+ * @param {string} projectId - The ID or URL-encoded path of the project
1961
+ * @param {number} milestoneId - The ID of the milestone
1962
+ * @param {Object} options - Options for editing a milestone
1963
+ * @returns {Promise<GitLabMilestones>} Updated milestone
1964
+ */
1965
+ async function editProjectMilestone(projectId, milestoneId, options) {
1966
+ projectId = decodeURIComponent(projectId);
1967
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}`);
1968
+ const response = await fetch(url.toString(), {
1969
+ ...DEFAULT_FETCH_CONFIG,
1970
+ method: "PUT",
1971
+ body: JSON.stringify(options),
1972
+ });
1973
+ await handleGitLabError(response);
1974
+ const data = await response.json();
1975
+ return GitLabMilestonesSchema.parse(data);
1976
+ }
1977
+ /**
1978
+ * Delete a milestone from a GitLab project
1979
+ * @param {string} projectId - The ID or URL-encoded path of the project
1980
+ * @param {number} milestoneId - The ID of the milestone
1981
+ * @returns {Promise<void>}
1982
+ */
1983
+ async function deleteProjectMilestone(projectId, milestoneId) {
1984
+ projectId = decodeURIComponent(projectId);
1985
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}`);
1986
+ const response = await fetch(url.toString(), {
1987
+ ...DEFAULT_FETCH_CONFIG,
1988
+ method: "DELETE",
1989
+ });
1990
+ await handleGitLabError(response);
1991
+ }
1992
+ /**
1993
+ * Get all issues assigned to a single milestone
1994
+ * @param {string} projectId - The ID or URL-encoded path of the project
1995
+ * @param {number} milestoneId - The ID of the milestone
1996
+ * @returns {Promise<GitLabIssue[]>} List of issues
1997
+ */
1998
+ async function getMilestoneIssues(projectId, milestoneId) {
1999
+ projectId = decodeURIComponent(projectId);
2000
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/issues`);
2001
+ const response = await fetch(url.toString(), {
2002
+ ...DEFAULT_FETCH_CONFIG,
2003
+ });
2004
+ await handleGitLabError(response);
2005
+ const data = await response.json();
2006
+ return z.array(GitLabIssueSchema).parse(data);
2007
+ }
2008
+ /**
2009
+ * Get all merge requests assigned to a single milestone
2010
+ * @param {string} projectId - The ID or URL-encoded path of the project
2011
+ * @param {number} milestoneId - The ID of the milestone
2012
+ * @returns {Promise<GitLabMergeRequest[]>} List of merge requests
2013
+ */
2014
+ async function getMilestoneMergeRequests(projectId, milestoneId) {
2015
+ projectId = decodeURIComponent(projectId);
2016
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/merge_requests`);
2017
+ const response = await fetch(url.toString(), {
2018
+ ...DEFAULT_FETCH_CONFIG,
2019
+ });
2020
+ await handleGitLabError(response);
2021
+ const data = await response.json();
2022
+ return z.array(GitLabMergeRequestSchema).parse(data);
2023
+ }
2024
+ /**
2025
+ * Promote a project milestone to a group milestone
2026
+ * @param {string} projectId - The ID or URL-encoded path of the project
2027
+ * @param {number} milestoneId - The ID of the milestone
2028
+ * @returns {Promise<GitLabMilestones>} Promoted milestone
2029
+ */
2030
+ async function promoteProjectMilestone(projectId, milestoneId) {
2031
+ projectId = decodeURIComponent(projectId);
2032
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/promote`);
2033
+ const response = await fetch(url.toString(), {
2034
+ ...DEFAULT_FETCH_CONFIG,
2035
+ method: "POST",
2036
+ });
2037
+ await handleGitLabError(response);
2038
+ const data = await response.json();
2039
+ return GitLabMilestonesSchema.parse(data);
2040
+ }
2041
+ /**
2042
+ * Get all burndown chart events for a single milestone
2043
+ * @param {string} projectId - The ID or URL-encoded path of the project
2044
+ * @param {number} milestoneId - The ID of the milestone
2045
+ * @returns {Promise<any[]>} Burndown chart events
2046
+ */
2047
+ async function getMilestoneBurndownEvents(projectId, milestoneId) {
2048
+ projectId = decodeURIComponent(projectId);
2049
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/burndown_events`);
2050
+ const response = await fetch(url.toString(), {
2051
+ ...DEFAULT_FETCH_CONFIG,
2052
+ });
2053
+ await handleGitLabError(response);
2054
+ const data = await response.json();
2055
+ return data;
2056
+ }
1833
2057
  server.setRequestHandler(ListToolsRequestSchema, async () => {
1834
2058
  // Apply read-only filter first
1835
2059
  const tools0 = GITLAB_READ_ONLY_MODE
1836
- ? allTools.filter((tool) => readOnlyTools.includes(tool.name))
2060
+ ? allTools.filter(tool => readOnlyTools.includes(tool.name))
1837
2061
  : allTools;
1838
2062
  // Toggle wiki tools by USE_GITLAB_WIKI flag
1839
- let tools = USE_GITLAB_WIKI
2063
+ const tools1 = USE_GITLAB_WIKI
1840
2064
  ? tools0
1841
- : tools0.filter((tool) => !wikiToolNames.includes(tool.name));
2065
+ : tools0.filter(tool => !wikiToolNames.includes(tool.name));
2066
+ // Toggle milestone tools by USE_MILESTONE flag
2067
+ let tools = USE_MILESTONE
2068
+ ? tools1
2069
+ : tools1.filter(tool => !milestoneToolNames.includes(tool.name));
1842
2070
  // <<< START: Gemini 호환성을 위해 $schema 제거 >>>
1843
- tools = tools.map((tool) => {
2071
+ tools = tools.map(tool => {
1844
2072
  // inputSchema가 존재하고 객체인지 확인
1845
- if (tool.inputSchema &&
1846
- typeof tool.inputSchema === "object" &&
1847
- tool.inputSchema !== null) {
2073
+ if (tool.inputSchema && typeof tool.inputSchema === "object" && tool.inputSchema !== null) {
1848
2074
  // $schema 키가 존재하면 삭제
1849
2075
  if ("$schema" in tool.inputSchema) {
1850
2076
  // 불변성을 위해 새로운 객체 생성 (선택적이지만 권장)
@@ -1872,9 +2098,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1872
2098
  try {
1873
2099
  const forkedProject = await forkProject(forkArgs.project_id, forkArgs.namespace);
1874
2100
  return {
1875
- content: [
1876
- { type: "text", text: JSON.stringify(forkedProject, null, 2) },
1877
- ],
2101
+ content: [{ type: "text", text: JSON.stringify(forkedProject, null, 2) }],
1878
2102
  };
1879
2103
  }
1880
2104
  catch (forkError) {
@@ -1918,9 +2142,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1918
2142
  const args = CreateRepositorySchema.parse(request.params.arguments);
1919
2143
  const repository = await createRepository(args);
1920
2144
  return {
1921
- content: [
1922
- { type: "text", text: JSON.stringify(repository, null, 2) },
1923
- ],
2145
+ content: [{ type: "text", text: JSON.stringify(repository, null, 2) }],
1924
2146
  };
1925
2147
  }
1926
2148
  case "get_file_contents": {
@@ -1939,7 +2161,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1939
2161
  }
1940
2162
  case "push_files": {
1941
2163
  const args = PushFilesSchema.parse(request.params.arguments);
1942
- const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map((f) => ({ path: f.file_path, content: f.content })));
2164
+ const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map(f => ({ path: f.file_path, content: f.content })));
1943
2165
  return {
1944
2166
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1945
2167
  };
@@ -1957,9 +2179,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1957
2179
  const { project_id, ...options } = args;
1958
2180
  const mergeRequest = await createMergeRequest(project_id, options);
1959
2181
  return {
1960
- content: [
1961
- { type: "text", text: JSON.stringify(mergeRequest, null, 2) },
1962
- ],
2182
+ content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
1963
2183
  };
1964
2184
  }
1965
2185
  case "update_merge_request_note": {
@@ -1996,9 +2216,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1996
2216
  const args = GetMergeRequestSchema.parse(request.params.arguments);
1997
2217
  const mergeRequest = await getMergeRequest(args.project_id, args.merge_request_iid, args.source_branch);
1998
2218
  return {
1999
- content: [
2000
- { type: "text", text: JSON.stringify(mergeRequest, null, 2) },
2001
- ],
2219
+ content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
2002
2220
  };
2003
2221
  }
2004
2222
  case "get_merge_request_diffs": {
@@ -2013,18 +2231,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2013
2231
  const { project_id, merge_request_iid, source_branch, ...options } = args;
2014
2232
  const mergeRequest = await updateMergeRequest(project_id, options, merge_request_iid, source_branch);
2015
2233
  return {
2016
- content: [
2017
- { type: "text", text: JSON.stringify(mergeRequest, null, 2) },
2018
- ],
2234
+ content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
2019
2235
  };
2020
2236
  }
2021
2237
  case "mr_discussions": {
2022
2238
  const args = ListMergeRequestDiscussionsSchema.parse(request.params.arguments);
2023
2239
  const discussions = await listMergeRequestDiscussions(args.project_id, args.merge_request_iid);
2024
2240
  return {
2025
- content: [
2026
- { type: "text", text: JSON.stringify(discussions, null, 2) },
2027
- ],
2241
+ content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
2028
2242
  };
2029
2243
  }
2030
2244
  case "list_namespaces": {
@@ -2049,9 +2263,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2049
2263
  const data = await response.json();
2050
2264
  const namespaces = z.array(GitLabNamespaceSchema).parse(data);
2051
2265
  return {
2052
- content: [
2053
- { type: "text", text: JSON.stringify(namespaces, null, 2) },
2054
- ],
2266
+ content: [{ type: "text", text: JSON.stringify(namespaces, null, 2) }],
2055
2267
  };
2056
2268
  }
2057
2269
  case "get_namespace": {
@@ -2077,9 +2289,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2077
2289
  const data = await response.json();
2078
2290
  const namespaceExists = GitLabNamespaceExistsResponseSchema.parse(data);
2079
2291
  return {
2080
- content: [
2081
- { type: "text", text: JSON.stringify(namespaceExists, null, 2) },
2082
- ],
2292
+ content: [{ type: "text", text: JSON.stringify(namespaceExists, null, 2) }],
2083
2293
  };
2084
2294
  }
2085
2295
  case "get_project": {
@@ -2165,9 +2375,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2165
2375
  const { project_id, issue_iid, ...options } = args;
2166
2376
  const discussions = await listIssueDiscussions(project_id, issue_iid, options);
2167
2377
  return {
2168
- content: [
2169
- { type: "text", text: JSON.stringify(discussions, null, 2) },
2170
- ],
2378
+ content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
2171
2379
  };
2172
2380
  }
2173
2381
  case "get_issue_link": {
@@ -2360,6 +2568,117 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2360
2568
  content: [{ type: "text", text: JSON.stringify(mergeRequests, null, 2) }],
2361
2569
  };
2362
2570
  }
2571
+ case "list_milestones": {
2572
+ const { project_id, ...options } = ListProjectMilestonesSchema.parse(request.params.arguments);
2573
+ const milestones = await listProjectMilestones(project_id, options);
2574
+ return {
2575
+ content: [
2576
+ {
2577
+ type: "text",
2578
+ text: JSON.stringify(milestones, null, 2),
2579
+ },
2580
+ ],
2581
+ };
2582
+ }
2583
+ case "get_milestone": {
2584
+ const { project_id, milestone_id } = GetProjectMilestoneSchema.parse(request.params.arguments);
2585
+ const milestone = await getProjectMilestone(project_id, milestone_id);
2586
+ return {
2587
+ content: [
2588
+ {
2589
+ type: "text",
2590
+ text: JSON.stringify(milestone, null, 2),
2591
+ },
2592
+ ],
2593
+ };
2594
+ }
2595
+ case "create_milestone": {
2596
+ const { project_id, ...options } = CreateProjectMilestoneSchema.parse(request.params.arguments);
2597
+ const milestone = await createProjectMilestone(project_id, options);
2598
+ return {
2599
+ content: [
2600
+ {
2601
+ type: "text",
2602
+ text: JSON.stringify(milestone, null, 2),
2603
+ },
2604
+ ],
2605
+ };
2606
+ }
2607
+ case "edit_milestone": {
2608
+ const { project_id, milestone_id, ...options } = EditProjectMilestoneSchema.parse(request.params.arguments);
2609
+ const milestone = await editProjectMilestone(project_id, milestone_id, options);
2610
+ return {
2611
+ content: [
2612
+ {
2613
+ type: "text",
2614
+ text: JSON.stringify(milestone, null, 2),
2615
+ },
2616
+ ],
2617
+ };
2618
+ }
2619
+ case "delete_milestone": {
2620
+ const { project_id, milestone_id } = DeleteProjectMilestoneSchema.parse(request.params.arguments);
2621
+ await deleteProjectMilestone(project_id, milestone_id);
2622
+ return {
2623
+ content: [
2624
+ {
2625
+ type: "text",
2626
+ text: JSON.stringify({
2627
+ status: "success",
2628
+ message: "Milestone deleted successfully",
2629
+ }, null, 2),
2630
+ },
2631
+ ],
2632
+ };
2633
+ }
2634
+ case "get_milestone_issue": {
2635
+ const { project_id, milestone_id } = GetMilestoneIssuesSchema.parse(request.params.arguments);
2636
+ const issues = await getMilestoneIssues(project_id, milestone_id);
2637
+ return {
2638
+ content: [
2639
+ {
2640
+ type: "text",
2641
+ text: JSON.stringify(issues, null, 2),
2642
+ },
2643
+ ],
2644
+ };
2645
+ }
2646
+ case "get_milestone_merge_requests": {
2647
+ const { project_id, milestone_id } = GetMilestoneMergeRequestsSchema.parse(request.params.arguments);
2648
+ const mergeRequests = await getMilestoneMergeRequests(project_id, milestone_id);
2649
+ return {
2650
+ content: [
2651
+ {
2652
+ type: "text",
2653
+ text: JSON.stringify(mergeRequests, null, 2),
2654
+ },
2655
+ ],
2656
+ };
2657
+ }
2658
+ case "promote_milestone": {
2659
+ const { project_id, milestone_id } = PromoteProjectMilestoneSchema.parse(request.params.arguments);
2660
+ const milestone = await promoteProjectMilestone(project_id, milestone_id);
2661
+ return {
2662
+ content: [
2663
+ {
2664
+ type: "text",
2665
+ text: JSON.stringify(milestone, null, 2),
2666
+ },
2667
+ ],
2668
+ };
2669
+ }
2670
+ case "get_milestone_burndown_events": {
2671
+ const { project_id, milestone_id } = GetMilestoneBurndownEventsSchema.parse(request.params.arguments);
2672
+ const events = await getMilestoneBurndownEvents(project_id, milestone_id);
2673
+ return {
2674
+ content: [
2675
+ {
2676
+ type: "text",
2677
+ text: JSON.stringify(events, null, 2),
2678
+ },
2679
+ ],
2680
+ };
2681
+ }
2363
2682
  default:
2364
2683
  throw new Error(`Unknown tool: ${request.params.name}`);
2365
2684
  }
@@ -2367,7 +2686,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2367
2686
  catch (error) {
2368
2687
  if (error instanceof z.ZodError) {
2369
2688
  throw new Error(`Invalid arguments: ${error.errors
2370
- .map((e) => `${e.path.join(".")}: ${e.message}`)
2689
+ .map(e => `${e.path.join(".")}: ${e.message}`)
2371
2690
  .join(", ")}`);
2372
2691
  }
2373
2692
  throw error;
@@ -2392,7 +2711,7 @@ async function runServer() {
2392
2711
  process.exit(1);
2393
2712
  }
2394
2713
  }
2395
- runServer().catch((error) => {
2714
+ runServer().catch(error => {
2396
2715
  console.error("Fatal error in main():", error);
2397
2716
  process.exit(1);
2398
2717
  });