Merge nms and common skills into netx-ops.

Login/CLI playbooks now share one skill with NMS tools so hosts cannot load alarms-only guidance.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-06 02:11:02 +08:00
parent 13ce2482ab
commit e732e1a0a7
8 changed files with 123 additions and 188 deletions

View file

@ -121,7 +121,7 @@ pip install "git+https://github.com/hansjone/netx.git#subdirectory=packages/netx
可选:oclaw 专用字段展开版 [`mcp_install_payload.json`](../mcp_install_payload.json)(与 `mcp.json` 等价)。
**Skills(真源)**:仓库根 [`skills/`](../skills/README.md) — `netx-nms` / `netx-common` / `netx-topology`。MCP 包不再带 skills 镜像;Cursor 直接指 `netx/skills/`。dsh-netxops 发版前可 `sync-skills-from-netx.ps1`。oclaw 旧 playbook 不再维护。
**Skills(真源)**:仓库根 [`skills/`](../skills/README.md) — `netx-ops` / `netx-topology` (ops = NMS + managed CLI)。MCP 包不再带 skills 镜像;Cursor 直接指 `netx/skills/`。dsh-netxops 发版前可 `sync-skills-from-netx.ps1`。oclaw 旧 playbook 不再维护。
**拓扑画布 MCP**(独立安装/绑定)见 [`MCP_TOPOLOGY.md`](./MCP_TOPOLOGY.md)(`server_id=netx-topology`)。

View file

@ -38,16 +38,17 @@ python -m netx_mcp
[`mcp.json`](./mcp.json) — `command: python`,`args: ["-m", "netx_mcp"]`,`env` 见文件。
## 工具(14)
## 工具(14)— skill 组 **ops**(`netx-ops`)
**NMS**(模型面通用名;当前适配器 zte-ume,REST 仍 `/v1/ume/*`):
`queryNmsAlarms`, `aggregateNmsAlarms`, `runNmsDiagnostics`, `queryNmsNeInventory`, `getNmsNe`, `queryNmsAlarmsRaw`, `aggregateNmsAlarmsRaw`, `listNmsAlarmFields`, `sqlQueryNms`
**common**:`listManagedNe`, `getManagedNe`, `execManagedNe`, `listCliTargets`, `findTopologyPaths`
**Managed CLI + paths**(问「能否登录」必须走这里,不要只查 inventory):
`listManagedNe`, `getManagedNe`, `execManagedNe`, `listCliTargets`, `findTopologyPaths`
参数优先 `nms_ne_id` / `nms_ne_ids`(保留 `ume_*` 别名)。
拓扑画布 / Fabric → 请单独安装 [`netx-topology-mcp`](../netx-topology-mcp)(见 [docs/MCP_TOPOLOGY.md](../../docs/MCP_TOPOLOGY.md))。
拓扑画布 / Fabric → skill **topology** / [`netx-topology-mcp`](../netx-topology-mcp)(见 [docs/MCP_TOPOLOGY.md](../../docs/MCP_TOPOLOGY.md))。
## Breaking (0.3.0)

View file

@ -4,8 +4,7 @@
| Group | Skill bundle | 工具(MCP 裸名 / DSH = `netx__`+stem) |
|-------|--------------|----------------------------------------|
| `nms` | `nms/netx-nms/` | `queryNmsAlarms` … `sqlQueryNms` |
| `common` | `common/netx-common/` | managed CLI + `findTopologyPaths` |
| `ops` | `ops/netx-ops/` | NMS 告警/库存/SQL + managed CLI + `findTopologyPaths` |
| `topology` | `topology/netx-topology/` | 画布 / Fabric / dual_unit / 布图 |
## DSH 怎么用
@ -13,6 +12,6 @@
1. 运行时优先:`NETX_SKILLS_ROOT` → 旁路 `../netx/skills` → 包内 `presets/netxops/skills`
2. 发 npm 前:`powershell -File netxops/scripts/sync-skills-from-netx.ps1`
3. Settings → 能力组开关:开哪组就注册哪组的 **tools + 对应 skill**
4. 其它预设可强制挂:`dsh-netxops/tools-nms|common|topology`
4. 其它预设可强制挂:`dsh-netxops/tools-ops|topology`(旧别名 `tools-nms` / `tools-common` → ops)
MCP / Cursor:skill 根直接指本目录。MCP 包内不镜像 skills。

View file

