Skip to main content
Glama
brendanong95

tenable-activity-mcp

by brendanong95

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.

Related MCP server: tenable-mcp-server

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.

Available Tools

6 tools
check_permission_prereqsA

Verify the configured API keys can actually read the audit log.

Makes one minimal audit-log request and reports pass/fail with a plain explanation and remediation steps. Run this first when another tool returns an authentication or permission error. No secret material is echoed - the access key is shown with only its last 4 characters.

Returns: A dict with ok, a human-readable message, remediation when failing, plus configuration and local-state diagnostics.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully discloses behavior: it makes exactly one minimal audit-log request, reports pass/fail with a plain explanation plus remediation steps, and never echoes secret material (the access key shows only the last 4 characters). This goes well beyond a bare operation statement and gives the agent confidence about side effects and output safety.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded in the first sentence, and the remaining sentences carry useful information about behavior, triggering, and secret handling. The 'Returns' section slightly overlaps with the provided output schema, but it only lists the key dictionary fields and is not padded. Overall it is tightly written for the information it needs to convey.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no parameters and a full output schema, the description covers everything an agent needs: what is checked, when to run it, what side effects occur (one request, no secret disclosure), and what the result looks like. There are no missing pieces that would cause an agent to misuses the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the baseline is 4. The description does not need to explain parameter meaning, and the empty input schema leaves nothing ambiguous; the description instead focuses on the output, which is appropriate here.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Verify the configured API keys can actually read the audit log,' which clearly states what the tool does. It also distinguishes itself from the sibling data-retrieval tools (list_activity_events, summarize_activity, etc.) by positioning this as a diagnostic preflight check rather than an analytics operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives an explicit trigger condition: 'Run this first when another tool returns an authentication or permission error.' This is clear contextual guidance, but it does not mention when not to use it or name alternative tools—though no sibling performs the same diagnostic role, so exclusions are not necessary.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

detect_anomaliesA

Compare a window against each actor's stored baseline and flag outliers.

Checks run: previously unseen actor, event volume above SPIKE_MULTIPLIER x the actor's historical daily average, source IPs absent from the baseline lookback, failed-event bursts, sustained high failure rate, off-hours activity spikes, and never-before-seen action types. Every finding includes the observed value, the threshold crossed, and a reasoning sentence.

Baselines live in the local SQLite state file and are refreshed from the period immediately preceding the window when they are stale. Events in the analysed window itself are not folded into the baseline, so re-running the same window yields the same findings.

Args: date_from: Start of the window under investigation, ISO-8601. date_to: End of the window under investigation, ISO-8601. baseline_days: Days of history preceding the window to baseline against.

Returns: A dict with findings (severity-ordered, each with evidence and reasoning), findings_by_type/findings_by_severity counts, and the active thresholds.

