Skip to main content

Sovereign MCP

Your AI assistant writes Terraform. This checks it before you do.

An MCP server that scans Terraform for security misconfigurations while the code is being generated, not after it lands in a pull request. It runs locally, needs no account, and your infrastructure code never leaves your machine.

Detection is Checkov (Apache-2.0), vendored so it runs offline and pinned to the same version the backend evaluates. What this adds is the shape around it: findings compact enough to sit in an assistant's context, curated remediation per check, a narrow allowlist of fixes that are safe to apply mechanically, hardened templates and org policy served before generation, and a handful of checks Checkov does not cover — a credential written as a literal in the HCL among them.

You:       "add an RDS instance for the orders service"
Assistant: [writes HCL] → [scans it] → [fixes 4 findings] → shows you the result

Why this exists

Provider defaults optimise for it works, not it is safe. Terraform generated from a model's memory is routinely unencrypted, publicly reachable, or missing deletion protection — and the cost of fixing that rises steeply the further it travels. In the editor it is one attribute. In a PR it is a review cycle. In production it is an incident.

CI already catches this. CI catches it three days and one argument later.


Install

Claude Code

claude mcp add sovereign -- uvx sovereign-observer

Cursor

~/.cursor/mcp.json:

{
  "mcpServers": {
    "sovereign": {
      "command": "uvx",
      "args": ["sovereign-observer"]
    }
  }
}

VS Code (GitHub Copilot)

.vscode/mcp.json:

{
  "servers": {
    "sovereign": {
      "type": "stdio",
      "command": "uvx",
      "args": ["sovereign-observer"]
    }
  }
}

Windsurf

~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "sovereign": {
      "command": "uvx",
      "args": ["sovereign-observer"]
    }
  }
}

First run downloads the engine (~100 MB) and takes a moment. After that it is local and fast.


Environments

A scanner that reports the same severity everywhere gets muted. Multi-AZ and deletion protection are the right call in production and noise on a sandbox torn down nightly, and once someone has dismissed the same Critical five times on a scratch stack they stop reading the output at all.

So every finding is tagged with the environment it was inferred to belong to, from an Environment tag or the directory the file sits in. unknown is a normal answer and changes nothing.

Passing environment_aware to scan_terraform lets that inference move severity — but only for an explicit allowlist of resilience, monitoring and housekeeping checks. Public access, encryption, identity and hardcoded credentials never move, in any environment. A dev bucket is usually where last month's production dump lives.

It is off by default, because a scan that quietly drops a finding below the level your merge gate keys on has weakened your pipeline without asking. Nothing is ever lowered below Low, and a finding that moved says what it moved from.


Tools

Tool What it does
scan_terraform Scan HCL — from disk or an unsaved buffer. Returns findings by severity with file and line, each tagged with its inferred environment.
explain_finding The full remediation for one finding: what is wrong and the exact Terraform to fix it.
apply_fixes Apply the mechanically-safe fixes and return patched HCL.
secure_template A hardened starting point for a resource type, so the insecure version never gets written.
check_compliance Map findings to SOC 2, ISO 27001, NIST 800-53, PCI-DSS, DORA, NIS2, NCA (Saudi), NESA (UAE).
framework_coverage Which articles of a regulation automated scanning can and cannot evidence.
org_requirements Your organization's own rules for a resource type — before the code is written.
org_status Whether org policy is in force, or built-in rules only.

You do not call these. The assistant does, on its own, because the server tells it to.


Organization policy

Everything above works with no account. Connecting an organization adds your company's own rules to the same local evaluation:

export SOVEREIGN_TOKEN=...   # Integrations → GitHub in the dashboard

The difference this makes is in when the rule applies. Without it, the assistant writes Terraform and then finds out it was wrong. With it:

You:       "add an RDS instance for the orders service"
Assistant: → org_requirements("aws_db_instance")
           ← "backup_retention_period must be at least 365"
             "region must be one of: eu-west-1, eu-central-1"
           [writes Terraform that already satisfies both]
           → scan_terraform → clean

The rule is supplied to the generator, not applied to the output. That is the whole point — a violation that never gets written costs nothing to fix.

