netxops/test/mcp-test-catalog.mjs
oliver fa82f530e9 feat(skills): pull group skills from netx /mcp resources, drop bundled copy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 23:14:39 +08:00

146 lines
3.8 KiB
JavaScript

/**
* Seed the process-local MCP catalog for registration / publication tests.
*/
import { publishNetxConnection } from '../src/netx/runtime.ts'
import { replaceSkillsCache } from '../src/netx/skills-cache.ts'
const STORE_KEY = Symbol.for('dsh-netxops.mcp-catalog-store')
/** Classic ops catalog size (matches historical TOOLS_BY_GROUP.ops.length). */
export const OPS_TOOL_NAMES = Object.freeze([
'queryNmsAlarms',
'aggregateNmsAlarms',
'runNmsDiagnostics',
'queryNmsNeInventory',
'getNmsNe',
'queryNmsAlarmsRaw',
'aggregateNmsAlarmsRaw',
'listNmsAlarmFields',
'sqlQueryNms',
'listManagedNe',
'getManagedNe',
'execManagedNe',
'getNeExecJob',
'listCliTargets',
'findTopologyPaths',
])
export const TOPOLOGY_TOOL_NAMES = Object.freeze([
'getTopologyTree',
'getTopologyView',
'createTopologyFolder',
'addTopologyViewNodes',
'removeTopologyViewNodes',
'copyTopologyViewNodes',
'updateTopologyViewPositions',
'projectTopologyNeighbors',
'queryTopologyFabricNodes',
'classifyTopologyFabricNodes',
'queryTopologyNeighborhood',
'queryTopologyEdges',
'layoutTopologyView',
'suggestSinkHubs',
'analyzeTopologyViewLayout',
])
export const BIZ_MONITOR_TOOL_NAMES = Object.freeze([
'listBizMonitors',
'listBizMonitorBatches',
'getBizMonitorContext',
'getBizMonitorBoard',
'listBizMonitorReds',
'getBizMonitorDiffs',
'getBizCollectBatch',
'getBizCollectCommandRaw',
])
const WRITE_OPS = new Set(['execManagedNe'])
const WRITE_TOPOLOGY = new Set([
'createTopologyFolder',
'addTopologyViewNodes',
'removeTopologyViewNodes',
'copyTopologyViewNodes',
'updateTopologyViewPositions',
'projectTopologyNeighbors',
'classifyTopologyFabricNodes',
'layoutTopologyView',
])
function descriptor(name, group) {
const write = (group === 'ops' && WRITE_OPS.has(name))
|| (group === 'topology' && WRITE_TOPOLOGY.has(name))
return {
name,
description: name,
inputSchema: { type: 'object', properties: {} },
readOnlyHint: !write,
group,
}
}
export function fullCatalog() {
return [
...OPS_TOOL_NAMES.map((name) => descriptor(name, 'ops')),
...TOPOLOGY_TOOL_NAMES.map((name) => descriptor(name, 'topology')),
...BIZ_MONITOR_TOOL_NAMES.map((name) => descriptor(name, 'bizMonitor')),
]
}
const DEFAULT_API = 'http://127.0.0.1:8890'
const MINIMAL_SKILLS = Object.freeze([
{
group: 'ops',
skill: 'netx-ops',
relPath: 'SKILL.md',
text: '---\nname: netx-ops\ndescription: Ops playbook\n---\nOps body\n',
},
{
group: 'topology',
skill: 'netx-topology',
relPath: 'SKILL.md',
text: '---\nname: netx-topology\ndescription: Topology playbook\n---\nTopology body\n',
},
{
group: 'bizMonitor',
skill: 'netx-biz-monitor',
relPath: 'SKILL.md',
text: '---\nname: netx-biz-monitor\ndescription: Biz monitor playbook\n---\nBiz body\n',
},
])
/** Write a minimal MCP skills cache for `apiUrl` (default test netx). */
export function seedSkillsCache(apiUrl = DEFAULT_API) {
return replaceSkillsCache(apiUrl, MINIMAL_SKILLS)
}
/** Install a frozen catalog into the shared Symbol.for store (no network). */
export function seedMcpCatalog(tools = fullCatalog()) {
const root = globalThis
const store = {
state: {
phase: 'connected',
mcpUrl: `${DEFAULT_API}/mcp`,
tools: Object.freeze([...tools]),
error: '',
listedAt: new Date().toISOString(),
fromCache: false,
},
listeners: new Set(),
generation: 1,
}
root[STORE_KEY] = store
seedSkillsCache(DEFAULT_API)
return store.state
}
export function clearMcpCatalogSeed() {
delete globalThis[STORE_KEY]
}
/** Convenience: seed catalog + publish a connection snapshot. */
export function publishWithCatalog(connection) {
seedMcpCatalog()
publishNetxConnection(connection)
}