Skills define how tools work. This file is for your specifics — the environment details that are unique to your deployment.
Devices are defined in testbed/testbed.yaml. Update that file with your SSH-accessible Cisco devices.
### Example Device Map
- R1 → 10.1.1.1, Core Router, IOS-XE 17.9
- R2 → 10.1.1.2, Distribution Router, IOS-XE 17.9
- SW1 → 10.1.2.1, Access Switch, IOS-XE 17.9
- SW2 → 10.1.2.2, Access Switch, IOS-XE 17.9
All credentials are in ~/.openclaw/.env. Never put credentials in skill files or this document.
### Batfish Configuration Analysis (reference only — actual values in .env)
- Batfish Host → BATFISH_HOST (default: localhost)
- Batfish Port → BATFISH_PORT (default: 9997)
- Batfish Network → BATFISH_NETWORK (default: netclaw)
- Docker Container → batfish/batfish (ports 9997, 9996)
### Connection Details (reference only — actual values in .env)
- pyATS Testbed → PYATS_TESTBED_PATH
- NetBox → NETBOX_URL, NETBOX_TOKEN
- ServiceNow → SERVICENOW_INSTANCE_URL, SERVICENOW_USERNAME, SERVICENOW_PASSWORD
- Cisco APIC → APIC_URL, APIC_USERNAME, APIC_PASSWORD
- Cisco ISE → ISE_BASE, ISE_USERNAME, ISE_PASSWORD
- NVD API → NVD_API_KEY
- F5 BIG-IP → F5_IP_ADDRESS, F5_AUTH_STRING
- Catalyst Center → CCC_HOST, CCC_USER, CCC_PWD
- Microsoft Graph → AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET
- SuzieQ → SUZIEQ_API_URL, SUZIEQ_API_KEY
- gNMI Telemetry → GNMI_TARGETS (JSON), GNMI_TLS_CA_CERT, GNMI_TLS_CLIENT_CERT, GNMI_TLS_CLIENT_KEY
- Azure Network MCP → AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID
- Canvas/A2UI Viz → No new credentials (uses existing MCP server connections)
- Chrome DevTools MCP → No credentials, no env vars at all (config is CLI flags only; auth is via manual browser sign-in)
- Token Optimization → ANTHROPIC_API_KEY (reused), NETCLAW_TOKEN_PRICING_OVERRIDE (optional)
- GitLab MCP → GITLAB_PERSONAL_ACCESS_TOKEN, GITLAB_API_URL (default: gitlab.com)
- Jenkins MCP → JENKINS_URL, JENKINS_AUTH_BASE64 (remote HTTP, Basic Auth)
- Auvik → AUVIK_USERNAME, AUVIK_API_KEY, AUVIK_BASE_URL (optional)
- HaloPSA / HaloITSM → HALO_BASE_URL, HALO_CLIENT_ID, HALO_CLIENT_SECRET, HALO_TENANT, HALO_SCOPE (OAuth2 client-credentials)
- Claroty xDome MCP → CLAROTY_API_URL (default: https://api.medigate.io), CLAROTY_API_TOKEN, CLAROTY_VERIFY_SSL, CLAROTY_TIMEOUT, CLAROTY_RATE_LIMIT_PER_MIN (default: 2000)
- Twitter MCP → TWITTER_API_KEY, TWITTER_API_SECRET, TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_SECRET, TWITTER_HEARTBEAT_ENABLED (default: false)
- Cisco PSIRT MCP → CISCO_CLIENT_ID, CISCO_CLIENT_SECRET (OAuth2 client-credentials via id.cisco.com), CISCO_PSIRT_CACHE_DIR, CISCO_PSIRT_CACHE_TTL_S (default 21600)
- Globalping MCP → GLOBALPING_TOKEN (bearer, remote endpoint mcp.globalping.dev; 401 without it)
For detailed infrastructure notes on specific MCP servers/skills (GitLab, Chrome DevTools, Computer Use, Jenkins, Atlassian, Token Optimization, gNMI, Memory MCP, MemPalace, Twitter, UE5, Sketchfab, Claroty), read TOOLS-REFERENCE.md:
- Not auto-loaded — this file has a size budget, so the verbose per-integration reference material lives separately
- Load with:
read("~/.openclaw/workspace/TOOLS-REFERENCE.md") - Same pattern as
SOUL.mddeferring toSOUL-SKILLS.md/SOUL-EXPERTISE.md— read it when a task needs that level of detail, don't hold it in context otherwise
### Channels
- #netclaw-alerts → P1/P2 critical alerts
- #netclaw-reports → Scheduled health reports, audit results
- #netclaw-general → General queries, P3/P4 notifications
- #incidents → Active incident threads
### Teams Channels (if using Microsoft Graph for Teams delivery)
- #netclaw-alerts → P1/P2 critical alerts, CVE exposure
- #netclaw-reports → Health reports, audit results, reconciliation
- #netclaw-changes → Change request updates, completion notices
- #network-general → P3/P4 notifications, topology updates
### SharePoint Sites
- Network Engineering → Topology diagrams, audit reports, config backups
### Jump Hosts / Bastion
- (your bastion host, if applicable)
### Console Servers
- (your console server, if applicable)
### Sites
- Site-A → Primary data center
- Site-B → DR site
- Lab → Non-production test environment (relaxed change control)
10 MCP tools for hybrid persistent memory combining structured storage, semantic search, and entity graphs:
- Facts:
memory_record_fact,memory_get_facts,memory_invalidate,memory_timeline— temporal key-value storage with automatic supersession - Semantic Search:
memory_store_session,memory_recall— ChromaDB + sentence-transformers for fuzzy session recall - Decisions:
memory_record_decision,memory_get_decisions— audit trail with context, rationale, and CR references - Graph Links:
memory_link_entities,memory_query_graph— entity relationships (peers_with, depends_on, connects_to) - Transport: stdio, Python 3.11+, uvx package, fully offline
- Data:
~/.openclaw/memory/(SQLite + ChromaDB) - No credentials required
10 MCP tools for the offline, user-curated document knowledge base (separate from Memory — RAG holds what USERS upload, Memory holds NetClaw's own experience):
rag_ingest— WHEN a document file on disk should be learnedrag_ingest_base64— WHEN a Slack attachment should be learned (decode → ingest)rag_ingest_url— WHEN the user asks to ingest a web page (always preview crawl scope first)rag_search— WHEN a question concerns vendor procedures, customer standards, install steps, or ingested content (NEVER for live network state or past sessions)rag_list— WHEN the user asks what the knowledge base containsrag_stats— WHEN asked about corpus size/health or retrieval telemetryrag_update_metadata— WHEN a document's doc_type/title/version needs fixingrag_delete— WHEN the user asks to remove a document (confirm with the user first)rag_reindex— WHEN chunking/embedding config changed (confirm with the user first)rag_snapshot— ONLY when the user explicitly asks to store live output for later comparison (confirm scope; never automatic)- Transport: stdio, Python 3.10+, fully offline (hybrid dense+BM25 retrieval, local reranker, cited results)
- Data:
~/.openclaw/rag/(ChromaDB + SQLite + BM25 pickles + retained sources) — never touches~/.openclaw/memory/ - No credentials required
39 MCP tools proxying the local bgp-daemon-v2 HTTP API for claw-to-claw federation over NCFED.
Observability / endpoint hygiene tool (feature 100 — see specs/100-federation-log-observability/):
n2n_forget_endpoint(peer, actor="operator")— WHEN a peer's recorded dial endpoint is known-wrong (it moved, its tunnel rotated, or it will not return) andn2n_healthshows repeated dial failures against it. A permanently-unreachable peer with a stale endpoint was the single largest source of federation log noise (23,366 lines in 7 days before this feature); clearing the endpoint stops it at the source. The peer stays federated and keeps trust material, chat setting and audit history — only the dial address is cleared, and it reconnects automatically the moment it re-registers by contacting this Border. A live channel is left running. Idempotent.- Config (dead-peer log dampening, all optional):
N2N_RECONNECT_DAMPEN(0= full bypass, restores per-attempt WARNING logging for diagnosis),N2N_RECONNECT_DEAD_CEILING_S(default900),N2N_RECONNECT_DEAD_AFTER(default20),N2N_RECONNECT_ENDPOINT_STALE_S(default86400),N2N_RECONNECT_SUMMARY_INTERVAL_S(default300),N2N_RECONNECT_STABLE_AFTER_S(default120) - No new credentials.
Replication-specific tools (feature 065, chroma-to-chroma vector replication — see
workspace/skills/n2n-federation/SKILL.mdfor when/how to use them): n2n_replicate— WHEN the user wants a standing local copy of a consenting peer's RAG collection (not just a one-off answer — usen2n_knowledge_queryfor that). Returns atask_idimmediately; does not block.n2n_replicate_resync— WHEN a previously replicated collection needs refreshing to match the source's current content (full replace, same async pattern)n2n_replicate_delete— WHEN the user wants a local replica removed entirely (distinct from revoking the grant, which only blocks future replication)- Requires a
knowledge_replicagrant vian2n_grant, distinct from theknowledge(query-only) grant feature 064 uses - Config:
N2N_REPLICATION_MAX_CHUNKS(default20000) caps the size of a collection replication will transfer;N2N_REPLICATION_BATCH_SIZE(default200) sizes each page pulled from the source - No new credentials — reuses existing NCFED peer identity/consent state
NetClaw Mobile edge-node tool (feature 066 — see mobile/netclaw-mobile/README.md and specs/066-netclaw-mobile-ncfed-edge/):
n2n_notify_phone(peer, content, kind="text")— WHEN the operator or agent wants to explicitly push a message to an enrolled phone (kind:text/voice/image). Reachable identically from Slack, TUI, HUD, or agent reasoning. NEVER a blanket mirror — only content pushed through this tool ever reaches the phone. Falls back to a platform push notification automatically if the device is disconnected.- Enrollment itself is operator-side, not an MCP tool:
netclaw risk token --edge [label]renders a scannable QR (scripts/netclaw) - Config:
N2N_EDGE_WS_PORT(Border-only, the phone-facing WebSocket listener port);FCM_SERVICE_ACCOUNT_JSON/APNS_KEY_PATH/APNS_KEY_ID/APNS_TEAM_ID/APNS_BUNDLE_ID/APNS_USE_SANDBOX(optional — only needed for the disconnected-device push-notification fallback) - No new credentials for the connected-phone path — reuses the same domain-verified/self-signed credential as eN2N/iN2N (feature 060)
NetClaw Mobile command channel (feature 067 — see specs/067-ncfed-mobile-command-channel/): no new MCP tool. A phone's typed/spoken/QR-triggered request reaches you as a real agent turn over the existing edge connection (n2n/edge/ask, wire-level only) — you answer it the same way you'd answer Slack/CLI, calling n2n_route/n2n_delegate/n2n_invoke/n2n_chat yourself if the question needs a member or a federated peer. Always state plainly whether you answered directly or are relaying a member's/peer's answer — the phone's conversation view has no other way to know. A phone request never carries elevated or reduced trust versus Slack/CLI/TUI.
NetClaw Mobile biometrics and capture (feature 068 — see specs/068-ncfed-mobile-biometrics-capture/): no new MCP tool, two slices:
- Biometric approval: your existing
notify_approvalhook (fired by the same tool/skill/delegation approval flow that already drives the CLI/HUD approval surface) now also pushes to every connected phone (n2n/edge/messagewithcontent_type="approval", wire-level only); the phone's operator resolves it there with device biometrics beforeresolve_approvalruns withvia="biometric"— everything else about the approval (grant/deny semantics, audit trail) is unchanged. - Capture: a phone can attach a photo/video/audio capture to its own
n2n/edge/askrequest (arrives to you as an ordinary multimodal ask). You can also request a capture FROM a phone via the existingn2n_delegate/capability-routing path — an edge node advertisingcamera.capture/camera.record_video/audio.recordin its member scope is selected by the sameRiskRoutermatching used for any other member's capability; a capability the operator disabled in Settings is simply absent from that scope, never a special refusal case.
19 MCP tools for persistent, structured, local-only AI memory across sessions (source):
- Palace: status, wings, rooms, taxonomy, search, duplicates, AAAK spec, add/delete drawers
- Knowledge Graph: entity query, add/invalidate temporal triples, timeline, stats
- Navigation: room traversal, cross-wing tunnels, graph stats
- Agent Diary: write/read specialist agent journals (AAAK-compressed)
- Transport: stdio, Python 3.9+, no credentials, fully offline
MEMPALACE_MCP_SCRIPT→ cloned repomcp_server.py
16 MCP tools for Twitter/X integration — bidirectional (pay-as-you-go tier) via stdio transport:
Posting Tools (9):
- Posting:
twitter_post_tweet,twitter_post_thread,twitter_post_tweet_with_media,twitter_delete_tweet - Rate Limits:
twitter_get_rate_limits— quota monitoring - Heartbeat:
twitter_generate_heartbeat_content,twitter_post_heartbeat— autonomous CCIE-persona tweets (opt-in) - Deduplication:
twitter_check_duplicate,twitter_get_history— 30-day memory-backed history
Bidirectional Tools (7):
-
Mentions:
twitter_get_mentions— fetch @mentions,twitter_classify_mention— categorize intent -
Conversation:
twitter_get_conversation— thread context for context-aware replies -
Reply:
twitter_generate_reply— CCIE-level draft,twitter_reply_to_tweet— post with human approval -
Tracking:
twitter_mark_processed— prevent duplicate handling,twitter_get_user_history— interaction memory -
Content guardrails: IPv4/IPv6 sanitization (RFC 5737/3849), MAC/credential/hostname blocking
-
Human approval required for all replies (Constitution Principle XIV)
-
Spam detection: account age, follower ratio, username patterns, content patterns
-
TWITTER_API_KEY,TWITTER_API_SECRET,TWITTER_ACCESS_TOKEN,TWITTER_ACCESS_SECRET -
TWITTER_MENTION_POLL_INTERVAL— polling frequency (default 300s)
The Unreal Engine 5.8 MCP server is built into UE5.8+ and provides enterprise-grade 3D network topology visualization via HTTP transport. Tool names below are confirmed against a real running UE5 8.0 MCP server (not the originally-assumed names) — see workspace/skills/ue5-network-viz/SKILL.md for the full incident history behind these:
- Tool Search Mode:
list_toolsets,describe_toolset,call_tool— meta-tools for discovering and executing UE5 tools.call_tooltakestoolset_name(full path, e.g.editor_toolset.toolsets.scene.SceneTools) andtool_nameas the short method name only (e.g.add_to_scene_from_class) — passing the fully-qualifiedtoolset.methodstring astool_namesilently returns "Unknown tool" on some builds. editor_toolset.toolsets.scene.SceneTools:add_to_scene_from_class,add_to_scene_from_asset,remove_from_scene,find_actors,load_level,get_current_level— spawn/find/remove device and link actorseditor_toolset.toolsets.actor.ActorTools:set_actor_transform,set_label,add_tag,get_components— position, label, and tag actors.set_actor_transformhas been observed to reset omitted fields (e.g. location, when only scale is set) to(0,0,0)on some builds despite its own docs claiming otherwise — always pass location + rotation + scale together.editor_toolset.toolsets.object.ObjectTools:set_properties,get_property— set mesh/material properties on a spawned actoreditor_toolset.toolsets.asset.AssetTools:load_asset,find_assets,save_assets,create_folder— load basic-shape meshes, manage/Game/folderseditor_toolset.toolsets.programmatic.ProgrammaticToolset:execute_tool_script({"script": "<python>"}) — run a script inside UE5's embedded Python in one MCP round trip instead of one call per actor. Not universally available: some UE5 8.0 builds' script sandbox forbidsimport unreal(only stdlib modules allowed), making this batch path unusable — the skill falls back to per-actor calls automatically when this happens. Still used for config/metrics panels and hierarchical zoom (045-ue5-digital-twin); everything else that used to depend on this (interface actors, labels, the legend, status/traffic/trap/incident/ping coloring, camera control) has since moved to the confirmed-real toolsets below, which are plain MCP calls unaffected by the script sandbox.editor_toolset.toolsets.material_instance.MaterialInstanceTools:create(folder_path, asset_name, parent),set_vector_parameter(instance, name, value),set_scalar_parameter,list_parameters,set_parent,clear_parameters— create/modifyMaterialInstanceConstantassets. Confirmed live 2026-07-03:/Engine/BasicShapes/BasicShapeMaterial(the material this skill's own primitive meshes already use) exposes aColorvector parameter and aRoughnessscalar — the confirmed-working, non-scripted way to recolor a spawned actor (actors.apply_color_to_actor_ref()).EditorToolset.EditorAppToolset:SetCameraTransform/GetCameraTransform,FocusOnActors,CaptureViewport(returns a base64 PNG directly),GetVisibleActors,SelectActors,WorldPosToScreenCoords— real, non-scripted viewport/camera control and screenshot capture.CaptureViewport'sannotationsoverlay config has the same "marked optional but actually required" schema quirk as elsewhere in this list — passcaptureTransformand everyannotationssub-field explicitly (gridSpacing/gridExtent/gridHeight/maxLabelDistance/maxLabelsall0, plus a validclassFilter) to suppress the grid/label overlay.ObjectTools.set_properties/get_propertieson mesh/material properties (staticMesh,overrideMaterials) must target the actor's StaticMeshComponent, not the actor itself. Confirmed live 2026-07-03: settingstaticMeshon the actor reference silently no-ops (get_propertiesafterward still shows"None"); resolving the component first viaActorTools.get_componentsand targeting that works. This had been silently breaking mesh assignment since 044 shipped.- The base primitive meshes (
/Engine/BasicShapes/Cube.Cube,Sphere.Sphere,Cylinder.Cylinder) are already 100cm (1m) per side/diameter atscale=1.0. This codebase assumed 1cm and usedscale=100for "1 meter" devices, actually producing 100-meter devices (confirmed viaActorTools.get_actor_bounds) — the root cause of most rendering-looks-wrong reports throughout 044/045. Any new mesh-scale constant should be verified againstget_actor_bounds()on a live build before trusting a comment's stated intent. - URL:
http://127.0.0.1:8000/mcp(local-only, loopback). Endpoint only accepts POST — a barecurlGET correctly returns HTTP 405, that's the server confirming it's up. - 045-ue5-digital-twin extends the base topology renderer into an interactive digital twin: interface-level actors, live traffic/health/SNMP-trap-driven color state, ping/traceroute animation, on-demand config/metrics panels, PagerDuty incident correlation, historical playback, and NetBox/Infrahub-sourced hierarchical zoom — all orchestrated by
workspace/skills/ue5-network-viz/reusingsnmptrap-mcp,gnmi-mcp, PagerDuty, NetBox, and Infrahub's existing MCP integrations rather than adding new ones. Seeworkspace/skills/ue5-network-viz/SKILL.md's "Digital Twin Capabilities (045)" section for the full command reference. - Requires: UE5.8+ with MCP plugin enabled (Edit > Plugins > "Unreal MCP")
- Auto-start or manually:
ModelContextProtocol.StartServerin UE5 console UE5_MCP_URL→ server endpoint (default:http://127.0.0.1:8000/mcp)- Client note: some builds respond over a keep-alive
text/event-streameven after the real answer has been sent — a client that waits for the full response body to complete (rather than reading the SSE stream line-by-line and stopping at the first complete JSON-RPC object) can hang for the full timeout on an answer that already arrived.
The Sketchfab MCP server (gregkop/sketchfab-mcp-server, vendored at mcp-servers/sketchfab-mcp-server/) provides 3D model search/download for workspace/skills/threejs-network-viz/'s optional real-stencil mode (046-threejs-network-viz, User Story 5) — it is not used by any other skill.
- Tools (3):
sketchfab-search(query/tags/categories/downloadable/limit),sketchfab-model-details(full model metadata including license, given a model ID),sketchfab-download(format gltf/glb/usdz/source, given a model ID) - Transport: stdio (Node.js), registered as
sketchfab-mcpinconfig/openclaw.json - Requires:
SKETCHFAB_API_KEY(get one at https://sketchfab.com/settings/password → API Tokens);SKETCHFAB_USERNAMEis reference/attribution only, not required by the API - Install:
cd mcp-servers/sketchfab-mcp-server && npm install && npm run build(producesbuild/index.js, the fileconfig/openclaw.jsonpoints at) - Patched during 046's implementation: the upstream server's
sketchfab-model-detailstool silently dropped thelicensefield from its formatted text output, even though the real Sketchfab API returns it — confirmed live againsthttps://api.sketchfab.com/v3/models/{uid}. Without it,threejs-network-viz/assets.pyhas no way to verify a candidate model is actually CC0-licensed before using it. Fixed inmcp-servers/sketchfab-mcp-server/index.ts'sformatModelForDisplay()(see the "NetClaw patch" comments) and rebuilt. If this vendored server is ever re-cloned fresh from upstream, this patch must be reapplied and rebuilt before real-stencil mode's license verification will work. - Sketchfab's catalog is mixed-license — the
sketchfab-searchtool has no license filter parameter, so every candidate must be individually verified viasketchfab-model-detailsbefore download (never trustdownloadable: truealone as a license signal). The real Sketchfab API's CC0 license record hasslug: "cc0",uid: "7c23a1ba438d4306920229c12afcb5f9"— confirmed live againstGET /v3/licenses. - Not every downloadable model has a ready-made glTF/GLB export —
sketchfab-downloadsilently substitutes a different format (source/gltf/usdz) when the requestedglbisn't available for that specific model; callers must check the tool's response text for the exact "in glb format." success phrasing rather than assuming success means the requested format was honored. - In practice, CC0-licensed models specific to network equipment are essentially nonexistent on Sketchfab — confirmed via live searches during development ("router", "server rack", "electronic box" all returned zero or irrelevant CC0 results). Procedural-shape fallback in
threejs-network-vizis the expected common case, not a rare edge case.
The Claroty xDome MCP server provides 21 tools (15 read-only + 6 ITSM-gated writes) for OT / IoT / IoMT visibility via stdio transport:
- Assets:
list_devices,get_device_details,get_device_communication_map - Alerts:
list_alerts,get_alert_with_devices - Vulnerabilities:
list_vulnerabilities,get_vulnerable_devices - Sites & sensors:
list_sites,get_site,list_edge_locations - Servers & OT activity:
list_servers,get_server_interfaces,list_ot_activity_events - Governance:
get_audit_log,list_organization_zones - Writes (ITSM-gated, CHG\d+ CR required):
acknowledge_alert,set_vulnerability_relevance,set_device_purdue_level,set_device_custom_attribute,label_alerts,assign_alerts - Default base URL
https://api.medigate.io; Bearer token auth; sliding-window rate gate at 2000 req/min matches the xDome upstream cap; lab-mode bypass viaNETCLAW_LAB_MODE=true(shared with gnmi-mcp). - Edge sensor lifecycle, site CRUD, and organisation policy CRUD are deferred to a future spec — see
specs/035-claroty-mcp/research.md.
- Add whatever helps NetClaw do its job — device nicknames, maintenance windows, ISP circuit IDs, TAC case numbers, anything environment-specific.
- This file is yours. Skills are shared. Keeping them apart means you can update skills without losing your notes.
Outside-in measurement — the only vantage point NetClaw has outside its own administrative domain. Official jsDelivr hosted MCP; no local server by design.
| Tool | Purpose |
|---|---|
ping |
Reachability and round-trip latency from chosen probes |
traceroute |
Path from a probe toward the target |
dns |
Resolution and propagation, per resolver |
mtr |
Per-hop loss and latency together |
http |
Application-layer reachability, status and timing |
limits |
Remaining budget and reset (free — costs nothing) |
locations |
Probe availability, before a narrow filter wastes units |
Three ways to get nothing back, and they are not the same: no_probes_found means the measurement
never ran (widen the filter — never report it as an outage); 0 of N successful means the target
genuinely did not answer (a real finding); a private/internal target is refused locally before any call,
so internal addressing is never transmitted.
Budget: 500 probe-measurements/hour authenticated, 250/hour anonymous per IP, rolling. Charged per
probe — limit: 20 spends 20 — so right-size limit rather than maximising it.
Location syntax: + is AND (London+UK, Amazon+Germany); an array for several places
(["London","Frankfurt"]); world for a global spread; AS3320 for an ASN. A comma inside one string
fails, and AS13335 never returns probes despite being the vendor's own schema example — Cloudflare
hosts none. Only ~1,390 of the internet's ASNs host a probe.
Privacy note: every tool requires a natural-language context field the vendor uses for intent
analytics. NetClaw sends a generic, task-shaped value with no customer name, internal hostname, ticket or
topology detail. limits output echoes a short token fragment — don't paste it into a public channel.
Answers whether a running Cisco version is affected by a published advisory. Read-only,
and it never contacts a device — versions come from pyATS or multivendor-cli.
| Tool | Purpose |
|---|---|
check_version |
Advisories for one (ostype, version) |
check_versions |
A fleet, de-duplicated by version first |
check_cve |
Cisco advisories covering a CVE id |
check_advisory |
One advisory by id |
list_recent |
Advisories by severity over a date range |
psirt_status |
Auth state, rate budget, cache stats, supported families |
An empty result is not a clean bill of health. none_published means Cisco published
nothing for that exact version; normalisation_failed and api_error mean the question
went unasked. Never report any of the three as "not vulnerable".
Version format is per-family and the families contradict each other: iosxe wants
17.3.1 and rejects 17.3(1); ios wants 15.2(4)E and rejects 15.2.4E; nxos wants
9.3(5); asa/ftd/fmc want dotted; aci wants the switch image version 15.2(3e),
not the APIC version. The server converts in whichever direction the family needs.
Not available (measured, not inferred): iosxr → 404, not an OSType on this API;
Bug/EoX/Case/Serial-to-Info → 403 under the API Console grant; CX Cloud → 504.
Rate budget: 5/sec and 30/min shared. Prefer check_versions over looping, and treat
refresh: true as an incident tool — it disables the 6-hour cache.
Reaches ~90 platform families no other NetClaw device server can — MikroTik, VyOS, SONiC, Nokia SR Linux, Extreme, Huawei, Dell, Ubiquiti EdgeOS. Read-only by default.
| Tool | Purpose |
|---|---|
server_info |
Identity, read/write mode, modelled platforms |
check_command_policy |
Would this command pass? No device contacted |
list_devices |
Inventory with source attribution |
check_device_readiness |
Resolvable, authenticable, ours to act on? |
check_reachability |
Separates unreachable / auth_failed / platform_mismatch |
run_command |
Raw CLI, filtered server-side before connecting |
get_facts |
NAPALM normalized facts, one shape across vendors |
run_fleet |
Concurrent fan-out, per-device results |
apply_config * |
Gated write: routing → filter → CR → approval → baseline → verify → rollback |
check_change_request * |
ServiceNow CR authorisation lookup |
* present only when MULTIVENDOR_WRITE_ENABLED is set.
Routing: Cisco → pyATS, Junos → junos-mcp, telemetry → gnmi-mcp. This server owns
everything else, plus cross-vendor normalized reads read-only. Writes are single-pathed per platform.
Dedicated virtualenv: napalm/netmiko resolve cryptography 49.x while the system carries 46.x,
which NCFED uses for X.509 issuance.
Spec 080 / roadmap R3. Three planes, 21 tools, stdio, read-only by default.
Replaces an earlier fortimanager-ops skill that named jmpijll/fortimanager-mcp —
a server that was never vendored, registered, or installable.
| Plane | Appliance | Answers | Transport |
|---|---|---|---|
manager |
FortiManager | policy intent — ADOMs, packages, objects, revisions | JSON-RPC /jsonrpc |
device |
FortiGate | observed state — interfaces, routes, VPN, HA, VDOM | REST, bearer token |
analyzer |
FortiAnalyzer | observed traffic — logs, policy activity | JSON-RPC /jsonrpc |
FortiManager and FortiAnalyzer share one JSON-RPC client — same endpoint, same envelope, different methods.
FORTINET_MCP_CMD · FORTIMANAGER_HOST / FORTIMANAGER_API_TOKEN ·
FORTIGATE_HOST / FORTIGATE_API_TOKEN · FORTIANALYZER_HOST /
FORTIANALYZER_API_TOKEN · FORTINET_VERIFY_SSL (default true) ·
FORTINET_ALLOW_WRITES (default false)
Each plane is independently optional; an unconfigured plane is not consulted and NetClaw says so rather than answering from another.
- Every response carries
planeandscopestructurally and is GAIT-audited — enforced at a chokepoint, so a new tool cannot omit either. - "No logs matched" is not "rule unused" — returns
no_logs_in_window, its own outcome. - VPN phase 1 and phase 2 are always separate fields. Phase 1 up / phase 2 down is a specific fault, not "half up".
fgt_compare_with_managerreports intent-vs-state divergence;only_in_deviceentries are candidate out-of-band changes.- Writes need two gates: human approval and an approved ServiceNow CR. Neither substitutes for the other.
monitor/system/interfacereturns a dict keyed by interface name, not a list.- An unregistered FortiGate returns 401 for every REST request regardless of
token validity or trusthost. Check
License Status: Validbefore suspecting credentials. - FortiOS 8.0.0 GA has a web-GUI logout loop on the 1 vCPU trial profile
(
VM resource exceeds license limit→httpsdrestart). SSH and REST unaffected; 7.4/7.6 do not exhibit it. - Evaluation licence caps: 1 vCPU, 2 GB RAM, 3 interfaces, 3 routes, 3 policies.
Spec 081 / roadmap R9. 10 tools, stdio, read-only, no credentials. The other half of the external plane: R8's Globalping measures toward a target; this looks up ownership, routing legitimacy and peering.
| Source | Provides |
|---|---|
rpki-validator.ripe.net |
RPKI origin validation (primary — RFC 6811 vocabulary, returns VRPs) |
stat.ripe.net |
RPKI fallback, AS overview, announced prefixes, visibility |
| IANA bootstrap → RIR RDAP | Registry ownership, abuse contacts |
peeringdb.com |
IXPs, facilities, peering policy |
atlas.ripe.net |
Anchors, per-AS probe counts |
state |
reason |
Finding? | Meaning |
|---|---|---|---|
valid |
— | no | A ROA authorises this origin |
invalid |
as |
yes | A ROA covers it; a different AS is authorised |
invalid |
length |
yes | Correct AS; prefix more specific than maxLength |
not_found |
— | no | No ROA exists. The normal case for most of the internet |
validation_unavailable is a separate outcome — an unreachable validator is not not_found.
BGP_INTEL_MCP_CMD · BGP_INTEL_USER_AGENT · BGP_INTEL_MAX_RPS (default 4) · BGP_INTEL_AUDIT_LOG
No API keys. Every source is public and unauthenticated.
- Every response carries
source+retrieved_atand is GAIT-audited — enforced at a chokepoint. no_recordandsource_unavailableare never conflated — a dead API is not an empty registry.- Registry data is allocation, not routing; PeeringDB is self-reported; visibility is RIPE's
collectors, not global truth. Each is stated in the response
caveats. - 4 req/s per source, true sliding window, strictly serial. Self-imposed — neither RIPEstat nor
PeeringDB publishes rate-limit headers. Parallel fan-out prohibited, including inside
resource_report. - Private/reserved/bogon input is refused locally with no outbound request — a disclosure control.
- Manifest measured at 1,376 / 5,000 tokens.
Spec 082 / roadmap R18. 6 tools, stdio, no credentials. Writes files; touches no device and no ticket, so there is no approval gate here. This is the deliverable layer — every other NetClaw capability produces findings, this turns a finding into something you can attach to a change record.
| Format | Tool | Built from |
|---|---|---|
.docx |
docx_write |
Ordered blocks: heading, paragraph, figure, table, keyvalue, image, pagebreak |
.xlsx |
xlsx_write |
Sheets of tagged rows, plus failed_rows for devices that could not be reached |
.pptx |
pptx_write |
Slides: bullets, figure, image |
.pdf |
pdf_inspect_form / pdf_fill_form |
An existing fillable form's named fields |
| — | list_documents |
Finding something generated earlier |
A document must never fabricate to fill a blank. Tool output is ephemeral; a document is emailed, filed
and read months later by someone who was not there, and it carries the authority of its formatting. So every
value is one of three tagged shapes — {"v":…, "src":…}, {"unavailable": reason}, {"failed": reason} —
and a bare scalar or a value with no src is refused. There is no way to express "missing" as a blank.
DOCUMENT_MCP_CMD · DOCUMENT_OUTPUT_DIR (default workspace/output/document-mcp/) ·
DOCUMENT_MAX_ROWS (50000) · DOCUMENT_MAX_BLOCKS (5000) · DOCUMENT_MAX_SLIDES (200) ·
DOCUMENT_AUDIT_LOG
No API keys. Nothing to rotate.
- Provenance is visible, never hidden. Source column per table row, per-figure parenthetical in prose, a visible source box on every slide, and a Sources section in every file. Word comments, document metadata and speaker notes are written additively but never count — they are collapsed by default, stripped on paste, and absent in print.
python-docxhas no footnote API (measured), so.docxattribution is inline. More visible than a footnote, not less.- openpyxl writes a leading
=as a live formula. Measured:ws["A1"] = "=1+1"produces<c r="A1"><f>1+1</f>…. Every string cell is forced toinlineStr, so a FortiGate interface description or a ServiceNow short-description cannot put executing content into an auditor's spreadsheet. - Admin and operational state must be separate columns. A merged
statuscolumn is refused — the distinction spec 080's completion established. - Failed devices are rows, not omissions. A shorter spreadsheet reads as a smaller estate. The banner reports attempted / returned / failed.
- Sources that disagree are both rendered with their origins and a caveat. NetClaw does not pick a winner.
- Office templates are refused, not ignored — scratch-only, because a template's empty field is the strongest fabrication pressure in the feature. PDF forms are supported precisely because their fields are explicitly named and machine-readable.
- A filled PDF carries no Sources section — it is the customer's document. For that one format provenance lives in the response and the GAIT record. Stated rather than papered over.
- Files are never overwritten.
O_EXCLcreate with a collision suffix, so a regenerated report cannot replace one already attached to a ticket. An unwritable output directory is a reported failure with no temp-directory fallback. okmeans complete. Any gap forceswritten_with_gaps; a caller cannot report a gapped document as clean.- Every call, including refusals, is GAIT-audited at the chokepoint.
- Manifest measured at 1,232 / 5,000 tokens.
drawio-diagram / markmap-viz / uml-diagram / threejs-network-viz produce diagrams — this
embeds them and never redraws. rag-mcp (feature 062) reads these formats for ingestion; this
writes them, sharing the same four libraries with identical bounds.
servicenow-change-workflow owns the CR lifecycle; this renders a document from one.
slack-report-delivery / webex-report-delivery send documents; this only writes them.
Spec 098 / roadmap R25. 4 tools, stdio, read-only, own virtualenv. Manifest measured 1,272 / 5,000 tokens for a 208-test catalogue.
The assertion layer: every other source reads state, this one asserts on it.
| Tool | Purpose |
|---|---|
anta_list_tests(category, keyword) |
Search the 208-test catalogue — contacts no device |
anta_describe_test(test) |
One test's description and input schema — contacts no device |
anta_run_tests(host, tests|category, inputs) |
Run tests against one EOS device |
anta_status() |
ANTA version, catalogue size, credential state |
pass / fail / not_applicable / skipped / error, counted separately.
The reclassification that matters: ANTA reports a test for an unconfigured feature as a
failure. Measured — VerifyBGPPeerCount on a device with no BGP returns
"'show bgp summary vrf all' failed: BGP inactive". Counted naively that claims a BGP fault on a box
with no BGP. The server reclassifies to not_applicable, keeps the original message, and the rule is
deliberately narrow so a real failure is never hidden.
No health percentage is emitted — passed/total is meaningless with not_applicable and
skipped in the denominator. The helper raises rather than computing one.
ANTA pulls cryptography 50.0.0 while the system holds 46.0.5 with four unbounded dependents
(Authlib, pygnmi, service-identity, sshsig) including NetClaw's federation TLS stack. Measured
by dry-run before installing — spec 076's cryptography incident.
Credentials: ANTA_USERNAME / ANTA_PASSWORD, environment only. ANTA_VERIFY_TLS defaults to
false and is always disclosed in output as tls_verified.
Spec 096 / roadmap R12. 5 tools, stdio via Docker, read-only. Manifest measured 1,094 / 5,000 tokens. NetClaw installs no cluster — this queries one the operator already runs (8.x/9.x).
| Tool | Purpose |
|---|---|
list_indices(index_pattern) |
Indices, status, document counts |
get_mappings(index) |
Field names and types — read before composing a query |
search(index, query_body) |
Query DSL retrieval (and aggregations) |
esql(query) |
ES-QL — counting, grouping, ranking |
get_shards() |
Shard allocation and health |
Elasticsearch caps hits.total at 10,000 and marks it relation: "gte". This server discards the
qualifier, printing a bare Total results: 10000 that is indistinguishable from an exact count.
Measured against 10,075 documents: unguarded search said 10000; esql and
search + track_total_hits: true both said 10075. The error is unbounded — a million-document
index still reports 10,000.
Count with esql or track_total_hits. An unguarded search retrieves example documents only.
Elastic deprecated this server in favour of Agent Builder's MCP endpoint, which is Enterprise-tier on
self-managed — so the supported path is paywalled and this one is not. Apache-2.0 and already
published, so it cannot be withdrawn. The image is pinned by digest
(sha256:d57ea11d…eb003) so a security-only update cannot change answers underneath the operator.
ES_URL resolves inside the container: a cluster on the host is http://host.docker.internal:9200,
never localhost. Credentials: ES_API_KEY (scope it read + view_index_metadata) or
ES_USERNAME/ES_PASSWORD.
Spec 083 / roadmap R11. 3 tools, stdio, read-only. Manifest measured 589 / 5,000 tokens — the smallest surface NetClaw has added for an entire product category.
This is the polled-history layer. Everything else NetClaw sees arrives when something happens — syslog, traps, flows. This is the only source that can answer what was it doing, is this normal, how long has it been down.
| Tool | Purpose |
|---|---|
zabbix_api(method, params) |
Generic JSON-RPC passthrough |
zabbix_api_docs(method) |
Upstream method documentation |
zabbix_api_list(object) |
Available methods for an object |
mpeirone/zabbix-mcp-server, pinned 0722f48, unmodified, GPL-3.0 retained verbatim. NetClaw invokes it
over stdio as a separate program; that is not linkage.
It requires fastmcp 3.x while five NetClaw servers pin <3 (netbox-mcp-server,
CiscoFMC-MCP-server-community, Wikipedia_MCP, rag-mcp, ISE_MCP). It therefore runs from a dedicated
virtualenv — the same reason multivendor-cli-mcp has one. Do not "simplify" that away.
ZABBIX_MCP_CMD · ZABBIX_URL · ZABBIX_TOKEN · VERIFY_SSL · READ_ONLY (forced true) ·
ZABBIX_API_BLACKLIST
Both measured against live Zabbix 7.0.29. Both silent — no error, no warning.
history.getdefaults its value type to unsigned (3), and 84 of 121 stock items are float (0). Ask with the default and you get[]. Always callitem.getfirst and pass the item's realvalue_type. Types cannot be mixed in one call — measured: 4 items, 2 returned each way, zero overlap.- Raw history ages out into hourly trends. A question older than the history window returns nothing
from
history.get.item.getreports per-itemhistoryandtrends; read them and route.
Retention can also be switched off: history=0 means raw values are never stored; trends=0 means no
aggregates. Measured on a stock install: 10 items with trends=0, 5 with both zero. That is a
configuration fact, not an absence.
- Read-only is FORCED by NetClaw, not inherited. The upstream library defaults it safe
(
utils.py:29→ True) but the shipped launcher inverts it (start_server.py:139→ False). A destructive-method deny-list is configured as a second layer and holds even with read-only disabled — verified. - Read/write classification upstream is a method-name prefix heuristic (
get,version,check,export), not a curated list. That is why the deny-list exists. - The two traps are enforced by the SKILLS, not by code. This is a generic passthrough with no chokepoint — the first NetClaw integration where a core distinction is guidance-level. Deliberate, and recorded.
- No per-call GAIT audit. The upstream has no audit concept and there is no platform-level MCP audit. Acceptable only because this is strictly read-only — there is no operation to record.
- Auth is API-token/bearer. The in-request credential property still works on 7.0 but is removed in 7.2+.
snmptrap-mcp receives traps; this polls and keeps history. ipfix-mcp is flows, not counters.
prometheus/grafana are pull-based stores for infrastructure you instrumented; this is the NMS for gear
you did not. auvik/thousandeyes/datadog are SaaS with their own agents. pyats/multivendor-cli/
fortinet read current device state; this answers what it was over time, and can answer for a
device that is unreachable right now.
Spec 084 / roadmap R14. 7 tools, stdio, strictly read-only. Manifest measured 1,643 / 5,000 tokens.
kubeshark sees packets inside a cluster; this reads the objects — pods, services, ingresses,
EndpointSlices and NetworkPolicies.
| Tool | Purpose |
|---|---|
resources_list / resources_get |
Any apiVersion+kind — NetworkPolicy, Service, Ingress, EndpointSlice, CRDs |
pods_list / pods_list_in_namespace / pods_get |
Workload inventory |
namespaces_list |
Establish which namespaces exist — needed to tell "no such namespace" from "empty" |
events_list |
The why behind a pod status |
Apache-2.0 (identical to NetClaw's) and a statically linked Go binary — zero runtime deps, so it
cannot collide with the fastmcp<3 pins. Pinned and verified against a recorded SHA-256; not committed.
The DEFAULT config is 21 tools / 5,716 tokens and busts the ceiling. Trimming to core + 6
disabled_tools is what makes adoption possible.
Given a credential without cluster-wide list permission the server does not error — it rewrites the query to one namespace and returns it plainly:
raw kubectl → Forbidden: cannot list networkpolicies at the cluster scope
this server → success, 1 policy ← the cluster had 2
resources.go:34-38 narrows on denial and discards the permission error, so an API blip looks the same
as a 403. Mitigated by a mandated cluster-wide-read ServiceAccount (makes the branch unreachable —
verified) plus a skill preflight (can-i before trusting any empty result).
- No NetworkPolicy means all traffic is permitted. Kubernetes is default-allow, so "no policies" is a finding, not a neutral observation.
- An empty list has six causes: insufficient permission · no such namespace · empty namespace · selector matched nothing · CRD not installed · cluster unreachable. A typo'd selector returns HTTP 200 with zero rows, identical to a genuine non-match — so the selector must be shown.
- Secrets denied at two layers — server
denied_resourcesand the ServiceAccount RBAC. - The kubeconfig must be explicit and token-only. A kubeconfig carrying a client certificate silently
ignores the token. Every candidate otherwise defaults to the ambient
current-context, possibly production. - No per-call GAIT audit.
--log-fileexists but at level 4 logs lifecycle only — no tool calls. - Reachable is not permitted.
kubesharkshows traffic that flowed; this shows what is declared.
kubeshark-traffic = observed traffic · prometheus/grafana = metrics ·
containerlab/gns3/cml = building labs · this = the declared object model, read-only.
Spec 087. 10 tools, stdio, strictly read-only. Manifest measured 1,821 / 5,000 tokens — with all 514 read-only operations reachable.
Cisco released an official Catalyst Center MCP server whose default bundle measures 515 tools / 64,420
tokens — 12.9x the ceiling. NetClaw adopts its catalogue (Apache-2.0, release/2.3.7.11), not its
runtime, and fronts it with 8 grouped dispatchers plus catc_find and catc_describe_operation.
| Tool | Use |
|---|---|
catc_find |
Start here — search all 514 operations locally. Names are generated, not guessable |
catc_describe_operation |
Parameter schema on demand |
catc_devices catc_sites catc_wireless catc_health catc_compliance catc_software catc_events catc_other |
(operation, params) |
Avoids three upstream properties at once: fastmcp>=2.0.0 unbounded (resolves 3.x against five servers
pinning <3 — the third occurrence of the spec-083 hazard), HTTP transport on port 7001 (every other
NetClaw MCP is stdio), and a container needed only to isolate the first. Dependencies here are mcp and
httpx.
- An empty inventory is not an empty network. Zero devices is a statement about the controller. Every
response is stamped at a chokepoint with which appliance answered and when — because the two
DevNet sandboxes share credentials and
sandboxdnac2has zero devices while authenticating perfectly. - Zero counts carry the same caveat as empty lists. A bare
0reads even more like data; found by live testing. unreachable,auth_failedandemptyare three different facts. Keeping them apart required a real fix —httpx.HTTPStatusErrorsubclasseshttpx.HTTPError, so a 401 initially surfaced asunreachable.- Read-only by curation: only GET operations are catalogued and the single upstream POST is excluded, so it cannot be dispatched. Upstream states it enforces no read-only access; curation plus account RBAC are the two controls.
- "Catalyst Center says unreachable" is not "the device is down" — one controller's last poll.
- Upstream is version-coupled: the branch name is the appliance version, and
maincontains no code.
pyats/multivendor-cli read the device (and win on disagreement) · netbox/nautobot hold intent, this
reports discovery · devnet-catalyst-search reads docs, this queries an appliance.