Skip to main content
Glama
aqamahn

Tenable OT Security MCP Server

by aqamahn

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
TOT_URLNoAppliance base URL. Alternative: TENABLE_URL.
TENABLE_URLNoAlternative to TOT_URL.
TOT_API_KEYNoService-account API key. Alternative: TENABLE_API_KEY.
TENABLE_API_KEYNoAlternative to TOT_API_KEY.
TENABLE_OT_HOSTNoBind address for HTTP transports.127.0.0.1
TENABLE_OT_PORTNoPort for HTTP transports.8000
TENABLE_OT_TIMEOUTNoGraphQL request timeout (seconds).30
TENABLE_OT_AUDIT_DIRNoDirectory holding audit.jsonl./data
TENABLE_OT_LOG_LEVELNoLog level. Logs go to stderr.INFO
TENABLE_OT_TRANSPORTNoTransport: stdio, streamable-http, or sse. The image defaults to streamable-http.stdio
TENABLE_OT_TLS_VERIFYNoVerify the appliance certificate. Turn on for trusted certificates.true (compose sets false)
TENABLE_OT_TOKEN_LABELNoOptional label stamped on audit entries. A label, never the key.
TENABLE_OT_UPLOAD_TIMEOUTNoFile-upload timeout; uploads are parsed server-side and take longer.180
TENABLE_OT_WRITE_TOOLS_ENABLEDNoSet to 1/true/yes/on to expose write tools. Anything else keeps them hidden.0

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
tenable_ot_statusA

Report whether this server can reach and authenticate to the configured Tenable OT deployment, with round-trip latency and, on failure, the underlying error. Call this first when other tools are failing, to tell an appliance/credential problem apart from a query problem.

READ-ONLY. Never returns the API key.

query_assetsA

Returns a list of OT assets in the Tenable OT deployment, filtered by the provided criteria. Returns up to limit (max 500) assets with each asset's identity, classification, IPs / MACs, Purdue level, segment membership, backplane (rack) membership, and aggregate risk. Call get_asset on a returned id for the per-asset bundle, or get_asset_vulnerabilities for that asset's open vulnerabilities.

total_count is the full number of assets matching the filter, independent of the page size — use it to answer 'how many' questions directly. When the match exceeds one page the response sets has_more: true and returns an end_cursor; pass that as after to fetch the next page, repeating until has_more is false to walk the entire matched set.

