Content
<p align="center">
<img src=".github/brand/logo-full.svg" alt="Meridian — AI Compliance Intelligence" width="720">
</p>
# Meridian
**Compliance Intelligence for AI startups.**
Meridian scans a repository deterministically, has one LLM call reason about what your business actually does, then deterministically resolves which of 17 real regulatory frameworks apply and produces an evidence-cited compliance report — DPDPA 2023, RBI FREE-AI, SEBI AI/ML, EU AI Act, GDPR, PCI DSS, SOC 2, CERT-In, and more. It answers the questions a compliance engineer would if they sat down with your CTO: **what applies, why, what's already verified, what's missing, and what to do next** — never a 1,445-row violation dump. By Karthik Nambiar
> Meridian provides automated compliance assessments and recommendations, not legal advice. Final compliance determinations should be reviewed by qualified legal, compliance, or security professionals where required. See [V1_SCOPE.md](V1_SCOPE.md) for the full V1 scope, data-handling details, and explicit non-goals.
[](https://www.python.org/downloads/)
[](LICENSE)
[](#privacy)
---
## How it works
Meridian is **one assessment pipeline**, not a deterministic scanner with an optional LLM bolted on. Exactly one LLM call happens per assessment — everything before and after it is deterministic and evidence-cited:
```
Repository
↓ (deterministic — tree-sitter parsing, no LLM)
Discovery → IR → Repository Graph → Fact Extraction → Capability Detection → Evidence Ledger
↓
Repository Context (aggregated tech stack / behavioral signals — no detection of its own)
↓
Repository Context Interpreter ← the ONE LLM call
↓
Repository Understanding + up to 5 Clarification Questions
↓ (a human — or an AI client on your behalf — answers)
Business Profile (deterministic lookup from the answers, no LLM)
↓ (deterministic from here on — zero LLM calls)
Framework Recommendation → Framework Loader → Objective Applicability
↓
Scoped Control Verdicts → Framework Summaries → Report
```
If the Interpreter is already confident about your business context, it asks zero questions and the assessment completes immediately — no human interaction required. `--offline` skips the Interpreter entirely and evaluates all 1,445 controls across all 17 frameworks deterministically, no LLM/API key needed at all.
**The compliance database** (`database/meridian.db`) is 17 real frameworks / 1,445 controls / 50 objectives / 49 capabilities, all shipped as data — not hardcoded in Python.
---
## Installation
```bash
pip install meridian-mcp
python3 -m meridian.cli configure # pick a provider, paste your API key — once
python3 -m meridian.cli scan . # done
```
`python3 -m meridian.cli ...` works identically on every OS with zero setup, because it never depends on the `meridian` console script being discoverable on `PATH` — only on `python3` itself, which is already guaranteed the moment `pip` ran successfully. If your shell already has the `meridian`/`meridian-mcp` commands on `PATH` (most installs do), you can drop the `python3 -m meridian.cli` prefix and just run `meridian configure` / `meridian scan .` directly.
If `meridian configure` reports "command not found" even though `pip install` succeeded, that's a `PATH` issue, not a broken install — pip's console script lands in a folder that isn't always on `PATH` by default (especially on macOS without a venv). The `python3 -m meridian.cli` form above sidesteps it entirely; [pipx](https://pipx.pypa.io) (`pipx install meridian-mcp`) is the more permanent fix, since it also isolates meridian-mcp's dependencies from anything else already installed.
The compliance database and all runtime metadata ship **inside the package** and are copied to `~/.meridian/meridian.db` automatically on first use. Nothing else to set up.
Installing gives you three console scripts on your `PATH` (once it resolves):
| Command | What it runs |
|---|---|
| `meridian` | The interactive CLI (`scan` / `report` / `compare` / `profile` / `configure`) |
| `meridian-ci` | Identical to `meridian` — same Typer app, kept as a separate alias for CI pipeline clarity |
| `meridian-mcp` | The MCP server (11 tools) for Claude Desktop / Cursor / Windsurf / any MCP client |
Optional extra: `pip install 'meridian-mcp[pdf]'` for PDF export (Playwright).
**From source (contributors):**
```bash
git clone https://github.com/KNambiarDJsc/Meridian.git
cd Meridian
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS / Linux
pip install -e ".[dev]"
```
A source checkout resolves the database and metadata from the repo's own `database/` and `data/` directories automatically — same zero-flag commands, no copying either way. `--db-path` / `MERIDIAN_DB_PATH` still exist for pointing at a non-default database deliberately.
---
## Configure an LLM (first run only)
`meridian scan` needs an LLM for the one mandatory Interpreter call. On first run, if no provider is already configured, an interactive wizard walks you through it:
```
$ meridian scan .
No AI model configured.
Select Provider
1. OpenAI
2. Anthropic
3. Google Gemini
4. OpenRouter
5. Groq
6. Azure OpenAI
7. Show all providers
Enter a number: 2
```
("Show all providers" expands the list to all 15 — Ollama, AWS Bedrock, Vertex AI, Fireworks AI, Together AI, DeepSeek, xAI, Mistral, Cohere. Every provider stays fully supported; the short list is just the first screen.)
```
Select Model
(live list from litellm — the models actually available for that provider today)
1. claude-haiku-4-5
2. claude-opus-4-8
3. claude-sonnet-4-6
...
21. Custom (enter a model name)
Enter a number: 2
Enter API Key: ****************
Save this configuration? [Y/n]: y
Saved to /home/you/.meridian/config.toml
```
The wizard writes `provider`/`model`/`api_key` to `~/.meridian/config.toml` (never inside the scanned repository) so it never runs again. **It's skipped entirely if you already have a provider's API key set as an environment variable** (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GROQ_API_KEY`, etc. — see the full table below) or an existing `~/.meridian/config.toml` — Meridian never asks twice, and never re-asks if credentials are already present.
**To switch providers/models, fix a broken saved config, or just re-run the wizard on demand**, either command forces it regardless of what's already configured:
```bash
meridian configure # standalone — just (re)runs the wizard, then exits
meridian scan . --reconfigure # runs the wizard first, then proceeds with the scan
```
If you don't want an LLM at all, use `--offline` (below) and skip this section entirely.
---
## CLI commands
### `meridian scan` — run a full assessment (the main command)
```bash
meridian scan <repository_path> [OPTIONS]
```
| Flag | Default | Meaning |
|---|---|---|
| `--db-path TEXT` | `~/.meridian/meridian.db` | Path to `meridian.db` |
| `--offline` | off | Skip the LLM entirely — deterministic scan only, all 1,445 controls, no questions asked |
| `--format TEXT` | `markdown` | Report format once the assessment completes: `json`, `markdown`, `sarif`, `html`, `pdf` |
| `--output TEXT` / `-o` | stdout | Write the report to a file instead of printing it (required for `pdf`) |
| `--fail-on TEXT` | `none` | Exit non-zero if any FAILED (code-verified) finding meets this severity: `none`, `low`, `medium`, `high`, `critical` — Needs Documentation/Attestation never trips it |
| `--reconfigure` | off | Force the provider/model/API key wizard to run first, even if already configured |
**Full interactive assessment** (asks clarification questions, produces a scoped report against only the frameworks that actually apply):
```bash
meridian scan .
```
You'll see a repository summary, then each clarification question with numbered options:
```
Question 1 of 3
Does the application store personal data?
1. Yes
2. No
3. Unsure
Enter a number (or press Enter to skip): 1
```
Answer each one (or press Enter to skip — that field just stays unanswered, never guessed), and the assessment completes: Framework Recommendation → Framework Loader → Objective Applicability → scoped verdicts → report, printed to stdout as markdown.
**Deterministic scan, no LLM, no questions, all 1,445 controls** (same as any earlier Meridian version's behavior):
```bash
meridian scan . --offline
# or, equivalently:
MERIDIAN_OFFLINE=1 meridian scan .
```
**Save the report to a file instead of printing it:**
```bash
meridian scan . --format json --output report.json
meridian scan . --format pdf --output report.pdf
```
**CI/CD gate — fail the build on Critical violations:**
```bash
meridian scan . --offline --fail-on critical
echo $? # 0 = clean, 1 = violations at/above threshold found, 2 = bad arguments
```
```yaml
# .github/workflows/compliance.yml
- name: Run Meridian compliance gate
run: meridian scan . --offline --fail-on critical
```
### `meridian report` — regenerate a report without re-answering questions
Always deterministic-only (re-runs the scan for full evidence fidelity, no LLM/Interpreter involved) — useful once you already know which frameworks matter and just want the full 1,445-control report:
```bash
meridian report <repository_path> [--format markdown|json|sarif|html|pdf] [--output FILE] [--db-path PATH]
```
### `meridian compare` — diff two historical scans
```bash
meridian compare <repository_path> <older_scan_hash> <newer_scan_hash>
```
Every `meridian scan` prints a `Scan hash:` you can use here. Shows newly-violated controls, fixed controls, and capability status changes.
### `meridian profile` — read the last scan's Repository Intelligence Profile
```bash
meridian profile <repository_path>
```
### `meridian configure` — (re)run the provider/model/API key wizard on demand
```bash
meridian configure
```
Runs the same wizard `meridian scan` runs automatically on first use, but unconditionally — even if a provider is already configured — so you can switch providers/models or overwrite a broken saved config. Equivalent to `meridian scan <path> --reconfigure` except it doesn't also run a scan afterward.
Reads persisted history — does not scan on its own; run `meridian scan` (with or without `--offline`) first.
---
## MCP server (Claude Desktop, Cursor, Windsurf, Antigravity)
`meridian-mcp` exposes 11 tools. The two that matter for the full assessment flow:
- **`start_scan(repository_path)`** — runs the deterministic scan + the Interpreter. Returns either a completed assessment (if the LLM was already confident) or a `session_id` + `repository_summary` + `questions` for the calling AI client to ask the human.
- **`continue_scan(session_id, answers)`** — resume with `{question_id: answer_text}`, completes the assessment and returns the scoped report.
Plus `scan_repository`/`generate_report` (the deterministic-only, no-LLM path), `repository_profile`, `technology_inventory`, `risk_summary`, `repository_summary`, `compare_scans`, `explain_control`, and `ai_governance_summary`.
### Claude Desktop (`%APPDATA%\Claude\claude_desktop_config.json`)
```json
{
"mcpServers": {
"meridian": {
"command": "meridian-mcp",
"env": {
"ANTHROPIC_API_KEY": "YOUR_KEY_HERE",
"MERIDIAN_DB_PATH": "D:\\meridian-mcp\\database\\meridian.db"
}
}
}
}
```
### Cursor / Windsurf / Antigravity (`~/.cursor/mcp.json`, etc.)
```json
{
"mcpServers": {
"meridian": {
"command": "meridian-mcp",
"env": {
"ANTHROPIC_API_KEY": "YOUR_KEY_HERE",
"MERIDIAN_DB_PATH": "/absolute/path/to/database/meridian.db"
}
}
}
}
```
Use the absolute path to `meridian-mcp` if it isn't globally on your `PATH` (e.g. inside a venv: `/path/to/.venv/Scripts/meridian-mcp.exe` on Windows, `/path/to/.venv/bin/meridian-mcp` on macOS/Linux).
---
## REST API + background worker
`src/meridian/api/app.py` (FastAPI) exposes `POST /scan`, `POST /scan/start` + `POST /scan/continue` (the same two-call assessment protocol as MCP), `POST /report`, `POST /compare`, `GET /findings`, `GET /controls`, `GET /profile`, plus `/admin/api-keys` and `/admin/audit-log`. It isn't installed as a console script — run it directly:
```bash
uvicorn meridian.api.app:app --host 0.0.0.0 --port 8000
```
Or via the bundled Docker image, which also supports a background worker mode for async scan jobs:
```bash
docker build -f deploy/docker/Dockerfile -t meridian .
docker run -p 8000:8000 -e MERIDIAN_DB_PATH=/data/meridian.db -v $(pwd)/database:/data meridian # api mode (default)
docker run -e MERIDIAN_DB_PATH=/data/meridian.db -v $(pwd)/database:/data meridian python -m meridian.docker_entrypoint worker
```
The repo-root `Dockerfile` instead runs only `meridian-mcp` (the MCP server), for a minimal MCP-only image.
---
## Environment variables
| Variable | Default | Meaning |
|---|---|---|
| `MERIDIAN_HOME` | `~/.meridian` | Base directory for the compliance database default below |
| `MERIDIAN_DB_PATH` | `$MERIDIAN_HOME/meridian.db` | Path to the compliance database |
| `MERIDIAN_OFFLINE` | unset | Set to `1`/`true`/`yes` to force the deterministic-only path (same as `meridian scan --offline`) |
| `MERIDIAN_INTERPRETER_MODEL` | resolved from `~/.meridian/config.toml` or provider default | Model for the one mandatory LLM call |
| `MERIDIAN_EVALUATOR_MODEL`, `MERIDIAN_CRITIC_MODEL` | provider defaults | Reserved for the optional LLM-augmentation layer (`evaluator/`/`critic/`) — not wired into any scan path today |
| `MERIDIAN_MAX_CONCURRENT` | `5` | Max concurrent LLM requests |
| `MERIDIAN_RPM_LIMIT`, `MERIDIAN_TPM_LIMIT` | provider-inferred | Override the auto-detected rate limits |
| `MERIDIAN_API_PORT`, `MERIDIAN_API_WORKERS` | `8000`, `1` | REST API server (Docker `api` mode) |
| `MERIDIAN_WORKER_COUNT` | `1` | Background worker count (Docker `worker` mode) |
| Provider keys | — | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `GROQ_API_KEY`, `OPENROUTER_API_KEY`, `AZURE_API_KEY`, `AWS_ACCESS_KEY_ID`+`AWS_SECRET_ACCESS_KEY` (Bedrock), `GOOGLE_APPLICATION_CREDENTIALS` (Vertex AI), `FIREWORKS_AI_API_KEY`, `TOGETHER_AI_API_KEY`, `DEEPSEEK_API_KEY`, `XAI_API_KEY`, `MISTRAL_API_KEY`, `COHERE_API_KEY` — any one of these, if set, skips the setup wizard |
---
## Project structure
```
src/meridian/
├── cli.py # meridian / meridian-ci — scan, report, compare, profile
├── cli_setup.py # Interactive LLM setup wizard + clarification-question prompting
├── config.py # Provider/model config, live litellm model catalogue, ~/.meridian/config.toml
├── engine.py # MeridianEngine — the one orchestration layer every transport calls
├── scan_state.py # ScanPaused / ScanCompleted / ScanFailed — the engine's execution contract
├── scan_orchestrator.py # run_scan() / run_and_persist_scan() — the M1-M6 deterministic pipeline
│
├── discovery.py, parsers/, ir/, ir_builder.py, graph/ # Repository → IR → Repository Graph
├── facts/, detectors/, evidence/, inventory/ # Fact extraction → capability detection → evidence
├── compliance/, verdicts/ # meridian.db access, control verdict resolution
├── profile/, ai_governance/ # Repository Intelligence Profile, AI-BOM/governance
│
├── context/, interpreter/, assessment/ # Sprint 1-2: Repository Context + the one LLM call
├── framework_recommendation/, framework_loader/ # Sprint 2-3: which frameworks apply
├── objective_applicability/ # Sprint 4: which objectives/controls apply
│
├── report/ # ReportContext + markdown/json/sarif/html/pdf exporters
├── scan_ledger/, ledger.py # Scan history, comparison, tamper-evident audit chain
├── search/, embeddings/ # Semantic search index (rebuilt explicitly, not on every scan)
│
├── mcp/server.py # FastMCP server, 11 tools
├── api/ # FastAPI REST server
└── workers/ # Background async scan job queue
```
`evaluator/`, `critic/`, `augmentation/`, `clustering/`, `cache_engine/`, `annotation/` are real, tested subsystems not currently wired into any scan path — see `CLAUDE.md` for the full picture of what's live vs. built-but-disconnected.
---
## Running tests
```bash
pytest tests/ -v
pytest tests/test_cli.py -v # a single file
pytest tests/test_cli.py::test_scan_real_counts -v # a single test
```
Most e2e tests need the local `database/meridian.db` (present in the repo) — they skip cleanly if it's missing. A handful of older tests additionally expect a real Flask checkout at `/tmp/flask-repo` and skip without it.
---
## Privacy
Meridian is local-first, and the claim is verifiable in the code — not marketing copy:
- **Your code never leaves your machine.** The entire scan pipeline (discovery, parsing, fact extraction, capability detection, evidence, verdicts, reporting) runs locally against the bundled compliance database. Meridian has no servers in any code path — there is nowhere to upload your repository *to*.
- **Zero telemetry.** Meridian sends no usage data anywhere, and LiteLLM's own telemetry is explicitly disabled in code (`litellm.telemetry = False` in `evaluator/provider.py`).
- **Keys never leave your machine.** API keys are read from your environment or `~/.meridian/config.toml` and passed directly to the provider SDK via [litellm](https://github.com/BerriAI/litellm) — no Meridian-run proxy, no logging of credentials.
- **Exactly one LLM call per assessment**, made directly from your machine to the provider you chose, with your own key. Every control verdict, every framework applicability decision, every objective evaluation is deterministic — nothing about compliance reasoning is ever sent to or decided by an LLM.
- **`--offline` needs no network access and no API key at all.**
- No other network access exists anywhere in the codebase beyond the one LLM call above and the one-time package install itself — grep-verified, not just asserted.
---
## License
Apache 2.0 — see [LICENSE](LICENSE). All 17 frameworks are available unconditionally; there is no tiered/licensed feature gate in the current codebase.
---
<!-- mcp-name: io.github.KNambiarDJsc/meridian-mcp -->
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.