Skip to main content
Glama
sapientpants

DeepSource MCP Server

by sapientpants

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
NameRequiredDescriptionDefault
projectKeyYesDeepSource project key to fetch runs for
analyzerInNoFilter runs by analyzer shortcodes
firstNoNumber of items to retrieve (forward pagination)
afterNoCursor to start retrieving items after (forward pagination)
lastNoNumber of items to retrieve (backward pagination)
beforeNoCursor to start retrieving items before (backward pagination)
page_sizeNoNumber of items per page (alias for first, for convenience)
max_pagesNoMaximum number of pages to fetch (enables automatic multi-page fetching)

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
runsYes
pageInfoYes
totalCountYes

Implementation Reference

  • Registers the 'runs' MCP tool with runsToolSchema and a handler that adapts input parameters and delegates to handleDeepsourceProjectRuns
    toolRegistry.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 structure
    export 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(),
      },
    };
  • 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 info
    export 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;
    }
  • Core execution logic for fetching project runs using domain AnalysisRunRepository.findByProject, mapping aggregate data to the expected output format including summaries, pagination, and usage guidance
    export 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 parameters
    export 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;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions 'filtering' but doesn't describe key behaviors like pagination handling (implied by parameters), rate limits, authentication needs, or what happens with invalid inputs. For a tool with 8 parameters and no annotation coverage, this is a significant gap in transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that directly states the tool's purpose. It's front-loaded with the core action and resource, with no wasted words or redundant information, making it highly concise and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (8 parameters, filtering, pagination) and the presence of an output schema, the description is minimally adequate. However, with no annotations and rich parameter semantics, it should provide more context on usage scenarios, behavioral traits, or sibling tool differentiation to be fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all 8 parameters. The description adds minimal value by hinting at filtering ('with filtering'), but doesn't provide additional context, examples, or clarification beyond what's in the schema. This meets the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('List') and resource ('analysis runs for a DeepSource project'), making the purpose specific and understandable. However, it doesn't explicitly differentiate this tool from sibling tools like 'run' (singular) or 'recent_run_issues', which could cause confusion about when to use each.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description mentions 'with filtering' but provides no explicit guidance on when to use this tool versus alternatives like 'run' (singular) or 'recent_run_issues'. There's no mention of prerequisites, context for filtering, or comparison to sibling tools, leaving the agent with insufficient usage direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sapientpants/deepsource-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server