@zereight/mcp-gitlab 1.0.65 → 1.0.66

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
@@ -35,11 +35,39 @@ When using with the Claude App, you need to set up your API key and URLs directl
35
35
  }
36
36
  ```
37
37
 
38
+ #### vscode .vscode/mcp.json
39
+
40
+ ```json
41
+ {
42
+ "inputs": [
43
+ {
44
+ "type": "promptString",
45
+ "id": "gitlab-token",
46
+ "description": "Gitlab Token to read API",
47
+ "password": true
48
+ }
49
+ ],
50
+ "servers": {
51
+ "GitLab-MCP": {
52
+ "type": "stdio",
53
+ "command": "npx",
54
+ "args": ["-y", "@zereight/mcp-gitlab"],
55
+ "env": {
56
+ "GITLAB_PERSONAL_ACCESS_TOKEN": "${input:gitlab-token}",
57
+ "GITLAB_API_URL": "your-fancy-gitlab-url",
58
+ "GITLAB_READ_ONLY_MODE": "true",
59
+ ...
60
+ }
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
38
66
  #### Docker
39
67
 
40
- - stdio
68
+ - stdio mcp.json
41
69
 
42
- ```mcp.json
70
+ ```json
43
71
  {
44
72
  "mcpServers": {
45
73
  "GitLab communication server": {
@@ -94,6 +122,7 @@ docker run -i --rm \
94
122
  {
95
123
  "mcpServers": {
96
124
  "GitLab communication server": {
125
+ "type": "sse",
97
126
  "url": "http://localhost:3333/sse"
98
127
  }
99
128
  }
@@ -110,6 +139,7 @@ $ sh scripts/image_push.sh docker_user_name
110
139
 
111
140
  - `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token.
112
141
  - `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`)
142
+ - `GITLAB_PROJECT_ID`: Default project ID. If set, Overwrite this value when making an API request.
113
143
  - `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.
114
144
  - `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.
115
145
  - `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.
package/build/index.js CHANGED
@@ -626,6 +626,7 @@ function normalizeGitLabApiUrl(url) {
626
626
  }
627
627
  // Use the normalizeGitLabApiUrl function to handle various URL formats
628
628
  const GITLAB_API_URL = normalizeGitLabApiUrl(process.env.GITLAB_API_URL || "");
629
+ const GITLAB_PROJECT_ID = process.env.GITLAB_PROJECT_ID;
629
630
  if (!GITLAB_PERSONAL_ACCESS_TOKEN) {
630
631
  console.error("GITLAB_PERSONAL_ACCESS_TOKEN environment variable is not set");
631
632
  process.exit(1);
@@ -652,6 +653,13 @@ async function handleGitLabError(response) {
652
653
  }
653
654
  }
654
655
  }
656
+ /**
657
+ * @param {string} projectId - The project ID parameter passed to the function
658
+ * @returns {string} The project ID to use for the API call
659
+ */
660
+ function getEffectiveProjectId(projectId) {
661
+ return GITLAB_PROJECT_ID || projectId;
662
+ }
655
663
  /**
656
664
  * Create a fork of a GitLab project
657
665
  * 프로젝트 포크 생성 (Create a project fork)
@@ -662,7 +670,8 @@ async function handleGitLabError(response) {
662
670
  */
663
671
  async function forkProject(projectId, namespace) {
664
672
  projectId = decodeURIComponent(projectId); // Decode project ID
665
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/fork`);
673
+ const effectiveProjectId = getEffectiveProjectId(projectId);
674
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/fork`);
666
675
  if (namespace) {
667
676
  url.searchParams.append("namespace", namespace);
668
677
  }
@@ -688,7 +697,8 @@ async function forkProject(projectId, namespace) {
688
697
  */
689
698
  async function createBranch(projectId, options) {
690
699
  projectId = decodeURIComponent(projectId); // Decode project ID
691
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/branches`);
700
+ const effectiveProjectId = getEffectiveProjectId(projectId);
701
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/repository/branches`);
692
702
  const response = await fetch(url.toString(), {
693
703
  ...DEFAULT_FETCH_CONFIG,
694
704
  method: "POST",
@@ -709,7 +719,8 @@ async function createBranch(projectId, options) {
709
719
  */
710
720
  async function getDefaultBranchRef(projectId) {
711
721
  projectId = decodeURIComponent(projectId); // Decode project ID
712
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}`);
722
+ const effectiveProjectId = getEffectiveProjectId(projectId);
723
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}`);
713
724
  const response = await fetch(url.toString(), {
714
725
  ...DEFAULT_FETCH_CONFIG,
715
726
  });
@@ -728,12 +739,13 @@ async function getDefaultBranchRef(projectId) {
728
739
  */
729
740
  async function getFileContents(projectId, filePath, ref) {
730
741
  projectId = decodeURIComponent(projectId); // Decode project ID
742
+ const effectiveProjectId = getEffectiveProjectId(projectId);
731
743
  const encodedPath = encodeURIComponent(filePath);
732
744
  // ref가 없는 경우 default branch를 가져옴
733
745
  if (!ref) {
734
746
  ref = await getDefaultBranchRef(projectId);
735
747
  }
736
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/files/${encodedPath}`);
748
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/repository/files/${encodedPath}`);
737
749
  url.searchParams.append("ref", ref);
738
750
  const response = await fetch(url.toString(), {
739
751
  ...DEFAULT_FETCH_CONFIG,
@@ -762,7 +774,8 @@ async function getFileContents(projectId, filePath, ref) {
762
774
  */
763
775
  async function createIssue(projectId, options) {
764
776
  projectId = decodeURIComponent(projectId); // Decode project ID
765
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues`);
777
+ const effectiveProjectId = getEffectiveProjectId(projectId);
778
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/issues`);
766
779
  const response = await fetch(url.toString(), {
767
780
  ...DEFAULT_FETCH_CONFIG,
768
781
  method: "POST",
@@ -793,7 +806,8 @@ async function createIssue(projectId, options) {
793
806
  */
794
807
  async function listIssues(projectId, options = {}) {
795
808
  projectId = decodeURIComponent(projectId); // Decode project ID
796
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues`);
809
+ const effectiveProjectId = getEffectiveProjectId(projectId);
810
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/issues`);
797
811
  // Add all query parameters
798
812
  Object.entries(options).forEach(([key, value]) => {
799
813
  if (value !== undefined) {
@@ -830,7 +844,7 @@ async function listIssues(projectId, options = {}) {
830
844
  */
831
845
  async function listMergeRequests(projectId, options = {}) {
832
846
  projectId = decodeURIComponent(projectId); // Decode project ID
833
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests`);
847
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests`);
834
848
  // Add all query parameters
835
849
  Object.entries(options).forEach(([key, value]) => {
836
850
  if (value !== undefined) {
@@ -860,7 +874,7 @@ async function listMergeRequests(projectId, options = {}) {
860
874
  */
861
875
  async function getIssue(projectId, issueIid) {
862
876
  projectId = decodeURIComponent(projectId); // Decode project ID
863
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`);
877
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
864
878
  const response = await fetch(url.toString(), {
865
879
  ...DEFAULT_FETCH_CONFIG,
866
880
  });
@@ -879,7 +893,7 @@ async function getIssue(projectId, issueIid) {
879
893
  */
880
894
  async function updateIssue(projectId, issueIid, options) {
881
895
  projectId = decodeURIComponent(projectId); // Decode project ID
882
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`);
896
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
883
897
  // Convert labels array to comma-separated string if present
884
898
  const body = { ...options };
885
899
  if (body.labels && Array.isArray(body.labels)) {
@@ -904,7 +918,7 @@ async function updateIssue(projectId, issueIid, options) {
904
918
  */
905
919
  async function deleteIssue(projectId, issueIid) {
906
920
  projectId = decodeURIComponent(projectId); // Decode project ID
907
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`);
921
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
908
922
  const response = await fetch(url.toString(), {
909
923
  ...DEFAULT_FETCH_CONFIG,
910
924
  method: "DELETE",
@@ -921,7 +935,7 @@ async function deleteIssue(projectId, issueIid) {
921
935
  */
922
936
  async function listIssueLinks(projectId, issueIid) {
923
937
  projectId = decodeURIComponent(projectId); // Decode project ID
924
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links`);
938
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links`);
925
939
  const response = await fetch(url.toString(), {
926
940
  ...DEFAULT_FETCH_CONFIG,
927
941
  });
@@ -940,7 +954,7 @@ async function listIssueLinks(projectId, issueIid) {
940
954
  */
941
955
  async function getIssueLink(projectId, issueIid, issueLinkId) {
942
956
  projectId = decodeURIComponent(projectId); // Decode project ID
943
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links/${issueLinkId}`);
957
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links/${issueLinkId}`);
944
958
  const response = await fetch(url.toString(), {
945
959
  ...DEFAULT_FETCH_CONFIG,
946
960
  });
@@ -962,7 +976,7 @@ async function getIssueLink(projectId, issueIid, issueLinkId) {
962
976
  async function createIssueLink(projectId, issueIid, targetProjectId, targetIssueIid, linkType = "relates_to") {
963
977
  projectId = decodeURIComponent(projectId); // Decode project ID
964
978
  targetProjectId = decodeURIComponent(targetProjectId); // Decode target project ID as well
965
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links`);
979
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links`);
966
980
  const response = await fetch(url.toString(), {
967
981
  ...DEFAULT_FETCH_CONFIG,
968
982
  method: "POST",
@@ -987,7 +1001,7 @@ async function createIssueLink(projectId, issueIid, targetProjectId, targetIssue
987
1001
  */
988
1002
  async function deleteIssueLink(projectId, issueIid, issueLinkId) {
989
1003
  projectId = decodeURIComponent(projectId); // Decode project ID
990
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links/${issueLinkId}`);
1004
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links/${issueLinkId}`);
991
1005
  const response = await fetch(url.toString(), {
992
1006
  ...DEFAULT_FETCH_CONFIG,
993
1007
  method: "DELETE",
@@ -1004,7 +1018,7 @@ async function deleteIssueLink(projectId, issueIid, issueLinkId) {
1004
1018
  */
1005
1019
  async function createMergeRequest(projectId, options) {
1006
1020
  projectId = decodeURIComponent(projectId); // Decode project ID
1007
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests`);
1021
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests`);
1008
1022
  const response = await fetch(url.toString(), {
1009
1023
  ...DEFAULT_FETCH_CONFIG,
1010
1024
  method: "POST",
@@ -1045,7 +1059,7 @@ async function createMergeRequest(projectId, options) {
1045
1059
  */
1046
1060
  async function listDiscussions(projectId, resourceType, resourceIid, options = {}) {
1047
1061
  projectId = decodeURIComponent(projectId); // Decode project ID
1048
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/${resourceType}/${resourceIid}/discussions`);
1062
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/${resourceType}/${resourceIid}/discussions`);
1049
1063
  // Add query parameters for pagination and sorting
1050
1064
  if (options.page) {
1051
1065
  url.searchParams.append("page", options.page.toString());
@@ -1117,7 +1131,7 @@ async function listIssueDiscussions(projectId, issueIid, options = {}) {
1117
1131
  */
1118
1132
  async function updateMergeRequestNote(projectId, mergeRequestIid, discussionId, noteId, body, resolved) {
1119
1133
  projectId = decodeURIComponent(projectId); // Decode project ID
1120
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes/${noteId}`);
1134
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes/${noteId}`);
1121
1135
  // Only one of body or resolved can be sent according to GitLab API
1122
1136
  const payload = {};
1123
1137
  if (body !== undefined) {
@@ -1146,7 +1160,7 @@ async function updateMergeRequestNote(projectId, mergeRequestIid, discussionId,
1146
1160
  */
1147
1161
  async function updateIssueNote(projectId, issueIid, discussionId, noteId, body) {
1148
1162
  projectId = decodeURIComponent(projectId); // Decode project ID
1149
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/discussions/${discussionId}/notes/${noteId}`);
1163
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/discussions/${discussionId}/notes/${noteId}`);
1150
1164
  const payload = { body };
1151
1165
  const response = await fetch(url.toString(), {
1152
1166
  ...DEFAULT_FETCH_CONFIG,
@@ -1168,7 +1182,7 @@ async function updateIssueNote(projectId, issueIid, discussionId, noteId, body)
1168
1182
  */
1169
1183
  async function createIssueNote(projectId, issueIid, discussionId, body, createdAt) {
1170
1184
  projectId = decodeURIComponent(projectId); // Decode project ID
1171
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/discussions/${discussionId}/notes`);
1185
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/discussions/${discussionId}/notes`);
1172
1186
  const payload = { body };
1173
1187
  if (createdAt) {
1174
1188
  payload.created_at = createdAt;
@@ -1195,7 +1209,7 @@ async function createIssueNote(projectId, issueIid, discussionId, body, createdA
1195
1209
  */
1196
1210
  async function createMergeRequestNote(projectId, mergeRequestIid, discussionId, body, createdAt) {
1197
1211
  projectId = decodeURIComponent(projectId); // Decode project ID
1198
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes`);
1212
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes`);
1199
1213
  const payload = { body };
1200
1214
  if (createdAt) {
1201
1215
  payload.created_at = createdAt;
@@ -1224,7 +1238,7 @@ async function createMergeRequestNote(projectId, mergeRequestIid, discussionId,
1224
1238
  async function createOrUpdateFile(projectId, filePath, content, commitMessage, branch, previousPath, last_commit_id, commit_id) {
1225
1239
  projectId = decodeURIComponent(projectId); // Decode project ID
1226
1240
  const encodedPath = encodeURIComponent(filePath);
1227
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/files/${encodedPath}`);
1241
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/files/${encodedPath}`);
1228
1242
  const body = {
1229
1243
  branch,
1230
1244
  content,
@@ -1291,7 +1305,7 @@ async function createOrUpdateFile(projectId, filePath, content, commitMessage, b
1291
1305
  */
1292
1306
  async function createTree(projectId, files, ref) {
1293
1307
  projectId = decodeURIComponent(projectId); // Decode project ID
1294
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/tree`);
1308
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/tree`);
1295
1309
  if (ref) {
1296
1310
  url.searchParams.append("ref", ref);
1297
1311
  }
@@ -1329,7 +1343,7 @@ async function createTree(projectId, files, ref) {
1329
1343
  */
1330
1344
  async function createCommit(projectId, message, branch, actions) {
1331
1345
  projectId = decodeURIComponent(projectId); // Decode project ID
1332
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/commits`);
1346
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits`);
1333
1347
  const response = await fetch(url.toString(), {
1334
1348
  ...DEFAULT_FETCH_CONFIG,
1335
1349
  method: "POST",
@@ -1430,10 +1444,10 @@ async function getMergeRequest(projectId, mergeRequestIid, branchName) {
1430
1444
  projectId = decodeURIComponent(projectId); // Decode project ID
1431
1445
  let url;
1432
1446
  if (mergeRequestIid) {
1433
- url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`);
1447
+ url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}`);
1434
1448
  }
1435
1449
  else if (branchName) {
1436
- url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests?source_branch=${encodeURIComponent(branchName)}`);
1450
+ url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests?source_branch=${encodeURIComponent(branchName)}`);
1437
1451
  }
1438
1452
  else {
1439
1453
  throw new Error("Either mergeRequestIid or branchName must be provided");
@@ -1468,7 +1482,7 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view
1468
1482
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
1469
1483
  mergeRequestIid = mergeRequest.iid;
1470
1484
  }
1471
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/changes`);
1485
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/changes`);
1472
1486
  if (view) {
1473
1487
  url.searchParams.append("view", view);
1474
1488
  }
@@ -1498,7 +1512,7 @@ async function listMergeRequestDiffs(projectId, mergeRequestIid, branchName, pag
1498
1512
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
1499
1513
  mergeRequestIid = mergeRequest.iid;
1500
1514
  }
1501
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/diffs`);
1515
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/diffs`);
1502
1516
  if (page) {
1503
1517
  url.searchParams.append("page", page.toString());
1504
1518
  }
@@ -1525,7 +1539,7 @@ async function listMergeRequestDiffs(projectId, mergeRequestIid, branchName, pag
1525
1539
  */
1526
1540
  async function getBranchDiffs(projectId, from, to, straight) {
1527
1541
  projectId = decodeURIComponent(projectId); // Decode project ID
1528
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/compare`);
1542
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/compare`);
1529
1543
  url.searchParams.append("from", from);
1530
1544
  url.searchParams.append("to", to);
1531
1545
  if (straight !== undefined) {
@@ -1560,7 +1574,7 @@ async function updateMergeRequest(projectId, options, mergeRequestIid, branchNam
1560
1574
  const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
1561
1575
  mergeRequestIid = mergeRequest.iid;
1562
1576
  }
1563
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`);
1577
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}`);
1564
1578
  const response = await fetch(url.toString(), {
1565
1579
  ...DEFAULT_FETCH_CONFIG,
1566
1580
  method: "PUT",
@@ -1584,7 +1598,7 @@ async function createNote(projectId, noteableType, // 'issue' 또는 'merge_requ
1584
1598
  noteableIid, body) {
1585
1599
  projectId = decodeURIComponent(projectId); // Decode project ID
1586
1600
  // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능
1587
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation
1601
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation
1588
1602
  );
1589
1603
  const response = await fetch(url.toString(), {
1590
1604
  ...DEFAULT_FETCH_CONFIG,
@@ -1616,7 +1630,7 @@ noteableIid, body) {
1616
1630
  */
1617
1631
  async function createMergeRequestThread(projectId, mergeRequestIid, body, position, createdAt) {
1618
1632
  projectId = decodeURIComponent(projectId); // Decode project ID
1619
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions`);
1633
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions`);
1620
1634
  const payload = { body };
1621
1635
  // Add optional parameters if provided
1622
1636
  if (position) {
@@ -1711,7 +1725,7 @@ async function verifyNamespaceExistence(namespacePath, parentId) {
1711
1725
  */
1712
1726
  async function getProject(projectId, options = {}) {
1713
1727
  projectId = decodeURIComponent(projectId); // Decode project ID
1714
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}`);
1728
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}`);
1715
1729
  if (options.license) {
1716
1730
  url.searchParams.append("license", "true");
1717
1731
  }
@@ -1768,7 +1782,7 @@ async function listProjects(options = {}) {
1768
1782
  async function listLabels(projectId, options = {}) {
1769
1783
  projectId = decodeURIComponent(projectId); // Decode project ID
1770
1784
  // Construct the URL with project path
1771
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`);
1785
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels`);
1772
1786
  // Add query parameters
1773
1787
  Object.entries(options).forEach(([key, value]) => {
1774
1788
  if (value !== undefined) {
@@ -1800,7 +1814,7 @@ async function listLabels(projectId, options = {}) {
1800
1814
  */
1801
1815
  async function getLabel(projectId, labelId, includeAncestorGroups) {
1802
1816
  projectId = decodeURIComponent(projectId); // Decode project ID
1803
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`);
1817
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`);
1804
1818
  // Add query parameters
1805
1819
  if (includeAncestorGroups !== undefined) {
1806
1820
  url.searchParams.append("include_ancestor_groups", includeAncestorGroups ? "true" : "false");
@@ -1825,7 +1839,7 @@ async function getLabel(projectId, labelId, includeAncestorGroups) {
1825
1839
  async function createLabel(projectId, options) {
1826
1840
  projectId = decodeURIComponent(projectId); // Decode project ID
1827
1841
  // Make the API request
1828
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`, {
1842
+ const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels`, {
1829
1843
  ...DEFAULT_FETCH_CONFIG,
1830
1844
  method: "POST",
1831
1845
  body: JSON.stringify(options),
@@ -1847,7 +1861,7 @@ async function createLabel(projectId, options) {
1847
1861
  async function updateLabel(projectId, labelId, options) {
1848
1862
  projectId = decodeURIComponent(projectId); // Decode project ID
1849
1863
  // Make the API request
1850
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`, {
1864
+ const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`, {
1851
1865
  ...DEFAULT_FETCH_CONFIG,
1852
1866
  method: "PUT",
1853
1867
  body: JSON.stringify(options),
@@ -1867,7 +1881,7 @@ async function updateLabel(projectId, labelId, options) {
1867
1881
  async function deleteLabel(projectId, labelId) {
1868
1882
  projectId = decodeURIComponent(projectId); // Decode project ID
1869
1883
  // Make the API request
1870
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`, {
1884
+ const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`, {
1871
1885
  ...DEFAULT_FETCH_CONFIG,
1872
1886
  method: "DELETE",
1873
1887
  });
@@ -1928,7 +1942,7 @@ async function listGroupProjects(options) {
1928
1942
  */
1929
1943
  async function listWikiPages(projectId, options = {}) {
1930
1944
  projectId = decodeURIComponent(projectId); // Decode project ID
1931
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis`);
1945
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis`);
1932
1946
  if (options.page)
1933
1947
  url.searchParams.append("page", options.page.toString());
1934
1948
  if (options.per_page)
@@ -1947,7 +1961,7 @@ async function listWikiPages(projectId, options = {}) {
1947
1961
  */
1948
1962
  async function getWikiPage(projectId, slug) {
1949
1963
  projectId = decodeURIComponent(projectId); // Decode project ID
1950
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, { ...DEFAULT_FETCH_CONFIG });
1964
+ const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, { ...DEFAULT_FETCH_CONFIG });
1951
1965
  await handleGitLabError(response);
1952
1966
  const data = await response.json();
1953
1967
  return GitLabWikiPageSchema.parse(data);
@@ -1960,7 +1974,7 @@ async function createWikiPage(projectId, title, content, format) {
1960
1974
  const body = { title, content };
1961
1975
  if (format)
1962
1976
  body.format = format;
1963
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis`, {
1977
+ const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis`, {
1964
1978
  ...DEFAULT_FETCH_CONFIG,
1965
1979
  method: "POST",
1966
1980
  body: JSON.stringify(body),
@@ -1981,7 +1995,7 @@ async function updateWikiPage(projectId, slug, title, content, format) {
1981
1995
  body.content = content;
1982
1996
  if (format)
1983
1997
  body.format = format;
1984
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, {
1998
+ const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, {
1985
1999
  ...DEFAULT_FETCH_CONFIG,
1986
2000
  method: "PUT",
1987
2001
  body: JSON.stringify(body),
@@ -1995,7 +2009,7 @@ async function updateWikiPage(projectId, slug, title, content, format) {
1995
2009
  */
1996
2010
  async function deleteWikiPage(projectId, slug) {
1997
2011
  projectId = decodeURIComponent(projectId); // Decode project ID
1998
- const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, {
2012
+ const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, {
1999
2013
  ...DEFAULT_FETCH_CONFIG,
2000
2014
  method: "DELETE",
2001
2015
  });
@@ -2010,7 +2024,7 @@ async function deleteWikiPage(projectId, slug) {
2010
2024
  */
2011
2025
  async function listPipelines(projectId, options = {}) {
2012
2026
  projectId = decodeURIComponent(projectId); // Decode project ID
2013
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines`);
2027
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines`);
2014
2028
  // Add all query parameters
2015
2029
  Object.entries(options).forEach(([key, value]) => {
2016
2030
  if (value !== undefined) {
@@ -2033,7 +2047,7 @@ async function listPipelines(projectId, options = {}) {
2033
2047
  */
2034
2048
  async function getPipeline(projectId, pipelineId) {
2035
2049
  projectId = decodeURIComponent(projectId); // Decode project ID
2036
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}`);
2050
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}`);
2037
2051
  const response = await fetch(url.toString(), {
2038
2052
  ...DEFAULT_FETCH_CONFIG,
2039
2053
  });
@@ -2054,7 +2068,7 @@ async function getPipeline(projectId, pipelineId) {
2054
2068
  */
2055
2069
  async function listPipelineJobs(projectId, pipelineId, options = {}) {
2056
2070
  projectId = decodeURIComponent(projectId); // Decode project ID
2057
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/jobs`);
2071
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/jobs`);
2058
2072
  // Add all query parameters
2059
2073
  Object.entries(options).forEach(([key, value]) => {
2060
2074
  if (value !== undefined) {
@@ -2078,7 +2092,7 @@ async function listPipelineJobs(projectId, pipelineId, options = {}) {
2078
2092
  }
2079
2093
  async function getPipelineJob(projectId, jobId) {
2080
2094
  projectId = decodeURIComponent(projectId); // Decode project ID
2081
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/jobs/${jobId}`);
2095
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}`);
2082
2096
  const response = await fetch(url.toString(), {
2083
2097
  ...DEFAULT_FETCH_CONFIG,
2084
2098
  });
@@ -2100,7 +2114,7 @@ async function getPipelineJob(projectId, jobId) {
2100
2114
  */
2101
2115
  async function getPipelineJobOutput(projectId, jobId, limit, offset) {
2102
2116
  projectId = decodeURIComponent(projectId); // Decode project ID
2103
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/jobs/${jobId}/trace`);
2117
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/trace`);
2104
2118
  const response = await fetch(url.toString(), {
2105
2119
  ...DEFAULT_FETCH_CONFIG,
2106
2120
  headers: {
@@ -2145,7 +2159,7 @@ async function getPipelineJobOutput(projectId, jobId, limit, offset) {
2145
2159
  */
2146
2160
  async function createPipeline(projectId, ref, variables) {
2147
2161
  projectId = decodeURIComponent(projectId); // Decode project ID
2148
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipeline`);
2162
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline`);
2149
2163
  const body = { ref };
2150
2164
  if (variables && variables.length > 0) {
2151
2165
  body.variables = variables.reduce((acc, { key, value }) => {
@@ -2171,7 +2185,7 @@ async function createPipeline(projectId, ref, variables) {
2171
2185
  */
2172
2186
  async function retryPipeline(projectId, pipelineId) {
2173
2187
  projectId = decodeURIComponent(projectId); // Decode project ID
2174
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/retry`);
2188
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/retry`);
2175
2189
  const response = await fetch(url.toString(), {
2176
2190
  method: "POST",
2177
2191
  headers: DEFAULT_HEADERS,
@@ -2189,7 +2203,7 @@ async function retryPipeline(projectId, pipelineId) {
2189
2203
  */
2190
2204
  async function cancelPipeline(projectId, pipelineId) {
2191
2205
  projectId = decodeURIComponent(projectId); // Decode project ID
2192
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/cancel`);
2206
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/cancel`);
2193
2207
  const response = await fetch(url.toString(), {
2194
2208
  method: "POST",
2195
2209
  headers: DEFAULT_HEADERS,
@@ -2248,7 +2262,7 @@ async function getRepositoryTree(options) {
2248
2262
  */
2249
2263
  async function listProjectMilestones(projectId, options) {
2250
2264
  projectId = decodeURIComponent(projectId);
2251
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones`);
2265
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones`);
2252
2266
  Object.entries(options).forEach(([key, value]) => {
2253
2267
  if (value !== undefined) {
2254
2268
  if (key === "iids" && Array.isArray(value) && value.length > 0) {
@@ -2276,7 +2290,7 @@ async function listProjectMilestones(projectId, options) {
2276
2290
  */
2277
2291
  async function getProjectMilestone(projectId, milestoneId) {
2278
2292
  projectId = decodeURIComponent(projectId);
2279
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}`);
2293
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
2280
2294
  const response = await fetch(url.toString(), {
2281
2295
  ...DEFAULT_FETCH_CONFIG,
2282
2296
  });
