AI tools like Claude Code and Cursor let us ship features faster than ever. The flip side: more code flowing faster means more potential vulnerabilities shipping faster.
Semgrep calls this the "vibe coding" problem. We're generating code at a pace where the old workflow of "commit, wait for CI scan, fix issues" doesn't cut it anymore. Security scanning needs to happen while you're still in the editor, not after you've moved on to the next thing.
I run Semgrep at three points: real-time via their MCP server, in a pre-commit hook, and in CI through GitHub Actions. Here's how to set it up.
The Security Gap in AI-Assisted Development
When Claude Code generates a function, it's optimizing for "does this work?" not "is this secure?" LLMs pattern-match against training data that includes plenty of insecure code examples, and they'll happily reproduce those patterns if they solve the immediate problem.
Common issues AI-generated code can introduce:
- SQL injection: String concatenation in database queries instead of parameterized statements
- Hardcoded secrets: API keys and credentials placed directly in source files
- Insecure deserialization: Parsing untrusted input without validation
- Path traversal: File operations without proper input sanitization
- Debug mode in production: Development flags left enabled in generated configs
If AI is generating code in real-time, security analysis should happen in real-time too. Waiting until CI to find out you have a SQL injection vulnerability is too late.
Three Layers of Security Scanning
I don't rely on a single checkpoint. I run Semgrep at three stages:
- Real-time: Semgrep MCP server scans code as the AI generates it
- Pre-commit: a git hook scans staged files before any commit
- CI: GitHub Actions blocks PRs with security findings
Most issues get caught at stage 1 or 2. CI is the safety net that rarely fires because the earlier layers caught everything. But when it does fire, I'm glad it's there.
Layer 1: Semgrep MCP Server for Real-Time Scanning
Semgrep released an open-source MCP server that plugs directly into Claude Code and Cursor. The AI can invoke Semgrep during code generation, catch a vulnerability, fix it, and re-scan to confirm the fix, all without you doing anything.
What the MCP Server Provides
The MCP server exposes these tools to the LLM:
- security_check: Scans code against Semgrep's rule registry
- semgrep_scan: Analyzes specific files with configurable rules
- semgrep_scan_with_custom_rule: Applies custom patterns you define
- get_abstract_syntax_tree: Returns AST for deeper semantic analysis
- semgrep_findings: Pulls issues from Semgrep's AppSec Platform (if authenticated)
The LLM can check its own output, spot a problem, fix it, and confirm the fix passed before it hands the code back to you.
Setting Up Semgrep MCP in Claude Code
# macOS
brew install semgrep
# Windows/Linux (via pip)
pip install semgrep
# Windows (via pipx for isolated install)
pipx install semgrep
# Verify install. MCP support landed in 1.146.0; check the
# Semgrep release notes for the current minimum if you hit issues.
semgrep --version
# Authenticate for Pro rules (optional but recommended)
semgrep login && semgrep install-semgrep-proOnce Semgrep is installed and in your PATH, add the MCP server to Claude Code:
# Launch Claude Code
claude
# Add the Semgrep marketplace
/plugin marketplace add semgrep/mcp-marketplace
# Install the plugin
/plugin install semgrep-plugin@semgrep
# Run setup
/semgrep-plugin:setup_semgrep_plugin
# Enable if needed
/plugin enable semgrep-plugin@semgrepSetting Up Semgrep MCP in Cursor
For Cursor, the setup involves configuring the MCP server in settings and adding hooks for automatic scanning:
// .cursor/mcp.json
{
"mcpServers": {
"semgrep": {
"command": "semgrep",
"args": ["mcp"]
}
}
}Add hooks for automatic scanning after file edits:
// .cursor/hooks.json
{
"version": 1,
"hooks": {
"stop": [
{
"command": "semgrep mcp -k stop-cli-scan -a cursor"
}
],
"afterFileEdit": [
{
"command": "semgrep mcp -k record-file-edit -a cursor"
}
]
}
}Configuring LLM Behavior for Security Scanning
Installing the MCP server isn't enough. You need to tell the LLM to use it. Without explicit instructions, it won't scan consistently.
For Claude Code, add this to your project's CLAUDE.md or
global ~/.claude/CLAUDE.md:
## Security Requirements
Security is a core requirement for all code in this project. After generating or modifying any code that handles user input, authentication, data access, or external APIs, invoke Semgrep to scan for vulnerabilities. If issues are found, fix them immediately and re-scan to verify remediation before considering the task complete.For Cursor, add equivalent instructions to your User Rules in Settings > Rules:
Security is a core requirement. After generating or modifying code that handles user input, authentication, data access, or external APIs, invoke Semgrep to scan for vulnerabilities. Fix any issues found and re-scan to verify remediation.Layer 2: A Pre-Commit Hook
Real-time scanning catches most issues, but it depends on the LLM remembering to call the tool. I want a checkpoint that doesn't depend on anyone remembering anything, so Semgrep also runs as a git pre-commit hook. Nothing gets committed until the staged files pass.
The pre-commit framework is the simplest way to wire that up, and Semgrep publishes a hook for it. Add a .pre-commit-config.yaml to the repo root:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/semgrep/pre-commit
rev: 'v1.172.0' # any current release tag; `pre-commit autoupdate` bumps it
hooks:
- id: semgrep
entry: semgrep
args: ['--config', 'auto', '--error', '--skip-unknown-extensions']Then install the hook once per clone:
# pip or Homebrew both work
pip install pre-commit
# Writes .git/hooks/pre-commit
pre-commit install
# Optional: scan the whole repo once instead of just staged files
pre-commit run --all-files--error makes Semgrep exit non-zero on any finding, which is what blocks the commit. The hook only scans staged files, so on a normal commit it finishes before I notice it ran. Point --config at the same rules your CI uses, so a commit that passes locally doesn't fail in the pipeline.
If you'd rather not add Python tooling to a Node project, a hook manager like Husky or Lefthook can run the same semgrep scan --config auto --error command against staged files. The point is the gate, not the tool that installs it.
Layer 3: GitHub Actions CI Enforcement
The final layer catches anything that slipped past local scanning. Here's the workflow I use:
# .github/workflows/security.yml
name: Security Scanning
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
semgrep:
name: Semgrep SAST
runs-on: ubuntu-latest
container:
image: semgrep/semgrep
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Semgrep
run: semgrep ci
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
# Add additional jobs as needed:
# - dependency-audit (npm audit, pip-audit, etc.)
# - secret-scanning
# - license-complianceIf you use Semgrep's cloud platform, semgrep ci uploads findings for tracking. For open-source repos, use semgrep scan --config auto instead.
Blocking PRs on Security Findings
Set up branch protection to require the security check:
- Settings > Branches > Branch protection rules
- Add rule for
main - Enable "Require status checks to pass before merging"
- Select "Semgrep SAST"
PRs with security findings get blocked until you fix them.
Real-World Application: AccessHawk
I use this setup on AccessHawk, my accessibility scanner SaaS. It handles user auth, subscription billing, and scan data. Security problems would be bad.
Having Semgrep at every layer means issues get caught during development instead of code review or production. I ship faster because I'm not worried about what I might have missed.
The Workflow in Practice
A typical feature implementation:
- Claude Code writes the code, with the Semgrep MCP server catching issues as it goes
- The pre-commit hook scans the staged files when I commit
- Push to GitHub, CI runs the security workflow
- PR blocked if findings exist
Most issues get caught at step 1 or 2. CI rarely finds anything new because the earlier layers already caught it.
Set This Up
I run Semgrep on every project now, even without AI involvement. But when an assistant can implement a feature in minutes, a "security review before release" step can't keep up. The scanning has to run continuously instead of as a gate at the end.
Real-time MCP scanning catches issues as code is generated. The pre-commit hook stops anything that slipped through from being committed. CI blocks the main branch from anything that got past both.
The setup is straightforward: install the MCP server, add the pre-commit config, drop in the GitHub Actions workflow. You can have all three layers running in 15 minutes.
