Skip to main content
Additional applications for the Greptile MCP server beyond direct coding assistance.

Code Quality Dashboards

Metrics Collection

Repository Health Overview using Claude: Ask Claude: β€œGive me a health overview of my repositories using Greptile data” Claude can now:
  1. List all merge requests across your repositories
  2. Get code review completion rates
  3. Count unaddressed security comments
  4. Identify repositories with the most issues
  5. Calculate review coverage percentages
Example metrics Claude can provide:
  • Total PRs reviewed vs unreviewed
  • Number of open security issues
  • Most active repositories by review volume
  • Custom context adoption rates
Team Performance Analytics:
async function generateTeamReport(timeRange) {
  const reviews = await mcpClient.call('list_code_reviews', {
    limit: 200
  });

  const completedReviews = reviews.codeReviews
    .filter(r => r.status === 'COMPLETED')
    .filter(r => withinTimeRange(r.completedAt, timeRange));

  const metrics = {
    totalReviews: completedReviews.length,
    averageReviewTime: calculateAverageTime(completedReviews),
    reviewThroughput: completedReviews.length / timeRange.days,
    topRepositories: getTopRepositories(completedReviews),
    issueCategories: categorizeIssues(completedReviews)
  };

  return metrics;
}

Visualization Integration

Grafana Dashboard:
// grafana-greptile-datasource.js
class GreptileDatasource {
  async query(options) {
    const queries = options.targets.map(async (target) => {
      switch (target.metric) {
        case 'review_count':
          return this.getReviewCount(target.timeRange);
        case 'comment_trends':
          return this.getCommentTrends(target.timeRange);
        case 'repository_health':
          return this.getRepositoryHealth();
      }
    });

    return Promise.all(queries);
  }

  async getReviewCount(timeRange) {
    const reviews = await mcpClient.call('list_code_reviews', {
      limit: 1000
    });

    return reviews.codeReviews
      .filter(r => withinTimeRange(r.createdAt, timeRange))
      .reduce((acc, review) => {
        const date = review.createdAt.split('T')[0];
        acc[date] = (acc[date] || 0) + 1;
        return acc;
      }, {});
  }
}

CI/CD Integration

The MCP server enables automated workflows that check code quality before merges.

Pre-merge Validation

Ask Claude to validate PRs: β€œCheck if this PR has any unaddressed Greptile security comments” Claude can:
  • Verify all critical comments are addressed
  • Check if code review is complete
  • Identify blocking issues before merge
  • Trigger new reviews if needed
GitHub Actions Integration:
# .github/workflows/greptile-validation.yml
name: Greptile Pre-merge Validation

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - name: Check Review Status
        env:
          GREPTILE_API_KEY: ${{ secrets.GREPTILE_API_KEY }}
        run: |
          node -e "
          const prNumber = process.env.GITHUB_EVENT.number;

          async function validate() {
            // Check if review exists and is complete
            const pr = await mcpClient.call('get_merge_request', {
              name: '${{ github.repository }}',
              remote: 'github',
              defaultBranch: 'main',
              prNumber: prNumber
            });

            const hasCompleteReview = pr.mergeRequest.codeReviews
              .some(r => r.status === 'COMPLETED');

            if (!hasCompleteReview) {
              console.log('Triggering code review...');
              await mcpClient.call('trigger_code_review', {
                name: '${{ github.repository }}',
                remote: 'github',
                defaultBranch: 'main',
                prNumber: prNumber
              });

              process.exit(1); // Block merge until review completes
            }

            // Check for unaddressed critical comments
            const comments = await mcpClient.call('list_merge_request_comments', {
              name: '${{ github.repository }}',
              remote: 'github',
              defaultBranch: 'main',
              prNumber: prNumber,
              addressed: false
            });

            const criticalComments = comments.comments.filter(c =>
              c.body.includes('security') || c.body.includes('critical')
            );

            if (criticalComments.length > 0) {
              console.log('Critical issues must be addressed before merge');
              process.exit(1);
            }
          }

          validate().catch(console.error);
          "
GitLab CI Integration:
# .gitlab-ci.yml
stages:
  - review-validation

