@zereight/mcp-gitlab 1.0.15 → 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -9,7 +9,7 @@ import { fileURLToPath } from "url";
9
9
  import { dirname } from "path";
10
10
  import fs from "fs";
11
11
  import path from "path";
12
- import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabMergeRequestDiffSchema, GetMergeRequestSchema, GetMergeRequestDiffsSchema, UpdateMergeRequestSchema, CreateNoteSchema, } from "./schemas.js";
12
+ import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, GitLabNamespaceSchema, GitLabNamespaceExistsResponseSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabMergeRequestDiffSchema, GetMergeRequestSchema, GetMergeRequestDiffsSchema, UpdateMergeRequestSchema, ListNamespacesSchema, GetNamespaceSchema, VerifyNamespaceSchema, GetProjectSchema, ListProjectsSchema, CreateNoteSchema, } from "./schemas.js";
13
13
  /**
14
14
  * Read version from package.json
15
15
  */
@@ -560,6 +560,121 @@ noteableIid, body) {
560
560
  }
561
561
  return await response.json();
562
562
  }
563
+ /**
564
+ * List all namespaces
565
+ * 사용 가능한 모든 네임스페이스 목록 조회
566
+ *
567
+ * @param {Object} options - Options for listing namespaces
568
+ * @param {string} [options.search] - Search query to filter namespaces
569
+ * @param {boolean} [options.owned_only] - Only return namespaces owned by the authenticated user
570
+ * @param {boolean} [options.top_level_only] - Only return top-level namespaces
571
+ * @returns {Promise<GitLabNamespace[]>} List of namespaces
572
+ */
573
+ async function listNamespaces(options) {
574
+ const url = new URL(`${GITLAB_API_URL}/namespaces`);
575
+ if (options.search) {
576
+ url.searchParams.append("search", options.search);
577
+ }
578
+ if (options.owned_only) {
579
+ url.searchParams.append("owned_only", "true");
580
+ }
581
+ if (options.top_level_only) {
582
+ url.searchParams.append("top_level_only", "true");
583
+ }
584
+ const response = await fetch(url.toString(), {
585
+ headers: DEFAULT_HEADERS,
586
+ });
587
+ await handleGitLabError(response);
588
+ const data = await response.json();
589
+ return z.array(GitLabNamespaceSchema).parse(data);
590
+ }
591
+ /**
592
+ * Get details on a namespace
593
+ * 네임스페이스 상세 정보 조회
594
+ *
595
+ * @param {string} id - The ID or URL-encoded path of the namespace
596
+ * @returns {Promise<GitLabNamespace>} The namespace details
597
+ */
598
+ async function getNamespace(id) {
599
+ const url = new URL(`${GITLAB_API_URL}/namespaces/${encodeURIComponent(id)}`);
600
+ const response = await fetch(url.toString(), {
601
+ headers: DEFAULT_HEADERS,
602
+ });
603
+ await handleGitLabError(response);
604
+ const data = await response.json();
605
+ return GitLabNamespaceSchema.parse(data);
606
+ }
607
+ /**
608
+ * Verify if a namespace exists
609
+ * 네임스페이스 존재 여부 확인
610
+ *
611
+ * @param {string} namespacePath - The path of the namespace to check
612
+ * @param {number} [parentId] - The ID of the parent namespace
613
+ * @returns {Promise<GitLabNamespaceExistsResponse>} The verification result
614
+ */
615
+ async function verifyNamespaceExistence(namespacePath, parentId) {
616
+ const url = new URL(`${GITLAB_API_URL}/namespaces/${encodeURIComponent(namespacePath)}/exists`);
617
+ if (parentId) {
618
+ url.searchParams.append("parent_id", parentId.toString());
619
+ }
620
+ const response = await fetch(url.toString(), {
621
+ headers: DEFAULT_HEADERS,
622
+ });
623
+ await handleGitLabError(response);
624
+ const data = await response.json();
625
+ return GitLabNamespaceExistsResponseSchema.parse(data);
626
+ }
627
+ /**
628
+ * Get a single project
629
+ * 단일 프로젝트 조회
630
+ *
631
+ * @param {string} projectId - The ID or URL-encoded path of the project
632
+ * @param {Object} options - Options for getting project details
633
+ * @param {boolean} [options.license] - Include project license data
634
+ * @param {boolean} [options.statistics] - Include project statistics
635
+ * @param {boolean} [options.with_custom_attributes] - Include custom attributes in response
636
+ * @returns {Promise<GitLabProject>} Project details
637
+ */
638
+ async function getProject(projectId, options = {}) {
639
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}`);
640
+ if (options.license) {
641
+ url.searchParams.append("license", "true");
642
+ }
643
+ if (options.statistics) {
644
+ url.searchParams.append("statistics", "true");
645
+ }
646
+ if (options.with_custom_attributes) {
647
+ url.searchParams.append("with_custom_attributes", "true");
648
+ }
649
+ const response = await fetch(url.toString(), {
650
+ headers: DEFAULT_HEADERS,
651
+ });
652
+ await handleGitLabError(response);
653
+ const data = await response.json();
654
+ return GitLabRepositorySchema.parse(data);
655
+ }
656
+ /**
657
+ * List projects
658
+ * 프로젝트 목록 조회
659
+ *
660
+ * @param {Object} options - Options for listing projects
661
+ * @returns {Promise<GitLabProject[]>} List of projects
662
+ */
663
+ async function listProjects(options = {}) {
664
+ const url = new URL(`${GITLAB_API_URL}/projects`);
665
+ // Add all the query parameters from options
666
+ Object.entries(options).forEach(([key, value]) => {
667
+ if (value !== undefined) {
668
+ url.searchParams.append(key, value.toString());
669
+ }
670
+ });
671
+ const response = await fetch(url.toString(), {
672
+ headers: DEFAULT_HEADERS,
673
+ });
674
+ await handleGitLabError(response);
675
+ const data = await response.json();
676
+ return z.array(GitLabRepositorySchema).parse(data);
677
+ }
563
678
  server.setRequestHandler(ListToolsRequestSchema, async () => {
564
679
  return {
565
680
  tools: [
@@ -628,6 +743,31 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
628
743
  description: "Create a new note (comment) to an issue or merge request",
629
744
  inputSchema: zodToJsonSchema(CreateNoteSchema),
630
745
  },
746
+ {
747
+ name: "list_namespaces",
748
+ description: "List all namespaces available to the current user",
749
+ inputSchema: zodToJsonSchema(ListNamespacesSchema),
750
+ },
751
+ {
752
+ name: "get_namespace",
753
+ description: "Get details on a specified namespace",
754
+ inputSchema: zodToJsonSchema(GetNamespaceSchema),
755
+ },
756
+ {
757
+ name: "verify_namespace",
758
+ description: "Verify if a specified namespace already exists",
759
+ inputSchema: zodToJsonSchema(VerifyNamespaceSchema),
760
+ },
761
+ {
762
+ name: "get_project",
763
+ description: "Get details on a specified project",
764
+ inputSchema: zodToJsonSchema(GetProjectSchema),
765
+ },
766
+ {
767
+ name: "list_projects",
768
+ description: "List projects accessible by the current user",
769
+ inputSchema: zodToJsonSchema(ListProjectsSchema),
770
+ },
631
771
  ],
632
772
  };
633
773
  });
@@ -638,11 +778,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
638
778
  }
639
779
  switch (request.params.name) {
640
780
  case "fork_repository": {
641
- const args = ForkRepositorySchema.parse(request.params.arguments);
642
- const fork = await forkProject(args.project_id, args.namespace);
643
- return {
644
- content: [{ type: "text", text: JSON.stringify(fork, null, 2) }],
645
- };
781
+ const forkArgs = ForkRepositorySchema.parse(request.params.arguments);
782
+ try {
783
+ const forkedProject = await forkProject(forkArgs.project_id, forkArgs.namespace);
784
+ return {
785
+ content: [{ type: "text", text: JSON.stringify(forkedProject, null, 2) }],
786
+ };
787
+ }
788
+ catch (forkError) {
789
+ console.error("Error forking repository:", forkError);
790
+ let forkErrorMessage = "Failed to fork repository";
791
+ if (forkError instanceof Error) {
792
+ forkErrorMessage = `${forkErrorMessage}: ${forkError.message}`;
793
+ }
794
+ return {
795
+ content: [{ type: "text", text: JSON.stringify({ error: forkErrorMessage }, null, 2) }],
796
+ };
797
+ }
646
798
  }
647
799
  case "create_branch": {
648
800
  const args = CreateBranchSchema.parse(request.params.arguments);
@@ -739,6 +891,42 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
739
891
  ],
740
892
  };
741
893
  }
894
+ case "list_namespaces": {
895
+ const args = ListNamespacesSchema.parse(request.params.arguments);
896
+ const namespaces = await listNamespaces(args);
897
+ return {
898
+ content: [{ type: "text", text: JSON.stringify(namespaces, null, 2) }],
899
+ };
900
+ }
901
+ case "get_namespace": {
902
+ const args = GetNamespaceSchema.parse(request.params.arguments);
903
+ const namespace = await getNamespace(args.id);
904
+ return {
905
+ content: [{ type: "text", text: JSON.stringify(namespace, null, 2) }],
906
+ };
907
+ }
908
+ case "verify_namespace": {
909
+ const args = VerifyNamespaceSchema.parse(request.params.arguments);
910
+ const result = await verifyNamespaceExistence(args.namespace, args.parent_id);
911
+ return {
912
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
913
+ };
914
+ }
915
+ case "get_project": {
916
+ const args = GetProjectSchema.parse(request.params.arguments);
917
+ const { id, ...options } = args;
918
+ const project = await getProject(id, options);
919
+ return {
920
+ content: [{ type: "text", text: JSON.stringify(project, null, 2) }],
921
+ };
922
+ }
923
+ case "list_projects": {
924
+ const args = ListProjectsSchema.parse(request.params.arguments);
925
+ const projects = await listProjects(args);
926
+ return {
927
+ content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
928
+ };
929
+ }
742
930
  case "create_note": {
743
931
  const args = CreateNoteSchema.parse(request.params.arguments);
744
932
  const { project_id, noteable_type, noteable_iid, body } = args;
package/build/schemas.js CHANGED
@@ -5,6 +5,31 @@ export const GitLabAuthorSchema = z.object({
5
5
  email: z.string(),
6
6
  date: z.string(),
7
7
  });
8
+ // Namespace related schemas
9
+ export const GitLabNamespaceSchema = z.object({
10
+ id: z.number(),
11
+ name: z.string(),
12
+ path: z.string(),
13
+ kind: z.enum(["user", "group"]),
14
+ full_path: z.string(),
15
+ parent_id: z.number().nullable(),
16
+ avatar_url: z.string().nullable(),
17
+ web_url: z.string(),
18
+ members_count_with_descendants: z.number().optional(),
19
+ billable_members_count: z.number().optional(),
20
+ max_seats_used: z.number().optional(),
21
+ seats_in_use: z.number().optional(),
22
+ plan: z.string().optional(),
23
+ end_date: z.string().nullable().optional(),
24
+ trial_ends_on: z.string().nullable().optional(),
25
+ trial: z.boolean().optional(),
26
+ root_repository_size: z.number().optional(),
27
+ projects_count: z.number().optional(),
28
+ });
29
+ export const GitLabNamespaceExistsResponseSchema = z.object({
30
+ exists: z.boolean(),
31
+ suggests: z.array(z.string()).optional(),
32
+ });
8
33
  // Repository related schemas
9
34
  export const GitLabOwnerSchema = z.object({
10
35
  username: z.string(), // Changed from login to match GitLab API
@@ -28,6 +53,48 @@ export const GitLabRepositorySchema = z.object({
28
53
  created_at: z.string().optional(),
29
54
  last_activity_at: z.string().optional(),
30
55
  default_branch: z.string().optional(),
56
+ namespace: z.object({
57
+ id: z.number(),
58
+ name: z.string(),
59
+ path: z.string(),
60
+ kind: z.string(),
61
+ full_path: z.string(),
62
+ avatar_url: z.string().nullable().optional(),
63
+ web_url: z.string().optional(),
64
+ }).optional(),
65
+ readme_url: z.string().optional().nullable(),
66
+ topics: z.array(z.string()).optional(),
67
+ tag_list: z.array(z.string()).optional(), // deprecated but still present
68
+ open_issues_count: z.number().optional(),
69
+ archived: z.boolean().optional(),
70
+ forks_count: z.number().optional(),
71
+ star_count: z.number().optional(),
72
+ permissions: z.object({
73
+ project_access: z.object({
74
+ access_level: z.number(),
75
+ notification_level: z.number().optional(),
76
+ }).optional().nullable(),
77
+ group_access: z.object({
78
+ access_level: z.number(),
79
+ notification_level: z.number().optional(),
80
+ }).optional().nullable(),
81
+ }).optional(),
82
+ container_registry_enabled: z.boolean().optional(),
83
+ container_registry_access_level: z.string().optional(),
84
+ issues_enabled: z.boolean().optional(),
85
+ merge_requests_enabled: z.boolean().optional(),
86
+ wiki_enabled: z.boolean().optional(),
87
+ jobs_enabled: z.boolean().optional(),
88
+ snippets_enabled: z.boolean().optional(),
89
+ can_create_merge_request_in: z.boolean().optional(),
90
+ resolve_outdated_diff_discussions: z.boolean().optional(),
91
+ shared_runners_enabled: z.boolean().optional(),
92
+ shared_with_groups: z.array(z.object({
93
+ group_id: z.number(),
94
+ group_name: z.string(),
95
+ group_full_path: z.string(),
96
+ group_access_level: z.number(),
97
+ })).optional(),
31
98
  });
32
99
  // File content schemas
33
100
  export const GitLabFileContentSchema = z.object({
@@ -141,11 +208,11 @@ export const GitLabForkParentSchema = z.object({
141
208
  username: z.string(), // Changed from login to match GitLab API
142
209
  id: z.number(),
143
210
  avatar_url: z.string(),
144
- }),
211
+ }).optional(), // Made optional to handle cases where GitLab API doesn't include it
145
212
  web_url: z.string(), // Changed from html_url to match GitLab API
146
213
  });
147
214
  export const GitLabForkSchema = GitLabRepositorySchema.extend({
148
- forked_from_project: GitLabForkParentSchema, // Changed from parent to match GitLab API
215
+ forked_from_project: GitLabForkParentSchema.optional(), // Made optional to handle cases where GitLab API doesn't include it
149
216
  });
150
217
  // Issue related schemas
151
218
  export const GitLabLabelSchema = z.object({
@@ -364,3 +431,44 @@ export const CreateNoteSchema = z.object({
364
431
  noteable_iid: z.number().describe("IID of the issue or merge request"),
365
432
  body: z.string().describe("Note content"),
366
433
  });
434
+ // Add namespace-related operation schemas
435
+ export const ListNamespacesSchema = z.object({
436
+ search: z.string().optional().describe("Only returns namespaces accessible by the current user"),
437
+ owned_only: z.boolean().optional().describe("If true, only returns namespaces by the current user"),
438
+ top_level_only: z.boolean().optional().describe("In GitLab 16.8 and later, if true, only returns top-level namespaces"),
439
+ });
440
+ export const GetNamespaceSchema = z.object({
441
+ id: z.string().describe("ID or URL-encoded path of the namespace"),
442
+ });
443
+ export const VerifyNamespaceSchema = z.object({
444
+ namespace: z.string().describe("Path of the namespace"),
445
+ parent_id: z.number().optional().describe("ID of the parent namespace. If unspecified, only returns top-level namespaces"),
446
+ });
447
+ // Project API operation schemas
448
+ export const GetProjectSchema = z.object({
449
+ id: z.string().describe("ID or URL-encoded path of the project"),
450
+ license: z.boolean().optional().describe("Include project license data"),
451
+ statistics: z.boolean().optional().describe("Include project statistics"),
452
+ with_custom_attributes: z.boolean().optional().describe("Include custom attributes in response"),
453
+ });
454
+ export const ListProjectsSchema = z.object({
455
+ archived: z.boolean().optional().describe("Limit by archived status"),
456
+ id_after: z.number().optional().describe("Limit results to projects with IDs greater than the specified ID"),
457
+ id_before: z.number().optional().describe("Limit results to projects with IDs less than the specified ID"),
458
+ membership: z.boolean().optional().describe("Limit by projects that the current user is a member of"),
459
+ min_access_level: z.number().optional().describe("Limit by minimum access level"),
460
+ order_by: z.enum(['id', 'name', 'path', 'created_at', 'updated_at', 'last_activity_at']).optional().describe("Return projects ordered by field"),
461
+ owned: z.boolean().optional().describe("Limit by projects explicitly owned by the current user"),
462
+ search: z.string().optional().describe("Return list of projects matching the search criteria"),
463
+ simple: z.boolean().optional().describe("Return only limited fields for each project"),
464
+ sort: z.enum(['asc', 'desc']).optional().describe("Return projects sorted in ascending or descending order"),
465
+ starred: z.boolean().optional().describe("Limit by projects starred by the current user"),
466
+ visibility: z.enum(['public', 'internal', 'private']).optional().describe("Limit by visibility"),
467
+ with_custom_attributes: z.boolean().optional().describe("Include custom attributes in response"),
468
+ with_issues_enabled: z.boolean().optional().describe("Limit by enabled issues feature"),
469
+ with_merge_requests_enabled: z.boolean().optional().describe("Limit by enabled merge requests feature"),
470
+ with_programming_language: z.string().optional().describe("Limit by projects which use the given programming language"),
471
+ with_shared: z.boolean().optional().describe("Include projects shared to this group"),
472
+ page: z.number().optional().describe("Page number for pagination"),
473
+ per_page: z.number().optional().describe("Number of items per page"),
474
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",