
Tenable OT Security MCP Server
ContributedTenable OT Security asset, vulnerability, event and policy tools, with an opt-in dry-run-by-default write layer.
Tools (59)
- tenable_ot_status
Check Tenable OT connectivity
- query_assets
Query OT assets
- get_asset
Get one OT asset
- get_asset_vulnerabilities
Get vulnerabilities for one asset
- list_custom_fields
List configured custom fields
- get_manual_asset_upload_status
Get manual asset-upload processing status
- query_attack_pathways
Query attack-pathway data (relational, not computed)
- query_vulnerability_clusters
Query vulnerability clusters (relational join, not computed)
- query_temporal_patterns
Query temporal patterns (event sequence, not motif analysis)
- get_asset_intelligence
Get asset intelligence bundle (joined data, not narrative)
- query_events
Query OT events
- get_event
Get one OT event
- export_assets
Bulk export assets
- export_plugin_definitions
Bulk export plugin definitions
- export_findings
Bulk export findings
- query_plugin_definitions
Query plugin (vulnerability) definitions
- get_plugin_definition
Get one plugin definition
- list_detection_policies
List detection policies
- get_policy
Get one policy's full configuration
- query_policy_findings
Query policy findings
- get_schema_enums
Introspect the Tenable OT GraphQL schema
- list_sensors
List sensors
- summarize_environment
Summarize the OT environment
- list_segments_and_zones
List network segments and zones
- get_communication_paths
Get communication paths for an asset
- query_vulnerabilities
Query OT vulnerabilities
- get_vulnerability
Get one vulnerability
- list_asset_groups
List asset groups (active)
- list_archived_asset_groups
List archived asset groups
- get_asset_group
Get an asset group by id
- list_email_groups
List email groups
- get_email_group
Get an email group by id
- find_email_groups_using_smtp_server
Find email groups that route through a given SMTP server
- list_schedule_groups
List schedule groups
- list_archived_schedule_groups
List archived schedule groups
- get_schedule_group
Get a schedule group by id
- list_tag_groups
List tag groups
- get_tag_group
Get a tag group by id
- list_eligible_tags
Discover tags eligible for a tag group
- list_rule_groups
List rule groups
- list_archived_rule_groups
List archived rule groups
- get_rule_group
Get a rule group by id
- list_port_groups
List port groups
- list_archived_port_groups
List archived port groups
- get_port_group
Get a port group by id
- list_protocol_groups
List protocol groups
- list_archived_protocol_groups
List archived protocol groups
- get_protocol_group
Get a protocol group by id
- list_user_groups
List user groups (ICP-level)
- list_archived_user_groups
List archived user groups (ICP-level)
- get_user_group
Get a user group by id (ICP-level)
- list_em_user_groups
List user groups (Enterprise Manager level)
- list_em_archived_user_groups
List archived user groups (Enterprise Manager level)
- get_em_user_group
Get a user group by id (Enterprise Manager level)
- list_active_scans
List active scans
- get_active_scan
Get one active scan
- get_active_scan_executions
Get past executions of an active scan
- list_network_config
Read what the appliance is configured to monitor
- list_policy_relationships
Check what references an object before changing it
An MCP server that connects any MCP client — Claude Desktop, Claude Code, or another — to a Tenable OT Security deployment, exposing OT/ICS asset inventory, vulnerabilities, events, detection policies and policy findings as read tools, plus an opt-in write layer for asset edits, group and policy management, scan definitions and finding resolution.
Because this server talks to industrial control systems, its safety model is structural rather than advisory: writes are invisible until explicitly enabled, every write previews before it acts, destructive operations need a second confirm flag, and every write attempt is recorded to an append-only audit log.
Not an official Tenable product. Copyright (c) 2026 Tenable, Inc., released under Apache 2.0, but shipped with no SLA, no warranty and no support commitment, and not covered by Tenable’s product security or vulnerability-disclosure processes.
What it does
59 read tools, always available. Asset inventory with identity, IPs/MACs, Purdue level, segment and backplane membership and aggregate risk; fleet-wide and per-asset vulnerability search with CVE, CVSS and exploit/KEV flags; OT events; detection policies and their per-asset findings; the plugin (vulnerability) definition catalog; and bulk exports of assets, findings and plugin definitions via pyTenable.
Beyond raw inventory it covers the environment’s shape and its dependencies:
network segments and zones as observed, per-asset communication paths, sensor
inventory, and list_network_config — what the appliance is configured to
watch, which flags disabled subnets as monitoring blind spots and is the usual
answer to “why is this asset missing”. list_policy_relationships answers
“what breaks if I change this?” before a write lands, which matters because
Tenable OT’s policy mutations are full replaces, not patches.
summarize_environment gives a one-call orientation on an unfamiliar
deployment, and get_schema_enums introspects the appliance’s own GraphQL
schema.
71 write tools, only when TENABLE_OT_WRITE_TOOLS_ENABLED=1. Asset
lifecycle (hide/restore, bulk variants, removal by address, merge, risk
recalculation, manual file upload), asset properties and custom-field schema,
detection policy enable/disable/archive and full policy editing, finding
resolution, group management across asset / email / schedule / tag / rule /
port / protocol / user groups, sensor management, and scan definition
create/edit.
Nothing in the codebase runs a scan. There is no run, start, or execute
mutation anywhere in it — definitions can be written, and enable_active_scan
sets whether a definition participates in runs, but only a human starting a
scan from the Tenable OT UI ever launches one against live equipment.
How it works
A Python 3.12+ server on the MCP SDK, wrapping Tenable OT’s GraphQL API
directly with httpx for everything Tenable doesn’t officially cover, and
bridging to pyTenable for the bulk-export surface. Most paginated read tools
are declared as data — a ConnectionSpec record naming a query and a field
projection — and one registrar turns specs into tools, so the pagination and
projection plumbing exists and is tested once rather than being re-implemented
per tool. Composite tools that fan out or join across queries stay
hand-written. Queries whose availability varies by appliance are marked
unconfirmed and return an actionable error envelope naming what to
introspect, instead of raising.
The four safety properties are enforced in the architecture, not by convention:
- Write tools are absent, not merely refused. Without the opt-in
environment variable the write modules are never imported, so no write tool
name appears in the MCP
tools/listresponse. A client cannot call — or discover — an operation that wasn’t enabled. - Every write defaults to
dry_run=True, returning the exact payload that would be sent without sending it, so the operator approves a concrete preview before anything changes. - Destructive operations require a second explicit flag.
dry_run=falsealone is insufficient;delete_custom_field, for instance, is rejected withoutconfirm_wipes_values=True. - Every write call is audited — dry-run, live, or failed — as one JSON line recording timestamp, tool, parameters, dry-run flag, outcome and error. The audit writer fails closed: if it cannot record, the call raises rather than proceeding silently.
Credentials come from the environment only. The API key is read from
TOT_API_KEY, held privately by the client, and is never logged, never written
to disk by the server, and never included in an audit entry; uploaded file
contents are never audited either, only filename and byte size.
Ships with a Dockerfile and compose file. The container entrypoint fixes
bind-mount ownership for the audit directory and proves the target uid can
actually write there before permanently dropping from root via setpriv — a
failure mode that otherwise surfaces late, only on the first write. The test
suite runs entirely against a mocked client with no network, appliance or
credentials, covering dry-run paths, live paths, audit-entry assertions and
confirm-flag rejection paths.