ParametersJSON Schema
NameRequiredDescriptionDefault
date_toYes
date_fromYes
baseline_daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden and covers side effects (baseline refresh from local SQLite when stale), idempotence (analyzed window not folded into baseline), and output guarantees (evidence, reasoning, severity ordering). This exceeds what schema alone could convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The definition is dense but every sentence adds necessary operational detail; purpose is front-loaded, checks are grouped, and Args/Returns are clearly structured. No filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no annotations and moderate input complexity, it explains inputs, output shape, baseline mechanics, staleness handling, and idempotence. An agent has enough to select and call it correctly without further discovery.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but the Args section documents all three parameters with semantic meaning: ISO-8601 window bounds and baseline history days. This fully compensates for the absent schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb-resource pair ('Compare a window against each actor's stored baseline') and an enumerated list of anomaly checks that distinguishes this from sibling event-listing/summary tools. The resource and behavior are unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The intended investigation scenario is clear: detect baseline deviations in a date window using prior history. It does not explicitly name alternatives or when-not-to-use, but the detailed scope leaves little ambiguity about when this tool applies.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_actor_profileA

Full historical view of one actor: roles, actions, access types, IPs.

Combines a fresh audit-log fetch for the actor with everything previously persisted in the local state database, so IPs and actions seen in earlier sessions still show up. The Tenable role is resolved on a best-effort basis from the user directory and is omitted when the API keys cannot list users.

Args: actor_id: The actor UUID (as it appears in actor.id on events). lookback_days: How far back to fetch fresh events for this actor.

Returns: A dict with identity, recent_activity (rollup over the lookback window), lifetime (accumulated state: all IPs, all actions, all access types, first/last seen) and pagination.

ParametersJSON Schema
NameRequiredDescriptionDefault
actor_idYes
lookback_daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations available, the description carries the behavioral burden and does so well. It discloses that data combines a fresh audit-log fetch with persisted local state, explains best-effort role resolution, and notes when role data is omitted. This is substantive behavioral context beyond a simple summary.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well structured with a one-sentence summary, a clear behavioral explanation, and separate Args/Returns sections. It is appropriately detailed for the tool's complexity without padding, and each sentence adds useful information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity, zero annotation coverage, and 0% schema description coverage, the description supplies everything needed: what the profile contains, how data is combined, parameter semantics, and return structure. The output schema also exists, but the description goes beyond it by explaining the lifetime/recent_activity distinction.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description fully compensates. actor_id is explained as the actor UUID as seen in actor.id, and lookback_days is defined as how far back to fetch fresh events. Both parameters receive meaningful semantic detail not present in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Full historical view of one actor: roles, actions, access types, IPs', which names a specific resource and scope. It clearly distinguishes this from siblings like list_activity_events or summarize_activity by emphasizing the per-actor, history-combining nature.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage context is implied rather than explicit: the tool is for obtaining a comprehensive actor profile that merges fresh data with persisted state. It does not explicitly state when to prefer this over sibling tools or provide exclusions, so an agent must infer the right selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_api_key_usageA

Report API-key-driven activity per actor (UI/session activity excluded).

Events are classified as API-key driven using the audit event's access/auth-method field, falling back to user-agent shape (scripted client vs browser). Each actor gets an action breakdown, distinct source IPs, and first/last seen timestamps - all pre-aggregated.

Args: actor_id: Optional actor UUID. Omit to cover every actor with API-key activity in the window. date_from: Start of the window, ISO-8601. Defaults to 30 days ago. date_to: End of the window, ISO-8601. Defaults to now.

Returns: A dict with actors (per-actor API-key usage profiles), an access_type_totals breakdown for context, and coverage metadata.

ParametersJSON Schema
NameRequiredDescriptionDefault
date_toNo
actor_idNo
date_fromNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the burden of explaining behavior, and it does so well: it discloses the classification method (audit access/auth-method with user-agent fallback), that results are pre-aggregated, and what per-actor fields are returned. It does not mention permissions or rate limits, but 'Report' and 'Returns' strongly imply a safe, read-only operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a clear one-sentence summary, followed by a concise classification note and clearly labeled Args and Returns sections. Every sentence adds useful information and there is no filler or redundant restating of the tool name.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a report tool with three optional parameters and an output schema, the description covers the core behavior, defaults, and return shape sufficiently. Minor gaps remain, such as date-boundary inclusivity and explicit guidance about which sibling tools to use instead, but nothing essential is missing for invoking it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it does. All three parameters are explained with semantics beyond the bare schema: actor_id is an optional UUID, date_from defaults to 30 days ago, and date_to defaults to now, with ISO-8601 format specified.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Report API-key-driven activity per actor' and explicitly excludes UI/session activity. It further clarifies the output (per-actor action breakdown, distinct source IPs, first/last seen timestamps), which distinguishes this tool from the sibling activity tools even without naming them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The intended use is clear: this tool is for API-key-driven activity reporting, and the description explicitly notes that UI/session activity is excluded. It also explains the effect of omitting actor_id, but it does not explicitly name alternative sibling tools for when this tool should not be used.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_activity_eventsA

List Tenable audit-log events for a date range, with optional filters.

Pagination is handled internally (the next cursor is followed automatically) up to a safety cap of 20 pages / 100k events per call; if the cap is hit, pagination.next_token is returned so the next call resumes exactly where this one stopped. Every event is normalised, tagged with an access type (API key vs UI session), and has credential-looking field values redacted to their last 4 characters.

Args: date_from: Start of the window, ISO-8601 (e.g. "2024-01-01" or "2024-01-01T00:00:00Z"). Inclusive. date_to: End of the window, ISO-8601. Inclusive. actor_id: Optional actor UUID to filter on (actor_id.eq). action: Optional exact action name, e.g. "user.create" (action.eq). limit: Page size sent to the API (1-5000). next_token: Opaque cursor from a previous call, to resume pagination.

Returns: A dict with events (classified, redacted), a summary rollup of those events, and a pagination block.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
actionNo
date_toYes
actor_idNo
date_fromYes
next_tokenNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden and does so exceptionally well. It discloses internal pagination with a 20-page/100k-event cap, resume behavior via next_token, normalization of events, access-type tagging, and redaction of credential-looking fields. This is far beyond what a minimal description would provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is information-dense yet well-structured: purpose first, then behavioral caveats, then parameter breakdown, then return structure. Every sentence adds value, and the length is justified by the number of parameters and the pagination/redaction behaviors that need explanation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 6-parameter tool with no annotations, the description covers use case, filtering, pagination limits, resume semantics, data normalization, redaction, and return shape. The optional filters and cap behavior are fully explained, leaving no critical gap for an agent attempting to invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description compensates completely by explaining every parameter: date format and inclusivity, optional filters with their eq semantics, the exact action example, limit range, and the opaque next_token cursor. An agent can correctly populate all six arguments without needing extra documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description begins with a specific verb ('List') and resource ('Tenable audit-log events'), and states the scope ('for a date range') with optional filters. This clearly distinguishes it from sibling tools like summarize_activity and detect_anomalies, so an agent knows exactly what this tool retrieves.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description makes the core use case clear: retrieve audit-log events within a date range, optionally filtered by actor or action. It does not explicitly name sibling alternatives or provide when-not-to-use guidance, but the event-listing purpose is unambiguous and the filter details help an agent choose appropriately.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

summarize_activityA

Summarise all audit-log activity in a window (counts computed in Python).

Returns event counts by actor, by action, by CRUD type and by access type, plus failure and anonymous rates - all pre-computed. Use these numbers directly; do not re-derive them from raw events.

Args: date_from: Start of the window, ISO-8601. Inclusive. date_to: End of the window, ISO-8601. Inclusive.

Returns: A dict with a summary block (total_events, by_actor, by_action, by_crud, by_access_type, failure_rate_pct, top_failed_actions) and a pagination block describing coverage.

ParametersJSON Schema
NameRequiredDescriptionDefault
date_toYes
date_fromYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses that counts are pre-computed in Python, that date boundaries are inclusive, and it exposes the exact return structure with summary and pagination blocks. It does not mention permissions, error behavior, or timezone assumptions, but for a read-only aggregation these are not critical.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-organized with a concise purpose line, a usage warning, and clear Args/Returns sections. There is slight redundancy where the prose mention of 'Returns event counts...' is later repeated as a structured returns block, but the text is otherwise efficient and front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter aggregation tool with an output schema present, the description covers purpose, parameter semantics, and return keys sufficiently. It lacks an explicit when-to-use-versus-siblings statement and has no annotation safety profile, but the core invocation details are complete enough for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema provides only bare string properties with 0% coverage. The description compensates fully by defining date_from as 'Start of the window, ISO-8601. Inclusive' and date_to as 'End of the window, ISO-8601. Inclusive', adding format, semantic role, and boundary inclusivity to both parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Summarise all audit-log activity in a window', and enumerates the output dimensions (by actor, action, CRUD, access type, failure and anonymous rates). This clearly frames the tool as an aggregation over raw events and distinguishes it from siblings like list_activity_events or get_actor_profile.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides a strong usage directive: 'Use these numbers directly; do not re-derive them from raw events.' This tells the agent to rely on pre-computed counts rather than computing from raw event lists, which is clear contextual guidance. It does not explicitly name alternative tools or exclusion conditions, but the implied comparison to raw event access is present.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 6 tool updatesv0.1.0
    • First observedcheck_permission_prereqs
    • First observeddetect_anomalies
    • First observedget_actor_profile
    • First observedget_api_key_usage
    • First observedlist_activity_events
    • First observedsummarize_activity

TDQS

A4.7/5.0

Scored across 6 tools

Disambiguation5/5

Each tool targets a clearly different concern: raw event listing, summary rollups, API-key-specific usage, anomaly detection, actor profiling, and prerequisite checks. Even the two aggregation tools (summarize_activity and get_api_key_usage) are separated by access-type scope, and their descriptions reinforce the distinction.

Naming Consistency5/5

All tool names follow a consistent snake_case verb_noun pattern: list_activity_events, summarize_activity, get_api_key_usage, detect_anomalies, get_actor_profile, check_permission_prereqs. The verbs clearly signal the action and the objects clearly signal the resource or concern, making the set predictable and easy to navigate.

Tool Count5/5

Six tools is well-scoped for an audit-log activity server: two raw/aggregate views, two analysis-focused tools, one deep-dive tool, and one operational prerequisite check. Each tool earns its place without redundancy or bloat.

Completeness5/5

The tool surface covers the full audit-monitoring lifecycle: listing raw events, summarizing them, isolating API-key usage, detecting anomalies, profiling individual actors, and verifying access prerequisites. The automatic pagination and baseline handling close potential dead ends, so an agent can move from raw data to insight without missing critical operations.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Exposes Azure Log Analytics workspace data with tools for querying AuditLogs and AzureActivity tables, supporting custom KQL queries, time range filters, and pagination.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server for Tenable Vulnerability Management and the Tenable One platform, enabling LLMs to query assets, vulnerabilities, scans, exposure metrics, attack paths, and more via natural language.
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables read-only querying of Azure Log Analytics and Azure Resource Graph through MCP, supporting KQL queries, workspace discovery, and resource inventory exploration with Azure RBAC authentication.
    5
    2
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Read-only MCP server for querying Microsoft Purview unified audit logs across M365 workloads, wrapping the Graph API's asynchronous audit log search.
    -