trackmcp
Back to directory

Claude MCP server for controlling and automating navi

0 stars PythonOthers Updated Sep 2, 2026

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

code
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 file

At 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:

bash
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.

code
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 call

Layer 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

ToolWrites gateCapability 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:

bash
pip install ".[navi]"
which navi-mcp          # absolute path for "command"
json
{
  "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:

bash
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

bash
python server/tests/run_all.py        # summary
python server/tests/run_all.py -v     # every check

The 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

Run your own MCP server? See who uses it and what to fix.

Measure it with TrackMCP