@ -1,91 +0,0 @@
---
name: netx-common
description: >-
netx common playbook (MCP + DSH): managed-NE CLI (SSH/Telnet batch-first) plus
native findTopologyPaths. Not NMS REST alarms/inventory. Trigger: execManagedNe,
show/display, optical, capacity A<>B, path between sites.
---
# netx-common (managed CLI + paths)
## Naming (MCP + DSH)
Canonical: **`netx/skills/common/netx-common/`**. Mirrored into MCP / dsh-netxops — edit once.
| Host | How tools appear |
|------|------------------|
| **MCP** (`netx-mcp`) | Bare names: `execManagedNe`, `findTopologyPaths`, … |
| **DSH** (`dsh-netxops`) | Prefixed: `netx__execManagedNe`, … |
NMS alarms/inventory → **netx-nms**. Canvas / dual_unit → **netx-topology**.
## Tools
| Purpose | Tool |
|---------|------|
| List managed NEs | `listManagedNe` |
| Managed detail | `getManagedNe` |
| Read-only CLI (batch-first) | `execManagedNe` |
| CLI target index | `listCliTargets` |
| Fabric paths | `findTopologyPaths` |
Prefer `nms_ne_id` / `nms_ne_ids`; legacy `ume_*` accepted. `listCliTargets(source=nms)` preferred (`ume` alias).
## CLI order
1. `listManagedNe` (`connect_status=pass`) or `listCliTargets` (**once** per session, cache ids)
2. Multi-NE → **one** `execManagedNe` with `ne_ids` / `nms_ne_ids` / `targets`
3. Paths → `findTopologyPaths` (`nms_ne_id` **or** `managed_ne_id` per end)
### Batch examples
**Same commands, many NEs**
```json
{
"nms_ne_ids": ["uuid-a", "uuid-b", "uuid-c"],
"commands": ["show version"],
"read_timeout_sec": 60,
"concurrency": 4
}
```
**Different commands per NE (still one call)**
```json
{
"targets": [
{"nms_ne_id": "uuid-zte", "commands": ["show opticalinfo brief"]},
{"nms_ne_id": "uuid-hw", "commands": ["display optical-module brief"]},
{"nms_ne_id": "uuid-cisco", "commands": ["show interface transceiver"]}
],
"read_timeout_sec": 90
}
```
**Wrong:** N× single-NE `execManagedNe` in one turn (stdio serial).
## Field recipes
### Capacity / optical A<>B
1. Resolve nicknames → `host_name`
2. `findTopologyPaths` and/or LLDP → both ports
3. Optics CLI on **both** ends; summarize RX/TX / thresholds
4. Do not answer with only NMS bandwidth/optical-power-threshold alarm tallies unless asked
### ZTE optical CLI
Try in order; one failure → switch spelling (do not blind-retry):
| Prefer | Fallback |
|--------|----------|
| `show opticalinfo brief` | Field-confirmed on many ZXR10 |
| `show optical brief` | Some EN platforms |
| `show opticalinfo brief \| begin <if>` | After port known |
## Guardrails
- Never pass NMS alarm UUIDs as managed `ne_id` for `getManagedNe`
- Allowlist prefixes: `show ` / `display ` / `ping ` / `traceroute` …
- Prefer `host_name` for users; keep ids for tool params

View file