Filter values use natural OT vocabulary: • kind: one of ['accesscontrolsystem', 'accesspoint', 'actuator', 'backplanemodule', 'barcodescanner', 'bms', 'camera', 'cnc', 'controller', 'converter', 'cp', 'datalogger', 'dcs', 'domaincontroller', 'drive', 'eng', 'fielddevice', 'fileserver', 'firewall', 'gateway', 'historian', 'hmi', 'hub', 'hvacmodule', 'ied', 'industrialgateway', 'industrialnetworkdevice', 'industrialprinter', 'industrialrouter', 'industrialsensor', 'industrialswitch', 'inverter', 'io', 'iot', 'lightingcontrol', 'medicaldevice', 'mobile', 'networkdevice', 'otdevice', 'otserver', 'otworkstation', 'panel', 'plc', 'powermeter', 'powersupply', 'printer', 'projector', 'radio', 'relay', 'remoteio', 'repeater', 'robot', 'router', 'rtu', 'securityappliance', 'sensor', 'serialethernetbridge', 'server', 'smarthub', 'smartsensor', 'smarttv', 'storagedevice', 'switch', 'tablet', 'tenableem', 'tenableicp', 'tenablesensor', 'threedprinter', 'unknown', 'ups', 'videomanagementsystem', 'virtualserver', 'virtualworkstation', 'voipdevice', 'webserver', 'workstation'] • category: one of 'controller', 'network', 'iot' • criticality_at_least: one of ['none', 'low', 'medium', 'high'] • vendor: equal-match on the vendor name • name_contains: substring match on the asset name • tags: list of exact tag strings; matches an asset carrying ANY of them. Project-file uploads (upload_manual_asset_file) are auto-tagged by Tenable as 'Project (SN: )' — call get_asset on one known asset from an import to read its exact tag, then use it here to pull every asset that same import produced. This is the reliable way to disambiguate duplicate-named modules from different imports (e.g. a redundant PLC pair, each with its own project file, producing two identically-named 'EN01' assets that are only distinguishable by which import's tag they carry) — module name and description alone can't tell them apart.

get_assetA

Returns the per-asset bundle for one OT asset by id: identity, classification, IPs / MACs, Purdue level, segment membership, criticality, run status, backplane (rack) membership, tags, and aggregate risk. backplane is null for assets not mounted in a chassis; when present, its id is what rename_backplane needs (a backplane's id is NOT the same as any asset's id). tags includes any Tenable-assigned project-file-import tag ('Project (SN: )') — pass one back into query_assets' tags filter to pull every asset that same import produced. Call after query_assets returns an id of interest. For the asset's open vulnerabilities call get_asset_vulnerabilities separately.

get_asset_vulnerabilitiesA

Returns the open vulnerabilities (Tenable plugins) affecting one OT asset. Each vulnerability includes CVEs, CVSS v3 score and vector, exploit availability flags (CISA KEV, exploit-available, exploited-by-malware), age, public disclosure date, and the official vendor solution. Use query_vulnerabilities for global vuln filtering (KEV-only, exploit-available, severity floor, etc.); this tool is asset-scoped.

list_custom_fieldsA

Returns the asset custom-field schema for this Tenable OT tenant. Custom fields are 10 fixed slots ('customField1'..'customField10'); operators map each slot to a human label (e.g. 'Plant ID', 'CDA Type') and a value type ('PlainText' or 'HyperLink').

Call this before reading or writing custom-field values so the AI knows which labels are configured. Read tools (get_asset, query_assets) already surface values keyed by label automatically; write tools (update_asset, bulk_edit_assets) accept custom_fields keyed by label and translate to slots internally.

get_manual_asset_upload_statusA

Check the processing status of the most recent file uploaded via Tenable OT's Manual Uploads data source (upload_manual_asset_file). Processing is asynchronous: poll this a few times after uploading if it hasn't updated yet. Returns null fields if no manual upload has ever been processed on this deployment.

CAVEAT (confirmed empirically): this field tracks literal CSV-type imports specifically — it stayed null through a live Rockwell .L5X project-file upload that DID create an asset. For project-file / OTD-scan-result uploads, don't treat a null result here as failure — confirm outcome with query_assets (by the controller/device name you expect, or by IP) instead of waiting on this to populate.

query_vulnerabilitiesA

Returns Tenable plugins (vulnerabilities) matching the filter criteria. Each result includes CVEs, CVSS v3 score and vector, exploit availability flags (CISA KEV, exploit-available, exploited-by-malware), age, public disclosure date, and the official vendor solution. Call get_vulnerability on a returned plugin id to see every affected asset.

total_count is the full number of plugins matching the filter, independent of the page size — use it to answer 'how many' questions directly. When the match exceeds one page the response sets has_more: true and returns an end_cursor; pass that as after to fetch the next page, repeating until has_more is false to walk the entire matched set.

Filter values use natural OT vocabulary: • severity_at_least: one of 'info', 'low', 'medium', 'high', 'critical' • cve: a CVE substring (e.g. 'CVE-2023-25619' or 'CVE-2023' for a year-bucket) • family / source: exact-match plugin metadata

For KEV-only or exploit-available filtering, inspect the projected flags in the response — those live on plugin details and aren't filterable server-side.

get_vulnerabilityA

Returns one Tenable plugin (vulnerability) by id, plus the full list of OT assets currently affected. Use this to reason about exposure breadth ('which assets are affected by this exploited-in-the-wild vuln?'). To look up by CVE id, call query_vulnerabilities(cve='CVE-...') first and then fetch by the returned plugin_id.

query_eventsA

Returns OT detection events matching the filter criteria, newest first. Use this to investigate alert windows, policy-firing patterns, source/dest IP context, or events in a specific time window. Each event includes the time, classification, severity, source/dest assets, the firing detection policy, and protocol/IP/MAC context. Call get_event on a returned id for full event detail.

total_count is the full number of events matching the filter, independent of the page size — use it to answer 'how many' questions directly. When the match exceeds one page the response sets has_more: true and returns an end_cursor; pass that as after to fetch the next page, repeating until has_more is false to walk the entire matched set. Event totals can be very large, so narrow with the filters (severity, time window, policy) before paging.

Filter values use natural OT vocabulary: • severity_at_least: one of 'none', 'low', 'medium', 'high' • event_type: a PolicyEventType name like 'FirmwareVersionChange', 'ConfigurationDownload', 'ProgrammingUpload', 'OperatingMode' • since / until: ISO-8601 timestamps for time-window scope

get_eventA

Returns full detail for a single OT event by id. Use after query_events returns an id of interest, or when a finding or case references a specific event.

list_detection_policiesA

Returns OT detection policies — the rules that fire events. Each policy has a level (severity), enabled / paused / archived flags, an event-type classification, and aggregate fired-event counts. Use this to audit which policies are configured, which are noisy, or which are paused. Call query_policy_findings for the per-asset hits one policy is producing.

Note: Tenable OT's policies query supports pagination only — the category / enabled / search filters below are applied client-side after fetching a page. For large deployments, increase limit to capture more before filtering.

get_policyA

Fetch one policy's COMPLETE configuration by id — source/destination asset-group scope (as AND'd rows of OR'd include/exclude conditions), schedule, severity, notification actions (syslog server / email group), and (for Communication/Protocol-schema policies) the protocol/port group. Unlike list_detection_policies's summary, this returns every field the policy's edit surface exposes.

Call this BEFORE update_policy — Tenable OT's policy mutations are full replaces, not patches, so you need to see the current state of every field before changing just the ones you actually want to change.

query_policy_findingsA

Returns per-asset findings for one or more detection policies — i.e. the rows of (policy × asset × hit count) that the policies have produced. Use this to see which assets keep tripping a policy (often a tuning gap), or which assets are MITRE-mapped to a specific technique. Each finding has firstHitTime / lastHitTime, activeHits / resolvedHits, status, and joined source / destination assets.

Filter values use natural OT vocabulary: • severity_at_least: one of 'none', 'low', 'medium', 'high' • status: a FindingStatus value (e.g. 'Open', 'Resolved') • mitre_technique: a MITRE ATT&CK id (e.g. 'T1565.001') • since: ISO-8601 timestamp; findings last-seen at or after

list_segments_and_zonesA

Returns Tenable OT's segmentation: every segment (with VLAN, subnet, asset-type filter, system flag, archived flag) and every zone (a higher-level grouping of asset groups). Use this to answer compliance questions about the IEC 62443 Zone & Conduit model, NERC CIP Electronic Security Perimeters, or NEI 08-09 defense-in-depth.

get_communication_pathsA

Returns observed L2 communication links involving one OT asset, with peer asset id, protocols seen, traffic / conversation count, and first/last-conversation times. Peer assets are returned as IDs only — call get_asset on each peer id to enrich with name / vendor / type. The consuming AI uses this as the graph adjacency for attack-path reasoning; call again on a peer's id to expand further.

list_network_configA

Returns the appliance's network CONFIGURATION — what it is set up to watch, as opposed to list_segments_and_zones and get_communication_paths, which report what it has actually observed.

Reach for this when inventory looks wrong. If an asset is missing from query_assets, the usual cause is that its subnet isn't an enabled monitored network — nothing was ever captured, so the asset cannot exist in the data.

kind selects what to read: • monitored_networks — subnets the appliance captures. Adds a coverage block naming any DISABLED subnets, which are blind spots • network_areas — named groupings, incl. whether each supports active queries • zones — the coarsest grouping, above segments • firewall_rules — appliance firewall operations/targets • tcp_port_pinnings — IP/port pinning rules that force protocol interpretation • network_interfaces — interfaces seen, by MAC and family

READ-ONLY.

list_sensorsA

Returns every Tenable OT sensor in the deployment with its current status, connection / tunnel status, version, addressing, error state, and whether updates are pending. Use this to verify visibility coverage before drawing conclusions from query_assets / query_events — an offline sensor means absence of evidence, not evidence of absence.

The status filter applies client-side after fetch.

list_asset_groupsA

Page through every active (non-archived) asset group in the deployment. Each entry includes its membership shape — IP list, IP range, asset-id list, filter expression, etc. — and whether it surfaces as a UI tag (display_tag). Use this before creating to avoid duplicating an existing group.

list_archived_asset_groupsA

Page through archived asset groups — those that were soft-deleted via archive_asset_group. Group definitions and historical membership are preserved by Tenable; they just stop appearing in active views.

get_asset_groupA

Fetch one asset group's full record by its id. The shape varies by group subtype: an AssetList returns an assets_sample preview, an IpRange returns start_ip / end_ip, a FilterGroup returns its filter expression, and so on.

list_email_groupsA

Page through every email group in the deployment. Each entry includes its recipients, the SMTP server it routes through, and last-modified metadata. Email groups are referenced by detection policies' actions to send alert emails.

get_email_groupA

Fetch one email group with its recipient list and bound SMTP server details.

find_email_groups_using_smtp_serverA

Given an SMTP-server id, return the email groups bound to it. Useful before retiring an SMTP server: any group returned here will lose its delivery path if the server is removed.

list_schedule_groupsA

Page through every active schedule group. Each entry surfaces its kind (one-shot TimeInterval, weekly RecurringGroup, or system ScheduleFunction) and the windows it defines. Every policy mutation's schedule argument resolves against one of these.

list_archived_schedule_groupsC

Soft-deleted schedule groups, paginated.

get_schedule_groupA

Fetch one schedule group. Shape varies by kind: TimeInterval returns start_time/end_time; RecurringGroup returns a list of weekly windows under schedules.

list_tag_groupsA

Page through every tag group. Tag groups bundle controller tags (by asset id + tag id) so a TagValuePolicy can fire against all members. tag_type indicates the scalar type Tenable evaluates the tag values as.

get_tag_groupB

One tag group with up to 100 member items.

list_eligible_tagsA

List controller tags that could be added to a tag group. Filterable by asset (asset_id) and tag-value type (tag_type: one of Unknown, Int, Bool, Short, DInt, Long, Float, MultipleTagTypes). Use this before create_tag_group to discover what's available without guessing tag ids.

list_rule_groupsA

Page through every active rule group. A rule group is a bundle of IDS rule SIDs referenced by IntrusionPolicy. Each entry includes a preview of up to 25 included rules.

list_archived_rule_groupsC

Soft-deleted rule groups, paginated.

get_rule_groupC

One rule group with the first 25 included rules.

list_port_groupsA

Page through every active port group. Port groups are reusable port-range bundles consumed by PortPolicy definitions.

list_archived_port_groupsC

Soft-deleted port groups, paginated.

get_port_groupC

One port group with up to 100 of its port-range items.

list_protocol_groupsA

Page through every active protocol group. Each item carries a protocol (TCP/UDP/MODBUS/S7/IEC104/DNP3/etc.) and optional port range.

list_archived_protocol_groupsC

Soft-deleted protocol groups, paginated.

get_protocol_groupC

One protocol group with up to 100 of its items.

list_user_groupsA

Page through every active user group at the ICP level. Each entry exposes its assigned roles and a sample of member users.

list_archived_user_groupsB

Soft-deleted user groups at the ICP level.

get_user_groupB

One ICP-level user group with its roles and member preview.

list_em_user_groupsA

Page through every active EM-level user group. Each entry exposes em_level (whether the group is EM-only) plus roles and member preview.

list_em_archived_user_groupsD

Soft-deleted EM-level user groups.

get_em_user_groupC

One EM-level user group with its roles and member preview.

list_active_scansA

Returns Tenable OT active-scan job specifications: name, description, scan operation type (PortScan, AssetDiscovery, SnmpType, etc.), category (IT / OT / Discovery), trigger (Manual / Periodic / System), enabled flag, status, and the asset group the job targets. Use this to audit what scans are configured. Predefined system scans appear with predefined: true. Note: this server does not expose any tool that runs a scan — that's a human-only action via the Tenable OT UI.

get_active_scanA

Returns the full specification for one active-scan job by id. Use after list_active_scans returns a job of interest, or when reading parameters before suggesting modifications.

get_active_scan_executionsA

Returns past execution records for one active scan: start/end time, elapsed time, status (Completed / Failed / Ongoing), who initiated, source (UI / API / system), and any failure explanation. Use this to audit when a scan was last run and whether it succeeded — but the underlying execution is triggered by humans, not this server.

summarize_environmentA

Returns a one-shot snapshot of the operator's OT environment — total counts and useful subtotals across assets, events, vulnerabilities, sensors, topology, and policies. Ideal as the first call when the AI doesn't yet know the deployment's shape and scale. Each section is a compact dict; subtotals are best-effort (only those Tenable's filters support are populated).

