{
  "markdown": "<!-- mcp-name: io.github.AIops-tools/fabric-aiops -->\n\n# Fabric AIops\n\n> **Disclaimer**: Community-maintained open-source project. **Not affiliated with, endorsed by, or sponsored by Cisco, Meraki, Arista, Ubiquiti, or any network-controller vendor.** \"Cisco\", \"Meraki\", \"Catalyst\", \"DNA Center\", \"Arista\", \"CloudVision\", \"Ubiquiti\", \"UniFi\" and all product/trademark names belong to their respective owners. MIT licensed.\n\nGoverned AI-ops for **network fabrics** managed through a controller — the\n**Cisco Meraki Dashboard API** (the reference platform, full read + write),\n**Cisco Catalyst Center** (formerly DNA Center; read subset), **Arista\nCloudVision Portal (CVP)** (read subset), and **UniFi Network** (self-hosted\ncontroller or UniFi OS console; read subset + device restart) — with a\n**built-in governance\nharness**: unified audit log, token/runaway budget guard,\nundo-token recording, and descriptive risk tiers. **Multi-platform by\nconstruction**: a registry keyed by `platform` maps every *canonical operation*\nonto each controller's REST API (path templates + response adapters), so adding\na controller is a registry entry, never new ops/CLI/MCP surface. An operation a\nplatform doesn't map returns a clear teaching error (\"not supported on X yet —\nopen an issue\"), never a silent no-op. The test suite is mock-based; no\nplatform has yet been exercised against a live controller — see\n[`docs/VERIFICATION.md`](docs/VERIFICATION.md).\n\n## What it does\n\nThree flagship signature analyses, plus the guarded reads and writes around them:\n\n- **Uplink loss & latency RCA** — pull MX WAN uplink loss + latency across an\n  org, rank the worst uplinks by a composite of average loss and latency, and\n  map each degraded uplink to a likely cause + recommended action. Every ranking\n  carries its numbers, not a black-box verdict.\n- **Network health score** — a composite 0-100 score per network from device\n  online %, uplink health %, and an alert-severity penalty (weighted 0.5/0.3/0.2),\n  with every component returned so the number is explainable.\n- **Config template drift** — for networks bound to a config template, list the\n  settings that have drifted from the template (expected vs actual).\n\n## What works\n\n- **CLI** (`fabric-aiops ...`): `init`, `overview`, `org`, `network`, `device`, `client`, `health`, `remediate`, `secret`, `doctor`, `mcp`.\n- **MCP server** (`fabric-aiops mcp` or `fabric-aiops-mcp`): **34 tools** (25 read, 9 write), every one wrapped with the bundled `@governed_tool` harness.\n- **Encrypted credentials**: the controller secret (Meraki API key / Catalyst Center `username:password` / CVP service-account token / UniFi API key) lives in an encrypted store `~/.fabric-aiops/secrets.enc` (Fernet + scrypt) — **never plaintext on disk**. Unlock with a master password from `FABRIC_AIOPS_MASTER_PASSWORD` (MCP/CI) or an interactive prompt (CLI).\n- **Reversibility**: mutating writes fetch the **real before-state first** and record a faithful inverse (`update_device`/`update_network_vlan` restore prior values; `claim`↔`remove`; `bind`↔`unbind`/rebind). Irreversible ops (`reboot_device`, `blink_device_leds`) record the prior state for audit but declare no undo.\n- **Safety**: every state-changing CLI op supports `--dry-run` and requires double confirmation; every write MCP tool takes a `dry_run` preview.\n\n## Capability matrix (34 MCP tools)\n\n| Domain | Tools | Count | R/W |\n|--------|-------|:-----:|:---:|\n| **Overview** | `overview` | 1 | read |\n| **Organizations** | `org_list`, `org_get`, `org_licensing`, `org_admins`, `org_device_statuses`, `org_api_requests` | 6 | read |\n| **Networks** | `network_list`, `network_get`, `network_vlans`, `network_alerts`, `network_traffic` | 5 | read |\n| **Devices** | `device_inventory`, `device_status`, `device_uplinks`, `switch_ports`, `wireless_ssids` | 5 | read |\n| **Clients** | `client_list`, `client_get`, `client_usage`, `client_connectivity` | 4 | read |\n| **Health (flagship)** | `uplink_loss_and_latency_rca`, `network_health_score`, `config_template_drift` | 3 | read |\n| **Remediation** | `reboot_device`, `claim_devices_into_network`, `remove_device_from_network`, `bind_network_to_template`, `unbind_network_from_template` | 5 | write (high) |\n| | `update_device`, `update_network_vlan` | 2 | write (medium) |\n| | `blink_device_leds` | 1 | write (low) |\n| **Undo** | `undo_list` | 1 | read |\n| | `undo_apply` | 1 | write (medium) |\n\n`network_health_score` and `config_template_drift` are injected-only (they score\ndata you already hold); `uplink_loss_and_latency_rca` accepts injected `records`\nfor offline analysis or pulls live from a configured target. Device models carry\na product-type prefix: **MX** appliance, **MS** switch, **MR** wireless AP, **MV**\ncamera, **MG** cellular gateway.\n\n## Platform support matrix\n\nOne tool, four controller platforms. The ops/CLI/MCP surface is identical\neverywhere; each platform maps the canonical operations it supports and raises\na teaching error for the rest (\"not supported on `<platform>` yet — open an\nissue or PR\").\n\n| Canonical operation | meraki | catalyst | cvp | unifi |\n|---------------------|:------:|:--------:|:---:|:-----:|\n| `overview` (org/site/container rollup) | ✅ | ✅ | ✅ | ✅ |\n| `org_list` / `org_get` | ✅ | ✅ sites | ✅ containers | ✅ sites (list; get ❌) |\n| `org_licensing`, `org_api_requests` | ✅ | ❌ | ❌ | ❌ |\n| `org_admins` | ✅ | ❌ | ✅ users | ❌ |\n| `org_device_statuses` | ✅ | ✅ device-health | ✅ inventory + streaming status | ✅ stat/device (state, uptime, firmware) |\n| `network_list` / `network_get` | ✅ | ✅ site-health / site | ✅ containers | ✅ sites / stat/health (subsystem rollup) |\n| `network_vlans`, `network_traffic` | ✅ | ❌ | ❌ | ❌ |\n| `network_alerts` | ✅ | ✅ issues (P1→critical, P2→warning) | ✅ events | ✅ alarms (*_Lost_Contact→critical) |\n| `device_inventory`, `device_status` | ✅ | ✅ network-device | ✅ inventory (+ complianceCode drift signal) | ✅ stat/device (id = device **MAC**) |\n| `device_uplinks` | ✅ | ❌ | ❌ | ❌ |\n| `switch_ports` | ✅ | ✅ interface stats (pass the device **uuid**) | ❌ | ✅ device `port_table` (pass the device **MAC**) |\n| `wireless_ssids` | ✅ | ❌ | ❌ | ❌ |\n| `client_list` / `client_get` | ✅ | ✅ client-health (aggregate) / client-detail (by MAC) | ❌ | ✅ stat/sta (connected) / stat/user (by MAC) |\n| `client_usage`, `client_connectivity` | ✅ | ❌ | ❌ | ❌ |\n| `uplink_loss_and_latency_rca` (live pull) | ✅ | ❌ (injected `records` still work) | ❌ (injected `records` still work) | ❌ (injected `records` still work) |\n| `network_health_score`, `config_template_drift` (injected-only) | ✅ | ✅ | ✅ | ✅ |\n| `reboot_device` | ✅ | ❌ teaching error | ❌ teaching error | ✅ `cmd/devmgr` restart-device |\n| **The other 7 writes** (blink/update/claim/remove/bind/unbind/VLAN) | ✅ | ❌ teaching error | ❌ teaching error | ❌ teaching error |\n\nConcept mapping: canonical *organizations/networks* are Catalyst Center\n**sites**, CVP **containers**, and UniFi **sites** (the canonical id is the\nsite's short name — the `/api/s/{site}/` path segment); all have one global\ntree, so the org scope does not filter their lists. On unifi, device-scoped\ncalls (`device get` / `switch_ports` / `reboot`) fill the site from the\ntarget's default `org_id` — set it in config.yaml (or the init wizard). Writes\nare **Meraki-only except UniFi device restart** — Catalyst Center and CVP\nchange models (task/configlet workflows) don't map cleanly onto these\ncanonical writes, so each write fails fast with a teaching error *before* any\ncontroller call (never a silent no-op). CVP config-drift surfaces through\n`device_inventory` (`complianceCode`/`complianceIndication` per device) and\n`network_alerts` (events); configlet-content retrieval and deep pagination on\ncatalyst/cvp/unifi are known deferrals.\n\n### Per-platform auth\n\n| Platform | `platform:` | Secret stored (encrypted) | Auth on the wire | Base URL |\n|----------|-------------|---------------------------|------------------|----------|\n| Cisco Meraki Dashboard | `meraki` | API key (Dashboard → Organization → Settings → API access) | `Authorization: Bearer` (or `auth_style: meraki-key` → `X-Cisco-Meraki-API-Key`) | default `https://api.meraki.com/api/v1` |\n| Cisco Catalyst Center | `catalyst` | `username:password` (one string) | exchanged via `POST /dna/system/api/v1/auth/token` (HTTP Basic) for a ~1 h `X-Auth-Token`, auto-refreshed once on a 401 | required, e.g. `https://<catalyst-center-host>` |\n| Arista CloudVision Portal | `cvp` | service-account token (Settings → Access Control → Service Accounts) | `Authorization: Bearer` | required, e.g. `https://<cvp-host>` |\n| UniFi Network | `unifi` | API key (UniFi OS: Settings → Control Plane → Integrations; self-hosted Network Server 9.0+) | `X-API-KEY` (stateless; legacy cookie login is a known deferral) | required — classic controller `https://<host>:8443`, or UniFi OS console `https://<console>/proxy/network` (keep the prefix) |\n\n`fabric-aiops init` walks through the platform choice and stores the right kind\nof secret; `fabric-aiops doctor` probes each target with the canonical\ntop-of-hierarchy read (organizations / sites / containers / UniFi sites),\nexercising the full auth flow.\n\n## What this tool does, and does not, decide\n\nIt delivers network-fabric operations — reads and writes — accurately and\nefficiently, and records every one of them. It does **not** decide whether a\nwrite is allowed to happen. That is the agent's judgement, or the permission of\nthe account you connect it with: give it a Meraki API key whose admin has\nread-only organization access (or the read-only equivalent on your controller)\nand the writes fail at the controller — the place that actually owns the\npermission.\n\nSo there is no read-only switch, no policy file, no approval gate to configure.\nThe one thing the tool guarantees is that nothing is silent: **every call, over\nMCP and over the CLI alike, lands an audit row** in `~/.fabric-aiops/audit.db`,\nand mutating writes still capture their before-state and record an inverse where\none exists.\n\n> Each tool declares a `risk_level`, kept in agreement with its `[READ]`/`[WRITE]`\n> documentation tag by a test, and carried into the audit row as a descriptive\n> tier — so a reviewer can see at a glance that a row was a high-risk delete. It\n> is a label, not a gate.\n\n## Quick start\n\n### As a Claude Code plugin\n\nOne install gives an agent both the skill and the MCP server:\n\n```\n/plugin marketplace add AIops-tools/marketplace\n/plugin install fabric-aiops@aiops-tools\n```\n\nThe MCP server is fetched with [uv](https://docs.astral.sh/uv/) and pinned to the\npackage version this plugin declares, so an audit row can be traced back to the\ncode that wrote it. Credentials are still configured with `fabric-aiops init` — see below.\n\n### As a CLI or standalone MCP server\n\n```bash\nuv tool install fabric-aiops              # or: pipx install fabric-aiops\nfabric-aiops init                         # wizard: choose platform (meraki/catalyst/cvp/unifi) + store the secret (encrypted)\nfabric-aiops doctor                       # verify config, secrets, connectivity (per-platform auth probe)\nfabric-aiops overview                     # one-shot fabric fleet health\nfabric-aiops health uplink-rca            # rank worst MX WAN uplinks + cause/action (meraki)\nfabric-aiops device inventory --model MS  # switches in the org\n```\n\nRun as an MCP server (stdio):\n\n```bash\nexport FABRIC_AIOPS_MASTER_PASSWORD=...   # unlock secrets non-interactively\nfabric-aiops-mcp\n```\n\n## Governance\n\nEvery operation — MCP **and** CLI — passes through the bundled `@governed_tool`\nharness. It records; it does not authorize (see above).\n\n- **Audit** — every call (params, result, status, duration, risk tier, and any\n  operator-supplied approver/rationale) is logged to `~/.fabric-aiops/audit.db`\n  (relocatable via `FABRIC_AIOPS_HOME`). The CLI writes the same row the MCP path\n  does — there is no unaudited entry point.\n- **Runaway guard** — a safety backstop, not an authorization gate: the same\n  call hammered in a tight loop trips a circuit breaker so a stuck agent can't\n  burn unbounded calls/time. Disable with `FABRIC_RUNAWAY_MAX=0`; optional hard\n  ceilings via `FABRIC_MAX_TOOL_CALLS` / `FABRIC_MAX_TOOL_SECONDS`.\n- **Undo recording** — reversible writes record an inverse descriptor built from\n  the fetched before-state.\n- **Risk tier** — a descriptive label on the audit row derived from\n  `risk_level`; it gates nothing.\n\n## Scope\n\nThis is the **network-fabric / controller** member of the AIops-tools family\n(governed AI-ops with audit + budget + undo + risk tiers). Do **NOT** use it for\nOT / industrial edge (Modbus, OPC-UA, PROFINET) — see the separate\n`industrial-aiops` line — nor for device-level CLI/SSH network automation.\n\n## Missing a capability?\n\nCoverage is intentionally a curated subset of each controller's API. Missing a\ncall or a device family on **Meraki**? A ❌ in the support matrix you need on\n**Catalyst Center**, **CloudVision Portal**, or **UniFi Network** (writes\nincluded — e.g. the UniFi cookie-login fallback for pre-9.0 controllers)? Want\nanother controller platform entirely? **Open an issue or PR** — contributions\nwelcome (a platform is a single descriptor module: path templates + response\nadapters).\n\n## Status\n\nThe test suite is mock-based. No platform has yet been exercised against a live\nMeraki organization, Catalyst Center appliance, CloudVision Portal instance, or\nUniFi controller — all four platforms' API paths are modelled from the public\nAPI shapes. [`docs/VERIFICATION.md`](docs/VERIFICATION.md) defines the checklist\na live run must cover; `fabric-aiops doctor` is the fastest live check.\n",
  "bytes": 13666,
  "sha": "e8460e453749c5f68284d5c7104d9e91ca792a2b80dcb6e745e02548ac660162",
  "repo_slug": "aiops-tools/fabric-aiops",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_aiops_tools_fabric_aiops_07ebc6e9/readme"
}