@ -1,70 +0,0 @@
---
name: netx-nms
description: >-
netx NMS playbook (MCP + DSH): vendor NMS adapter (zte-ume) for alarm
query/aggregate/diagnostics, NE inventory, raw fields, and read-only SQL.
Trigger: NMS alarms, host_name, Critical Top, LOS, BN EMS, netx ops.
---
# netx-nms (vendor NMS adapter)
## Naming (MCP + DSH)
Canonical playbooks live in **`netx/skills/`** (this file is mirrored into MCP packages / dsh-netxops).
Edit the netx repo copy; run sync scripts — do not maintain divergent forks.
| Host | How tools appear |
|------|------------------|
| **MCP** (`netx-mcp`) | Bare names: `queryNmsAlarms`, … |
| **DSH** (`dsh-netxops`) | Prefixed: `netx__queryNmsAlarms`, … (same camelCase stem) |
REST still `/v1/ume/*` for provider `zte-ume`.
Prefer `nms_ne_id` / `nms_ne_ids`; legacy `ume_*` aliases still work.
Path lookup → **common** skill `netx-common` (`findTopologyPaths`).
Canvas → **netx-topology**(含 dual_unit / 布图;DSH 可把部分布局工具放在实验 tool group)。
## Tool names
| Purpose | Tool |
|---------|------|
| Alarm list | `queryNmsAlarms` |
| Alarm aggregate | `aggregateNmsAlarms` |
| Diagnostics | `runNmsDiagnostics` |
| NE inventory | `queryNmsNeInventory` |
| NE detail | `getNmsNe` |
| Field list | `listNmsAlarmFields` |
| Raw rows | `queryNmsAlarmsRaw` |
| Dynamic aggregate | `aggregateNmsAlarmsRaw` |
| SQL | `sqlQueryNms` |
| Managed CLI (other skill) | `listManagedNe` / `getManagedNe` / `execManagedNe` / `listCliTargets` |
## Tool order
1. **Freshness first**: `runNmsDiagnostics` or `aggregateNmsAlarms` → `meta.last_seen_min` / `last_seen_max`.
2. Overview: `aggregateNmsAlarms` + `runNmsDiagnostics`; samples via `queryNmsAlarms` (one page).
3. Evidence: `listNmsAlarmFields` → `queryNmsAlarmsRaw` (`field_preset=evidence`).
4. Custom aggregate: `aggregateNmsAlarmsRaw`.
5. SQL: `sqlQueryNms` (SELECT only; `statement_timeout_ms`).
6. Paths: `findTopologyPaths` via **netx-common**.
7. Device CLI: **netx-common** — multi-NE = **one** `execManagedNe` batch.
## Short-intent recipes
| User says | Recipe |
|-----------|--------|
| fiber cut / LOS / 断纤 | `queryNmsAlarmsRaw(keyword=LOS)` and/or `Fiber Break` → **host_name** list |
| offline / BN EMS | keyword=`BN EMS` |
| Critical Top | `aggregateNmsAlarms(severity=critical, top_ne=20)` |
| CRC in area | Raw `keyword=CRC` + `AREA-` hostname prefix |
| optical power **threshold** | keyword=`optical power` + area — **not** fiber-cut |
| one hostname | host-scoped `queryNmsAlarms` / Raw only |
## Guardrails
- Prefer non-SQL; filter severity → keyword/host → time → ne_id.
- Lists ≤2 pages; `page_size` default 50.
- Display **host_name** only; never bare UUID to users.
- `getManagedNe` needs managed id; NMS UUID → `getNmsNe` / `execManagedNe(nms_ne_id=...)`.
See [reference.md](reference.md).

View file

@ -1,20 +0,0 @@
# netx-nms quick reference
## Freshness
- `runNmsDiagnostics` / `aggregateNmsAlarms` → `meta.last_seen_min` / `last_seen_max`
- Snapshot: windows inside min~max — do not default to `now()-30m`
## Tools
| Intent | Call |
|--------|------|
| Critical Top | `aggregateNmsAlarms(severity=critical, top_ne=20)` |
| Fiber / LOS | Raw `keyword=LOS` / `Fiber Break` |
| Offline / BN EMS | Raw keyword=`BN EMS` |
| Single host | `queryNmsAlarms(host_name=…)` |
| Inventory | `queryNmsNeInventory(keyword=…)` / `getNmsNe` |
| Paths | `findTopologyPaths(from_nms_ne_id, to_nms_ne_id)` — **common** skill |
| SQL | `sqlQueryNms` SELECT; `statement_timeout_ms=8000` |
DSH: prefix every tool with `netx__`.

View file