Asset criticality buckets: none / low / medium / high. Plugin severity buckets: info / low / medium / high / critical. Event subtotals split by resolved flag.

query_attack_pathwaysA

Returns the asset's 1-hop network neighborhood: the asset itself plus a list of peer-asset IDs it has communicated with, with the protocols and conversation count of each link. Use this AS THE GRAPH the AI walks to reason about attack paths — call again on each peer's id to expand further. The server does NOT compute paths, pick highest-risk routes, or score compromise time. That's the AI's job. Peer assets are returned as IDs only — call get_asset on each to enrich with name / vendor / type.

query_vulnerability_clustersA

Returns the per-asset → vulnerabilities join the consuming AI uses to spot common CVEs across multiple assets, exploit chains (KEV + exploit-available + high criticality), or single-patch leverage points (one CVE fixing many). Two modes:

• Pass asset_ids: parallel per-asset traversal of asset.plugins, returning each asset's vulns with the same schema. The AI walks the result to find shared CVEs. • Pass cve_substring only: global plugin search (e.g. 'CVE-2023' for a year-bucket), each plugin coming with its full affectedAssets list joined.

Both args may be combined for a per-asset CVE-filtered view. The server does NOT cluster server-side.

query_temporal_patternsA

Returns events in a time window, ordered chronologically, with their classification, firing policy, and source/dest IPs joined. The AI uses this raw sequence to detect patterns (e.g. config-download + firmware-change + operating-mode-change within minutes = high-priority investigation). The server does NOT detect motifs, score patterns, or label sequences.

