#!/usr/bin/env node // mcp-baseline-reset.mjs — Reset MCP description-cache baselines. // // Purpose: // The description cache (scanners/lib/mcp-description-cache.mjs) anchors a // sticky baseline per MCP tool so that cumulative drift can be detected // across many small updates. After a legitimate MCP server upgrade the // baseline becomes a stale "what the tool used to say" reference and must // be reset so the next call seeds a fresh baseline. // // Modes: // --list Read-only — list current baselines as JSON. // --target Clear baseline for one tool. // (no args) Clear baselines for all tools. // // Output: JSON summary on stdout. Exit 0 always (idempotent). // // Used by /security mcp-baseline-reset slash command. Not part of // scan-orchestrator. import { clearBaseline, listBaselines, loadCache, } from './lib/mcp-description-cache.mjs'; function parseArgs(argv) { const args = { list: false, target: null }; for (let i = 2; i < argv.length; i++) { const a = argv[i]; if (a === '--list') { args.list = true; } else if (a === '--target' || a === '-t') { args.target = argv[++i] || null; } else if (a === '--help' || a === '-h') { args.help = true; } else if (!a.startsWith('--')) { // bare positional treated as target for convenience args.target = a; } } return args; } function help() { process.stdout.write( 'mcp-baseline-reset.mjs — Reset MCP description-cache baselines.\n\n' + 'Usage:\n' + ' node scanners/mcp-baseline-reset.mjs --list\n' + ' node scanners/mcp-baseline-reset.mjs --target \n' + ' node scanners/mcp-baseline-reset.mjs # clear all\n\n' + 'Output: JSON. Exit code 0 always.\n', ); } function emit(obj) { process.stdout.write(JSON.stringify(obj, null, 2) + '\n'); } function main() { const args = parseArgs(process.argv); if (args.help) { help(); return 0; } if (args.list) { const baselines = listBaselines(); emit({ mode: 'list', count: baselines.length, baselines: baselines.map((b) => ({ tool: b.tool, baseline_excerpt: (b.baseline || '').slice(0, 120), seen_at: b.seenAt, last_seen: b.lastSeen, history_events: b.history, })), }); return 0; } // Reset path const result = clearBaseline(args.target || undefined); // After clearing, count remaining baselines const cache = loadCache(); let remaining = 0; for (const entry of Object.values(cache)) { if (entry && entry.baseline) remaining++; } emit({ mode: 'reset', target: args.target || null, cleared: result.cleared, tools: result.tools, remaining, }); return 0; } process.exit(main());