tenable-activity-mcp

tenable-activity-mcp

Enables querying and analyzing Tenable Vulnerability Management audit logs, including activity summaries, API key usage, and anomaly detection through MCP tools.

Category
访问服务器

README

tenable-activity-mcp

Tests

An MCP server that exposes the Tenable Vulnerability Management audit/activity log (GET /audit-log/v1/events) as a small set of tools, so any MCP client can ask about platform activity, API-key usage, and anomalous behaviour on demand.

The server does the analysis. Counting, grouping, rate math and threshold comparisons all happen in Python; tools return finished, structured results (failure_rate_pct, by_actor, findings with reasoning) rather than dumping raw events for the model to add up.

What it gives you

Tool Purpose
list_activity_events Event feed for a window, with actor/action filters. Pagination is followed automatically; returns a resumable next_token if a safety cap is hit.
summarize_activity Deterministic rollup for a window: counts by actor, action, CRUD type and access type, plus failure/anonymous rates.
get_api_key_usage API-key-driven activity only, grouped by actor: action breakdown, distinct source IPs, first/last seen.
detect_anomalies Compares a window against each actor's stored baseline. Flags new actors, volume spikes, unseen source IPs, failed-event bursts, sustained failure rates, off-hours spikes and never-before-seen actions - each with evidence and a reasoning sentence.
get_actor_profile One actor's full picture: role (best effort), all-time action breakdown, access types, every source IP seen.
check_permission_prereqs Pass/fail on whether the configured keys can actually read the audit log, with remediation text.

Safety properties worth knowing:

  • Nothing that looks like a credential is ever returned. Field values whose key names a secret (secret_key, api_key, token, password, ...) or whose value looks like Tenable key material are masked to their last 4 characters.
  • Pagination is capped at 20 pages / 100k events per tool call; hitting the cap is reported explicitly along with the cursor needed to continue.
  • 429s back off using the X-RateLimit-Reset header (the endpoint sends no Retry-After), with exponential fallback and a retry ceiling.

Requirements

  • Python 3.11+
  • uv
  • Tenable VM API keys whose owner can read the audit log

Tenable role / permissions

Reading audit-log/v1/events requires the Administrator role, or a custom role with explicit audit-log read permission, on the user that owns the API keys. Anything less gets HTTP 403; check_permission_prereqs reports that in plain language.

Generate keys in Tenable VM under Settings → My Account → API Keys. The keys inherit the permissions of the user that created them.

get_actor_profile additionally tries to resolve an actor's role from the user directory. If the keys cannot list users, the profile is still returned - just without the role label.

Setup

uv sync --extra dev

Then copy .env.example to .env and fill in your keys:

cp .env.example .env

Verify credentials and permissions before wiring it into a client:

uv run python -c "from dotenv import load_dotenv; load_dotenv(); from src.server import check_permission_prereqs; print(check_permission_prereqs())"

Run the server directly (it speaks MCP over stdio, so it will just sit there waiting for a client - that is the correct behaviour):

uv run python -m src.server

Connecting a client

Use the absolute path to your clone in the config below. To print it, run pwd from the repository root on macOS/Linux, or (Get-Location).Path in PowerShell.

Claude Desktop

Edit claude_desktop_config.json:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "tenable-activity": {
      "command": "uv",
      "args": [
        "--directory",
        "C:\\path\\to\\tenable-activity-mcp",
        "run",
        "python",
        "-m",
        "src.server"
      ],
      "env": {
        "TENABLE_ACCESS_KEY": "your_access_key",
        "TENABLE_SECRET_KEY": "your_secret_key",
        "TENABLE_MCP_BASE_URL": "https://cloud.tenable.com"
      }
    }
  }
}

Restart Claude Desktop afterwards. On macOS/Linux use a POSIX path (/Users/you/tenable-activity-mcp).

If uv is not on the launcher's PATH, use its absolute path (which uv / (Get-Command uv).Source) as command.

Claude Code

claude mcp add tenable-activity --env TENABLE_ACCESS_KEY=your_access_key --env TENABLE_SECRET_KEY=your_secret_key -- uv --directory /absolute/path/to/tenable-activity-mcp run python -m src.server

Or add the same block as above to a project-level .mcp.json.

Credentials passed via env take precedence over .env; the .env file is a local-development convenience, and either mechanism works.

Example questions to ask once connected

  • "Check whether my Tenable credentials can read the audit log."
  • "Summarise Tenable platform activity for the last 7 days - who was most active, and what's the failure rate?"
  • "Which API keys were used against Tenable in the last 30 days, and from which source IPs?"
  • "Look for anomalies in Tenable activity over the past 3 days against a 30-day baseline, and explain anything you flag."
  • "Show me everything actor 00000000-1111-4222-8333-444444444444 has ever done - actions, access types, and IPs."

How anomaly detection works

detect_anomalies needs history to compare against, which lives in a local SQLite file (state.db, created automatically):

  1. If stored baselines are older than BASELINE_REFRESH_MAX_AGE_HOURS (12), the server fetches the baseline_days immediately preceding your window and recomputes per-actor averages, known IPs, known actions and an hour-of-day histogram.
  2. Your window is fetched and compared against those baselines.
  3. Events in the analysed window are not folded into the baseline, so re-running the same window returns the same findings.