get_asset_intelligenceA

Returns one asset's full relational bundle in a single shot: asset core + open vulnerabilities + recent events where the asset is source or destination + 1-hop comms peers. The AI uses this bundle to write a per-asset intelligence narrative if asked. The server does NOT generate the narrative itself.

list_policy_relationshipsA

Answers 'what breaks if I change this?' — the impact check to run BEFORE any write that touches a shared object (asset group, schedule group, port/protocol group, baseline).

Tenable OT's policy mutations are FULL REPLACES, so an edit lands completely and silently. If a live detection policy scopes to the group you're about to archive or re-scope, that policy's coverage changes with no warning. This tool surfaces those dependants first.

kind selects the relationship: • policies_containing — policies that CONTAIN the given object id (needs target_id) • used_in_policies — policies that USE the given object id (needs target_id). Tenable exposes both directions; check both when the answer matters. • baseline_policies — policies bound to the network baseline (no id needed) • ids_rules — the IDS rule catalogue with SIDs, classifications and enabled state (no id needed)

The impact block gives the verdict: how many referencing policies are actually LIVE (not disabled/archived/paused), and their titles. An archived policy referencing the object is not a reason to hold back; a live one is.

READ-ONLY.

get_schema_enumsA

Read this deployment's own GraphQL schema — enum values, input-object shapes, and the available root query/mutation fields.

