⌘K
Block merges when an MCP server fails the MCPSafe security check.
Add MCPSafe to your CI/CD pipeline to automatically scan MCP servers on every pull request.
# .github/workflows/mcp-security.yml
name: MCP Security Scan
on:
pull_request:
paths:
- "claude_desktop_config.json"
- ".mcp.json"
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan MCP server
id: scan
run: |
SCAN_ID=$(curl -s -X POST https://api.mcpsafe.io/scan \
-H "Content-Type: application/json" \
-d '{"input": "@modelcontextprotocol/server-github"}' \
| jq -r '.data.scan_id')
echo "Waiting for scan $SCAN_ID..."
for i in $(seq 1 20); do
RESULT=$(curl -s https://api.mcpsafe.io/scan/$SCAN_ID)
STATUS=$(echo $RESULT | jq -r '.status')
if [ "$STATUS" = "complete" ]; then
echo "result=$RESULT" >> $GITHUB_OUTPUT
break
fi
sleep 3
done
- name: Check security grade
run: |
GRADE=$(echo '${{ steps.scan.outputs.result }}' | jq -r '.safety_grade')
SCORE=$(echo '${{ steps.scan.outputs.result }}' | jq -r '.safety_score')
echo "Security grade: $GRADE ($SCORE/100)"
if [[ "$GRADE" == "D" || "$GRADE" == "F" ]]; then
echo "::error::MCP server failed security check (grade $GRADE)"
exit 1
fiScan every MCP server listed in claude_desktop_config.json:
- name: Extract and scan MCP servers
run: |
SERVERS=$(jq -r '.mcpServers | keys[]' claude_desktop_config.json)
FAILED=0
for SERVER in $SERVERS; do
echo "Scanning $SERVER..."
SCAN_ID=$(curl -s -X POST https://api.mcpsafe.io/scan \
-H "Content-Type: application/json" \
-d "{\"input\": \"$SERVER\"}" \
| jq -r '.data.scan_id')
# Wait for completion
for i in $(seq 1 20); do
RESULT=$(curl -s https://api.mcpsafe.io/scan/$SCAN_ID)
STATUS=$(echo $RESULT | jq -r '.status')
[ "$STATUS" = "complete" ] && break
sleep 3
done
GRADE=$(echo $RESULT | jq -r '.safety_grade')
echo "$SERVER: $GRADE"
[[ "$GRADE" == "D" || "$GRADE" == "F" ]] && FAILED=1
done
[ $FAILED -eq 1 ] && exit 1
echo "All servers passed."- name: Comment scan results on PR
uses: actions/github-script@v7
with:
script: |
const result = JSON.parse(`${{ steps.scan.outputs.result }}`);
const grade = result.safety_grade;
const score = result.safety_score;
const emoji = { A: "✅", B: "✅", C: "⚠️", D: "❌", F: "❌" }[grade];
const body = `## MCPSafe Security Scan ${emoji}\n\n**Grade: ${grade}** (${score}/100)\n\n` +
`| Severity | Count |\n|---|---|\n` +
Object.entries(result.severity_distribution)
.map(([k,v]) => `| ${k} | ${v} |`).join("\n") +
`\n\n[View full report](https://mcpsafe.io/scan/${result.scan_id})`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body,
});Use API keys for private repos
Set MCPSAFE_API_KEY as a GitHub secret and pass it as Authorization: Bearer ${{ secrets.MCPSAFE_API_KEY }} for authenticated requests and higher rate limits.