Use Cases¶
Practical real-world examples for using mcp-scan effectively.
Table of Contents¶
- Developing an MCP Server
- Security Audit
- Legacy Code Migration
- Third-Party Code Review
- Compliance
- Continuous Monitoring
- Integration with Existing Tools
- Team Training
Developing an MCP Server¶
Scenario¶
You are developing an MCP server that allows users to execute queries against a database.
Initial Code (Vulnerable)¶
# server.py
from mcp import Server, tool
import sqlite3
server = Server("db-query-server")
@tool
def query_database(sql_query: str) -> str:
"""Execute a SQL query against the database."""
conn = sqlite3.connect('app.db')
cursor = conn.cursor()
cursor.execute(sql_query) # VULNERABLE: SQL Injection
return str(cursor.fetchall())
@tool
def read_config(filename: str) -> str:
"""Read a configuration file."""
with open(filename, 'r') as f: # VULNERABLE: Path Traversal
return f.read()
if __name__ == "__main__":
server.run()
Step 1: Initial Scan¶
Results:
CRITICAL: 2
- MCP-D002: SQL injection via string concatenation (server.py:12)
- MCP-B002: Potential path traversal (server.py:17)
MSSS Score: 35/100
Compliance Level: 0 (Not Compliant)
Step 2: Fix Vulnerabilities¶
# server.py (fixed)
from mcp import Server, tool
import sqlite3
import os
server = Server("db-query-server")
# List of allowed queries
ALLOWED_QUERIES = {
"list_users": "SELECT id, name FROM users",
"count_orders": "SELECT COUNT(*) FROM orders",
}
ALLOWED_CONFIG_FILES = ["app.yaml", "settings.yaml"]
CONFIG_DIR = "/etc/myapp/"
@tool
def query_database(query_name: str) -> str:
"""Execute a predefined SQL query.
Args:
query_name: Name of the query to execute. Must be one of:
'list_users', 'count_orders'
"""
if query_name not in ALLOWED_QUERIES:
return f"Error: Unknown query '{query_name}'. Allowed: {list(ALLOWED_QUERIES.keys())}"
sql = ALLOWED_QUERIES[query_name]
conn = sqlite3.connect('app.db')
cursor = conn.cursor()
cursor.execute(sql) # Safe: predefined SQL
return str(cursor.fetchall())
@tool
def read_config(filename: str) -> str:
"""Read a configuration file.
Args:
filename: Name of the config file. Must be one of:
'app.yaml', 'settings.yaml'
"""
# Validate filename
if filename not in ALLOWED_CONFIG_FILES:
return f"Error: File '{filename}' not allowed"
# Use basename to prevent path traversal
safe_name = os.path.basename(filename)
filepath = os.path.join(CONFIG_DIR, safe_name)
# Verify it stays in allowed directory
if not os.path.abspath(filepath).startswith(os.path.abspath(CONFIG_DIR)):
return "Error: Invalid path"
with open(filepath, 'r') as f:
return f.read()
if __name__ == "__main__":
server.run()
Step 3: Re-scan¶
Results:
Step 4: Configure CI/CD¶
# .github/workflows/security.yml
name: Security
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
curl -sSL https://raw.githubusercontent.com/mcphub/mcp-scan/main/install.sh | bash
mcp-scan scan . --fail-on high --output sarif > results.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
Security Audit¶
Scenario¶
Your company needs to audit an MCP server before deploying it to production.
Step 1: Audit Configuration¶
# .mcp-scan-audit.yaml
include:
- "**/*.py"
- "**/*.ts"
- "**/*.js"
exclude:
- "**/tests/**"
- "**/node_modules/**"
scan:
mode: deep
timeout: 1h
fail_on: medium
rules:
disabled: []
severity_overrides:
MCP-E001: critical
MCP-E002: critical
output:
redact_snippets: true # Confidentiality
llm:
enabled: true
threshold: 0.8
ml:
enabled: true
confidence_threshold: 0.8
codeql:
enabled: true
Step 2: Run Audit¶
# Create audit directory
mkdir -p audit-$(date +%Y%m%d)
cd audit-$(date +%Y%m%d)
# Run deep scan
mcp-scan scan /path/to/mcp-server \
--config ../.mcp-scan-audit.yaml \
--mode deep \
--output evidence \
> evidence-bundle.json
# Generate SARIF report
mcp-scan scan /path/to/mcp-server \
--config ../.mcp-scan-audit.yaml \
--mode deep \
--output sarif \
> audit-sarif.sarif
# Extract MCP surface
mcp-scan surface /path/to/mcp-server \
--output json \
> mcp-surface.json
Step 3: Analyze Results¶
# Summary of findings
jq '.summary' evidence-bundle.json
# Critical findings
jq '.findings[] | select(.severity == "critical")' evidence-bundle.json
# MSSS score
jq '.msss_score' evidence-bundle.json
# Detected tools
jq '.mcp_surface.tools[].name' evidence-bundle.json
Step 4: Generate Report¶
# Script to generate report
cat > generate-report.sh << 'EOF'
#!/bin/bash
BUNDLE=$1
DATE=$(date +%Y-%m-%d)
echo "# Security Audit Report"
echo "Date: $DATE"
echo ""
echo "## Executive Summary"
echo ""
echo "| Metric | Value |"
echo "|--------|-------|"
echo "| MSSS Score | $(jq '.msss_score.score' $BUNDLE)/100 |"
echo "| Compliance Level | $(jq -r '.msss_score.level_name' $BUNDLE) |"
echo "| Total Findings | $(jq '.summary.total' $BUNDLE) |"
echo "| Critical | $(jq '.summary.by_severity.critical' $BUNDLE) |"
echo "| High | $(jq '.summary.by_severity.high' $BUNDLE) |"
echo ""
echo "## Critical Findings"
echo ""
jq -r '.findings[] | select(.severity == "critical") | "- **\(.rule_id)**: \(.title)\n - File: \(.location.file):\(.location.line)\n - Remediation: \(.remediation)"' $BUNDLE
EOF
chmod +x generate-report.sh
./generate-report.sh evidence-bundle.json > AUDIT-REPORT.md
Legacy Code Migration¶
Scenario¶
You have an old MCP server that needs to be updated and secured.
Step 1: Initial Assessment¶
# Complete scan of legacy code
mcp-scan scan ./legacy-server \
--mode deep \
--output json \
> legacy-scan.json
# View summary
jq '.summary' legacy-scan.json
# List all findings by file
jq '[.findings[] | {file: .location.file, rule: .rule_id}] | group_by(.file) | map({file: .[0].file, count: length})' legacy-scan.json
Step 2: Create Legacy Baseline¶
# Generate baseline for findings that cannot be fixed immediately
mcp-scan baseline generate \
--from legacy-scan.json \
--reason "Legacy code - scheduled for refactoring in Q2 2026" \
--accepted-by "tech-lead@example.com" \
--output legacy-baseline.json
# Review and edit baseline
nano legacy-baseline.json
Step 3: Prioritize Fixes¶
# Create priority list
jq -r '.findings | group_by(.severity) | map({severity: .[0].severity, items: map({file: .location.file, rule: .rule_id, line: .location.line})})' legacy-scan.json > priorities.json
# Critical first
echo "=== CRITICAL (Fix immediately) ==="
jq '.[] | select(.severity == "critical") | .items[]' priorities.json
# Then high
echo "=== HIGH (Fix this week) ==="
jq '.[] | select(.severity == "high") | .items[]' priorities.json
Step 4: Incremental Migration¶
# Configuration for migration
cat > .mcp-scan-migration.yaml << 'EOF'
scan:
mode: fast
fail_on: critical # Only block new critical issues
# Use legacy baseline
# mcp-scan scan . --baseline legacy-baseline.json
EOF
# Scan for new findings only
mcp-scan scan ./legacy-server \
--baseline legacy-baseline.json \
--fail-on critical
Step 5: Progress Tracking¶
# Tracking script
cat > track-progress.sh << 'EOF'
#!/bin/bash
DATE=$(date +%Y-%m-%d)
BASELINE_COUNT=$(jq '.entries | length' legacy-baseline.json)
SCAN_COUNT=$(jq '.summary.total' legacy-scan.json)
echo "$DATE,$BASELINE_COUNT,$SCAN_COUNT" >> migration-progress.csv
echo "Date: $DATE"
echo "Baselined: $BASELINE_COUNT"
echo "Total (pre-fix): $SCAN_COUNT"
echo "Fixed: $((SCAN_COUNT - BASELINE_COUNT))"
EOF
Third-Party Code Review¶
Scenario¶
You need to evaluate a third-party MCP server before integrating it.
Step 1: Preparation¶
# Clone repository
git clone https://github.com/third-party/mcp-server.git
cd mcp-server
# View structure
find . -name "*.py" -o -name "*.ts" -o -name "*.js" | head -20
Step 2: Complete Scan¶
# Scan with all options
mcp-scan scan . \
--mode deep \
--output evidence \
> third-party-audit.json
# View MCP surface
mcp-scan surface . --output json > surface.json
Step 3: Risk Analysis¶
# Analysis script
cat > analyze-third-party.sh << 'EOF'
#!/bin/bash
EVIDENCE=$1
echo "=== RISK ANALYSIS ==="
echo ""
# Score
SCORE=$(jq '.msss_score.score' $EVIDENCE)
echo "MSSS Score: $SCORE/100"
if [ $SCORE -lt 60 ]; then
echo "RISK: HIGH - Does not meet minimum requirements"
elif [ $SCORE -lt 80 ]; then
echo "RISK: MEDIUM - Meets basic requirements"
else
echo "RISK: LOW - Meets enterprise requirements"
fi
echo ""
echo "=== ATTACK SURFACE ==="
TOOLS=$(jq '.mcp_surface.tools | length' $EVIDENCE)
echo "Exposed tools: $TOOLS"
jq -r '.mcp_surface.tools[] | " - \(.name): \(.description // "No description")"' $EVIDENCE
echo ""
echo "=== FINDINGS BY SEVERITY ==="
jq -r '.summary.by_severity | to_entries | map(" \(.key): \(.value)") | .[]' $EVIDENCE
echo ""
echo "=== CRITICAL FINDINGS ==="
jq -r '.findings[] | select(.severity == "critical") | " - \(.rule_id): \(.title)"' $EVIDENCE
echo ""
echo "=== RECOMMENDATION ==="
CRITICAL=$(jq '.summary.by_severity.critical' $EVIDENCE)
HIGH=$(jq '.summary.by_severity.high' $EVIDENCE)
if [ $CRITICAL -gt 0 ]; then
echo "DO NOT APPROVE - Contains critical vulnerabilities"
elif [ $HIGH -gt 2 ]; then
echo "REVIEW - Multiple high vulnerabilities"
elif [ $SCORE -ge 80 ]; then
echo "APPROVE with conditions"
else
echo "APPROVE for limited use"
fi
EOF
chmod +x analyze-third-party.sh
./analyze-third-party.sh third-party-audit.json
Step 4: Document Decision¶
# Generate decision document
cat > SECURITY-REVIEW.md << EOF
# Security Review: third-party/mcp-server
## Date: $(date +%Y-%m-%d)
## Scan Results
- **MSSS Score:** $(jq '.msss_score.score' third-party-audit.json)/100
- **Level:** $(jq -r '.msss_score.level_name' third-party-audit.json)
## Findings
| Severity | Count |
|----------|-------|
| Critical | $(jq '.summary.by_severity.critical' third-party-audit.json) |
| High | $(jq '.summary.by_severity.high' third-party-audit.json) |
| Medium | $(jq '.summary.by_severity.medium' third-party-audit.json) |
| Low | $(jq '.summary.by_severity.low' third-party-audit.json) |
## Decision
[ ] Approved
[ ] Approved with conditions
[ ] Rejected
## Conditions (if applicable)
-
## Reviewed by
-
EOF
Compliance¶
Scenario¶
Your organization requires security analysis evidence for SOC2/ISO27001 audits.
Step 1: Compliance Configuration¶
# .mcp-scan-compliance.yaml
scan:
mode: deep
timeout: 1h
rules:
disabled: []
severity_overrides:
MCP-E001: critical
MCP-E002: critical
MCP-E005: critical
MCP-A003: critical
MCP-A004: critical
output:
redact_snippets: true
llm:
enabled: true
threshold: 0.9
codeql:
enabled: true
Step 2: Scan with Evidence¶
# Create evidence directory
AUDIT_DIR="compliance-evidence-$(date +%Y%m%d)"
mkdir -p $AUDIT_DIR
# Complete scan
mcp-scan scan . \
--config .mcp-scan-compliance.yaml \
--mode deep \
--output evidence \
> $AUDIT_DIR/evidence-bundle.json
# Hash the evidence
sha256sum $AUDIT_DIR/evidence-bundle.json > $AUDIT_DIR/evidence.sha256
# Metadata
cat > $AUDIT_DIR/metadata.json << EOF
{
"scan_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"tool": "mcp-scan",
"version": "$(mcp-scan version --short)",
"repository": "$(git remote get-url origin)",
"commit": "$(git rev-parse HEAD)",
"branch": "$(git branch --show-current)"
}
EOF
Step 3: Generate Compliance Report¶
# Report script
cat > generate-compliance-report.py << 'EOF'
#!/usr/bin/env python3
import json
import sys
from datetime import datetime
with open(sys.argv[1]) as f:
evidence = json.load(f)
report = f"""
# Security Compliance Report
## General Information
- **Scan Date:** {evidence['scan_info']['started_at']}
- **Tool:** mcp-scan v{evidence['version']}
- **Mode:** {evidence['scan_info']['mode']}
- **Duration:** {evidence['duration']['total_ms']}ms
## Security Result
| Metric | Value |
|--------|-------|
| MSSS Score | {evidence['msss_score']['score']}/100 |
| Compliance Level | {evidence['msss_score']['level_name']} |
### Requirements Mapping
| Requirement | Status | Evidence |
|-------------|--------|----------|
| Access Control (A.9) | {'Compliant' if evidence['msss_score']['score'] >= 60 else 'Non-Compliant'} | Score >= 60 |
| Development Security (A.14) | {'Compliant' if evidence['summary']['by_severity'].get('critical', 0) == 0 else 'Non-Compliant'} | No critical findings |
| Vulnerability Management (A.12) | Compliant | Automated scanning |
## Findings
| Severity | Count |
|----------|-------|
| Critical | {evidence['summary']['by_severity'].get('critical', 0)} |
| High | {evidence['summary']['by_severity'].get('high', 0)} |
| Medium | {evidence['summary']['by_severity'].get('medium', 0)} |
| Low | {evidence['summary']['by_severity'].get('low', 0)} |
## Conclusion
The system {'meets' if evidence['msss_score']['level'] >= 1 else 'DOES NOT meet'} the
minimum security requirements established.
---
Automatically generated by mcp-scan
"""
print(report)
EOF
python3 generate-compliance-report.py $AUDIT_DIR/evidence-bundle.json > $AUDIT_DIR/COMPLIANCE-REPORT.md
Step 4: Archive Evidence¶
# Create evidence package
cd $AUDIT_DIR
tar -czvf ../compliance-evidence-$(date +%Y%m%d).tar.gz .
# Verify integrity
sha256sum ../compliance-evidence-$(date +%Y%m%d).tar.gz
Continuous Monitoring¶
Scenario¶
You want to monitor the security posture of your MCP server over time.
Step 1: Monitoring Script¶
#!/bin/bash
# monitor-security.sh
REPO_PATH=$1
DATA_DIR="${2:-./security-metrics}"
mkdir -p $DATA_DIR
# Run scan
mcp-scan scan $REPO_PATH --mode fast --output json > /tmp/scan-result.json
# Extract metrics
DATE=$(date +%Y-%m-%d)
SCORE=$(jq '.msss_score.score' /tmp/scan-result.json)
TOTAL=$(jq '.summary.total' /tmp/scan-result.json)
CRITICAL=$(jq '.summary.by_severity.critical' /tmp/scan-result.json)
HIGH=$(jq '.summary.by_severity.high' /tmp/scan-result.json)
MEDIUM=$(jq '.summary.by_severity.medium' /tmp/scan-result.json)
# Save metrics
echo "$DATE,$SCORE,$TOTAL,$CRITICAL,$HIGH,$MEDIUM" >> $DATA_DIR/metrics.csv
# Save complete result
cp /tmp/scan-result.json $DATA_DIR/scan-$DATE.json
# Show summary
echo "=== Security Metrics for $DATE ==="
echo "Score: $SCORE/100"
echo "Total: $TOTAL (C:$CRITICAL H:$HIGH M:$MEDIUM)"
# Alert if score drops
if [ -f $DATA_DIR/last-score ]; then
LAST_SCORE=$(cat $DATA_DIR/last-score)
if [ $SCORE -lt $LAST_SCORE ]; then
echo "WARNING: Score decreased from $LAST_SCORE to $SCORE"
fi
fi
echo $SCORE > $DATA_DIR/last-score
Step 2: Configure Cron¶
# Run daily
crontab -e
# Add line:
0 2 * * * /path/to/monitor-security.sh /path/to/repo /path/to/metrics >> /var/log/security-monitor.log 2>&1
Step 3: Metrics Dashboard¶
#!/usr/bin/env python3
# generate-dashboard.py
import csv
import json
from datetime import datetime
def generate_dashboard(metrics_file, output_file):
with open(metrics_file) as f:
reader = csv.reader(f)
data = list(reader)
html = """
<!DOCTYPE html>
<html>
<head>
<title>Security Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.card { border: 1px solid #ddd; padding: 20px; margin: 10px; border-radius: 8px; }
.score { font-size: 48px; font-weight: bold; }
.score.good { color: green; }
.score.medium { color: orange; }
.score.bad { color: red; }
</style>
</head>
<body>
<h1>Security Dashboard</h1>
<div class="card">
<h2>Current Score</h2>
<div class="score {score_class}">{current_score}/100</div>
</div>
<div class="card">
<h2>Score Trend</h2>
<canvas id="scoreChart"></canvas>
</div>
<script>
const ctx = document.getElementById('scoreChart').getContext('2d');
new Chart(ctx, {{
type: 'line',
data: {{
labels: {labels},
datasets: [{{
label: 'MSSS Score',
data: {scores},
borderColor: 'blue',
fill: false
}}]
}},
options: {{
scales: {{
y: {{ min: 0, max: 100 }}
}}
}}
}});
</script>
</body>
</html>
"""
labels = [row[0] for row in data[-30:]] # Last 30 days
scores = [int(row[1]) for row in data[-30:]]
current_score = scores[-1] if scores else 0
score_class = "good" if current_score >= 80 else "medium" if current_score >= 60 else "bad"
html = html.format(
current_score=current_score,
score_class=score_class,
labels=json.dumps(labels),
scores=json.dumps(scores)
)
with open(output_file, 'w') as f:
f.write(html)
if __name__ == "__main__":
import sys
generate_dashboard(sys.argv[1], sys.argv[2])
Integration with Existing Tools¶
With SonarQube¶
# Convert results to SonarQube Generic Issue format
mcp-scan scan . --output json | jq '{
issues: [.findings[] | {
engineId: "mcp-scan",
ruleId: .rule_id,
severity: (if .severity == "critical" then "CRITICAL" elif .severity == "high" then "MAJOR" elif .severity == "medium" then "MINOR" else "INFO" end),
type: "VULNERABILITY",
primaryLocation: {
message: .title,
filePath: .location.file,
textRange: {
startLine: .location.line,
endLine: .location.end_line
}
}
}]
}' > sonar-issues.json
# Upload to SonarQube
sonar-scanner -Dsonar.externalIssuesReportPaths=sonar-issues.json
With Jira¶
# Create Jira issues for critical findings
mcp-scan scan . --output json | jq -r '.findings[] | select(.severity == "critical") | @json' | while read finding; do
TITLE=$(echo $finding | jq -r '.title')
DESC=$(echo $finding | jq -r '.description')
FILE=$(echo $finding | jq -r '.location.file')
LINE=$(echo $finding | jq -r '.location.line')
curl -X POST \
-H "Content-Type: application/json" \
-u "$JIRA_USER:$JIRA_TOKEN" \
"$JIRA_URL/rest/api/2/issue" \
-d "{
\"fields\": {
\"project\": {\"key\": \"SEC\"},
\"summary\": \"[MCP-Scan] $TITLE\",
\"description\": \"$DESC\\n\\nLocation: $FILE:$LINE\",
\"issuetype\": {\"name\": \"Security Vulnerability\"},
\"priority\": {\"name\": \"Critical\"}
}
}"
done
With Slack Webhooks¶
#!/bin/bash
# notify-slack.sh
WEBHOOK_URL=$1
RESULTS=$2
SCORE=$(jq '.msss_score.score' $RESULTS)
CRITICAL=$(jq '.summary.by_severity.critical' $RESULTS)
HIGH=$(jq '.summary.by_severity.high' $RESULTS)
if [ $CRITICAL -gt 0 ] || [ $HIGH -gt 0 ]; then
COLOR="danger"
EMOJI=":warning:"
else
COLOR="good"
EMOJI=":white_check_mark:"
fi
curl -X POST $WEBHOOK_URL \
-H 'Content-Type: application/json' \
-d "{
\"attachments\": [{
\"color\": \"$COLOR\",
\"title\": \"$EMOJI Security Scan Results\",
\"fields\": [
{\"title\": \"Score\", \"value\": \"$SCORE/100\", \"short\": true},
{\"title\": \"Critical\", \"value\": \"$CRITICAL\", \"short\": true},
{\"title\": \"High\", \"value\": \"$HIGH\", \"short\": true}
]
}]
}"
Team Training¶
Scenario¶
You want to use mcp-scan to train your team on MCP security.
Training Material¶
# Create training project
mkdir mcp-security-training
cd mcp-security-training
# Create vulnerable exercises
cat > exercise1_rce.py << 'EOF'
"""Exercise 1: Detect RCE
Your mission: Find and fix the command execution vulnerability.
"""
from mcp import tool
import subprocess
@tool
def run_command(cmd: str) -> str:
"""Run a system command."""
# TODO: This line is vulnerable. How would you fix it?
return subprocess.check_output(cmd, shell=True).decode()
# HINT: Use a list of allowed commands or subprocess without shell=True
EOF
cat > exercise2_sqli.py << 'EOF'
"""Exercise 2: Detect SQL Injection
Your mission: Find and fix the SQL injection vulnerability.
"""
from mcp import tool
import sqlite3
@tool
def search_user(name: str) -> str:
"""Search for a user by name."""
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
# TODO: This query is vulnerable. How would you fix it?
cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")
return str(cursor.fetchall())
# HINT: Use parameters instead of f-strings
EOF
# Create exercise guide
cat > TRAINING.md << 'EOF'
# MCP Security Training
## Exercises
### Exercise 1: Remote Command Execution
- File: exercise1_rce.py
- Objective: Identify and fix RCE vulnerability
```bash
# Scan
mcp-scan scan exercise1_rce.py
# View finding
mcp-scan scan exercise1_rce.py --output json | jq '.findings[0]'
Exercise 2: SQL Injection¶
- File: exercise2_sqli.py
- Objective: Identify and fix SQL injection
Verification¶
After fixing, run:
If there are no findings, you completed the exercise. EOF
### Interactive Workshop
```bash
#!/bin/bash
# workshop.sh - Interactive security workshop
echo "=== MCP Security Workshop ==="
echo ""
echo "This workshop will guide you through detecting and fixing vulnerabilities."
echo ""
# Exercise 1
echo "=== EXERCISE 1: RCE ==="
echo "Scanning exercise1_rce.py..."
mcp-scan scan exercise1_rce.py
echo ""
read -p "Press Enter to see the solution..."
echo ""
echo "SOLUTION: Use a whitelist of allowed commands:"
echo "
ALLOWED_COMMANDS = ['ls', 'pwd', 'date']
if cmd not in ALLOWED_COMMANDS:
return 'Command not allowed'
return subprocess.check_output([cmd])
"
# Exercise 2
echo ""
echo "=== EXERCISE 2: SQL Injection ==="
echo "Scanning exercise2_sqli.py..."
mcp-scan scan exercise2_sqli.py
echo ""
read -p "Press Enter to see the solution..."
echo ""
echo "SOLUTION: Use SQL parameters:"
echo "
cursor.execute('SELECT * FROM users WHERE name = ?', (name,))
"
echo ""
echo "=== WORKSHOP COMPLETED ==="
Previous: CI/CD Integration | Back to Index: README