greptile-check:
  stage: review-validation
  script:
    - |
      # Get merge request details
      MR_DATA=$(curl -s "https://api.greptile.com/v2/mcp" \
        -H "Authorization: Bearer $GREPTILE_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "jsonrpc": "2.0",
          "id": 1,
          "method": "tools/call",
          "params": {
            "name": "get_merge_request",
            "arguments": {
              "name": "'$CI_PROJECT_PATH'",
              "remote": "gitlab",
              "defaultBranch": "'$CI_DEFAULT_BRANCH'",
              "prNumber": '$CI_MERGE_REQUEST_IID'
            }
          }
        }')

      # Check review status and block if incomplete
      echo $MR_DATA | jq -e '.result.content[0].text | fromjson | .mergeRequest.codeReviews | map(select(.status == "COMPLETED")) | length > 0'
  only:
    - merge_requests

Automated Review Triggering

Webhook Handler:
// webhook-handler.js
const express = require('express');
const app = express();

app.post('/github/webhook', async (req, res) => {
  const { action, pull_request, repository } = req.body;

  if (action === 'opened' || action === 'synchronize') {
    // Auto-trigger review for new or updated PRs
    try {
      await mcpClient.call('trigger_code_review', {
        name: repository.full_name,
        remote: 'github',
        defaultBranch: repository.default_branch,
        prNumber: pull_request.number
      });

      console.log(`Review triggered for PR #${pull_request.number}`);
    } catch (error) {
      console.error('Failed to trigger review:', error);
    }
  }

  res.status(200).send('OK');
});

Development Workflows

IDE Integration

VS Code Extension:
// extension.js
const vscode = require('vscode');

function activate(context) {
  // Register command to get PR context
  const prContextCommand = vscode.commands.registerCommand(
    'greptile.getPRContext',
    async () => {
      const workspaceFolder = vscode.workspace.workspaceFolders[0];
      const currentBranch = await git.getCurrentBranch();

      // Get PR for current branch
      const prs = await mcpClient.call('list_merge_requests', {
        name: workspaceFolder.name,
        remote: 'github',
        defaultBranch: 'main',
        state: 'open',
        limit: 50
      });

      const currentPR = prs.mergeRequests.find(pr =>
        pr.branches?.source === currentBranch
      );

      if (currentPR) {
        const prDetails = await mcpClient.call('get_merge_request', {
          name: workspaceFolder.name,
          remote: 'github',
          defaultBranch: 'main',
          prNumber: currentPR.number
        });

        // Show PR info in panel
        showPRPanel(prDetails.mergeRequest);
      }
    }
  );

  context.subscriptions.push(prContextCommand);
}

Onboarding Workflows

New Developer Setup:
async function generateOnboardingGuide(repository) {
  // Get organization patterns
  const patterns = await mcpClient.call('list_custom_context', {
    type: 'CUSTOM_INSTRUCTION',
    limit: 50
  });

  // Get recent review patterns
  const recentComments = await mcpClient.call('search_greptile_comments', {
    query: repository.name,
    limit: 100,
    includeAddressed: true
  });

  // Analyze common issues
  const commonIssues = categorizeComments(recentComments.comments);

  return {
    codingStandards: patterns.customContexts,
    commonPitfalls: commonIssues.topCategories,
    exampleFixes: commonIssues.resolutions,
    setupChecklist: generateSetupTasks(patterns.customContexts)
  };
}

Analytics and Monitoring

Historical Analysis:
async function analyzeQualityTrends(timeRange, repositories) {
  const data = [];

  for (let date of generateDateRange(timeRange)) {
    const dayMetrics = await Promise.all(repositories.map(async (repo) => {
      const [reviews, comments] = await Promise.all([
        mcpClient.call('list_code_reviews', {
          name: repo.name,
          remote: repo.remote,
          defaultBranch: repo.branch,
          limit: 100
        }),
        mcpClient.call('search_greptile_comments', {
          query: `${repo.name} ${date}`,
          limit: 100
        })
      ]);

      const dayReviews = reviews.codeReviews.filter(r =>
        r.createdAt.startsWith(date)
      );

      const dayComments = comments.comments.filter(c =>
        c.createdAt.startsWith(date)
      );

      return {
        date,
        repository: repo.name,
        reviewsCount: dayReviews.length,
        issuesFound: dayComments.length,
        criticalIssues: dayComments.filter(c =>
          c.body.includes('security') || c.body.includes('critical')
        ).length
      };
    }));

    data.push(...dayMetrics);
  }

  return data;
}

