Documentation

GitLab CI Integration

This guide shows you how to integrate MCP Security Score into your GitLab CI/CD pipelines 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 Variable

  1. Go to your GitLab project
  2. Navigate to Settings → CI/CD → Variables
  3. Click Add variable
  4. Key: MCP_SCANNER_API_KEY
  5. Value: Your API key (mcp_sk_...)
  6. Check Mask variable to hide in logs
  7. Click Add variable

Step 2: Create the Pipeline

Create .gitlab-ci.yml in your repository:

stages:
  - security
 
mcp-security-scan:
  stage: security
  image: alpine:latest
  variables:
    SCORE_THRESHOLD: "70"
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      set -e
 
      REPO_URL="${CI_PROJECT_URL}"
 
      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 $(seq 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 "$SCORE_THRESHOLD" ]; then
        echo "❌ Score $SCORE is below threshold $SCORE_THRESHOLD"
        exit 1
      else
        echo "✅ Security check passed"
      fi
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Complete Example with Artifacts

This version saves results as an artifact:

stages:
  - security
 
mcp-security-scan:
  stage: security
  image: alpine:latest
  variables:
    SCORE_THRESHOLD: "70"
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      set -e
 
      REPO_URL="${CI_PROJECT_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')
 
      # Poll for completion
      for i in $(seq 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
 
      # Save full results
      echo "$RESULT" | jq '.' > security-report.json
 
      # Extract key metrics
      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')
 
      # Create summary
      cat > security-summary.txt << EOF
      MCP Security Score Security Report
      ===========================
      Repository: $REPO_URL
      Scan ID: $SCAN_ID
 
      Score: $SCORE ($GRADE)
      Total Findings: $FINDINGS
      - Critical: $CRITICAL
      - High: $HIGH
 
      Full report: https://mcpscanner.com/dashboard/scan/$SCAN_ID
      EOF
 
      cat security-summary.txt
 
      # Fail if below threshold
      if [ "$SCORE" -lt "$SCORE_THRESHOLD" ]; then
        exit 1
      fi
  artifacts:
    paths:
      - security-report.json
      - security-summary.txt
    expire_in: 30 days
    when: always
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Merge Request Only

Scan only on merge requests to save API calls:

mcp-security-scan:
  stage: security
  # ... same configuration ...
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Scheduled Scans

Run security scans on a schedule:

mcp-security-scan:
  stage: security
  # ... same configuration ...
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
    - if: $CI_PIPELINE_SOURCE == "web"  # Manual trigger

Then create a schedule in CI/CD → Schedules.

Using Docker Image

For faster builds, use an image with curl and jq pre-installed:

mcp-security-scan:
  stage: security
  image: curlimages/curl:latest
  before_script:
    - apk add --no-cache jq
  # ... rest of configuration ...

Troubleshooting

"Unauthorized" Error

Check that:

  • Variable name is exactly MCP_SCANNER_API_KEY
  • Variable is not protected (or branch is protected)
  • API key is valid and not expired

Scan Times Out

Increase the poll iterations:

# Poll for longer (60 iterations × 5 seconds = 5 minutes)
for i in $(seq 1 60); do

Variable Not Available

If the variable is protected, it won't be available on unprotected branches. Either:

  • Uncheck "Protected" on the variable
  • Or run scans only on protected branches

Next Steps