mcp-sentinel-detection-engine
An MCP server exposing a Sigma → KQL → Microsoft Sentinel Terraform pipeline to Claude. Local stdio, certificate-based Azure auth, read-only by construction.
Detection engineering as it’s practiced today is sigma-cli plus four
context-switches: pick the right Log Analytics table, catch the column
typo, smoke-test on live data, emit Terraform. This server collapses
that loop into a Claude conversation. Every step is safe to expose to
a model and auditable on stderr.
Companion to mcp-defender-xdr: hunt
there, ship detections from here.
Why this matters
sigma-cli, pySigma, uncoder.io, and SigmaToARM already convert
Sigma rules to KQL or ARM. None of them close the loop on the four
things that actually take an analyst’s morning: choosing the right Log
Analytics table for the rule, catching the column-name typo before it
goes to production, smoke-testing the converted query against live
data, and emitting Terraform that drops cleanly into an existing
infra-as-code repo. The agent is the orchestrator; the server’s job
is to make every step safe to expose to a model.
Key technical decisions
- Certificate-based OAuth, not client secrets. PFX private key
never leaves the host. MSAL caches tokens in memory per
(tenant, scope)and refreshes 60 s before expiry. No shared client secrets, no on-behalf-of flow. - Read-only by construction. Only
https://api.loganalytics.io/.defaultis requested. Destructive KQL verbs (.drop,.alter,.ingest,.purge, …) are rejected before any HTTP call. Row cap ≤ 10; 60 s server timeout. - Multi-tenant fan-out with N distinct MSAL apps. Per-tenant results are labelled with the server-provided tenant key, never derived from upstream JSON — the model can’t be tricked into attributing one tenant’s data to another by a crafted response.
- Prompt-injection-aware input validation. Control characters,
zero-width characters, and RTL overrides are stripped at the
validation boundary before pySigma sees the payload. An
examples/sigma/adversarial/rule ships with a real injection payload and a test that asserts it still converts cleanly. - Terraform emission, not
terraform apply. Emission only. Apply is a human-gated step in a separate write-credentialed pipeline kept off the LLM-facing host.
Threat model summary
- T1 — Prompt injection inside Sigma rule fields. Adversary
control of
description,falsepositives, orreferencesfields. Mitigated by stripping control / zero-width / RTL characters at the validation boundary; pySigma never sees the raw bytes. - T2 — Token theft from the running host. PFX-only on disk, never passed to the model, never logged. MSAL keeps the access token in memory only; no token cache file is written.
- T3 — Tenant-existence oracle. Per-tenant errors are normalized before they hit the model so a missing-tenant response is indistinguishable from an auth-failure response.
Full threat model → github.com/MFisher14/mcp-sentinel-detection-engine/blob/main/THREAT_MODEL.md
Screenshots
What’s next
- Live workspace schema via the Log Analytics metadata API
- HTTP/SSE transport in addition to stdio
- Per-tenant rate-limit handling
Full v0.2 roadmap → github.com/MFisher14/mcp-sentinel-detection-engine/milestone/1
Threat model
Read-only by construction — only `https://api.loganalytics.io/.default` is requested; destructive KQL verbs (`.drop`, `.alter`, `.ingest`, `.purge`, …) are rejected before any HTTP call; row cap ≤ 10; 60 s server timeout. Auth is OAuth 2.0 client credentials with an X.509 certificate per Azure App Registration — the PFX private key never leaves the host, and MSAL caches tokens in memory per `(tenant, scope)` with a 60 s pre-expiry refresh. Multi-tenant fan-out uses N distinct MSAL apps; per-tenant results are labelled with the server-provided tenant key, never derived from upstream JSON. Prompt-injection-aware input validation strips control characters / zero-width / RTL overrides at the validation boundary before pySigma sees the payload. Terraform emission only — `terraform apply` is a human-gated step in a separate write-credentialed pipeline kept off the LLM-facing host.