A security MCP server for AI coding agents: each language via its native analyzer — Kotlin (detekt), Java (SpotBugs+FindSecBugs), Python (Bandit), C# (Roslyn), JS/TS (ESLint) — plus diff review and secure patterns.
Project description
code-security-mcp
A security MCP server for AI coding agents. It gives Claude Code, Cursor, and other agents real security findings while they write, not after — and each language is analyzed by its best native security tool, not one generic scanner stretched across everything.
A specialist, not a generalist. Kotlin is analyzed by detekt with a 216-rule, framework-aware ruleset (Spring, WebFlux, Ktor, Quarkus, Micronaut, Vert.x); Java by SpotBugs + FindSecBugs; Python by Bandit; C# by the Roslyn security analyzers; JS/TS by ESLint + eslint-plugin-security — each the established native security analyzer for its language, routed automatically.
New languages arrive as their own native analyzer — never a single lowest-common-denominator scanner.
Install
pipx install code-security-mcp # or: uvx code-security-mcp
This installs the code-security-mcp server. Python analysis works immediately
(pipx inject code-security-mcp bandit); the JVM/.NET/JS analyzers are external
tools you point at with env vars — the setup script below fetches them for you.
Quickstart (from source, with all analyzers)
git clone https://github.com/JasminGuberinic/code-security-mcp
cd code-security-mcp
python3 -m venv .venv && .venv/bin/pip install -e ".[python]"
# Download the analyzers into an isolated cache (nothing global is touched):
./scripts/setup.sh # Kotlin + Java
./scripts/setup.sh --with-dotnet --with-eslint # + C# and JS/TS (optional)
The script prints the claude mcp add … command to paste.
Why
Generic assistants and multi-language scanners are shallow on framework idioms —
they don't know that a @GetMapping is missing @PreAuthorize, that a WebClient
trusts all certificates, or that a Vert.x cookie isn't HttpOnly. This server
wraps a 216-rule, framework-aware analyzer (the
kotlin-security-scanner
detekt ruleset) and exposes it to agents through three tools.
Design principle: each language is handled by its best native security analyzer — routed automatically — never a single lowest-common-denominator generic scanner.
Languages
| Language | Analyzer | Analyzes |
|---|---|---|
| Kotlin | detekt + the 216-rule framework-aware ruleset | source (.kt, .kts) |
| Java | SpotBugs + FindSecBugs | compiled bytecode — point at the project root (it finds build/classes, target/classes, …), a classes dir, or a .jar (build first) |
| Python | Bandit | source (.py) — no build, just pip install |
| C# | Roslyn security analyzers | builds the project — point at a .sln, .csproj, or its directory |
| JavaScript / TypeScript | ESLint + eslint-plugin-security | source (.js, .ts, .jsx, .tsx, …) |
Tools
| Tool | What it does |
|---|---|
security_scan(path) |
Scan a file/directory (Kotlin, Java, Python, C#, or JS/TS) and return every security finding (rule, line, severity, CWE). |
review_diff(diff) |
Review a unified diff and flag only the issues the change introduces — a pre-commit self-check. |
secure_pattern(task, framework?) |
Get the vetted secure way to do a risky task — before writing it. |
For Kotlin, security_scan returns security findings only — detekt's
built-in style/complexity rules are switched off, so the agent gets signal, not
noise.
Example
The agent calls security_scan and gets structured findings back:
// security_scan("src/main/kotlin/HttpClient.kt")
{
"target": "src/main/kotlin/HttpClient.kt",
"count": 2,
"findings": [
{ "rule_id": "WebClientInsecureSsl", "line": 42, "severity": "error",
"cwe": "CWE-295", "message": "Reactor-Netty WebClient trusts all certificates." },
{ "rule_id": "HardcodedJwtSecret", "line": 88, "severity": "error",
"cwe": "CWE-798", "message": "Hard-coded JWT signing secret." }
]
}
…and it can ask for the fix before writing it:
"What's the secure way to create a session cookie in Vert.x?"
// secure_pattern(task = "create a session cookie", framework = "vertx") → CWE-614
val cookie = Cookie.cookie("session", token)
.setSecure(true) // only sent over HTTPS
.setHttpOnly(true) // hidden from JavaScript
.setSameSite(CookieSameSite.STRICT)
response.addCookie(cookie)
Architecture
Clean, hexagonal (ports & adapters). Dependencies point inward; the domain knows nothing about detekt or MCP.
server.py MCP surface + composition root (knows MCP)
│
application/ use cases: scan / review_diff / secure_pattern
│
domain/ Finding, ScanResult, ports (pure vocabulary)
↑ implemented by
adapters/ DetektAnalyzer, JavaAnalyzer, RoutingAnalyzer,
SARIF & diff parsers, pattern catalog
A RoutingAnalyzer sends each target to the analyzers that support it, so the
use cases treat "one language" and "many" identically. Adding a language = one
new adapter implementing the same LanguageAnalyzer port — the domain and use
cases do not change.
Requirements
- Python 3.11+
- Kotlin: a JDK + the detekt CLI jar + the ruleset jar(s)
- Java: a JDK + the SpotBugs jar + the FindSecBugs plugin jar
- Python: Bandit (
pip install "code-security-mcp[python]") — no JDK, no jars - C#: the .NET SDK (its Roslyn security analyzers are built in)
- JS/TS: Node.js + ESLint with
eslint-plugin-security(andtypescript-eslint)
Each analyzer is optional and enabled independently — configure only the languages you need. (Python auto-enables whenever Bandit is installed.)
Configuration
./scripts/setup.sh fills these in for you and prints the ready-to-paste
command; the reference below is for when you want to point at your own tools.
The server locates its tools through environment variables:
| Variable | Meaning |
|---|---|
KSM_JAVA |
Path to the java executable (shared) |
KSM_DETEKT_CLI_JAR |
Kotlin: detekt CLI (-all) jar |
KSM_PLUGIN_JARS |
Kotlin: comma-separated ruleset jar(s) |
KSM_DETEKT_CONFIG |
Kotlin: (optional) path to a detekt.yml |
KSM_SPOTBUGS_JAR |
Java: SpotBugs engine jar |
KSM_FINDSECBUGS_JARS |
Java: comma-separated plugin jar(s) (FindSecBugs) |
KSM_JAVA_AUXCLASSPATH |
Java: (optional) comma-separated dependency jars/dirs for more accurate analysis |
KSM_DOTNET_ROOT |
C#: root of the .NET SDK install |
KSM_NUGET_PACKAGES |
C#: (optional) NuGet cache dir (keeps restores isolated) |
KSM_DOTNET_CLI_HOME |
C#: (optional) dotnet CLI home dir (keeps state isolated) |
KSM_ESLINT_BIN |
JS/TS: path to the ESLint executable |
KSM_ESLINT_CONFIG |
JS/TS: path to the security flat config (see configs/) |
Python needs no variables — install Bandit and it is used automatically.
Use with Claude Code
claude mcp add code-security -s user \
-e KSM_JAVA=/path/to/java \
-e KSM_DETEKT_CLI_JAR=/path/to/detekt-cli-<ver>-all.jar \
-e KSM_PLUGIN_JARS=/path/to/scanner-core.jar,/path/to/scanner-spring-boot.jar,... \
-- /path/to/.venv/bin/code-security-mcp
Then ask the agent, e.g. "run security_scan on src/Main.kt" or "review_diff on my staged changes".
Development
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
The default suite is hermetic — it uses fake analyzers and hand-built SARIF / diff / Bandit-JSON fixtures, so it needs no external analyzer to run.
Integration tests run the real analyzers over examples/ and assert the
expected findings. They self-skip when a tool is not configured, so run them
once you have set up the analyzers (e.g. via ./scripts/setup.sh):
.venv/bin/pytest -m integration
Roadmap
Each new language arrives as its own native, framework-aware analyzer adapter (never a generic multi-language scanner):
- ✅ Kotlin — detekt + the 216-rule framework-aware ruleset.
- ✅ Java — SpotBugs + FindSecBugs.
- ✅ Python — Bandit.
- ✅ C# — Roslyn security analyzers.
- ✅ JavaScript / TypeScript — ESLint + eslint-plugin-security.
- Deeper C# via Security Code Scan (taint analysis).
- Zero-setup: auto-resolve the analyzer runtimes and rulesets.
License
MIT — see LICENSE.
Keywords: MCP server, Model Context Protocol, Kotlin security, JVM security, SAST, static analysis, detekt, Spring Security, AI coding agent, Claude Code, Cursor, secure coding.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file code_security_mcp-0.1.0.tar.gz.
File metadata
- Download URL: code_security_mcp-0.1.0.tar.gz
- Upload date:
- Size: 63.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7cbd6f9e410ecbf7f20855db12dc9ebd5090ad14b009b9d1d88bf87552ed939b
|
|
| MD5 |
b2542439435b811f31b593b9ba52ada2
|
|
| BLAKE2b-256 |
1d4f243cf6bef46990dd3aa6db14b8cabb2c0d26233a6e80f1c02137a5c2d80e
|
Provenance
The following attestation bundles were made for code_security_mcp-0.1.0.tar.gz:
Publisher:
release.yml on JasminGuberinic/code-security-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
code_security_mcp-0.1.0.tar.gz -
Subject digest:
7cbd6f9e410ecbf7f20855db12dc9ebd5090ad14b009b9d1d88bf87552ed939b - Sigstore transparency entry: 2189526214
- Sigstore integration time:
-
Permalink:
JasminGuberinic/code-security-mcp@eff6cd71420a85b71996c6a5ea4ed8aae680c93e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/JasminGuberinic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@eff6cd71420a85b71996c6a5ea4ed8aae680c93e -
Trigger Event:
release
-
Statement type:
File details
Details for the file code_security_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: code_security_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 43.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c04724d15c2cd954ef800fccc3f080b5ffc99ffd2467ad8214c63cc11e8cc248
|
|
| MD5 |
7d15e75d90e71268dddaec8bb15b88fb
|
|
| BLAKE2b-256 |
e097692a21ea4eeac291d82fb69ddbc9a1bcfa126bbea361eba4d4eb45dc9367
|
Provenance
The following attestation bundles were made for code_security_mcp-0.1.0-py3-none-any.whl:
Publisher:
release.yml on JasminGuberinic/code-security-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
code_security_mcp-0.1.0-py3-none-any.whl -
Subject digest:
c04724d15c2cd954ef800fccc3f080b5ffc99ffd2467ad8214c63cc11e8cc248 - Sigstore transparency entry: 2189526246
- Sigstore integration time:
-
Permalink:
JasminGuberinic/code-security-mcp@eff6cd71420a85b71996c6a5ea4ed8aae680c93e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/JasminGuberinic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@eff6cd71420a85b71996c6a5ea4ed8aae680c93e -
Trigger Event:
release
-
Statement type: