navi-mcp
Claude MCP server for controlling and automating navi
Documentation
navi-mcp suite
An MCP server for the Tenable **navi** CLI
(Tenable Vulnerability Management / Tenable One), plus the companion
navi-claude-skills set,
vendored here so the server can serve them.
The tool surface is validated against navi's source — the `@click.option`
declarations in `navi/plugins/*.py`, not a `--help` capture — because help text
cannot show you a guard that warns without exiting, a flag that is silently
ignored for the selector you paired it with, or a prompt that will deadlock a
tool call. `docs/gap-ledger.md` carries the per-finding trail.
Layout
server/ server.py — the MCP server (20 tools + resources)
server/tests/ argv-level check suites + run_all.py (no live tenant needed)
skills/ the 17 skills, in NAVI_SKILL_DIR layout — a vendored copy of
upstream (/SKILL.md, plus references/ on the denser ones)
tools/ navi_mcp_config.py — auto-detects paths, emits the install config
sync_skills.py — refresh skills/ from upstream, verify vs server
docs/ audit framework, gap ledger, verified findings, help-crawler,
fix-xref-prompt.md (a reported bug in the navi CLI, not this repo)
INSTALL.md step-by-step install for Claude Desktop
README.md this fileAt the repo root, `pyproject.toml` builds the above into an installable package
(`navi-mcp-suite/server/` → `navi_mcp/`, `navi-mcp-suite/skills/` →
`navi_mcp/resources/skills/`) without moving any files, and `fix-mcp.sh` is a
doctor script that checks the whole launch chain.
The 17 skills
Driving the server: `navi` (router) · `navi-core` · `navi-mcp` ·
`navi-troubleshooting` · `navi-acr` · `navi-export` · `navi-scan` · `navi-was` ·
`navi-action` · `navi-mail` · `navi-remote-exec` · `navi-explore` · `navi-enrich`
Authoring Nessus compliance content: `navi-audit` · `navi-audit-syntax` ·
`navi-audit-platforms` · `navi-audit-catalog`. These do not use the MCP server,
but the router routes to them, so they travel with the set.
Deep material (full schema, exhaustive selector catalog, long worked examples)
lives in `references/*.md` and is pulled on demand.
These are a vendored copy. They are maintained at
packetchaos/navi-claude-skills
and live here only so `NAVI_SKILL_DIR` has something to serve. Refresh them —
and check them against the server's actual tool surface — with:
python tools/sync_skills.py --dry-run # what would change
python tools/sync_skills.py --verify # sync, then cross-check vs server.py`--verify` parses every `navi_*(...)` call written in the skills and flags tool
names the server doesn't register, keyword arguments a tool doesn't accept, and
tools no skill documents. Run it whenever the tool surface changes: it is how
`navi_action_delete(kind="scan", id=…)` was caught, where the real parameter is
`object_id`.
Running the MCP server
The server shells out to the `navi` binary and reads the local `navi.db`. It
does not manage API keys — set those out-of-band with `navi config keys`
first (see `skills/navi-core`).
Requirements
| `mcp` >= 1.9, /resources/skills`. Installed as a package this resolves to the bundled skills and the var is optional; running from a checkout it does not exist, so skill resources 404 until you set it | ||
|---|---|---|
| `NAVI_SKILL_PATH` | Legacy: a single monolithic `SKILL.md`. Setting it puts the server in single-file mode and `NAVI_SKILL_DIR` is ignored. Prefer `NAVI_SKILL_DIR`. | unset |
| `NAVI_MCP_ALLOW_WRITES` | `1` opens the master write gate (see below) | unset → read-only |
| `NAVI_EMAIL` | `1` enables `navi_action_mail`. Stacks on the write gate. | unset → off |
| `NAVI_REMOTE_CODE_EXECUTION` | `1` enables `navi_action_push`. Stacks on the write gate. | unset → off |
> First-install trap. `NAVI_WORKDIR` defaults to `~/.navi-mcp`, *not* your
> current directory. The navi CLI writes `navi.db` into whatever directory you
> ran it from, so if you leave `NAVI_WORKDIR` unset the server will quietly
> create an empty `~/.navi-mcp`, find no database there, and every read will come
> back empty — looking like a broken tenant rather than a wrong path. Read
> `navi://workdir` first: it prints the resolved workdir and whether `navi.db` is
> actually present.
Point `NAVI_SKILL_DIR` at this repo's `skills/` folder. The server reads
unpacked skill folders, not packaged `.skill`/`.plugin` zips.
The gates
Three independent layers. A tool runs only when every layer that applies to
it is satisfied; they are `AND`ed, never `OR`ed.
Layer 1 NAVI_MCP_ALLOW_WRITES=1 master write gate server env, restart
Layer 2 NAVI_EMAIL=1 email capability server env, restart
NAVI_REMOTE_CODE_EXECUTION=1 remote-exec capability server env, restart
Layer 3 confirm=True per-call intent in the tool callLayer 1 — the master write gate. Off by default, so a fresh install is
read-only and safe to point at production. Opening it enables everything that
changes state in your Tenable tenant: tagging, ACR, asset import, scan control,
WAS launches, deletes, key rotation, export cancellation, `navi_config(kind='url')`,
and `navi_explore_api` POST/PUT. It also covers `navi_config_rebuild` — that one
destroys *local* data rather than tenant data (see below), but it is destructive
enough to belong behind the same switch.
Layer 2 — capability gates. Two capabilities are hazardous in ways ordinary
platform writes are not, so each needs its own separate opt-in *on top of* layer
1. Opening the write gate alone does not enable either:
- `NAVI_EMAIL=1` → `navi_action_mail` may send mail as you. Also needs SMTP
configured out-of-band via `navi config smtp`. Harness: `skills/navi-mail`.
- `NAVI_REMOTE_CODE_EXECUTION=1` → `navi_action_push` may run shell commands on
remote hosts. Also needs SSH credentials via `navi config ssh`. This is the
highest-risk capability in navi. Harness: `skills/navi-remote-exec`.
Layer 3 — `confirm=True`. A per-call flag on every gated tool. Layers 1 and 2
are standing decisions made once by the operator; layer 3 is a decision about
*this specific call*, and the convention is that the model narrates exactly what
it is about to do and gets a human answer before passing it. Because it lives in
the tool call rather than the environment, it is the only layer a model can
satisfy on its own — which is precisely why it is never the only layer for
anything that touches the tenant.
What needs what
| Tool | Writes gate | Capability gate | `confirm=True` |
|---|---|---|---|
| `navi_enrich_tag`, `navi_enrich_acr`, `navi_enrich_add` | ✅ | — | ✅ |
| `navi_scan` (create/start/stop/pause/resume) | ✅ | — | ✅ |
| `navi_was` (scan/start/upload) | ✅ | — | ✅ |
| `navi_action_delete`, `navi_action_rotate`, `navi_action_cancel` | ✅ | — | ✅ |
| `navi_config(kind='url')` | ✅ | — | ✅ |
| `navi_explore_api` POST/PUT | ✅ | — | ✅ |
| `navi_config_rebuild` | ✅ | — | ✅ |
| `navi_action_mail` | ✅ | `NAVI_EMAIL=1` | ✅ |
| `navi_action_push` | ✅ | `NAVI_REMOTE_CODE_EXECUTION=1` | ✅ |
| `navi_explore_query` non-SELECT | — | — | ✅ |
| everything else (reads, exports, `navi_explore_api` GET, encrypt/decrypt) | — | — | — |
Two asymmetries worth knowing rather than discovering:
- `navi_explore_query` non-SELECT is confirm-only. A `DELETE`/`DROP` through
it hits your local navi.db, never the tenant, so it sits outside the write
gate — but it *is* destructive, and unlike `navi_config_rebuild` nothing but
`confirm=True` stands in front of it. If you want a strictly read-only local
database as well as a read-only tenant, that is not what the write gate gives
you today.
- `navi_config_rebuild`'s `confirm=True` is doing literal work. navi's own
`-rebuild` path calls `click.confirm()` before dropping the table. Under MCP,
stdin is closed, so that prompt would abort the command — the server answers it
on your behalf. Your `confirm=True` *is* the "y" being typed. That is why the
tool refuses without it rather than treating it as a formality.
Read-only by default
With no environment variables set beyond `NAVI_WORKDIR` and `NAVI_BIN`, the
server exposes reads only. That is the recommended starting posture: connect it,
read `navi://workdir`, run a query or two, and open gates deliberately once you
trust what it is pointed at.
Destructive tools
Only two tools destroy anything, and neither touches Tenable:
- `navi_config_rebuild` — DROPs a local `assets` or `vulns` table, re-creates
it, and re-downloads. Nothing in Tenable VM changes; what you lose is the local
cache and the hours it took to build on a large tenant. Tables *derived* from
those (`certs`, `software`, `vuln_route`, `vuln_paths`) go stale at the same
moment — the tool's `_notice` names the calls that refresh each. Annotated
`destructiveHint=True`, which `navi_config_update` deliberately is not: an
update merges into the existing table and never drops.
- `navi_action_delete` — removes tags, users, scans, assets, target groups,
user groups, or TONE tags in the tenant. Write-gated and confirm-gated.
Rebuilding both tables at once is `navi config update full -rebuild` at the CLI;
`full` is not exposed as a tool because it runs for hours regardless.
Install in Claude Desktop
Full walkthrough in **INSTALL.md**. The short version — install
the package, then point the config at the console script:
pip install ".[navi]"
which navi-mcp # absolute path for "command"{
"mcpServers": {
"navi": {
"command": "/absolute/path/to/navi-mcp",
"env": {
"NAVI_WORKDIR": "/absolute/path/to/folder-with-navi.db",
"NAVI_MCP_ALLOW_WRITES": "0",
"NAVI_EMAIL": "0",
"NAVI_REMOTE_CODE_EXECUTION": "0"
}
}
}
}No `args`, no `NAVI_SKILL_DIR` (the skills ship in the package), and no
`NAVI_BIN` when `navi` is installed alongside via the `[navi]` extra. Use an
absolute path — Claude Desktop won't have your shell's `PATH`.
Running from a checkout instead? Let the helper discover the paths, run with the
interpreter you want Claude Desktop to use:
python tools/navi_mcp_config.py # print the mcpServers JSON
python tools/navi_mcp_config.py --write # merge into your config (backs up first)Gate flags on the helper map one-to-one to the env vars above:
`--allow-writes`, `--allow-email`, `--allow-remote-code-execution`. The last two
have no effect without the first.
After editing the config, fully quit and reopen Claude Desktop, then read
`navi://workdir` to confirm it connected.
Resources
- `navi://schema/{table}` — live column definitions for a navi.db table
- `navi://workdir` — resolved workdir, `navi.db` presence/size/freshness, all
three gate states, navi binary, call budget, and skill-dir status
- `navi://skill/{name}` — load a skill (router/core/mcp/…); lists its references
- `navi://skill/{name}/{ref}` — load a bundled reference (e.g. `navi://skill/core/schema`)
Plus the `navi_workflow` prompt, which injects the router skill.
Long-running operations
navi exports can run for tens of minutes on large tenants — past the MCP host's
~4-minute tool-call ceiling. The server enforces a call budget (~220s) and
returns a clean error naming the exact CLI command to run instead, scoped
identically to the call that timed out. Foundational syncs
(`navi config update full`) remain intentionally CLI-only. The main lever for
fitting a sync inside the budget is scope: `days`, `since`, `severity`,
`plugin_id`, or a `category`/`value` tag pair on `navi_config_update`. See
`skills/navi-core` and `skills/navi-troubleshooting`.
Tests
python server/tests/run_all.py # summary
python server/tests/run_all.py -v # every checkThe suites stub the navi subprocess and assert on the argv each tool builds,
so they need no tenant, no API keys, and no `navi.db`. `NAVI_WORKDIR` is
redirected to a temp directory and the real `navi` binary is never invoked —
safe to run against a production install. Run them after installing on a new
machine: they will catch a broken `mcp` SDK, a Python too old for the type
syntax, or a partial checkout before you connect a client.
Installing the skills as Claude skills
Install them from
packetchaos/navi-claude-skills,
which builds a `navi-skills.plugin` bundle for Claude.ai / Claude Cowork /
Claude Code. This repo no longer ships packaged copies: a second distribution
channel is a second thing to forget to update, and that is precisely how the
skills here ended up describing a server that had moved on without them. The
`skills/` folder stays because the MCP server reads it directly.
Validation status
`server.py` compiles cleanly, every tool is annotated, all 20 register, and the
suites in `server/tests/` are green. Tool annotations require `mcp` >= 1.9;
`mcp.types.ToolAnnotations` is imported behind a `try/except` so an older SDK
degrades to unannotated tools rather than failing. A 2.x SDK does not degrade
gracefully — it fails outright, which is why the dependency is pinned `<2`.
It has not been runtime-tested against a live Tenable tenant. The checks
verify what the server *asks navi to do*; they cannot verify what navi and the
Tenable API do in response. Before relying on it, validate with one live read —
`navi_explore_data(subcommand="cve", cve="CVE-2021-44228")` — and read
`navi://workdir` to confirm the workdir and gate states are what you intended.
See `docs/verified-findings.md` for the per-bug inventory and
`docs/gap-ledger.md` for the full audit trail.
Frequently asked questions
What is navi-mcp?
navi-mcp is Claude MCP server for controlling and automating navi
How do I install navi-mcp?
Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.
Is navi-mcp open source?
Yes — it is hosted on GitHub at https://github.com/packetchaos/navi-mcp.
Related MCP tools
Cognee is the open-source AI memory platform for agents. Give your AI agents persistent long-term memory across sessions with a self-hosted knowledge graph engine.
Automate browser based workflows with AI
Hindsight: Agent Memory That Learns
A privacy-first app that strips AI watermarks from content you own.
Agent framework and applications built upon Qwen>=3.0, featuring Function Calling, MCP, Code Interpreter, RAG, Chrome extension, etc.
The power of Claude Code / GeminiCLI / CodexCLI + [Gemini / OpenAI / OpenRouter / Azure / Grok / Ollama / Custom Model / All Of The Above] working as one.
Run your own MCP server? See who uses it and what to fix.
Measure it with TrackMCP