Documentation

GitHub Actions Integration

This guide shows you how to integrate MCP Security Score into your GitHub Actions workflows using the API.

Prerequisites

  1. Pro subscription with API access
  2. API key from Dashboard → Settings

Setup

Step 1: Add Your API Key as a Secret

  1. Go to your GitHub repository
  2. Navigate to Settings → Secrets and variables → Actions
  3. Click New repository secret
  4. Name: MCP_SCANNER_API_KEY
  5. Value: Your API key (mcp_sk_...)
  6. Click Add secret

Step 2: Create the Workflow

Create .github/workflows/security-scan.yml:

name: MCP Security Scan
 
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
 
jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Scan MCP Server
        env:
          MCP_SCANNER_API_KEY: ${{ secrets.MCP_SCANNER_API_KEY }}
        run: |
          set -e
 
          REPO_URL="${{ github.server_url }}/${{ github.repository }}"
          THRESHOLD=70
 
          echo "Scanning $REPO_URL..."
 
          # Create scan
          SCAN_RESPONSE=$(curl -sf -X POST "https://mcpscanner.com/api/v1/scan" \
            -H "Authorization: Bearer $MCP_SCANNER_API_KEY" \
            -H "Content-Type: application/json" \
            -d "{\"url\": \"$REPO_URL\"}")
 
          SCAN_ID=$(echo "$SCAN_RESPONSE" | jq -r '.id')
          echo "Scan ID: $SCAN_ID"
 
          # Poll for completion
          for i in {1..24}; do
            RESULT=$(curl -sf "https://mcpscanner.com/api/v1/scan/$SCAN_ID" \
              -H "Authorization: Bearer $MCP_SCANNER_API_KEY")
 
            STATUS=$(echo "$RESULT" | jq -r '.status')
            echo "Status: $STATUS"
 
            if [ "$STATUS" = "complete" ]; then
              break
            elif [ "$STATUS" = "failed" ]; then
              echo "Scan failed!"
              exit 1
            fi
 
            sleep 5
          done
 
          # Check results
          SCORE=$(echo "$RESULT" | jq -r '.score')
          GRADE=$(echo "$RESULT" | jq -r '.grade')
          FINDINGS=$(echo "$RESULT" | jq -r '.findings | length')
 
          echo ""
          echo "=== Results ==="
          echo "Score: $SCORE ($GRADE)"
          echo "Findings: $FINDINGS"
 
          if [ "$SCORE" -lt "$THRESHOLD" ]; then
            echo "❌ Score $SCORE is below threshold $THRESHOLD"
            exit 1
          else
            echo "✅ Security check passed"
          fi

Complete Example with Summary

This version adds a GitHub Actions summary with detailed results:

name: MCP Security Scan
 
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
 
env:
  SCORE_THRESHOLD: 70
 
jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Scan MCP Server
        id: scan
        env:
          MCP_SCANNER_API_KEY: ${{ secrets.MCP_SCANNER_API_KEY }}
        run: |
          set -e
 
          REPO_URL="${{ github.server_url }}/${{ github.repository }}"
 
          # Create scan
          SCAN_RESPONSE=$(curl -sf -X POST "https://mcpscanner.com/api/v1/scan" \
            -H "Authorization: Bearer $MCP_SCANNER_API_KEY" \
            -H "Content-Type: application/json" \
            -d "{\"url\": \"$REPO_URL\"}")
 
          SCAN_ID=$(echo "$SCAN_RESPONSE" | jq -r '.id')
          echo "scan_id=$SCAN_ID" >> $GITHUB_OUTPUT
 
          # Poll for completion (max 2 minutes)
          for i in {1..24}; do
            RESULT=$(curl -sf "https://mcpscanner.com/api/v1/scan/$SCAN_ID" \
              -H "Authorization: Bearer $MCP_SCANNER_API_KEY")
 
            STATUS=$(echo "$RESULT" | jq -r '.status')
            if [ "$STATUS" = "complete" ] || [ "$STATUS" = "failed" ]; then
              break
            fi
            sleep 5
          done
 
          # Extract results
          SCORE=$(echo "$RESULT" | jq -r '.score')
          GRADE=$(echo "$RESULT" | jq -r '.grade')
          FINDINGS=$(echo "$RESULT" | jq -r '.findings | length')
          CRITICAL=$(echo "$RESULT" | jq '[.findings[] | select(.severity == "critical")] | length')
          HIGH=$(echo "$RESULT" | jq '[.findings[] | select(.severity == "high")] | length')
 
          echo "score=$SCORE" >> $GITHUB_OUTPUT
          echo "grade=$GRADE" >> $GITHUB_OUTPUT
          echo "findings=$FINDINGS" >> $GITHUB_OUTPUT
          echo "critical=$CRITICAL" >> $GITHUB_OUTPUT
          echo "high=$HIGH" >> $GITHUB_OUTPUT
 
      - name: Create Summary
        run: |
          echo "## MCP Security Scan Results" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY
          echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
          echo "| Score | ${{ steps.scan.outputs.score }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Grade | ${{ steps.scan.outputs.grade }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Total Findings | ${{ steps.scan.outputs.findings }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Critical | ${{ steps.scan.outputs.critical }} |" >> $GITHUB_STEP_SUMMARY
          echo "| High | ${{ steps.scan.outputs.high }} |" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "[View Full Report](https://mcpscanner.com/dashboard/scan/${{ steps.scan.outputs.scan_id }})" >> $GITHUB_STEP_SUMMARY
 
      - name: Check Threshold
        run: |
          if [ ${{ steps.scan.outputs.score }} -lt ${{ env.SCORE_THRESHOLD }} ]; then
            echo "❌ Security score ${{ steps.scan.outputs.score }} is below threshold ${{ env.SCORE_THRESHOLD }}"
            exit 1
          fi
          echo "✅ Security check passed"

Pull Request Only

Scan only on pull requests to save API calls:

name: PR Security Check
 
on:
  pull_request:
    branches: [main, develop]
 
jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      # ... same as above

Scheduled Scans

Run security scans on a schedule:

name: Weekly Security Scan
 
on:
  schedule:
    - cron: '0 9 * * 1'  # Every Monday at 9 AM UTC
  workflow_dispatch:  # Allow manual trigger
 
jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      # ... same as above

Reusable Workflow

Create a reusable workflow for multiple repositories:

# .github/workflows/mcp-scan.yml
name: MCP Scan
 
on:
  workflow_call:
    inputs:
      threshold:
        required: false
        type: number
        default: 70
    secrets:
      api_key:
        required: true
 
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - name: Scan
        env:
          MCP_SCANNER_API_KEY: ${{ secrets.api_key }}
        run: |
          # ... scan logic here

Call from other workflows:

jobs:
  security:
    uses: ./.github/workflows/mcp-scan.yml
    with:
      threshold: 80
    secrets:
      api_key: ${{ secrets.MCP_SCANNER_API_KEY }}

Troubleshooting

"Unauthorized" Error

Check that:

  • Secret name is exactly MCP_SCANNER_API_KEY
  • Secret is set at repository level
  • API key is valid and not expired

Scan Times Out

Increase the poll iterations or timeout:

# Poll for longer (60 iterations × 5 seconds = 5 minutes)
for i in {1..60}; do

Rate Limit Exceeded

  • Reduce scan frequency
  • Use caching (scans are cached 24h)
  • Upgrade to higher tier

Next Steps