AI-Coding-Assistant Integration
How Gadriel Code plugs into AI coding assistants so that vulnerable code never enters git history. This page covers the MCP server and its tool surface, the agentic find→fix→re-validate loop, multi-platform support, and autonomous remediation campaigns.
Related: Tiered analysis model · Privacy boundary
1. The model: shift-left to the moment of generation
Every existing scanner (Snyk, Semgrep, SonarQube) runs after code lands:
agent writes vulnerable code → commit → batch scan → developer reads report
→ developer types "fix this" → agent rewrites → wait for rescan → repeat
The vulnerability entered git history at step 1 and the developer is a relay.
Gadriel removes the relay:
agent writes code → Gadriel validates on save (graph already built)
→ finding surfaced into the agent's live context → agent rewrites
→ Gadriel re-validates and clears the finding
→ every step appended to .security/audit-log.jsonl
The vulnerability never reaches git; the audit log is direct compliance
evidence and stays local only (see privacy.md).
2. The MCP server
Gadriel ships as an MCP server — JSON-RPC 2.0 over stdio. It is the primary integration surface; any MCP-aware assistant can call its tools during code generation. It runs in-process with the graph engine, so there is no IPC overhead and no network egress.
Start it:
bashgadriel code mcp
The server speaks the three MCP protocol methods (initialize, tools/list,
tools/call) plus direct tool invocation. serverInfo.name is gadriel;
the advertised protocolVersion is 2024-11-05. stdio transport is
unauthenticated by design (the auth-token field is reserved for a future
HTTP/SSE transport).
Fallback floor. Any assistant that cannot speak MCP can poll
.security/findings-current.json (written by gadriel watch). Both
mechanisms emit the same finding format.
2.1 Tool surface
The registry wires 8 tools (verified in
crates/code-security/gadriel-mcp/src/server.rs and
crates/code-security/gadriel-mcp/src/tools/):
| Tool | Purpose |
|---|---|
validate_buffer | Scan an unsaved code buffer (SAST + Secrets) and return findings. |
validate_file | Scan a saved file on disk (SAST + Secrets) and return findings. |
findings_for_path | Read .security/findings.json and return findings filtered by path. |
fix_finding | Invoke the per-pillar reasoning runner for a finding. |
dismiss_false_positive | Mark a finding as a false positive (writes an audit-log event). |
submit_ai_findings | Submit host-agent AI findings for fusion with a prior scan session's deterministic results. |
start_remediation_campaign | Start a repo-scale remediation campaign over filtered findings. |
campaign_advance | Advance a campaign: propose the next fix, or finalize an applied one. |
The MCP server never writes files. fix_finding and campaign_advance
return a diff + rationale; the host agent applies every edit and
self-reports applied: true. No auto-commit, no auto-git add, ever. A
buggy or malicious diff can never be silently
committed by Gadriel.
2.2 The core round-trip
validate_file / validate_buffer return:
json{ "findings": [ ... ], "count": 3, "tier2_request": null }
fix_finding (params { finding_id, repo_root? }) returns a
FixProposal envelope:
json{ "status": "...", "runner_id": "...", "finding_id": "CODE-W1-AI-001","diff": "...", "rationale": "...", "iterations_used": 1, "tool_calls": [] }
The tier2_request field is null unless Tier 2 is explicitly enabled — see
§2.4 and tiers.md.
3. The agentic loop
The loop the host agent runs during a coding session:
┌─────────────────────────────────────────────────────────────┐
│ assistant generates / edits code │
│ │ │
│ ▼ │
│ validate_buffer (before save — code never lands) │
│ │ findings? │
│ ├── none ──▶ save file ──▶ validate_file (confirm) │
│ │ │
│ └── findings ──▶ fix_finding ──▶ assistant applies │
│ │ the returned diff │
│ ▼ │
│ re-validate ──▶ finding cleared? │
│ │ │
│ └── audit-log.jsonl (local) │
└─────────────────────────────────────────────────────────────┘
Because validate_buffer runs on the unsaved buffer, the assistant can
correct a vulnerability before the file is written — so vulnerable code never
enters the working tree, let alone git. dismiss_false_positive closes the
loop for findings the developer judges to be false positives, writing an
audit event (and updating the Bayesian model).
4. Multi-platform support
Claude Code is the native reference platform — it keeps its dedicated
scaffolder in init.rs (full parity: local stdio MCP, real-time hooks,
CLAUDE.md rules, skills + commands + subagents). Every other assistant is
driven by a data-driven registry
(crates/gadriel-cli/src/commands/code/platform/registry.rs).
4.1 Wiring it up: gadriel code init
gadriel code init scaffolds the integration. For Claude Code it merges an
.mcp.json stanza (deep-merge, never clobbering sibling tools):
json{"mcpServers": {"gadriel": { "command": "gadriel", "args": ["code", "mcp"] }},"enabledMcpjsonServers": ["gadriel"]}
For other platforms, init writes the platform-appropriate MCP config, hook
config, rules/skills, and slash-command files, using the absolute path to
the running gadriel binary (GUI assistants do not inherit the shell PATH).
Target platforms may be listed explicitly, auto-detected from marker
directories (e.g. .cursor/, .windsurf/, .vscode/, .codex/), or set via
[init] platforms in .gadriel.toml.
4.2 Parity report: gadriel code doctor --platform <name>
Prints the integration parity manifest — MCP/hooks/rules support and version gates — for one platform:
bashgadriel code doctor --platform cursor
Valid --platform values (run gadriel code doctor --help for the current
list):
claude-code · cursor · windsurf · copilot · codex · jetbrains ·
claude-desktop · chatgpt · ai-studio
Platforms fall into three integration classes:
| Class | Meaning | Example |
|---|---|---|
| A | Repo-editing IDE agent — full tier stack | Cursor, Windsurf, Copilot/VS Code, Codex, JetBrains |
| B | Local desktop chat with stdio MCP — MCP-tools only | Claude Desktop |
| C | Hosted cloud chat — CI-gate + static report only (no local stdio, no repo FS) | ChatGPT, AI Studio |
Per-platform parity notes surfaced by doctor (from the registry):
| Platform | MCP config path | Real-time hooks | Notes |
|---|---|---|---|
| Claude Code | .mcp.json (project) | ✓ .claude/settings.json | full parity — reference platform |
| Cursor | .cursor/mcp.json (project) | ✓ .cursor/hooks.json | near-full parity |
| Windsurf (Cascade) | .codeium/windsurf/mcp_config.json (global) | ✓ (blocks, no auto-retry) | MCP global-scope; 12 KB rules cap |
| Copilot / VS Code | .vscode/mcp.json (project) | Preview (agent edits) | save-scan needs the gadriel-vscode extension |
| OpenAI Codex CLI | .codex/config.toml (project) | ✓ | apply_patch hook needs Codex CLI ≥ v0.117; no slash commands; 32 KB cap |
| JetBrains | print-and-paste (no CLI-writable MCP) | ✓ | block/squiggle/statusline need the JetBrains plugin |
| Claude Desktop | claude_desktop_config.json (global) | — | macOS/Windows only; MCP-tools tier |
| ChatGPT / AI Studio | — | — | CI-gate + static report only |
The CI gate and audit log are platform-agnostic — they work identically regardless of which assistant (or none) is connected.
5. Autonomous remediation campaigns
A single fix_finding call fixes one finding. To fix a whole class at
repo-scale ("fix all 40 confirmed SQLi findings") without the host agent
hand-rolling a 40× loop, Gadriel exposes a stateful two-tool pair.
5.1 start_remediation_campaign
Params: a filter (severity[] / pillar[] / scan_type[] / rule_id[] /
confidence_tier[]) plus an optional finding-ID allowlist, repo_root, and
optional runner_id. It loads findings.json, orders the worklist, persists
campaign state to .security/campaigns/<campaign_id>.json, and returns
{ campaign_id, total, first_batch }.
Ordering: primary sort by effective_risk(severity, confidence)
descending; tiebreak by presence of a materialized attack path, then by
ascending path length; findings sharing a file stay adjacent.
5.2 campaign_advance
The fix → verify → next loop the host agent calls repeatedly
({ campaign_id, repo_root, applied?, after_hash? }):
- First call — reads the next
pendingitem, calls the existingfix_findingpropose logic, and returns a diff + rationale. - Host applies the diff, then calls again with
applied: true. - Second call — runs verification and updates state, returning the next
item or
{ status: "campaign_complete", summary }.
Per-item terminal states: fixed, verified, verified_partial,
regressed, waived, skipped_no_agent.
Verification: an instant re-scan via validate_file on the touched
file. This is accurate/instant only for Python-SAST + Secrets; for
SCA/Container/API or non-Python SAST the item is marked verified_partial
(and the summary says so explicitly — no overclaiming).
Failure handling, all mechanized versions of what a human does:
- Regression / new finding introduced →
regressed, never waived, never dropped, surfaced prominently in the summary. - Agent unavailable (only Security and Safety pillar agents are authored
today; the other 6 fail closed) →
skipped_no_agent, continue. - Runner can't fix / host declines → mechanized durable waiver in
.gadriel-waivers.tomlwith an auto-populatedreason.
The same discipline holds throughout: campaign_advance never writes files;
the host agent applies every diff. The optional per-fix Tier-2
second-opinion gate inherits the never-gate invariant — a negative
opinion flags and demotes, it never hard-fails the campaign or auto-reverts
(this gate is not yet wired).
