Skip to main content
Glama
tarhou

tenable-mcp-server

by tarhou
README.md
# Tenable MCP Server

An MCP (Model Context Protocol) server for **Tenable Vulnerability Management** and the wider **Tenable One** platform. Connects LLMs like Claude to your Tenable instance for querying assets, vulnerabilities, scans, exposure metrics, attack paths, web app scans, cloud security findings, and more — via natural language.

Built with [FastMCP v3](https://gofastmcp.com) and [pyTenable](https://pytenable.readthedocs.io/).

## Features

- **58 tools** across 25 modules: assets, vulnerabilities, scans, exposure scoring, Attack Path Analysis, Web App Scanning (WAS), Attack Surface Management (ASM), Cloud Security (CNAPP), container security, compliance, tags, agents, networks, policies, users/groups, audit log, and patch verification
- **Mock mode** for demos and development — every result carries a visible simulation
  banner plus machine-readable `simulated: true` / `authoritative: false` metadata
- **Honest pagination** on list endpoints: filters apply to the complete observed
  capped sample, caps are reported as partial lower bounds, and no unreachable next
  offset is advertised beyond a cap
- **Markdown output** optimized for LLM consumption
- **Graceful degradation** when licensed features (Lumin/Tenable One, WAS, CNAPP, ASM) are unavailable
- **STDIO-only transport** so API keys are never exposed through an unauthenticated
  network listener
- **Read-mostly surface**: the only write operations are scan launch/stop and tag assign/unassign, all marked with MCP tool annotations

## Quick Start

### Prerequisites

- Python 3.11+
- [uv](https://docs.astral.sh/uv/) (recommended) or pip
- Tenable VM API keys ([generate here](https://cloud.tenable.com/app.html#/settings/my-account/api-keys)) — or none at all for mock mode

### Install

```bash
git clone <repo-url> && cd tenable-mcp-server
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"
```

### Configure

```bash
cp .env.example .env
# Edit .env with your Tenable API keys
```

Or export directly:

```bash
export TENABLE_ACCESS_KEY=your-access-key
export TENABLE_SECRET_KEY=your-secret-key
```

### Run

```bash
# STDIO transport (default, for Claude Desktop / Claude Code)
tenable-mcp

# Mock mode (no API keys needed)
TENABLE_MOCK=true tenable-mcp
```

### Claude Desktop Integration

Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "tenable": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/tenable-mcp-server", "tenable-mcp"],
      "env": {
        "TENABLE_ACCESS_KEY": "your-access-key",
        "TENABLE_SECRET_KEY": "your-secret-key"
      }
    }
  }
}
```

For mock/demo mode, set `"TENABLE_MOCK": "true"` in `env` and omit the API keys.
The server prepends **SIMULATED TENABLE DATA** to every tool response and marks result
metadata non-authoritative, so mock output cannot silently pass as live evidence.

## Tools Reference

All tools return Markdown. Read-only tools carry `readOnlyHint: true` annotations.

### Assets & Exposure

| Tool | Description |
|------|-------------|
| `tenable_list_assets` | List assets with hostname/IP/OS filters and pagination |
| `tenable_get_asset_details` | Full asset detail by UUID (hostname, IPs, OS, AES, ACR, tags) |
| `tenable_get_exposure_score` | Cyber Exposure Score (CES) from Tenable One |
| `tenable_get_asset_exposure` | Per-asset AES (0-1000) and ACR (1-10) |
| `tenable_get_severity_summary` | Open vulnerability counts by severity |
| `tenable_list_exposure_views` / `tenable_get_exposure_view` | Tenable One exposure view cards with CES trend series |

### Vulnerabilities & Plugins

| Tool | Description |
|------|-------------|
| `tenable_list_vulnerabilities` | List vulns with severity/VPR/CVE/state filters |
| `tenable_get_vulnerability_details` | Full plugin details (description, solution, references) |
| `tenable_get_asset_vulnerabilities` | Per-asset vulnerability list |
| `tenable_list_plugin_families` / `tenable_get_plugin_details` | Plugin family and plugin metadata |
| `tenable_verify_patched_assets` | Fail-closed before/after verification that CVEs/plugins are fixed on valid VM assets. Each UUID is first resolved in the workbenches namespace; unknown or mismatched IDs are not verifiable, never clean. Per-asset status is VERIFIED, NOT_PATCHED, UNKNOWN, or ERROR; only a complete, successful query with understood finding states and zero matching open findings yields VERIFIED. Every report carries source, UTC query time, scope, and collection completeness. |
| `tenable_search_recast_rules` | Recast/accept rule search |

### Scans, Scanners & Policies

| Tool | Description |
|------|-------------|
| `tenable_list_scans` / `tenable_get_scan_results` / `tenable_get_scan_history` | Scan inventory, results, run history |
| `tenable_launch_scan` / `tenable_stop_scan` | Scan lifecycle (write operations) |
| `tenable_list_scanners` / `tenable_get_scanner_details` | Scanner fleet |
| `tenable_list_policies` / `tenable_get_policy_details` | Scan policies |
| `tenable_list_folders` / `tenable_list_exclusions` / `tenable_get_exclusion_details` | Folders and scan exclusions |

### Attack Path Analysis (Tenable One)

| Tool | Description |
|------|-------------|
| `tenable_list_attack_paths` | Prioritized attack paths with source/target and techniques |
| `tenable_list_attack_findings` | Attack path findings |
| `tenable_list_attack_techniques` | MITRE ATT&CK technique inventory |
| `tenable_apa_country_coverage` | Per-country APA coverage map bucketed by hostname prefix (ISO 3166-1 defaults, configurable) |

### Web App Scanning, ASM, Cloud & Containers

| Tool | Description |
|------|-------------|
| `tenable_was_list_configs` / `tenable_was_get_scan_details` / `tenable_was_list_vulnerabilities` | Web App Scanning configs, scans, findings |
| `tenable_asm_list_assets` / `tenable_asm_get_asset_details` | Attack Surface Management inventory |
| `tenable_cloud_list_resources` / `tenable_cloud_list_findings` | CNAPP cloud resources and misconfigurations |
| `tenable_cloud_list_vm_vulns` / `tenable_cloud_list_container_image_vulns` | CNAPP VM and container image vulnerabilities |
| `tenable_list_container_images` / `tenable_list_container_repos` / `tenable_get_container_report` | Container Security |

### Platform Administration

| Tool | Description |
|------|-------------|
| `tenable_list_tags` / `tenable_create_tag_value` / `tenable_assign_tags` / `tenable_unassign_tags` / `tenable_get_asset_tags` | Tag management |
| `tenable_list_agents` / `tenable_get_agent_details` / `tenable_list_agent_groups` / `tenable_get_agent_group_details` | Nessus agent fleet |
| `tenable_list_networks` / `tenable_get_network_details` | Network objects |
| `tenable_list_users` / `tenable_list_groups` | Users and groups |
| `tenable_list_audit_events` | Platform audit log |
| `tenable_list_compliance_findings` / `tenable_get_compliance_summary` | Compliance/audit findings |

## Configuration

| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `TENABLE_ACCESS_KEY` | *(required)* | Tenable API access key |
| `TENABLE_SECRET_KEY` | *(required)* | Tenable API secret key |
| `TENABLE_URL` | `https://cloud.tenable.com` | Tenable cloud URL |
| `TENABLE_MOCK` | `false` | Enable mock mode for demo/testing |
| `TENABLE_MAX_EXPORT` | `10000` | Safety cap for export consumption |
| `TENABLE_DEFAULT_LIMIT` | `50` | Default pagination limit |
| `TENABLE_CLOUD_SECURITY_API_KEY` | *(optional)* | Tenable Cloud Security (CNAPP) API key |
| `TENABLE_ASM_API_KEY` | *(optional)* | Attack Surface Management API key |
| `TENABLE_HOSTNAME_COUNTRY_MAP` | *(optional)* | JSON map of hostname prefixes to country labels for APA coverage bucketing (defaults to ISO 3166-1 alpha-2) |

## Development

```bash
# Run tests (uses mock mode automatically -- no credentials needed)
uv sync --extra dev
uv run pytest tests/ -v

# Lint
uv run ruff check src/ tests/
```

## Architecture

- **FastMCP v3** server with lifespan-managed `TenableIO` client
- All pyTenable calls wrapped in `asyncio.to_thread()` (pyTenable is synchronous)
- Export iterators use one-record lookahead to detect the safety cap
  (`MAX_EXPORT_CONSUME = 10,000`), then paginate the observed sample in memory
- Cloud Security tools use direct GraphQL over httpx (https-only guard) instead of pyTenable's CNAPP client
- Error handling via `@handle_tenable_errors` decorator mapping API errors to actionable `ToolError` messages
- Mock client mirrors the pyTenable interface with deterministic UUIDs for consistent test/demo data

## Limitations

- **License-gated features degrade, not fail**: CES/AES/ACR, exposure views, APA, WAS, ASM, CNAPP, and Container Security require the corresponding Tenable licenses; tools return an explanatory message when the feature is unavailable on your instance.
- **Tenable VM (cloud) only** — Tenable Security Center (on-prem) is not supported; its API differs substantially.
- **Export scale**: vulnerability/asset exports are capped at `TENABLE_MAX_EXPORT` records per call to keep responses LLM-sized. This is not a full data-lake export tool.
- **Capped totals are lower bounds**: a partial-export banner and metadata say when
  the cap was reached. At the end of the observed sample, `has_more` is false because
  no further offset is addressable through that capped call; `truncated` remains true
  to show that the source itself contains more data.
- **No HTTP transport**: credential-bearing operation is deliberately limited to
  stdio. Run one process per client trust boundary.
- **Write surface is minimal by design**: scan launch/stop and tag operations only. No remediation, recast, or user-management writes.
- **APA country coverage** relies on hostname prefixes when asset tags are absent; hostnames that don't encode location won't bucket (they are reported as unmapped rather than guessed).

## License

MIT