WHY THIS MATTERS: some filters on this server refuse to run with a SchemaNotConfirmedError because their Tenable-side enum values were never confirmed (this server does not guess enum values — a wrong one silently filters or writes the wrong thing instead of failing). Call this tool with no arguments to dump exactly the types those mappings need, then hand the output to whoever maintains tools/_enums.py; filling them in is a single edit and unblocks the gated filters.

It also answers 'does this appliance actually expose query X?' — useful when a topology tool reports that a top-level query was rejected.

Arguments: • type_names: specific GraphQL types to dump. Omit to get the types this server still needs. • include_inputs: also dump input-object field shapes (for filter/mutation argument structures). • list_root_fields: also list every root query and mutation name the appliance exposes.

READ-ONLY. Touches no OT data — GraphQL reflection only.

query_plugin_definitionsA

Returns Tenable plugin DEFINITIONS — the vulnerability checks themselves (id, name, family, severity, VPR score, and how many assets each affects). This is the catalog, NOT per-asset findings: a plugin appears here whether or not anything in your environment is affected.

For the vulnerabilities on a specific asset use get_asset_vulnerabilities. For a bulk dump of every finding instance use export_findings.

search is a single-term substring match. Structured field filters need a filter-expression shape not yet confirmed against a live deployment, so they are not exposed rather than guessed.

