Skip to main content
Glama
skippr-hq

Skippr Extension MCP Server

by skippr-hq

List Skippr Issues

skippr_list_issues

List Skippr issues with filters for project, review, severity, category, and resolution status to find and address product problems.

Instructions

Lists all available Skippr issues with optional filtering by project, review, severity, category, and resolution status

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectIdYesProject identifier
reviewIdNoFilter by review ID
severityNoFilter by severity level
categoryNoFilter by category
resolvedNoFilter by resolved status

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
issuesYes
totalCountYes

Implementation Reference

  • Main handler function for skippr_list_issues. Takes validated input, finds all issue files via findAllIssues, reads each issue file, applies optional filters (reviewId, severity, category, resolved), and returns a list of IssueSummary objects with totalCount.
    export async function listIssues(input: ListIssuesInput): Promise<ListIssuesOutput> {
      // Validate input
      const validated = ListIssuesInputSchema.parse(input);
      const { projectId, reviewId, severity, category, resolved } = validated;
    
      // Find all issue file paths
      const issueFiles = await findAllIssues(projectId);
    
      // Read and parse each issue file
      const issues: IssueSummary[] = [];
    
      for (const filePath of issueFiles) {
        // Extract reviewId and issueId from path
        // Path format: .skippr/projects/{projectId}/reviews/{reviewId}/issues/{issueId}.md
        const pathParts = filePath.split('/');
        const fileReviewId = pathParts[4]; // reviews/{reviewId}
        const fileName = pathParts[6]; // {issueId}.md
        const fileIssueId = fileName.replace('.md', '');
    
        try {
          const { frontmatter } = await readIssueFile(projectId, fileReviewId, fileIssueId);
    
          // Apply filters
          if (reviewId && frontmatter.reviewId !== reviewId) continue;
          if (severity && frontmatter.severity !== severity) continue;
          if (category && frontmatter.category !== category) continue;
          if (resolved !== undefined && frontmatter.resolved !== resolved) continue;
    
          // Add to results
          issues.push({
            id: frontmatter.id,
            reviewId: frontmatter.reviewId,
            title: frontmatter.title,
            severity: frontmatter.severity as IssueSummary['severity'],
            resolved: frontmatter.resolved,
            category: frontmatter.category as IssueSummary['category'],
          });
        } catch (error) {
          // Skip files that can't be parsed
          console.error(`Failed to parse issue file: ${filePath}`, error);
          continue;
        }
      }
    
      return {
        issues,
        totalCount: issues.length,
      };
    }
  • Input schema (ListIssuesInputSchema) defines required projectId and optional filters: reviewId (uuid), severity, category, resolved. Output schema (ListIssuesOutputSchema) defines an array of issues with id, reviewId, title, severity, resolved, optional category, plus totalCount.
    export const ListIssuesInputSchema = z.object({
      projectId: z.string().describe('Project identifier'),
      reviewId: z.string().uuid().optional().describe('Filter by review ID'),
      severity: IssueSeveritySchema.optional().describe('Filter by severity level'),
      category: CategorySchema.optional().describe('Filter by category'),
      resolved: z.boolean().optional().describe('Filter by resolved status'),
    });
    
    // Output schema for the tool
    export const ListIssuesOutputSchema = z.object({
      issues: z.array(
        z.object({
          id: z.string().uuid(),
          reviewId: z.string().uuid(),
          title: z.string(),
          severity: IssueSeveritySchema,
          resolved: z.boolean(),
          category: CategorySchema.optional(),
        })
      ),
      totalCount: z.number(),
    });
  • Registration of 'skippr_list_issues' MCP tool on the MCP server via mcpServer.registerTool() with title, description, inputSchema, outputSchema, and an async handler that calls listIssues().
    mcpServer.registerTool(
      'skippr_list_issues',
      {
        title: 'List Skippr Issues',
        description: 'Lists all available Skippr issues with optional filtering by project, review, severity, category, and resolution status',
        inputSchema: ListIssuesInputSchema.shape,
        outputSchema: ListIssuesOutputSchema.shape
      },
      async (args) => {
        const result = await listIssues(args as ListIssuesInput);
        return createStructuredResponse(result);
      }
    );
  • Helper function findAllIssues() that scans .skippr/projects/{projectId}/reviews/*/issues/ for .md files and returns all issue file paths.
    export async function findAllIssues(projectId: string): Promise<string[]> {
      const skipprDir = join(getWorkingDirectory(), '.skippr', 'projects', projectId, 'reviews');
      const issueFiles: string[] = [];
    
      try {
        // Check if .skippr/reviews exists
        await stat(skipprDir);
      } catch {
        // .skippr directory doesn't exist
        return [];
      }
    
      // Read all review directories
      const reviewDirs = await readdir(skipprDir, { withFileTypes: true });
    
      for (const reviewDir of reviewDirs) {
        if (!reviewDir.isDirectory()) continue;
    
        const issuesDir = join(skipprDir, reviewDir.name, 'issues');
    
        try {
          const issueFilenames = await readdir(issuesDir);
    
          for (const filename of issueFilenames) {
            if (filename.endsWith('.md')) {
              issueFiles.push(join('.skippr', 'projects', projectId, 'reviews', reviewDir.name, 'issues', filename));
            }
          }
        } catch {
          // issues directory doesn't exist for this review, skip
          continue;
        }
      }
    
      return issueFiles;
    }
  • Helper function readIssueFile() that reads and parses a single issue .md file, returning structured frontmatter and raw markdown content.
    export async function readIssueFile(
      projectId: string,
      reviewId: string,
      issueId: string
    ): Promise<{ frontmatter: ParsedIssueFrontmatter; markdown: string }> {
      const filePath = join(getWorkingDirectory(), '.skippr', 'projects', projectId, 'reviews', reviewId, 'issues', `${issueId}.md`);
    
      const content = await readFile(filePath, 'utf-8');
      const parsed = parseIssueFrontmatter(content);
    
      return {
        frontmatter: parsed.frontmatter,
        markdown: parsed.content, // Raw markdown for Claude to read
      };
    }
Behavior2/5

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

No annotations provided. The description only states it lists issues with filtering; no disclosure of pagination, result limits, ordering, or any behavioral traits beyond what is obvious.

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?

Single sentence, front-loaded with the action and resource, efficiently listing all filter options. No wasted words.

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

Completeness4/5

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

The description covers the action and filter options; output schema exists so return values need not be detailed. Could mention pagination or result limits, but overall complete for a list tool.

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%; the tool description merely lists the filterable fields without adding new meaning beyond the schema. Baseline 3 is appropriate.

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 tool lists Skippr issues and enumerates the filterable attributes. It distinguishes from get_issue by using 'list' vs 'get', but does not explicitly differentiate from siblings.

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

Usage Guidelines3/5

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

The description implies usage for listing issues with optional filters, but no explicit guidance on when to use this vs alternatives like skippr_get_issue or skippr_verify_issue_fix.

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/skippr-hq/extension-mcp-server'

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