Meridian — Local-first AI compliance scanner. DPDPA 2023 · RBI FREE-AI · SEBI AI/ML · EU AI Act. Works with any LLM provider. AST-level code analysis across Python, TypeScript, Go, Java.
Project description
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 for the full V1 scope, data-handling details, and explicit non-goals.
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
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 (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):
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:
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)
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):
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):
meridian scan . --offline
# or, equivalently:
MERIDIAN_OFFLINE=1 meridian scan .
Save the report to a file instead of printing it:
meridian scan . --format json --output report.json
meridian scan . --format pdf --output report.pdf
CI/CD gate — fail the build on Critical violations:
meridian scan . --offline --fail-on critical
echo $? # 0 = clean, 1 = violations at/above threshold found, 2 = bad arguments
# .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:
meridian report <repository_path> [--format markdown|json|sarif|html|pdf] [--output FILE] [--db-path PATH]
meridian compare — diff two historical scans
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
meridian profile <repository_path>
meridian configure — (re)run the provider/model/API key wizard on demand
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 asession_id+repository_summary+questionsfor 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)
{
"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.)
{
"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:
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:
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
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 = Falseinevaluator/provider.py). - Keys never leave your machine. API keys are read from your environment or
~/.meridian/config.tomland passed directly to the provider SDK via 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.
--offlineneeds 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. All 17 frameworks are available unconditionally; there is no tiered/licensed feature gate in the current codebase.
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 meridian_mcp-0.6.7.tar.gz.
File metadata
- Download URL: meridian_mcp-0.6.7.tar.gz
- Upload date:
- Size: 4.3 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e9102bc65a96307754e2e05f7d5011d7fb95d65d277d811ad1c3571b899ee82
|
|
| MD5 |
fbee57e8de5ae277e9e30682ac141f3b
|
|
| BLAKE2b-256 |
ae0aedc0910cd8082ebc60df99dffd29349ed9dfae73acc7c70fccb2f060f1cc
|
File details
Details for the file meridian_mcp-0.6.7-py3-none-any.whl.
File metadata
- Download URL: meridian_mcp-0.6.7-py3-none-any.whl
- Upload date:
- Size: 854.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f2361aef5ad90be8276bc32d682c4c6a2bbd09553684608aaff3251f665f9f0d
|
|
| MD5 |
cf69b3550a6501b1667c9a8419a66237
|
|
| BLAKE2b-256 |
9e94e76234c8fbf9b26a5db76cfe0caa5713dbbd8361dc8fa3b98a775c006e79
|