READ-ONLY.

get_plugin_definitionA

Returns one plugin definition by its NUMERIC plugin id, including the assets it affects. Note the id is an integer (e.g. 500123), not the UUID used for assets.

READ-ONLY.

export_assetsA

Bulk asset export via Tenable OT's export API. Returns full asset records for offline analysis, inventory reconciliation, or feeding another system.

PREFER query_assets for interactive questions — it is far lower latency, supports the natural-vocabulary filters, and returns a trimmed projection that is cheaper to read. Use this tool when you genuinely need whole records in bulk.

search is a single-term substring. Structured export filters take a filter shape pyTenable's own documentation does not specify, so they are not exposed rather than guessed.

READ-ONLY. High latency on large deployments.

export_plugin_definitionsA

Bulk plugin-definition export via Tenable OT's export API. Returns full plugin records in bulk.

PREFER query_plugin_definitions for interactive lookups. Use this when you need the whole catalog.

READ-ONLY. High latency.

export_findingsA

Bulk export of FINDING INSTANCES — one record per (vulnerability or policy violation x affected asset) — via Tenable OT's official export API.

Choosing between the finding tools: • get_asset_vulnerabilities — the open vulnerabilities on ONE asset. Lowest latency; start here for a specific asset. • query_policy_findings — detection-policy findings, with severity/status/MITRE filters. • this tool — every finding instance in bulk, for offline analysis or reporting across the whole deployment.

search is a single-term substring. Structured export filters take a filter shape pyTenable's documentation does not specify, so they are not exposed rather than guessed.

READ-ONLY. High latency on large deployments.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aqamahn/MCP-server-Tenable-OT-V2'

If you have feedback or need assistance with the MCP directory API, please join our Discord server