Pattern Effectiveness

Pattern Usage Analytics:
async function analyzePatternEffectiveness() {
  // Get all patterns
  const patterns = await mcpClient.call('list_custom_context', {
    limit: 200
  });

  const effectiveness = await Promise.all(
    patterns.customContexts.map(async (pattern) => {
      // Get comments linked to this pattern
      const linkedComments = await mcpClient.call('search_greptile_comments', {
        query: pattern.id,
        limit: 100,
        includeAddressed: true
      });

      const comments = linkedComments.comments.filter(c =>
        c.linkedMemory?.id === pattern.id
      );

      const addressed = comments.filter(c => c.addressed);

      return {
        patternId: pattern.id,
        patternBody: pattern.body,
        timesTriggered: comments.length,
        resolutionRate: addressed.length / comments.length,
        averageResolutionTime: calculateAverageResolutionTime(comments),
        effectiveness: calculateEffectivenessScore(comments)
      };
    })
  );

  return effectiveness.sort((a, b) => b.effectiveness - a.effectiveness);
}

Automation Workflows

Slack Integration

Daily Standups:
async function sendDailyStandup() {
  const yesterday = getYesterday();

  // Get yesterday's reviews
  const reviews = await mcpClient.call('list_code_reviews', {
    limit: 100
  });

  const yesterdayReviews = reviews.codeReviews.filter(r =>
    r.completedAt?.startsWith(yesterday)
  );

  // Get unaddressed comments
  const openComments = await mcpClient.call('search_greptile_comments', {
    query: 'created:' + yesterday,
    limit: 50,
    includeAddressed: false
  });

  const message = {
    blocks: [
      {
        type: "section",
        text: {
          type: "mrkdwn",
          text: `*Daily Code Review Summary - ${yesterday}*`
        }
      },
      {
        type: "section",
        fields: [
          {
            type: "mrkdwn",
            text: `*Reviews Completed:* ${yesterdayReviews.length}`
          },
          {
            type: "mrkdwn",
            text: `*Open Issues:* ${openComments.comments.length}`
          }
        ]
      }
    ]
  };

  await slackClient.chat.postMessage({
    channel: '#engineering',
    ...message
  });
}

Release Management

Pre-release Validation:
async function validateRelease(releaseTag, repositories) {
  const issues = [];

  for (const repo of repositories) {
    // Get unaddressed critical comments
    const criticalComments = await mcpClient.call('search_greptile_comments', {
      query: `${repo.name} security OR critical`,
      limit: 100,
      includeAddressed: false
    });

    if (criticalComments.comments.length > 0) {
      issues.push({
        repository: repo.name,
        type: 'critical_comments',
        count: criticalComments.comments.length,
        details: criticalComments.comments.slice(0, 5)
      });
    }

    // Check for incomplete reviews on recent PRs
    const recentPRs = await mcpClient.call('list_merge_requests', {
      name: repo.name,
      remote: repo.remote,
      defaultBranch: repo.branch,
      state: 'merged',
      limit: 50
    });

    const unreviewedPRs = recentPRs.mergeRequests.filter(pr =>
      !pr.codeReviews || pr.codeReviews.length === 0
    );

    if (unreviewedPRs.length > 0) {
      issues.push({
        repository: repo.name,
        type: 'unreviewed_prs',
        count: unreviewedPRs.length,
        prs: unreviewedPRs.slice(0, 5)
      });
    }
  }

  return {
    canRelease: issues.length === 0,
    blockers: issues,
    recommendation: issues.length === 0
      ? 'Release approved'
      : 'Address issues before release'
  };
}
⌘I