list_issues
Retrieve issues from a GitHub repository with filters for state, labels, sort order, and date. Use to find open, closed, or all issues.
Instructions
List issues in a GitHub repository.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| owner | Yes | Repository owner | |
| repo | Yes | Repository name | |
| state | No | Filter by state | |
| labels | No | Filter by labels | |
| sort | No | Sort order | |
| direction | No | Sort direction | |
| since | No | Filter by date (ISO 8601 timestamp) | |
| per_page | No | Results per page (default 10, max 100) | |
| page | No | Page number (default 1) |
Implementation Reference
- src/tools/issues.ts:217-322 (handler)Handler function for the 'list_issues' tool. Calls octokit.rest.issues.listForRepo with params (owner, repo, state, labels, sort, direction, since, per_page, page), filters out pull requests, and formats results as markdown.
// Tool: List Issues server.tool( "list_issues", "List issues in a GitHub repository.", { owner: z.string().describe("Repository owner"), repo: z.string().describe("Repository name"), state: z .enum(["open", "closed", "all"]) .optional() .describe("Filter by state"), labels: z.array(z.string()).optional().describe("Filter by labels"), sort: z .enum(["created", "updated", "comments"]) .optional() .describe("Sort order"), direction: z.enum(["asc", "desc"]).optional().describe("Sort direction"), since: z .string() .optional() .describe("Filter by date (ISO 8601 timestamp)"), per_page: z .number() .optional() .default(10) .describe("Results per page (default 10, max 100)"), page: z .number() .optional() .default(1) .describe("Page number (default 1)"), }, async ({ owner, repo, state, labels, sort, direction, since, per_page, page, }) => { try { const response = await octokit.rest.issues.listForRepo({ owner, repo, state, labels: labels ? labels.join(",") : undefined, sort, direction, since, per_page, page, }) // Format the response as clean markdown const issues = response.data.filter(item => !item.pull_request) // Filter out PRs if (issues.length === 0) { return { content: [{ type: "text", text: "No issues found." }], } } let markdown = `# Issues for ${owner}/${repo}\n\n` markdown += `Showing ${issues.length} issue(s) - Page ${page}\n` if (response.data.length === per_page) { markdown += `*Note: More results may be available. Use 'page' parameter to see next page.*\n` } markdown += `\n` issues.forEach(issue => { markdown += `## #${issue.number}: ${issue.title}\n\n` markdown += `- **State**: ${issue.state}\n` markdown += `- **Author**: ${issue.user?.login || "Unknown"}\n` markdown += `- **Created**: ${new Date(issue.created_at).toLocaleDateString()}\n` markdown += `- **Updated**: ${new Date(issue.updated_at).toLocaleDateString()}\n` if (issue.labels && issue.labels.length > 0) { markdown += `- **Labels**: ${issue.labels.map(l => (typeof l === "string" ? l : l.name)).join(", ")}\n` } if (issue.assignees && issue.assignees.length > 0) { markdown += `- **Assignees**: ${issue.assignees.map(a => a.login).join(", ")}\n` } if (issue.milestone) { markdown += `- **Milestone**: ${issue.milestone.title}\n` } markdown += `- **Comments**: ${issue.comments}\n` markdown += `- **URL**: ${issue.html_url}\n` markdown += `\n` }) return { content: [{ type: "text", text: markdown }], } } catch (e: any) { return { content: [{ type: "text", text: `Error: ${e.message}` }], } } }, ) - src/tools/issues.ts:221-248 (schema)Input schema for 'list_issues' tool: owner, repo, state (open/closed/all), labels (array), sort (created/updated/comments), direction (asc/desc), since (ISO date string), per_page (default 10), page (default 1).
{ owner: z.string().describe("Repository owner"), repo: z.string().describe("Repository name"), state: z .enum(["open", "closed", "all"]) .optional() .describe("Filter by state"), labels: z.array(z.string()).optional().describe("Filter by labels"), sort: z .enum(["created", "updated", "comments"]) .optional() .describe("Sort order"), direction: z.enum(["asc", "desc"]).optional().describe("Sort direction"), since: z .string() .optional() .describe("Filter by date (ISO 8601 timestamp)"), per_page: z .number() .optional() .default(10) .describe("Results per page (default 10, max 100)"), page: z .number() .optional() .default(1) .describe("Page number (default 1)"), }, - src/tools/issues.ts:218-322 (registration)Registration of 'list_issues' tool via server.tool('list_issues', ...) inside registerIssueTools.
server.tool( "list_issues", "List issues in a GitHub repository.", { owner: z.string().describe("Repository owner"), repo: z.string().describe("Repository name"), state: z .enum(["open", "closed", "all"]) .optional() .describe("Filter by state"), labels: z.array(z.string()).optional().describe("Filter by labels"), sort: z .enum(["created", "updated", "comments"]) .optional() .describe("Sort order"), direction: z.enum(["asc", "desc"]).optional().describe("Sort direction"), since: z .string() .optional() .describe("Filter by date (ISO 8601 timestamp)"), per_page: z .number() .optional() .default(10) .describe("Results per page (default 10, max 100)"), page: z .number() .optional() .default(1) .describe("Page number (default 1)"), }, async ({ owner, repo, state, labels, sort, direction, since, per_page, page, }) => { try { const response = await octokit.rest.issues.listForRepo({ owner, repo, state, labels: labels ? labels.join(",") : undefined, sort, direction, since, per_page, page, }) // Format the response as clean markdown const issues = response.data.filter(item => !item.pull_request) // Filter out PRs if (issues.length === 0) { return { content: [{ type: "text", text: "No issues found." }], } } let markdown = `# Issues for ${owner}/${repo}\n\n` markdown += `Showing ${issues.length} issue(s) - Page ${page}\n` if (response.data.length === per_page) { markdown += `*Note: More results may be available. Use 'page' parameter to see next page.*\n` } markdown += `\n` issues.forEach(issue => { markdown += `## #${issue.number}: ${issue.title}\n\n` markdown += `- **State**: ${issue.state}\n` markdown += `- **Author**: ${issue.user?.login || "Unknown"}\n` markdown += `- **Created**: ${new Date(issue.created_at).toLocaleDateString()}\n` markdown += `- **Updated**: ${new Date(issue.updated_at).toLocaleDateString()}\n` if (issue.labels && issue.labels.length > 0) { markdown += `- **Labels**: ${issue.labels.map(l => (typeof l === "string" ? l : l.name)).join(", ")}\n` } if (issue.assignees && issue.assignees.length > 0) { markdown += `- **Assignees**: ${issue.assignees.map(a => a.login).join(", ")}\n` } if (issue.milestone) { markdown += `- **Milestone**: ${issue.milestone.title}\n` } markdown += `- **Comments**: ${issue.comments}\n` markdown += `- **URL**: ${issue.html_url}\n` markdown += `\n` }) return { content: [{ type: "text", text: markdown }], } } catch (e: any) { return { content: [{ type: "text", text: `Error: ${e.message}` }], } } }, ) - src/tools/issues.ts:5-5 (helper)The registerIssueTools function that wraps all issue tool registrations.
export function registerIssueTools(server: McpServer, octokit: Octokit) {