Every threshold is a named constant at the top of src/anomaly.py and is echoed back in each result under thresholds:

Constant Default Meaning
SPIKE_MULTIPLIER 3.0 Window events/day must exceed this multiple of the baseline average
SPIKE_MIN_WINDOW_EVENTS 20 Floor before a spike can be flagged at all
NEW_IP_LOOKBACK_DAYS 30 How recently an IP must have been seen to count as "known"
FAILED_AUTH_BURST_COUNT / FAILED_AUTH_BURST_WINDOW_MINUTES 5 / 10 Failure-clustering trigger
HIGH_FAILURE_RATE_PCT 50.0 Sustained failure-rate trigger (over at least 10 events)
OFF_HOURS_START_HOUR / OFF_HOURS_END_HOUR 20 / 6 (UTC) Off-hours band
OFF_HOURS_RATIO_MULTIPLIER 2.0 Off-hours share must exceed this multiple of the actor's baseline share

Baselines are per actor, so a service account that legitimately runs 500 scans a day does not get flagged for doing exactly that.

Layout

src/
  server.py          MCP entrypoint (FastMCP-style) + the six tool definitions
  tenable_client.py  Auth, filter building, cursor pagination, 429 backoff, typed errors
  classifier.py      API-key vs UI/session tagging, IP extraction, redaction, rollups
  anomaly.py         Thresholds and the individual anomaly checks
  state.py           SQLite: cursors, accumulated actor history, computed baselines
tests/
  test_pagination.py test_classifier.py test_anomaly.py

Dependency direction is one-way: server → {anomaly, classifier, state} → tenable_client.

Testing

Three levels, in the order you should run them.

1. Unit tests (no credentials, no network)

uv run pytest -q

105 tests covering pagination/cursor handling, rate-limit backoff, API-key vs session classification, redaction, and every anomaly threshold. Every API response is faked through a stub transport, so the suite never touches a live tenant.

2. Offline end-to-end (no credentials, no network)

uv run python scripts/smoke_local.py

Runs all six tools against a scripted fake Tenable (a quiet baseline month, then a noisy night from a new IP) and asserts the results: anomalies flagged, planted secrets redacted, bad input returned as a structured error instead of an exception. Exits non-zero on any failure, so it works as a pre-commit or CI gate.

3. Live check against your tenant (read-only)

With .env filled in:

uv run python scripts/live_check.py 7

Verifies audit-log permissions first and stops with remediation text if they are wrong, then prints a real summary, API-key usage breakdown, anomaly findings, and the busiest actor's profile for the last N days (default 7). All calls are GETs; nothing is written to Tenable.

4. Through an MCP client

Any MCP client works. To poke at the tools interactively without a chat client:

npx @modelcontextprotocol/inspector uv --directory . run python -m src.server

Or wire it into Claude Desktop / Claude Code (above) and ask one of the example questions. check_permission_prereqs is the right first call - it confirms the server started, found its credentials, and can reach the audit log.

Inspecting local state

uv run python -c "from src.state import StateStore; print(StateStore().stats())"

Delete state.db to reset baselines; the next detect_anomalies call rebuilds them.

Known limitations

  • Requires the Administrator role. Reading audit-log/v1/events needs the Administrator role, or a custom role with explicit audit-log read permission, on the user that owns the API keys. Anything less returns HTTP 403. Run check_permission_prereqs first - it reports exactly this, with remediation text.

  • Anomaly detection needs history before it is useful. The first detect_anomalies call against a fresh state.db builds baselines from the 30 days preceding your window and then compares against them. Actors with little or no prior activity flag as new_actor, so early runs are noisier than later ones.

  • Role resolution is best effort. get_actor_profile tries to resolve an actor's Tenable role from the user directory. If the keys cannot list users, the profile is still returned - just without the role label.

  • Off-hours detection uses a fixed UTC band. The off-hours window is 20:00-06:00 UTC and does not adjust to the tenant's working timezone. Distributed teams will see off-hours findings that are simply another region's working morning.

  • Baselines are local to the machine running the server. state.db is not shared between installs, so two operators running their own copies build independent baselines and can reach different conclusions about the same window.

  • Wide windows return partial results by design. One tool call follows at most 20 pages / 100,000 events. Hitting that cap is reported explicitly along with the next_token needed to resume, so it is never a silent truncation - but a very large window does take several calls.

  • Only the first 1,000 events come back inline. list_activity_events caps the inline events array at 1,000 and sets inline_truncated when it does. The summary block still covers every event fetched, so the aggregate numbers stay correct even when the inline list is trimmed.

  • get_actor_profile looks back 365 days at most, and cannot see further back than the audit log itself retains.

Notes

  • Built against mcp==2.0.0, where the SDK renamed FastMCP to MCPServer. server.py imports whichever name the installed SDK provides, so it also works on mcp 1.x.
  • Event fetching goes through pyTenable's TenableIO session (audit_log.events(..., return_json=True)), which keeps auth and connection handling in the maintained library while leaving the pagination.next cursor visible to us. If pyTenable is unavailable, an equivalent requests transport using the X-ApiKeys: accessKey=...;secretKey=... header takes over.
  • Timestamps are UTC everywhere, including the off-hours band.
  • state.db accumulates per-actor history. Delete it to reset all baselines; the next detect_anomalies call rebuilds them.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选