Company rules appear in scans tagged source: org_policy, so a developer can always tell a company requirement from a built-in one. They are authored in the dashboard as YAML and enforced identically in the editor, in CI, and in a cloud scan.

This does not change what leaves your machine. Rules come down; code never goes up. The only request this server makes is a GET for your org's rules — tests/test_org_policy.py::test_no_terraform_is_ever_uploaded asserts that at the transport, and asserts an unconnected install opens no socket at all. If the API is unreachable or the token is rejected, the built-in rules still run locally and the scan still works.


What it does not do

Worth stating plainly, because a security tool that overstates its scope is worse than no tool:

  • It is not a compliance assessment. check_compliance returns control mappings — evidence that shortens an audit. Every framework it maps also carries governance, process and training obligations no configuration scanner can observe. A clean scan is not a compliant organisation.
  • NCA control identifiers are provisional, pending reconciliation against the authority's published catalogue. Cite the subdomain names.
  • apply_fixes is deliberately narrow. It applies only single-attribute, in-place changes from a hand-verified allowlist, and never overwrites a value wired to a variable or expression. Everything else stays advisory, because a mechanical fix that is syntactically clean can still take a running system down.
  • It scans Terraform, not live cloud accounts, container images, or dependencies.

For live multi-cloud posture management, attack-path analysis and audit-ready reporting, this is the editor-side slice of Sovereign Observer.


Privacy

The scan runs in this process, on your machine. There is no API key, no account, and no network call in the default path — the server works with networking disabled. Your Terraform is never uploaded.


Development

From a checkout of the product repository:

pip install -e ".[dev]"

The IaC engine is copied in from the backend at build time by scripts/vendor_engine.py, into a gitignored sovereign_mcp/_vendor/. That is deliberate: the repository holds exactly one copy of the rule logic, so the editor and CI can never disagree about whether a resource is insecure. sovereign_mcp/engine.py vendors on demand in a source checkout, so there is no build step for day-to-day work.

Never edit anything under _vendor/. Edit the backend module and re-run the script.

python -m pytest tests/ -q

tests/test_parity.py is the one that matters — it fails if the vendored copy drifts from its source, or if the Checkov pin stops matching the backend's.

Releasing

The public sovereign-observer-mcp repository is a curated copy, not a fork: three files exist only there, and its README differs because _vendor is committed there and generated here. scripts/sync_mirror.py encodes those rules — it copies everything else, deletes what upstream dropped, and stops rather than clobbering a file that is meant to differ.

python scripts/sync_mirror.py --mirror ../path/to/sovereign-observer-mcp   # dry run
python scripts/sync_mirror.py --mirror ../path/to/sovereign-observer-mcp --apply

Then bump the version, make wheel, and upload. Build from a vendored tree or the wheel ships no engine — make wheel does both in order.

Licensed Apache-2.0. Built on Checkov (Apache-2.0).

Metadata

Release files for sovereign-observer 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sovereign-observer 0.3.0
File Size Uploaded
sovereign_observer-0.3.0.tar.gz 131.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sovereign-observer 0.3.0
File Interpreter ABI Platform
sovereign_observer-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 257.6 kB

Release files / sovereign_observer-0.3.0.tar.gz

Download URL sovereign_observer-0.3.0.tar.gz
Size 131.0 kB
Tags Source
SHA-256 checksum
How to use checksums
cb6778aee6f54a5f936ee40720205a534445645f28c2b34bbc3167493c20fab7
BLAKE2b-256 checksum
How to use checksums
d9bc6a85cba03ab3e42eccf8c94edfe64817e7fcd5baeca3c5b5e21da7435de3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.0

Release files / sovereign_observer-0.3.0-py3-none-any.whl

Download URL sovereign_observer-0.3.0-py3-none-any.whl
Size 126.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a07d8b2395fd074a00efb25b082b60c1a3f51a18e3666a54d158efedc6b7cf8d
BLAKE2b-256 checksum
How to use checksums
8aa4c2c0713014e20b5eddd5c9f1144adc6f47a75c80ba49e4d4068ba8c84af8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.0

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page