@@ -2292,7 +2306,7 @@ async function getProjectMilestone(projectId, milestoneId) {
2292
2306
  */
2293
2307
  async function createProjectMilestone(projectId, options) {
2294
2308
  projectId = decodeURIComponent(projectId);
2295
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones`);
2309
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones`);
2296
2310
  const response = await fetch(url.toString(), {
2297
2311
  ...DEFAULT_FETCH_CONFIG,
2298
2312
  method: "POST",
@@ -2311,7 +2325,7 @@ async function createProjectMilestone(projectId, options) {
2311
2325
  */
2312
2326
  async function editProjectMilestone(projectId, milestoneId, options) {
2313
2327
  projectId = decodeURIComponent(projectId);
2314
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}`);
2328
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
2315
2329
  const response = await fetch(url.toString(), {
2316
2330
  ...DEFAULT_FETCH_CONFIG,
2317
2331
  method: "PUT",
@@ -2329,7 +2343,7 @@ async function editProjectMilestone(projectId, milestoneId, options) {
2329
2343
  */
2330
2344
  async function deleteProjectMilestone(projectId, milestoneId) {
2331
2345
  projectId = decodeURIComponent(projectId);
2332
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}`);
2346
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
2333
2347
  const response = await fetch(url.toString(), {
2334
2348
  ...DEFAULT_FETCH_CONFIG,
2335
2349
  method: "DELETE",
@@ -2344,7 +2358,7 @@ async function deleteProjectMilestone(projectId, milestoneId) {
2344
2358
  */
2345
2359
  async function getMilestoneIssues(projectId, milestoneId) {
2346
2360
  projectId = decodeURIComponent(projectId);
2347
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/issues`);
2361
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/issues`);
2348
2362
  const response = await fetch(url.toString(), {
2349
2363
  ...DEFAULT_FETCH_CONFIG,
2350
2364
  });
@@ -2360,7 +2374,7 @@ async function getMilestoneIssues(projectId, milestoneId) {
2360
2374
  */
2361
2375
  async function getMilestoneMergeRequests(projectId, milestoneId) {
2362
2376
  projectId = decodeURIComponent(projectId);
2363
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/merge_requests`);
2377
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/merge_requests`);
2364
2378
  const response = await fetch(url.toString(), {
2365
2379
  ...DEFAULT_FETCH_CONFIG,
2366
2380
  });
@@ -2376,7 +2390,7 @@ async function getMilestoneMergeRequests(projectId, milestoneId) {
2376
2390
  */
2377
2391
  async function promoteProjectMilestone(projectId, milestoneId) {
2378
2392
  projectId = decodeURIComponent(projectId);
2379
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/promote`);
2393
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/promote`);
2380
2394
  const response = await fetch(url.toString(), {
2381
2395
  ...DEFAULT_FETCH_CONFIG,
2382
2396
  method: "POST",
@@ -2393,7 +2407,7 @@ async function promoteProjectMilestone(projectId, milestoneId) {
2393
2407
  */
2394
2408
  async function getMilestoneBurndownEvents(projectId, milestoneId) {
2395
2409
  projectId = decodeURIComponent(projectId);
2396
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/burndown_events`);
2410
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/burndown_events`);
2397
2411
  const response = await fetch(url.toString(), {
2398
2412
  ...DEFAULT_FETCH_CONFIG,
2399
2413
  });
@@ -2463,7 +2477,7 @@ async function getUsers(usernames) {
2463
2477
  */
2464
2478
  async function listCommits(projectId, options = {}) {
2465
2479
  projectId = decodeURIComponent(projectId);
2466
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/commits`);
2480
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits`);
2467
2481
  // Add query parameters
2468
2482
  if (options.ref_name)
2469
2483
  url.searchParams.append("ref_name", options.ref_name);
@@ -2507,7 +2521,7 @@ async function listCommits(projectId, options = {}) {
2507
2521
  */
2508
2522
  async function getCommit(projectId, sha, stats) {
2509
2523
  projectId = decodeURIComponent(projectId);
2510
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/commits/${encodeURIComponent(sha)}`);
2524
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits/${encodeURIComponent(sha)}`);
2511
2525
  if (stats) {
2512
2526
  url.searchParams.append("stats", "true");
2513
2527
  }
@@ -2528,7 +2542,7 @@ async function getCommit(projectId, sha, stats) {
2528
2542
  */
2529
2543
  async function getCommitDiff(projectId, sha) {
2530
2544
  projectId = decodeURIComponent(projectId);
2531
- const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/commits/${encodeURIComponent(sha)}/diff`);
2545
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits/${encodeURIComponent(sha)}/diff`);
2532
2546
  const response = await fetch(url.toString(), {
2533
2547
  ...DEFAULT_FETCH_CONFIG,
2534
2548
  });
@@ -3305,6 +3319,12 @@ async function runServer() {
3305
3319
  res.status(400).send("No transport found for sessionId");
3306
3320
  }
3307
3321
  });
3322
+ app.get("/health", (_, res) => {
3323
+ res.status(200).json({
3324
+ status: "healthy",
3325
+ version: process.env.npm_package_version || "unknown",
3326
+ });
3327
+ });
3308
3328
  const PORT = process.env.PORT || 3002;
3309
3329
  app.listen(PORT, () => {
3310
3330
  console.log(`Server is running on port ${PORT}`);
package/build/schemas.js CHANGED
@@ -608,13 +608,13 @@ export const GitLabMergeRequestSchema = z.object({
608
608
  export const LineRangeSchema = z.object({
609
609
  start: z.object({
610
610
  line_code: z.string().nullable().optional().describe("CRITICAL: Line identifier in format '{file_path_sha1_hash}_{old_line_number}_{new_line_number}'. USUALLY REQUIRED for GitLab diff comments despite being optional in schema. Example: 'a1b2c3d4e5f6_10_15'. Get this from GitLab diff API response, never fabricate."),
611
- type: z.enum(["new", "old"]).nullable().optional().describe("Line type: 'old' = deleted/original line, 'new' = added/modified line, null = unchanged context. MUST match the line_code format and old_line/new_line values."),
611
+ type: z.enum(["new", "old", "expanded"]).nullable().optional().describe("Line type: 'old' = deleted/original line, 'new' = added/modified line, null = unchanged context. MUST match the line_code format and old_line/new_line values."),
612
612
  old_line: z.number().nullable().optional().describe("Line number in original file (before changes). REQUIRED when type='old', NULL when type='new' (for purely added lines), can be present for context lines."),
613
613
  new_line: z.number().nullable().optional().describe("Line number in modified file (after changes). REQUIRED when type='new', NULL when type='old' (for purely deleted lines), can be present for context lines."),
614
614
  }).describe("Start line position for multiline comment range. MUST specify either old_line OR new_line (or both for context), never neither."),
615
615
  end: z.object({
616
616
  line_code: z.string().nullable().optional().describe("CRITICAL: Line identifier in format '{file_path_sha1_hash}_{old_line_number}_{new_line_number}'. USUALLY REQUIRED for GitLab diff comments despite being optional in schema. Example: 'a1b2c3d4e5f6_12_17'. Must be from same file as start.line_code."),
617
- type: z.enum(["new", "old"]).nullable().optional().describe("Line type: 'old' = deleted/original line, 'new' = added/modified line, null = unchanged context. SHOULD MATCH start.type for consistent ranges (don't mix old/new types)."),
617
+ type: z.enum(["new", "old", "expanded"]).nullable().optional().describe("Line type: 'old' = deleted/original line, 'new' = added/modified line, null = unchanged context. SHOULD MATCH start.type for consistent ranges (don't mix old/new types)."),
618
618
  old_line: z.number().nullable().optional().describe("Line number in original file (before changes). REQUIRED when type='old', NULL when type='new' (for purely added lines), can be present for context lines. MUST be >= start.old_line if both specified."),
619
619
  new_line: z.number().nullable().optional().describe("Line number in modified file (after changes). REQUIRED when type='new', NULL when type='old' (for purely deleted lines), can be present for context lines. MUST be >= start.new_line if both specified."),
620
620
  }).describe("End line position for multiline comment range. MUST specify either old_line OR new_line (or both for context), never neither. Range must be valid (end >= start)."),
@@ -643,28 +643,12 @@ export const GitLabDiscussionNoteSchema = z.object({
643
643
  base_sha: z.string(),
644
644
  start_sha: z.string(),
645
645
  head_sha: z.string(),
646
- old_path: z.string().optional().describe("File path before change"),
647
- new_path: z.string().optional().describe("File path after change"),
646
+ old_path: z.string().nullable().optional().describe("File path before change"),
647
+ new_path: z.string().nullable().optional().describe("File path after change"),
648
648
  position_type: z.enum(["text", "image", "file"]),
649
649
  new_line: z.number().nullable().optional().describe("Line number in the modified file (after changes). Used for added lines and context lines. Null for deleted lines."),
650
650
  old_line: z.number().nullable().optional().describe("Line number in the original file (before changes). Used for deleted lines and context lines. Null for newly added lines."),
651
- line_range: z
652
- .object({
653
- start: z.object({
654
- line_code: z.string().nullable().optional().describe("Line identifier in format: '{file_path_sha1_hash}_{old_line_number}_{new_line_number}'. Used to uniquely identify a specific line in the diff."),
655
- type: z.enum(["new", "old", "expanded"]),
656
- old_line: z.number().nullable().optional().describe("Line number in the original file (before changes). Null for newly added lines or unchanged context lines."),
657
- new_line: z.number().nullable().optional().describe("Line number in the modified file (after changes). Null for deleted lines or unchanged context lines."),
658
- }),
659
- end: z.object({
660
- line_code: z.string().nullable().optional().describe("Line identifier in format: '{file_path_sha1_hash}_{old_line_number}_{new_line_number}'. Used to uniquely identify a specific line in the diff."),
661
- type: z.enum(["new", "old", "expanded"]),
662
- old_line: z.number().nullable().optional().describe("Line number in the original file (before changes). Null for newly added lines or unchanged context lines."),
663
- new_line: z.number().nullable().optional().describe("Line number in the modified file (after changes). Null for deleted lines or unchanged context lines."),
664
- }),
665
- })
666
- .nullable()
667
- .optional(), // For multi-line diff notes
651
+ line_range: LineRangeSchema.nullable().optional(), // For multi-line diff notes
668
652
  width: z.number().optional(), // For image diff notes
669
653
  height: z.number().optional(), // For image diff notes
670
654
  x: z.number().optional(), // For image diff notes
@@ -1156,8 +1140,8 @@ export const MergeRequestThreadPositionSchema = z.object({
1156
1140
  head_sha: z.string().describe("REQUIRED: SHA referencing HEAD of the source branch. Get this from merge request diff_refs.head_sha."),
1157
1141
  start_sha: z.string().describe("REQUIRED: SHA referencing the start commit of the source branch. Get this from merge request diff_refs.start_sha."),
1158
1142
  position_type: z.enum(["text", "image", "file"]).describe("REQUIRED: Position type. Use 'text' for code diffs, 'image' for image diffs, 'file' for file-level comments."),
1159
- new_path: z.string().optional().describe("File path after changes. REQUIRED for most diff comments. Use same as old_path if file wasn't renamed."),
1160
- old_path: z.string().optional().describe("File path before changes. REQUIRED for most diff comments. Use same as new_path if file wasn't renamed."),
1143
+ new_path: z.string().nullable().optional().describe("File path after changes. REQUIRED for most diff comments. Use same as old_path if file wasn't renamed."),
1144
+ old_path: z.string().nullable().optional().describe("File path before changes. REQUIRED for most diff comments. Use same as new_path if file wasn't renamed."),
1161
1145
  new_line: z.number().nullable().optional().describe("Line number in modified file (after changes). Use for added lines or context lines. NULL for deleted lines. For single-line comments on new lines."),
1162
1146
  old_line: z.number().nullable().optional().describe("Line number in original file (before changes). Use for deleted lines or context lines. NULL for added lines. For single-line comments on old lines."),
1163
1147
  line_range: LineRangeSchema.optional().describe("MULTILINE COMMENTS: Specify start/end line positions for commenting on multiple lines. Alternative to single old_line/new_line."),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "1.0.65",
3
+ "version": "1.0.66",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",