project_issues
Retrieve and filter code quality issues from DeepSource projects by file path, analyzer, tags, or pagination parameters to identify and address potential problems.
Instructions
Get issues from a DeepSource project with filtering capabilities
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectKey | Yes | DeepSource project key to fetch issues for | |
| path | No | Filter issues by file path | |
| analyzerIn | No | Filter issues by analyzer shortcodes | |
| tags | No | Filter issues by tags | |
| first | No | Number of items to retrieve (forward pagination) | |
| after | No | Cursor to start retrieving items after (forward pagination) | |
| last | No | Number of items to retrieve (backward pagination) | |
| before | No | Cursor to start retrieving items before (backward pagination) | |
| page_size | No | Number of items per page (alias for first, for convenience) | |
| max_pages | No | Maximum number of pages to fetch (enables automatic multi-page fetching) |
Implementation Reference
- src/handlers/project-issues.ts:35-133 (handler)Core handler factory for the 'project_issues' tool. Creates a handler that fetches issues from a DeepSource project using the API client, applies filters and pagination parameters, formats the response with pagination metadata, and wraps it in ApiResponse.export const createProjectIssuesHandler = createBaseHandlerFactory( 'project_issues', async ( deps: BaseHandlerDeps, { projectKey, path, analyzerIn, tags, first, after, last, before, page_size, max_pages, }: DeepsourceProjectIssuesParams ) => { const apiKey = deps.getApiKey(); deps.logger.debug('API key retrieved from config', { length: apiKey.length, prefix: `${apiKey.substring(0, 5)}...`, }); const client = new DeepSourceClient(apiKey); deps.logger.info('Fetching project issues', { projectKey, hasFilterPath: Boolean(path), hasAnalyzerFilter: Boolean(analyzerIn), hasTagsFilter: Boolean(tags), maxPages: max_pages, }); const params: IssueFilterParams = {}; if (path !== undefined) params.path = path; if (analyzerIn !== undefined) params.analyzerIn = analyzerIn; if (tags !== undefined) params.tags = tags; if (first !== undefined) params.first = first; if (after !== undefined) params.after = after; if (last !== undefined) params.last = last; if (before !== undefined) params.before = before; if (page_size !== undefined) params.page_size = page_size; if (max_pages !== undefined) params.max_pages = max_pages; const issues = await client.getIssues(projectKey, params); deps.logger.info('Successfully fetched project issues', { count: issues.items.length, totalCount: issues.totalCount, hasNextPage: issues.pageInfo?.hasNextPage, hasPreviousPage: issues.pageInfo?.hasPreviousPage, }); // Create pagination metadata for user-friendly response const paginationMetadata = createPaginationMetadata(issues); const issuesData = { issues: issues.items.map((issue: DeepSourceIssue) => ({ id: issue.id, title: issue.title, shortcode: issue.shortcode, category: issue.category, severity: issue.severity, status: issue.status, issue_text: issue.issue_text, file_path: issue.file_path, line_number: issue.line_number, tags: issue.tags, })), // Include both formats for backward compatibility and user convenience pageInfo: { hasNextPage: issues.pageInfo?.hasNextPage || false, hasPreviousPage: issues.pageInfo?.hasPreviousPage || false, startCursor: issues.pageInfo?.startCursor || null, endCursor: issues.pageInfo?.endCursor || null, }, pagination: paginationMetadata, totalCount: issues.totalCount, // Provide helpful guidance on filtering and pagination usage_examples: { filtering: { by_path: 'Use the path parameter to filter issues by file path', by_analyzer: 'Use the analyzerIn parameter to filter by specific analyzers', by_tags: 'Use the tags parameter to filter by specific tags', }, pagination: { next_page: max_pages ? 'Multi-page fetching enabled with max_pages parameter' : 'For forward pagination, use first and after parameters', previous_page: 'For backward pagination, use last and before parameters', page_size: 'Use page_size parameter as a convenient alias for first', multi_page: 'Use max_pages to automatically fetch multiple pages (e.g., max_pages: 5)', }, }, }; return wrapInApiResponse(issuesData); } );
- Zod-based input and output schema definition for the project_issues tool, specifying parameters like projectKey, path, analyzerIn, tags, pagination options, and the structure of the issues response.export const projectIssuesToolSchema = { name: 'project_issues', description: 'Get issues from a DeepSource project with filtering capabilities', inputSchema: { projectKey: z.string().describe('DeepSource project key to fetch issues for'), path: z.string().optional().describe('Filter issues by file path'), analyzerIn: z.array(z.string()).optional().describe('Filter issues by analyzer shortcodes'), tags: z.array(z.string()).optional().describe('Filter issues by tags'), first: z.number().optional().describe('Number of items to retrieve (forward pagination)'), after: z .string() .optional() .describe('Cursor to start retrieving items after (forward pagination)'), last: z.number().optional().describe('Number of items to retrieve (backward pagination)'), before: z .string() .optional() .describe('Cursor to start retrieving items before (backward pagination)'), page_size: z .number() .optional() .describe('Number of items per page (alias for first, for convenience)'), max_pages: z .number() .optional() .describe('Maximum number of pages to fetch (enables automatic multi-page fetching)'), }, outputSchema: { issues: z.array( z.object({ id: z.string(), title: z.string(), shortcode: z.string(), category: z.string(), severity: z.string(), status: z.string(), issue_text: z.string(), file_path: z.string(), line_number: z.number(), tags: z.array(z.string()), }) ), pageInfo: z.object({ hasNextPage: z.boolean(), hasPreviousPage: z.boolean(), startCursor: z.string().nullable(), endCursor: z.string().nullable(), }), pagination: z .object({ has_more_pages: z.boolean(), next_cursor: z.string().optional(), previous_cursor: z.string().optional(), total_count: z.number().optional(), page_size: z.number(), pages_fetched: z.number().optional(), limit_reached: z.boolean().optional(), }) .optional() .describe('User-friendly pagination metadata'), totalCount: z.number(), }, };
- src/index-registry.ts:140-146 (registration)Registers the project_issues tool in the MCP ToolRegistry using the predefined schema and an async handler that adapts incoming parameters and delegates to the DeepSource project issues handler.toolRegistry.registerTool({ ...projectIssuesToolSchema, handler: async (params) => { const adaptedParams = adaptProjectIssuesParams(params); return handleDeepsourceProjectIssues(adaptedParams); }, });
- Helper function that adapts raw parameters from the MCP tool call (unknown type) to the strongly-typed DeepsourceProjectIssuesParams interface required by the handler.export function adaptProjectIssuesParams(params: unknown): DeepsourceProjectIssuesParams { const typedParams = params as Record<string, unknown>; const result: DeepsourceProjectIssuesParams = { projectKey: typedParams.projectKey as string, // Handler still expects string }; const path = typedParams.path as string | undefined; if (path !== undefined) result.path = path; const analyzerIn = typedParams.analyzerIn as string[] | undefined; if (analyzerIn !== undefined) result.analyzerIn = analyzerIn; const tags = typedParams.tags as string[] | undefined; if (tags !== undefined) result.tags = tags; const first = typedParams.first as number | undefined; if (first !== undefined) result.first = first; const last = typedParams.last as number | undefined; if (last !== undefined) result.last = last; const after = typedParams.after as string | undefined; if (after !== undefined) result.after = after; const before = typedParams.before as string | undefined; if (before !== undefined) result.before = before; return result; }