list_issues
Retrieve and filter GitLab project issues by assignee, author, labels, milestones, and more using customizable search parameters to streamline issue tracking and management.
Instructions
Get issues for a GitLab project
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| assignee_id | No | ||
| author_id | No | ||
| created_after | No | ||
| created_before | No | ||
| iid | No | ||
| labels | No | ||
| milestone | No | ||
| order_by | No | ||
| page | No | ||
| per_page | No | ||
| project_id | No | ||
| scope | No | ||
| search | No | ||
| sort | No | ||
| state | No | ||
| updated_after | No | ||
| updated_before | No |
Implementation Reference
- src/index.ts:481-521 (handler)Handler for the list_issues tool that validates input, calls the GitLab API via gitlabApi.listIssues, and formats the response.case "list_issues": { // Parse and validate the arguments const args = ListIssuesSchema.parse(request.params.arguments); // Additional validation for pagination parameters if (args.per_page && (args.per_page < 1 || args.per_page > 100)) { throw new Error("per_page must be between 1 and 100"); } if (args.page && args.page < 1) { throw new Error("page must be greater than 0"); } // Validate date formats if provided const dateFields = [ "created_after", "created_before", "updated_after", "updated_before", ]; dateFields.forEach((field) => { const value = args[field as keyof typeof args]; if ( typeof value === 'string' && !isValidISODate(value) ) { throw new Error( `${field} must be a valid ISO 8601 date (YYYY-MM-DDTHH:MM:SSZ)` ); } }); // Extract project_id and options const { project_id, ...options } = args; // Call the API function const issues = await gitlabApi.listIssues(project_id, options); // Format and return the response return formatIssuesResponse(issues); }
- src/schemas.ts:478-496 (schema)Zod schema defining the input parameters and validation for the list_issues tool.export const ListIssuesSchema = z.object({ project_id: z.string(), iid: z.union([z.number(), z.string()]).optional(), state: z.enum(['opened', 'closed', 'all']).optional(), labels: z.string().optional(), milestone: z.string().optional(), scope: z.enum(['created_by_me', 'assigned_to_me', 'all']).optional(), author_id: z.number().optional(), assignee_id: z.number().optional(), search: z.string().optional(), created_after: z.string().optional(), created_before: z.string().optional(), updated_after: z.string().optional(), updated_before: z.string().optional(), order_by: z.string().optional(), sort: z.enum(['asc', 'desc']).optional(), page: z.number().optional(), per_page: z.number().optional() });
- src/index.ts:180-185 (registration)Registration of the list_issues tool in the ALL_TOOLS array, specifying name, description, input schema, and read-only status.{ name: "list_issues", description: "Get issues for a GitLab project", inputSchema: createJsonSchema(ListIssuesSchema), readOnly: true },
- src/gitlab-api.ts:543-608 (helper)GitLabApi class method that performs the HTTP request to fetch issues, handles iid filtering client-side, and parses the response.async listIssues( projectId: string, options: { iid?: number | string; state?: "opened" | "closed" | "all"; labels?: string; milestone?: string; scope?: "created_by_me" | "assigned_to_me" | "all"; author_id?: number; assignee_id?: number; search?: string; created_after?: string; created_before?: string; updated_after?: string; updated_before?: string; order_by?: string; sort?: "asc" | "desc"; page?: number; per_page?: number; } = {} ): Promise<GitLabIssuesResponse> { // Extract iid for client-side filtering if provided const { iid, ...apiOptions } = options; // Construct the URL with the project ID const url = new URL( `${this.apiUrl}/projects/${encodeURIComponent(projectId)}/issues` ); // Add all query parameters except iid (we'll filter that client-side) Object.entries(apiOptions).forEach(([key, value]) => { if (value !== undefined) { url.searchParams.append(key, value.toString()); } }); const response = await fetch(url.toString(), { headers: { Authorization: `Bearer ${this.token}`, }, }); if (!response.ok) { throw new McpError( ErrorCode.InternalError, `GitLab API error: ${response.statusText}` ); } // Parse the response JSON const issues = await response.json() as any[]; // If iid is provided, filter the issues by iid const filteredIssues = iid !== undefined ? issues.filter(issue => issue.iid?.toString() === iid.toString()) : issues; // Get the total count - if filtered, use the filtered length const totalCount = iid !== undefined ? filteredIssues.length : parseInt(response.headers.get("X-Total") || "0"); // Validate and return the response return GitLabIssuesResponseSchema.parse({ count: totalCount, items: filteredIssues, }); }
- src/formatters.ts:82-115 (helper)Formats the issues data into a readable summary and structured JSON for the MCP tool response.export function formatIssuesResponse(issues: GitLabIssuesResponse) { // Create a summary of the issues const summary = `Found ${issues.count} issues`; // Format the issues data const formattedIssues = issues.items.map(issue => ({ id: issue.id, iid: issue.iid, title: issue.title, description: issue.description, state: issue.state, created_at: issue.created_at, updated_at: issue.updated_at, closed_at: issue.closed_at, labels: issue.labels, author: { name: issue.author.name, username: issue.author.username }, assignees: issue.assignees.map(assignee => ({ name: assignee.name, username: assignee.username })), web_url: issue.web_url })); // Return the formatted response return { content: [ { type: "text", text: summary }, { type: "text", text: JSON.stringify(formattedIssues, null, 2) } ] }; }