runs
List and filter analysis runs for a DeepSource project to track code quality assessments and review results.
Instructions
List analysis runs for a DeepSource project with filtering
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectKey | Yes | DeepSource project key to fetch runs for | |
| analyzerIn | No | Filter runs by analyzer shortcodes | |
| 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/index-registry.ts:157-163 (registration)Registers the 'runs' MCP tool with runsToolSchema and a handler that adapts input parameters and delegates to handleDeepsourceProjectRunstoolRegistry.registerTool({ ...runsToolSchema, handler: async (params) => { const adaptedParams = adaptProjectRunsParams(params); return handleDeepsourceProjectRuns(adaptedParams); }, });
- Defines the inputSchema and outputSchema for the 'runs' tool using Zod validation, including parameters like projectKey, analyzerIn, pagination options, and detailed run response structureexport const runsToolSchema = { name: 'runs', description: 'List analysis runs for a DeepSource project with filtering', inputSchema: { projectKey: z.string().describe('DeepSource project key to fetch runs for'), analyzerIn: z.array(z.string()).optional().describe('Filter runs by analyzer shortcodes'), 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: { runs: z.array( z.object({ id: z.string(), runUid: z.string(), commitOid: z.string(), branchName: z.string(), baseOid: z.string(), status: z.string(), createdAt: z.string(), updatedAt: z.string(), finishedAt: z.string().optional(), summary: z.object({ occurrencesIntroduced: z.number(), occurrencesResolved: z.number(), occurrencesSuppressed: z.number(), occurrenceDistributionByAnalyzer: z .array( z.object({ analyzerShortcode: z.string(), introduced: z.number(), }) ) .optional(), occurrenceDistributionByCategory: z .array( z.object({ category: z.string(), introduced: z.number(), }) ) .optional(), }), repository: z.object({ name: z.string(), id: z.string(), }), }) ), pageInfo: z.object({ hasNextPage: z.boolean(), hasPreviousPage: z.boolean(), startCursor: z.string().nullable(), endCursor: z.string().nullable(), }), totalCount: z.number(), }, };
- src/handlers/project-runs.ts:265-293 (handler)Main handler function for the 'runs' tool. Sets up domain repository (analysisRunRepository), creates a handler with repo deps, executes the query via findByProject, maps results to MCP response format, handles errors and pagination infoexport async function handleDeepsourceProjectRuns( params: DeepsourceProjectRunsParams ): Promise<ApiResponse> { const baseDeps = createDefaultHandlerDeps({ logger }); const apiKey = baseDeps.getApiKey(); const repositoryFactory = new RepositoryFactory({ apiKey }); const analysisRunRepository = repositoryFactory.createAnalysisRunRepository(); const deps: ProjectRunsHandlerDeps = { analysisRunRepository, logger, }; const handler = createProjectRunsHandlerWithRepo(deps); const result = await handler(params); // If the domain handler returned an error response, throw an error for backward compatibility if (result.isError) { const firstContent = result.content[0]; if (firstContent) { const errorData = JSON.parse(firstContent.text); throw new Error(errorData.error); } else { throw new Error('Unknown project runs error'); } } return result; }
- src/handlers/project-runs.ts:46-160 (handler)Core execution logic for fetching project runs using domain AnalysisRunRepository.findByProject, mapping aggregate data to the expected output format including summaries, pagination, and usage guidanceexport function createProjectRunsHandlerWithRepo(deps: ProjectRunsHandlerDeps) { return async function handleProjectRuns(params: DeepsourceProjectRunsParams) { try { const { projectKey, analyzerIn, first } = params; // Note: after, last, before pagination parameters not yet implemented const projectKeyBranded = asProjectKey(projectKey); deps.logger.info('Fetching project runs from repository', { projectKey, hasAnalyzerFilter: Boolean(analyzerIn), }); // Get the analysis runs from repository // Note: Basic pagination using page/pageSize for now // Note: Advanced pagination and analyzer filtering can be implemented in future versions const pageSize = first || 20; // Default page size const page = 1; // For now, always fetch first page const result = await deps.analysisRunRepository.findByProject(projectKeyBranded, { page, pageSize, }); deps.logger.info('Successfully fetched project runs', { count: result.items.length, totalCount: result.totalCount, hasNextPage: result.hasNextPage, hasPreviousPage: result.hasPreviousPage, }); const runsData = { runs: result.items.map((run: AnalysisRun) => ({ id: run.runId, runUid: run.runId, // Domain aggregate uses runId as the unique identifier commitOid: run.commitInfo.oid, branchName: run.commitInfo.branch, baseOid: run.commitInfo.baseOid, status: run.status, createdAt: run.timestamps.createdAt, updatedAt: run.timestamps.startedAt || run.timestamps.createdAt, // Use startedAt or fallback to createdAt finishedAt: run.timestamps.finishedAt, summary: { occurrencesIntroduced: run.summary.totalIntroduced.count, occurrencesResolved: run.summary.totalResolved.count, occurrencesSuppressed: run.summary.totalSuppressed.count, occurrenceDistributionByAnalyzer: run.summary.byAnalyzer.map((dist) => ({ analyzerShortcode: dist.analyzerShortcode, introduced: dist.introduced.count, })), occurrenceDistributionByCategory: run.summary.byCategory.map((dist) => ({ category: dist.category, introduced: dist.introduced.count, })), }, repository: { name: 'Repository', // Domain aggregate doesn't store repository name directly id: run.repositoryId, }, })), pageInfo: { hasNextPage: result.hasNextPage || false, hasPreviousPage: result.hasPreviousPage || false, startCursor: null, // Cursor-based pagination not yet implemented in domain layer endCursor: null, // Cursor-based pagination not yet implemented in domain layer }, totalCount: result.totalCount, // Provide helpful guidance on filtering and pagination usage_examples: { filtering: { by_analyzer: 'Use the analyzerIn parameter to filter by specific analyzers', }, pagination: { next_page: 'For forward pagination, use first and after parameters', previous_page: 'For backward pagination, use last and before parameters', }, related_tools: { run_details: 'Use the run tool to get detailed information about a specific run', run_issues: 'Use the recent_run_issues tool to get issues from the most recent run', }, }, }; return { content: [ { type: 'text' as const, text: JSON.stringify(runsData), }, ], }; } catch (error) { deps.logger.error('Error in handleProjectRuns', { errorType: typeof error, errorName: error instanceof Error ? error.name : 'Unknown', errorMessage: error instanceof Error ? error.message : String(error), errorStack: error instanceof Error ? error.stack : 'No stack available', }); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; deps.logger.debug('Returning error response', { errorMessage }); return { isError: true, content: [ { type: 'text' as const, text: JSON.stringify({ error: errorMessage, details: 'Failed to retrieve project runs', }), }, ], }; } }; }
- Adapts raw MCP tool parameters to DeepsourceProjectRunsParams interface expected by the handler, extracting and typing projectKey, analyzerIn, and pagination parametersexport function adaptProjectRunsParams(params: unknown): DeepsourceProjectRunsParams { const typedParams = params as Record<string, unknown>; const result: DeepsourceProjectRunsParams = { projectKey: typedParams.projectKey as string, // Handler still expects string }; const analyzerIn = typedParams.analyzerIn as AnalyzerShortcode[] | undefined; if (analyzerIn !== undefined) result.analyzerIn = analyzerIn; 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; }