diff --git a/README.md b/README.md
index 1bcc076..bd28a4f 100644
--- a/README.md
+++ b/README.md
@@ -40,36 +40,146 @@ env GITLAB_PERSONAL_ACCESS_TOKEN=your_gitlab_token GITLAB_API_URL=your_gitlab_ap
- `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token.
- `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`)
-## Tools Reference 🛠️
+## Tools 🛠️
+
+1. `create_or_update_file`
+
+ - Create or update a single file in a GitLab project. 📝
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `file_path` (string): Path to create/update the file
+ - `content` (string): File content
+ - `commit_message` (string): Commit message
+ - `branch` (string): Branch to create/update the file in
+ - `previous_path` (optional string): Previous file path when renaming a file
+ - Returns: File content and commit details
+
+2. `push_files`
+
+ - Push multiple files in a single commit. 📤
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `branch` (string): Branch to push to
+ - `files` (array): Array of files to push, each with `file_path` and `content` properties
+ - `commit_message` (string): Commit message
+ - Returns: Updated branch reference
+
+3. `search_repositories`
+
+ - Search for GitLab projects. 🔍
+ - Inputs:
+ - `search` (string): Search query
+ - `page` (optional number): Page number (default: 1)
+ - `per_page` (optional number): Results per page (default: 20, max: 100)
+ - Returns: Project search results
+
+4. `create_repository`
+
+ - Create a new GitLab project. ➕
+ - Inputs:
+ - `name` (string): Project name
+ - `description` (optional string): Project description
+ - `visibility` (optional string): Project visibility level (public, private, internal)
+ - `initialize_with_readme` (optional boolean): Initialize with README
+ - Returns: Details of the created project
+
+5. `get_file_contents`
+
+ - Get the contents of a file or directory. 📂
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `file_path` (string): Path to the file/directory
+ - `ref` (optional string): Branch, tag, or commit SHA (default: default branch)
+ - Returns: File/directory content
+
+6. `create_issue`
+
+ - Create a new issue. 🐛
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `title` (string): Issue title
+ - `description` (string): Issue description
+ - `assignee_ids` (optional number[]): Array of assignee IDs
+ - `milestone_id` (optional number): Milestone ID
+ - `labels` (optional string[]): Array of labels
+ - Returns: Details of the created issue
+
+7. `create_merge_request`
+
+ - Create a new merge request. 🚀
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `title` (string): Merge request title
+ - `description` (string): Merge request description
+ - `source_branch` (string): Branch with changes
+ - `target_branch` (string): Branch to merge into
+ - `allow_collaboration` (optional boolean): Allow collaborators to push commits to the source branch
+ - `draft` (optional boolean): Create as a draft merge request
+ - Returns: Details of the created merge request
+
+8. `fork_repository`
+
+ - Fork a project. 🍴
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path to fork
+ - `namespace` (optional string): Namespace to fork into (default: user namespace)
+ - Returns: Details of the forked project
+
+9. `create_branch`
+
+ - Create a new branch. 🌿
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `name` (string): New branch name
+ - `ref` (optional string): Ref to create the branch from (branch, tag, commit SHA, default: default branch)
+ - Returns: Created branch reference
+
+10. `get_merge_request`
+
+ - Get details of a merge request. ℹ️
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `merge_request_iid` (number): Merge request IID
+ - Returns: Merge request details
+
+11. `get_merge_request_diffs`
+
+ - Get changes (diffs) of a merge request. diff
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `merge_request_iid` (number): Merge request IID
+ - `view` (optional string): Diff view type ('inline' or 'parallel')
+ - Returns: Array of merge request diff information
+
+12. `update_merge_request`
+
+ - Update a merge request. 🔄
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `merge_request_iid` (number): Merge request IID
+ - `title` (optional string): New title
+ - `description` (string): New description
+ - `target_branch` (optional string): New target branch
+ - `state_event` (optional string): Merge request state change event ('close', 'reopen')
+ - `remove_source_branch` (optional boolean): Remove source branch after merge
+ - `allow_collaboration` (optional boolean): Allow collaborators to push commits to the source branch
+ - Returns: Updated merge request details
+
+13. `create_note`
+ - Create a new note (comment) to an issue or merge request. 💬
+ - Inputs:
+ - `project_id` (string): Project ID or namespace/project_path
+ - `noteable_type` (string): Type of noteable ("issue" or "merge_request")
+ - `noteable_iid` (number): IID of the issue or merge request
+ - `body` (string): Note content
+ - Returns: Details of the created note
-| Tool | Description | Parameters | Returns |
-|------|-------------|------------|---------|
-| **`create_or_update_file`** | Create or update a single file in a GitLab project 📝 | • `project_id` (string): Project ID or path
• `file_path` (string): Path to create/update
• `content` (string): File content
• `commit_message` (string): Commit message
• `branch` (string): Target branch
• `previous_path` (optional): Previous path when renaming | File content and commit details |
-| **`push_files`** | Push multiple files in a single commit 📤 (internally creates a tree and commit) | • `project_id` (string): Project ID or path
• `branch` (string): Target branch
• `files` (array): Array of files with `file_path` and `content`
• `commit_message` (string): Commit message | Updated branch reference |
-| **`search_repositories`** | Search for GitLab projects 🔍 | • `search` (string): Search query
• `page` (optional): Page number (default: 1)
• `per_page` (optional): Results per page (default: 20) | Project search results |
-| **`create_repository`** | Create a new GitLab project ➕ | • `name` (string): Project name
• `description` (optional): Project description
• `visibility` (optional): Visibility level
• `initialize_with_readme` (optional): Initialize with README | Created project details |
-| **`get_file_contents`** | Get the contents of a file or directory 📂 | • `project_id` (string): Project ID or path
• `file_path` (string): Path to file/directory
• `ref` (optional): Branch, tag, or commit SHA | File/directory content |
-| **`create_issue`** | Create a new issue 🐛 | • `project_id` (string): Project ID or path
• `title` (string): Issue title
• `description` (string): Issue description
• `assignee_ids` (optional): Array of assignee IDs
• `milestone_id` (optional): Milestone ID
• `labels` (optional): Array of labels | Created issue details |
-| **`list_issues`** | List issues in a project with comprehensive filtering options 📋 | • `project_id` (string): Project ID or path
• Optional filters: `assignee_id`, `assignee_username`, `author_id`, `author_username`, `confidential`, `created_after/before`, `due_date`, `label_name`, `milestone`, `scope`, `search`, `state`, `updated_after/before`
• Pagination: `page`, `per_page` | Array of issues |
-| **`get_issue`** | Get details of a specific issue | • `project_id` (string): Project ID or path
• `issue_iid` (number): Issue IID | Issue details |
-| **`update_issue`** | Update an existing issue ✏️ | • `project_id` (string): Project ID or path
• `issue_iid` (number): Issue IID
• Editable fields: `title`, `description`, `assignee_ids`, `labels`, `milestone_id`, `state_event` (close/reopen), `confidential`, `discussion_locked`, `due_date`, `weight` | Updated issue details |
-| **`delete_issue`** | Delete an issue | • `project_id` (string): Project ID or path
• `issue_iid` (number): Issue IID | Success message |
-| **`list_issue_links`** | List all links for a specific issue | • `project_id` (string): Project ID or path
• `issue_iid` (number): Issue IID | Array of linked issues |
-| **`get_issue_link`** | Get details of a specific issue link | • `project_id` (string): Project ID or path
• `issue_iid` (number): Issue IID
• `issue_link_id` (number): Link ID | Issue link details |
-| **`create_issue_link`** | Create a link between two issues | • `project_id` (string): Project ID or path
• `issue_iid` (number): Source issue IID
• `target_project_id` (string): Target project ID
• `target_issue_iid` (number): Target issue IID
• `link_type` (optional): Relationship type | Created link details |
-| **`delete_issue_link`** | Delete an issue link | • `project_id` (string): Project ID or path
• `issue_iid` (number): Issue IID
• `issue_link_id` (number): Link ID | Success message |
-| **`create_merge_request`** | Create a new merge request 🚀 | • `project_id` (string): Project ID or path
• `title` (string): MR title
• `description` (string): MR description
• `source_branch` (string): Branch with changes
• `target_branch` (string): Branch to merge into
• `allow_collaboration` (optional): Allow collaborators
• `draft` (optional): Create as draft | Created merge request details |
-| **`fork_repository`** | Fork a project 🍴 | • `project_id` (string): Project ID or path to fork
• `namespace` (optional): Namespace to fork into | Forked project details |
-| **`create_branch`** | Create a new branch 🌿 | • `project_id` (string): Project ID or path
• `branch` (string): New branch name
• `ref` (optional): Reference to create from | Created branch reference |
-| **`get_merge_request`** | Get details of a merge request ℹ️ | • `project_id` (string): Project ID or path
• `merge_request_iid` (number): MR IID | Merge request details |
-| **`get_merge_request_diffs`** | Get changes of a merge request | • `project_id` (string): Project ID or path
• `merge_request_iid` (number): MR IID
• `view` (optional): Diff view type | Array of merge request diffs |
-| **`update_merge_request`** | Update a merge request 🔄 | • `project_id` (string): Project ID or path
• `merge_request_iid` (number): MR IID
• Editable fields: `title`, `description`, `target_branch`, `assignee_ids`, `labels`, `state_event` (close/reopen), `remove_source_branch`, `squash`, `draft` | Updated merge request details |
-| **`create_note`** | Create a comment on an issue or MR 💬 | • `project_id` (string): Project ID or path
• `noteable_type` (string): "issue" or "merge_request"
• `noteable_iid` (number): IID of the issue or MR
• `body` (string): Comment content | Created note details |
-| **`list_namespaces`** | List available namespaces | • `search` (optional): Search term
• `page` (optional): Page number
• `per_page` (optional): Results per page
• `owned` (optional): Filter by ownership | Array of namespaces |
-| **`get_namespace`** | Get details of a namespace | • `namespace_id` (string): Namespace ID or path | Namespace details |
-| **`verify_namespace`** | Check if a namespace exists | • `path` (string): Namespace path to verify | Verification result |
-| **`get_project`** | Get details of a specific project | • `project_id` (string): Project ID or path | Project details |
| **`list_projects`** | List accessible projects with rich filtering options 📊 | • Search/filtering: `search`, `owned`, `membership`, `archived`, `visibility`
• Features filtering: `with_issues_enabled`, `with_merge_requests_enabled`
• Sorting: `order_by`, `sort`
• Access control: `min_access_level`
• Pagination: `page`, `per_page`, `simple` | Array of projects |
+| **`list_labels`** | List all labels for a project with filtering options 🏷️ | • `project_id` (string): Project ID or path
• `with_counts` (optional): Include issue and merge request counts
• `include_ancestor_groups` (optional): Include ancestor groups
• `search` (optional): Filter labels by keyword | Array of labels |
+| **`get_label`** | Get a single label from a project 🏷️ | • `project_id` (string): Project ID or path
• `label_id` (number/string): Label ID or name
• `include_ancestor_groups` (optional): Include ancestor groups | Label details |
+| **`create_label`** | Create a new label in a project 🏷️➕ | • `project_id` (string): Project ID or path
• `name` (string): Label name
• `color` (string): Color in hex format (e.g., "#FF0000")
• `description` (optional): Label description
• `priority` (optional): Label priority | Created label details |
+| **`update_label`** | Update an existing label in a project 🏷️✏️ | • `project_id` (string): Project ID or path
• `label_id` (number/string): Label ID or name
• `new_name` (optional): New label name
• `color` (optional): New color in hex format
• `description` (optional): New description
• `priority` (optional): New priority | Updated label details |
+| **`delete_label`** | Delete a label from a project 🏷️❌ | • `project_id` (string): Project ID or path
• `label_id` (number/string): Label ID or name | Success message |
## Environment Variable Configuration
diff --git a/index.ts b/index.ts
index 322583f..9b9c9d4 100644
--- a/index.ts
+++ b/index.ts
@@ -27,6 +27,7 @@ import {
GitLabNamespaceSchema,
GitLabNamespaceExistsResponseSchema,
GitLabProjectSchema,
+ GitLabLabelSchema,
CreateRepositoryOptionsSchema,
CreateIssueOptionsSchema,
CreateMergeRequestOptionsSchema,
@@ -59,6 +60,11 @@ import {
VerifyNamespaceSchema,
GetProjectSchema,
ListProjectsSchema,
+ ListLabelsSchema,
+ GetLabelSchema,
+ CreateLabelSchema,
+ UpdateLabelSchema,
+ DeleteLabelSchema,
CreateNoteSchema,
type GitLabFork,
type GitLabReference,
@@ -77,6 +83,7 @@ import {
type GitLabNamespace,
type GitLabNamespaceExistsResponse,
type GitLabProject,
+ type GitLabLabel,
} from "./schemas.js";
/**
@@ -1204,22 +1211,187 @@ async function getProject(
* @returns {Promise} List of projects
*/
async function listProjects(options: z.infer = {}): Promise {
- const url = new URL(`${GITLAB_API_URL}/projects`);
+ // Construct the query parameters
+ const params = new URLSearchParams();
+ for (const [key, value] of Object.entries(options)) {
+ if (value !== undefined && value !== null) {
+ if (typeof value === "boolean") {
+ params.append(key, value ? "true" : "false");
+ } else {
+ params.append(key, String(value));
+ }
+ }
+ }
- // Add all the query parameters from options
+ // Make the API request
+ const response = await fetch(
+ `${GITLAB_API_URL}/projects?${params.toString()}`,
+ {
+ method: "GET",
+ headers: DEFAULT_HEADERS,
+ }
+ );
+
+ // Handle errors
+ await handleGitLabError(response);
+
+ // Parse and return the data
+ const data = await response.json();
+ return z.array(GitLabProjectSchema).parse(data);
+}
+
+/**
+ * List labels for a project
+ *
+ * @param projectId The ID or URL-encoded path of the project
+ * @param options Optional parameters for listing labels
+ * @returns Array of GitLab labels
+ */
+async function listLabels(
+ projectId: string,
+ options: Omit, "project_id"> = {}
+): Promise {
+ // Construct the URL with project path
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`);
+
+ // Add query parameters
Object.entries(options).forEach(([key, value]) => {
if (value !== undefined) {
- url.searchParams.append(key, value.toString());
+ if (typeof value === "boolean") {
+ url.searchParams.append(key, value ? "true" : "false");
+ } else {
+ url.searchParams.append(key, String(value));
+ }
}
});
+ // Make the API request
const response = await fetch(url.toString(), {
headers: DEFAULT_HEADERS,
});
+ // Handle errors
await handleGitLabError(response);
+
+ // Parse and return the data
const data = await response.json();
- return z.array(GitLabRepositorySchema).parse(data);
+ return data as GitLabLabel[];
+}
+
+/**
+ * Get a single label from a project
+ *
+ * @param projectId The ID or URL-encoded path of the project
+ * @param labelId The ID or name of the label
+ * @param includeAncestorGroups Whether to include ancestor groups
+ * @returns GitLab label
+ */
+async function getLabel(
+ projectId: string,
+ labelId: number | string,
+ includeAncestorGroups?: boolean
+): Promise {
+ const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`);
+
+ // Add query parameters
+ if (includeAncestorGroups !== undefined) {
+ url.searchParams.append("include_ancestor_groups", includeAncestorGroups ? "true" : "false");
+ }
+
+ // Make the API request
+ const response = await fetch(url.toString(), {
+ headers: DEFAULT_HEADERS,
+ });
+
+ // Handle errors
+ await handleGitLabError(response);
+
+ // Parse and return the data
+ const data = await response.json();
+ return data as GitLabLabel;
+}
+
+/**
+ * Create a new label in a project
+ *
+ * @param projectId The ID or URL-encoded path of the project
+ * @param options Options for creating the label
+ * @returns Created GitLab label
+ */
+async function createLabel(
+ projectId: string,
+ options: Omit, "project_id">
+): Promise {
+ // Make the API request
+ const response = await fetch(
+ `${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`,
+ {
+ method: "POST",
+ headers: DEFAULT_HEADERS,
+ body: JSON.stringify(options),
+ }
+ );
+
+ // Handle errors
+ await handleGitLabError(response);
+
+ // Parse and return the data
+ const data = await response.json();
+ return data as GitLabLabel;
+}
+
+/**
+ * Update an existing label in a project
+ *
+ * @param projectId The ID or URL-encoded path of the project
+ * @param labelId The ID or name of the label to update
+ * @param options Options for updating the label
+ * @returns Updated GitLab label
+ */
+async function updateLabel(
+ projectId: string,
+ labelId: number | string,
+ options: Omit, "project_id" | "label_id">
+): Promise {
+ // Make the API request
+ const response = await fetch(
+ `${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`,
+ {
+ method: "PUT",
+ headers: DEFAULT_HEADERS,
+ body: JSON.stringify(options),
+ }
+ );
+
+ // Handle errors
+ await handleGitLabError(response);
+
+ // Parse and return the data
+ const data = await response.json();
+ return data as GitLabLabel;
+}
+
+/**
+ * Delete a label from a project
+ *
+ * @param projectId The ID or URL-encoded path of the project
+ * @param labelId The ID or name of the label to delete
+ */
+async function deleteLabel(
+ projectId: string,
+ labelId: number | string
+): Promise {
+ // Make the API request
+ const response = await fetch(
+ `${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`,
+ {
+ method: "DELETE",
+ headers: DEFAULT_HEADERS,
+ }
+ );
+
+ // Handle errors
+ await handleGitLabError(response);
}
server.setRequestHandler(ListToolsRequestSchema, async () => {
@@ -1358,6 +1530,31 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
description: "List projects accessible by the current user",
inputSchema: zodToJsonSchema(ListProjectsSchema),
},
+ {
+ name: "list_labels",
+ description: "List labels for a project",
+ inputSchema: zodToJsonSchema(ListLabelsSchema),
+ },
+ {
+ name: "get_label",
+ description: "Get a single label from a project",
+ inputSchema: zodToJsonSchema(GetLabelSchema),
+ },
+ {
+ name: "create_label",
+ description: "Create a new label in a project",
+ inputSchema: zodToJsonSchema(CreateLabelSchema),
+ },
+ {
+ name: "update_label",
+ description: "Update an existing label in a project",
+ inputSchema: zodToJsonSchema(UpdateLabelSchema),
+ },
+ {
+ name: "delete_label",
+ description: "Delete a label from a project",
+ inputSchema: zodToJsonSchema(DeleteLabelSchema),
+ },
],
};
});
@@ -1613,6 +1810,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
case "list_projects": {
const args = ListProjectsSchema.parse(request.params.arguments);
const projects = await listProjects(args);
+
return {
content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
};
@@ -1699,6 +1897,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
};
}
+ case "list_labels": {
+ const args = ListLabelsSchema.parse(request.params.arguments);
+ const labels = await listLabels(args.project_id, args);
+ return {
+ content: [{ type: "text", text: JSON.stringify(labels, null, 2) }],
+ };
+ }
+
+ case "get_label": {
+ const args = GetLabelSchema.parse(request.params.arguments);
+ const label = await getLabel(args.project_id, args.label_id, args.include_ancestor_groups);
+ return {
+ content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
+ };
+ }
+
+ case "create_label": {
+ const args = CreateLabelSchema.parse(request.params.arguments);
+ const label = await createLabel(args.project_id, args);
+ return {
+ content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
+ };
+ }
+
+ case "update_label": {
+ const args = UpdateLabelSchema.parse(request.params.arguments);
+ const { project_id, label_id, ...options } = args;
+ const label = await updateLabel(project_id, label_id, options);
+ return {
+ content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
+ };
+ }
+
+ case "delete_label": {
+ const args = DeleteLabelSchema.parse(request.params.arguments);
+ await deleteLabel(args.project_id, args.label_id);
+ return {
+ content: [{ type: "text", text: JSON.stringify({ status: "success", message: "Label deleted successfully" }, null, 2) }],
+ };
+ }
+
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}
diff --git a/package.json b/package.json
index 14bd4f3..f98c4b6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@zereight/mcp-gitlab",
- "version": "1.0.18",
+ "version": "1.0.19",
"description": "MCP server for using the GitLab API",
"license": "MIT",
"author": "zereight",
diff --git a/schemas.ts b/schemas.ts
index a3fd19c..19a7904 100644
--- a/schemas.ts
+++ b/schemas.ts
@@ -228,7 +228,15 @@ export const GitLabLabelSchema = z.object({
id: z.number(),
name: z.string(),
color: z.string(),
- description: z.string().optional(),
+ text_color: z.string(),
+ description: z.string().nullable(),
+ description_html: z.string().nullable(),
+ open_issues_count: z.number().optional(),
+ closed_issues_count: z.number().optional(),
+ open_merge_requests_count: z.number().optional(),
+ subscribed: z.boolean().optional(),
+ priority: z.number().nullable().optional(),
+ is_project_label: z.boolean().optional(),
});
export const GitLabUserSchema = z.object({
@@ -619,6 +627,42 @@ export const ListProjectsSchema = z.object({
min_access_level: z.number().optional().describe("Filter by minimum access level"),
});
+// Label operation schemas
+export const ListLabelsSchema = z.object({
+ project_id: z.string().describe("Project ID or URL-encoded path"),
+ with_counts: z.boolean().optional().describe("Whether or not to include issue and merge request counts"),
+ include_ancestor_groups: z.boolean().optional().describe("Include ancestor groups"),
+ search: z.string().optional().describe("Keyword to filter labels by"),
+});
+
+export const GetLabelSchema = z.object({
+ project_id: z.string().describe("Project ID or URL-encoded path"),
+ label_id: z.union([z.number(), z.string()]).describe("The ID or title of a project's label"),
+ include_ancestor_groups: z.boolean().optional().describe("Include ancestor groups"),
+});
+
+export const CreateLabelSchema = z.object({
+ project_id: z.string().describe("Project ID or URL-encoded path"),
+ name: z.string().describe("The name of the label"),
+ color: z.string().describe("The color of the label given in 6-digit hex notation with leading '#' sign"),
+ description: z.string().optional().describe("The description of the label"),
+ priority: z.number().nullable().optional().describe("The priority of the label"),
+});
+
+export const UpdateLabelSchema = z.object({
+ project_id: z.string().describe("Project ID or URL-encoded path"),
+ label_id: z.union([z.number(), z.string()]).describe("The ID or title of a project's label"),
+ new_name: z.string().optional().describe("The new name of the label"),
+ color: z.string().optional().describe("The color of the label given in 6-digit hex notation with leading '#' sign"),
+ description: z.string().optional().describe("The new description of the label"),
+ priority: z.number().nullable().optional().describe("The new priority of the label"),
+});
+
+export const DeleteLabelSchema = z.object({
+ project_id: z.string().describe("Project ID or URL-encoded path"),
+ label_id: z.union([z.number(), z.string()]).describe("The ID or title of a project's label"),
+});
+
// Export types
export type GitLabAuthor = z.infer;
export type GitLabFork = z.infer;
@@ -645,3 +689,4 @@ export type GitLabIssueLink = z.infer;
export type GitLabNamespace = z.infer;
export type GitLabNamespaceExistsResponse = z.infer;
export type GitLabProject = z.infer;
+export type GitLabLabel = z.infer;