@ -0,0 +1,94 @@
---
name: netx-ops
description: >-
Netx Ops playbook (MCP + DSH): NMS alarms/inventory/SQL plus managed-NE CLI
login (execManagedNe batch-first) and findTopologyPaths. Trigger: alarms,
host_name, Critical Top, LOS, 能否登录, show/display, optical, capacity A<>B,
path between sites, netx ops.
---
# netx-ops(告警 + 纳管登录)
**一组一个 skill。** 问「能否登录 / CLI / show」必须走本 skill 的 managed 工具,不要只查 NMS inventory。
## Hosts
| Host | Tools |
|------|--------|
| **MCP** `netx-mcp` | 裸名 `queryNmsAlarms` / `execManagedNe` / … |
| **DSH** `dsh-netxops` | `netx__` + 同 stem;能力组 **ops**(默认开) |
REST NMS 仍 `/v1/ume/*`(`nmsProvider=zte-ume`)。优先 `nms_ne_id`;`ume_*` 别名可用。
画布 / dual_unit → **netx-topology**(能力组 topology)。
## Tools
### NMS
| Purpose | Tool |
|---------|------|
| Alarm list / aggregate / diagnostics | `queryNmsAlarms` / `aggregateNmsAlarms` / `runNmsDiagnostics` |
| Inventory / detail | `queryNmsNeInventory` / `getNmsNe` |
| Raw / fields / SQL | `queryNmsAlarmsRaw` / `listNmsAlarmFields` / `aggregateNmsAlarmsRaw` / `sqlQueryNms` |
### Managed CLI + paths
| Purpose | Tool |
|---------|------|
| List CLI targets | `listManagedNe` / `listCliTargets` |
| Managed detail | `getManagedNe`(**仅**纳管 id) |
| Login / show | `execManagedNe`(batch-first) |
| Fabric paths | `findTopologyPaths` |
## 「能否登录」决策树(强制)
1. `listCliTargets(keyword=host_or_ip)` 或 `listManagedNe(keyword=…, connect_status=pass)`
- 命中 → `execManagedNe(ne_id|nms_ne_id, commands=["show version"])` **验证真正能登**
- 未命中 → `queryNmsNeInventory(keyword=…)` 说明 NMS 有无 / `connection_status`,并明确:**未纳管 netx CLI 则不能用本通道登录**
2. 禁止只查 inventory 就下「不能登录」或「能登录」结论而不尝试 `execManagedNe`(已纳管时)。
3. NMS UUID 不要塞进 `getManagedNe`;用 `execManagedNe(nms_ne_id=…)` 或先 list 拿 managed `ne_id`。
## NMS 工具顺序
1. Freshness: `runNmsDiagnostics` / `aggregateNmsAlarms` → `meta.last_seen_*`
2. Overview + one-page `queryNmsAlarms`
3. Evidence: `queryNmsAlarmsRaw` (`field_preset=evidence`)
4. Paths / CLI as needed (same skill)
## CLI order
1. `listManagedNe` / `listCliTargets`(每会话最多一次,缓存 id)
2. 多台 → **一次** `execManagedNe`(`ne_ids` / `nms_ne_ids` / `targets`)
3. 路径 → `findTopologyPaths`
### Batch 示例
```json
{ "nms_ne_ids": ["uuid-a", "uuid-b"], "commands": ["show version"], "concurrency": 4 }
```
```json
{
"targets": [
{"nms_ne_id": "uuid-zte", "commands": ["show opticalinfo brief"]},
{"nms_ne_id": "uuid-hw", "commands": ["display optical-module brief"]}
]
}
```
## Short recipes
| User says | Recipe |
|-----------|--------|
| 能否登录 / login / SSH | 上表「能否登录」决策树 |
| fiber / LOS | Raw `keyword=LOS` / `Fiber Break` |
| Critical Top | `aggregateNmsAlarms(severity=critical, top_ne=20)` |
| capacity A<>B | paths / LLDP → 两端 optic CLI(一批) |
## Guardrails
- 展示 **host_name**;勿对用户甩裸 UUID
- CLI 白名单:`show` / `display` / `ping` / `traceroute` …
- 同轮禁止 N× 单台 `execManagedNe`
See [reference.md](reference.md).

View file

@ -0,0 +1,22 @@
# netx-ops quick reference
## Can it log in?
1. `listCliTargets` / `listManagedNe` → if found, `execManagedNe(…, commands=["show version"])`
2. Else `queryNmsNeInventory` → report NMS presence; say CLI not managed if absent from managed list
## NMS freshness
- `runNmsDiagnostics` / `aggregateNmsAlarms` → `meta.last_seen_min` / `max`
## Shortcuts
| Intent | Call |
|--------|------|
| Critical Top | `aggregateNmsAlarms(severity=critical, top_ne=20)` |
| Fiber / LOS | Raw `keyword=LOS` / `Fiber Break` |
| Inventory | `queryNmsNeInventory(keyword=…)` / `getNmsNe` |
| CLI batch | `execManagedNe(nms_ne_ids=[…], commands=[…])` |
| Paths | `findTopologyPaths(from_nms_ne_id, to_nms_ne_id)` |
DSH: prefix tools with `netx__`.