@zereight/mcp-gitlab 1.0.35 → 1.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/build/index.js +56 -2
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -373,6 +373,7 @@ async function handleGitLabError(response) {
373
373
  * @returns {Promise<GitLabFork>} The created fork
374
374
  */
375
375
  async function forkProject(projectId, namespace) {
376
+ projectId = decodeURIComponent(projectId); // Decode project ID
376
377
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/fork`);
377
378
  if (namespace) {
378
379
  url.searchParams.append("namespace", namespace);
@@ -398,6 +399,7 @@ async function forkProject(projectId, namespace) {
398
399
  * @returns {Promise<GitLabReference>} The created branch reference
399
400
  */
400
401
  async function createBranch(projectId, options) {
402
+ projectId = decodeURIComponent(projectId); // Decode project ID
401
403
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/branches`);
402
404
  const response = await fetch(url.toString(), {
403
405
  ...DEFAULT_FETCH_CONFIG,
@@ -418,6 +420,7 @@ async function createBranch(projectId, options) {
418
420
  * @returns {Promise<string>} The name of the default branch
419
421
  */
420
422
  async function getDefaultBranchRef(projectId) {
423
+ projectId = decodeURIComponent(projectId); // Decode project ID
421
424
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}`);
422
425
  const response = await fetch(url.toString(), {
423
426
  ...DEFAULT_FETCH_CONFIG,
@@ -436,6 +439,7 @@ async function getDefaultBranchRef(projectId) {
436
439
  * @returns {Promise<GitLabContent>} The file content
437
440
  */
438
441
  async function getFileContents(projectId, filePath, ref) {
442
+ projectId = decodeURIComponent(projectId); // Decode project ID
439
443
  const encodedPath = encodeURIComponent(filePath);
440
444
  // ref가 없는 경우 default branch를 가져옴
441
445
  if (!ref) {
@@ -469,6 +473,7 @@ async function getFileContents(projectId, filePath, ref) {
469
473
  * @returns {Promise<GitLabIssue>} The created issue
470
474
  */
471
475
  async function createIssue(projectId, options) {
476
+ projectId = decodeURIComponent(projectId); // Decode project ID
472
477
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues`);
473
478
  const response = await fetch(url.toString(), {
474
479
  ...DEFAULT_FETCH_CONFIG,
@@ -499,6 +504,7 @@ async function createIssue(projectId, options) {
499
504
  * @returns {Promise<GitLabIssue[]>} List of issues
500
505
  */
501
506
  async function listIssues(projectId, options = {}) {
507
+ projectId = decodeURIComponent(projectId); // Decode project ID
502
508
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues`);
503
509
  // Add all query parameters
504
510
  Object.entries(options).forEach(([key, value]) => {
@@ -528,6 +534,7 @@ async function listIssues(projectId, options = {}) {
528
534
  * @returns {Promise<GitLabIssue>} The issue
529
535
  */
530
536
  async function getIssue(projectId, issueIid) {
537
+ projectId = decodeURIComponent(projectId); // Decode project ID
531
538
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`);
532
539
  const response = await fetch(url.toString(), {
533
540
  ...DEFAULT_FETCH_CONFIG,
@@ -546,6 +553,7 @@ async function getIssue(projectId, issueIid) {
546
553
  * @returns {Promise<GitLabIssue>} The updated issue
547
554
  */
548
555
  async function updateIssue(projectId, issueIid, options) {
556
+ projectId = decodeURIComponent(projectId); // Decode project ID
549
557
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`);
550
558
  // Convert labels array to comma-separated string if present
551
559
  const body = { ...options };
@@ -570,6 +578,7 @@ async function updateIssue(projectId, issueIid, options) {
570
578
  * @returns {Promise<void>}
571
579
  */
572
580
  async function deleteIssue(projectId, issueIid) {
581
+ projectId = decodeURIComponent(projectId); // Decode project ID
573
582
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`);
574
583
  const response = await fetch(url.toString(), {
575
584
  ...DEFAULT_FETCH_CONFIG,
@@ -586,6 +595,7 @@ async function deleteIssue(projectId, issueIid) {
586
595
  * @returns {Promise<GitLabIssueWithLinkDetails[]>} List of issues with link details
587
596
  */
588
597
  async function listIssueLinks(projectId, issueIid) {
598
+ projectId = decodeURIComponent(projectId); // Decode project ID
589
599
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links`);
590
600
  const response = await fetch(url.toString(), {
591
601
  ...DEFAULT_FETCH_CONFIG,
@@ -604,6 +614,7 @@ async function listIssueLinks(projectId, issueIid) {
604
614
  * @returns {Promise<GitLabIssueLink>} The issue link
605
615
  */
606
616
  async function getIssueLink(projectId, issueIid, issueLinkId) {
617
+ projectId = decodeURIComponent(projectId); // Decode project ID
607
618
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links/${issueLinkId}`);
608
619
  const response = await fetch(url.toString(), {
609
620
  ...DEFAULT_FETCH_CONFIG,
@@ -624,6 +635,8 @@ async function getIssueLink(projectId, issueIid, issueLinkId) {
624
635
  * @returns {Promise<GitLabIssueLink>} The created issue link
625
636
  */
626
637
  async function createIssueLink(projectId, issueIid, targetProjectId, targetIssueIid, linkType = "relates_to") {
638
+ projectId = decodeURIComponent(projectId); // Decode project ID
639
+ targetProjectId = decodeURIComponent(targetProjectId); // Decode target project ID as well
627
640
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links`);
628
641
  const response = await fetch(url.toString(), {
629
642
  ...DEFAULT_FETCH_CONFIG,
@@ -648,6 +661,7 @@ async function createIssueLink(projectId, issueIid, targetProjectId, targetIssue
648
661
  * @returns {Promise<void>}
649
662
  */
650
663
  async function deleteIssueLink(projectId, issueIid, issueLinkId) {
664
+ projectId = decodeURIComponent(projectId); // Decode project ID
651
665
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links/${issueLinkId}`);
652
666
  const response = await fetch(url.toString(), {
653
667
  ...DEFAULT_FETCH_CONFIG,
@@ -664,6 +678,7 @@ async function deleteIssueLink(projectId, issueIid, issueLinkId) {
664
678
  * @returns {Promise<GitLabMergeRequest>} The created merge request
665
679
  */
666
680
  async function createMergeRequest(projectId, options) {
681
+ projectId = decodeURIComponent(projectId); // Decode project ID
667
682
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests`);
668
683
  const response = await fetch(url.toString(), {
669
684
  ...DEFAULT_FETCH_CONFIG,
@@ -697,6 +712,7 @@ async function createMergeRequest(projectId, options) {
697
712
  * @returns {Promise<GitLabDiscussion[]>} List of discussions
698
713
  */
699
714
  async function listMergeRequestDiscussions(projectId, mergeRequestIid) {
715
+ projectId = decodeURIComponent(projectId); // Decode project ID
700
716
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions`);
701
717
  const response = await fetch(url.toString(), {
702
718
  ...DEFAULT_FETCH_CONFIG,
@@ -719,6 +735,7 @@ async function listMergeRequestDiscussions(projectId, mergeRequestIid) {
719
735
  * @returns {Promise<GitLabDiscussionNote>} The updated note
720
736
  */
721
737
  async function updateMergeRequestNote(projectId, mergeRequestIid, discussionId, noteId, body, resolved) {
738
+ projectId = decodeURIComponent(projectId); // Decode project ID
722
739
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes/${noteId}`);
723
740
  const payload = { body };
724
741
  if (resolved !== undefined) {
@@ -746,6 +763,7 @@ async function updateMergeRequestNote(projectId, mergeRequestIid, discussionId,
746
763
  * @returns {Promise<GitLabCreateUpdateFileResponse>} The file update response
747
764
  */
748
765
  async function createOrUpdateFile(projectId, filePath, content, commitMessage, branch, previousPath, last_commit_id, commit_id) {
766
+ projectId = decodeURIComponent(projectId); // Decode project ID
749
767
  const encodedPath = encodeURIComponent(filePath);
750
768
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/files/${encodedPath}`);
751
769
  const body = {
@@ -813,6 +831,7 @@ async function createOrUpdateFile(projectId, filePath, content, commitMessage, b
813
831
  * @returns {Promise<GitLabTree>} The created tree
814
832
  */
815
833
  async function createTree(projectId, files, ref) {
834
+ projectId = decodeURIComponent(projectId); // Decode project ID
816
835
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/tree`);
817
836
  if (ref) {
818
837
  url.searchParams.append("ref", ref);
@@ -850,6 +869,7 @@ async function createTree(projectId, files, ref) {
850
869
  * @returns {Promise<GitLabCommit>} The created commit
851
870
  */
852
871
  async function createCommit(projectId, message, branch, actions) {
872
+ projectId = decodeURIComponent(projectId); // Decode project ID
853
873
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/commits`);
854
874
  const response = await fetch(url.toString(), {
855
875
  ...DEFAULT_FETCH_CONFIG,
@@ -948,6 +968,7 @@ async function createRepository(options) {
948
968
  * @returns {Promise<GitLabMergeRequest>} The merge request details
949
969
  */
950
970
  async function getMergeRequest(projectId, mergeRequestIid, branchName) {
971
+ projectId = decodeURIComponent(projectId); // Decode project ID
951
972
  let url;
952
973
  if (mergeRequestIid) {
953
974
  url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`);
@@ -980,6 +1001,7 @@ async function getMergeRequest(projectId, mergeRequestIid, branchName) {
980
1001
  * @returns {Promise<GitLabMergeRequestDiff[]>} The merge request diffs
981
1002
  */
982
1003
  async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view) {
1004
+ projectId = decodeURIComponent(projectId); // Decode project ID
983
1005
  if (!mergeRequestIid && !branchName) {
984
1006
  throw new Error("Either mergeRequestIid or branchName must be provided");
985
1007
  }
@@ -1009,6 +1031,7 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view
1009
1031
  * @returns {Promise<GitLabMergeRequest>} The updated merge request
1010
1032
  */
1011
1033
  async function updateMergeRequest(projectId, options, mergeRequestIid, branchName) {
1034
+ projectId = decodeURIComponent(projectId); // Decode project ID
1012
1035
  if (!mergeRequestIid && !branchName) {
1013
1036
  throw new Error("Either mergeRequestIid or branchName must be provided");
1014
1037
  }
@@ -1038,6 +1061,7 @@ async function updateMergeRequest(projectId, options, mergeRequestIid, branchNam
1038
1061
  */
1039
1062
  async function createNote(projectId, noteableType, // 'issue' 또는 'merge_request' 타입 명시
1040
1063
  noteableIid, body) {
1064
+ projectId = decodeURIComponent(projectId); // Decode project ID
1041
1065
  // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능
1042
1066
  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
1043
1067
  );
@@ -1128,6 +1152,7 @@ async function verifyNamespaceExistence(namespacePath, parentId) {
1128
1152
  * @returns {Promise<GitLabProject>} Project details
1129
1153
  */
1130
1154
  async function getProject(projectId, options = {}) {
1155
+ projectId = decodeURIComponent(projectId); // Decode project ID
1131
1156
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}`);
1132
1157
  if (options.license) {
1133
1158
  url.searchParams.append("license", "true");
@@ -1183,6 +1208,7 @@ async function listProjects(options = {}) {
1183
1208
  * @returns Array of GitLab labels
1184
1209
  */
1185
1210
  async function listLabels(projectId, options = {}) {
1211
+ projectId = decodeURIComponent(projectId); // Decode project ID
1186
1212
  // Construct the URL with project path
1187
1213
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`);
1188
1214
  // Add query parameters
@@ -1215,6 +1241,7 @@ async function listLabels(projectId, options = {}) {
1215
1241
  * @returns GitLab label
1216
1242
  */
1217
1243
  async function getLabel(projectId, labelId, includeAncestorGroups) {
1244
+ projectId = decodeURIComponent(projectId); // Decode project ID
1218
1245
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`);
1219
1246
  // Add query parameters
1220
1247
  if (includeAncestorGroups !== undefined) {
@@ -1238,6 +1265,7 @@ async function getLabel(projectId, labelId, includeAncestorGroups) {
1238
1265
  * @returns Created GitLab label
1239
1266
  */
1240
1267
  async function createLabel(projectId, options) {
1268
+ projectId = decodeURIComponent(projectId); // Decode project ID
1241
1269
  // Make the API request
1242
1270
  const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`, {
1243
1271
  ...DEFAULT_FETCH_CONFIG,
@@ -1259,6 +1287,7 @@ async function createLabel(projectId, options) {
1259
1287
  * @returns Updated GitLab label
1260
1288
  */
1261
1289
  async function updateLabel(projectId, labelId, options) {
1290
+ projectId = decodeURIComponent(projectId); // Decode project ID
1262
1291
  // Make the API request
1263
1292
  const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`, {
1264
1293
  ...DEFAULT_FETCH_CONFIG,
@@ -1278,6 +1307,7 @@ async function updateLabel(projectId, labelId, options) {
1278
1307
  * @param labelId The ID or name of the label to delete
1279
1308
  */
1280
1309
  async function deleteLabel(projectId, labelId) {
1310
+ projectId = decodeURIComponent(projectId); // Decode project ID
1281
1311
  // Make the API request
1282
1312
  const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`, {
1283
1313
  ...DEFAULT_FETCH_CONFIG,
@@ -1339,6 +1369,7 @@ async function listGroupProjects(options) {
1339
1369
  * List wiki pages in a project
1340
1370
  */
1341
1371
  async function listWikiPages(projectId, options = {}) {
1372
+ projectId = decodeURIComponent(projectId); // Decode project ID
1342
1373
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis`);
1343
1374
  if (options.page)
1344
1375
  url.searchParams.append("page", options.page.toString());
@@ -1355,6 +1386,7 @@ async function listWikiPages(projectId, options = {}) {
1355
1386
  * Get a specific wiki page
1356
1387
  */
1357
1388
  async function getWikiPage(projectId, slug) {
1389
+ projectId = decodeURIComponent(projectId); // Decode project ID
1358
1390
  const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, { ...DEFAULT_FETCH_CONFIG });
1359
1391
  await handleGitLabError(response);
1360
1392
  const data = await response.json();
@@ -1364,6 +1396,7 @@ async function getWikiPage(projectId, slug) {
1364
1396
  * Create a new wiki page
1365
1397
  */
1366
1398
  async function createWikiPage(projectId, title, content, format) {
1399
+ projectId = decodeURIComponent(projectId); // Decode project ID
1367
1400
  const body = { title, content };
1368
1401
  if (format)
1369
1402
  body.format = format;
@@ -1380,6 +1413,7 @@ async function createWikiPage(projectId, title, content, format) {
1380
1413
  * Update an existing wiki page
1381
1414
  */
1382
1415
  async function updateWikiPage(projectId, slug, title, content, format) {
1416
+ projectId = decodeURIComponent(projectId); // Decode project ID
1383
1417
  const body = {};
1384
1418
  if (title)
1385
1419
  body.title = title;
@@ -1400,6 +1434,7 @@ async function updateWikiPage(projectId, slug, title, content, format) {
1400
1434
  * Delete a wiki page
1401
1435
  */
1402
1436
  async function deleteWikiPage(projectId, slug) {
1437
+ projectId = decodeURIComponent(projectId); // Decode project ID
1403
1438
  const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, {
1404
1439
  ...DEFAULT_FETCH_CONFIG,
1405
1440
  method: "DELETE",
@@ -1413,6 +1448,7 @@ async function deleteWikiPage(projectId, slug) {
1413
1448
  * @returns {Promise<GitLabTreeItem[]>}
1414
1449
  */
1415
1450
  async function getRepositoryTree(options) {
1451
+ options.project_id = decodeURIComponent(options.project_id); // Decode project_id within options
1416
1452
  const queryParams = new URLSearchParams();
1417
1453
  if (options.path)
1418
1454
  queryParams.append("path", options.path);
@@ -1447,11 +1483,29 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
1447
1483
  ? allTools.filter((tool) => readOnlyTools.includes(tool.name))
1448
1484
  : allTools;
1449
1485
  // Toggle wiki tools by USE_GITLAB_WIKI flag
1450
- const tools = USE_GITLAB_WIKI
1486
+ let tools = USE_GITLAB_WIKI
1451
1487
  ? tools0
1452
1488
  : tools0.filter((tool) => !wikiToolNames.includes(tool.name));
1489
+ // <<< START: Gemini 호환성을 위해 $schema 제거 >>>
1490
+ tools = tools.map((tool) => {
1491
+ // inputSchema가 존재하고 객체인지 확인
1492
+ if (tool.inputSchema &&
1493
+ typeof tool.inputSchema === "object" &&
1494
+ tool.inputSchema !== null) {
1495
+ // $schema 키가 존재하면 삭제
1496
+ if ("$schema" in tool.inputSchema) {
1497
+ // 불변성을 위해 새로운 객체 생성 (선택적이지만 권장)
1498
+ const modifiedSchema = { ...tool.inputSchema };
1499
+ delete modifiedSchema.$schema;
1500
+ return { ...tool, inputSchema: modifiedSchema };
1501
+ }
1502
+ }
1503
+ // 변경이 필요 없으면 그대로 반환
1504
+ return tool;
1505
+ });
1506
+ // <<< END: Gemini 호환성을 위해 $schema 제거 >>>
1453
1507
  return {
1454
- tools,
1508
+ tools, // $schema가 제거된 도구 목록 반환
1455
1509
  };
1456
1510
  });
1457
1511
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "1.0.35",
3
+ "version": "1.0.36",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",