v1.2.0 — From Tools to Contracts
Three releases of spec-driven hardening, in one theme: the discipline NetCopilot always practiced in prose is now machine-enforced — in the type system, in the rule catalog, and on the MCP wire.
🏛 A constitution as step 0
NetCopilot's operating principles are now codified in CONSTITUTION.md: context agent, never actuator · determinism over cleverness · null over guessing · least privilege in config · machine-enforced contracts over prose · evidence traceability. Every future spec and PR is checked against it.
📜 Rule-catalog spec hygiene
- First-class
status: active | deferred | manual_reviewon all 426 rules — deferral is now explicit metadata, never disguised by renaming. - Spec/implementation drift found by audit and fixed (CIS timezone rule aligned to its check; mismatch-family check-logic blocks corrected).
- The loader validates status and reports what it skipped — nothing silently disappears.
📦 Typed tool-result envelope
Every one of the 25 MCP tools now returns a frozen ToolResult(status, text, verdict, highlight, verbatim) instead of a free-form string:
- Named failure modes as machine-readable status codes:
no_data≠not_found≠ambiguous≠error. A tool that has no evidence says so in a way code can branch on — the antidote to false-OK results. - The contract is enforced, not requested: a handler returning anything else raises. Prose contracts drift; typed contracts fail loud.
- Three string-convention hacks (prose-scraping, inline markers, hardcoded tool-name sets) deleted; the orchestrator consumes typed fields. Model-facing text stayed byte-identical throughout.
🌐 The full MCP surface — generated, not enumerated
The server previously exposed 3 of 25 tools to external MCP clients while the README claimed the full surface (found by our own audit). Now:
- The surface is generated from the tool registry — names, descriptions, and parameter schemas have a single source of truth; a new tool appears externally with zero server changes. Drift between the internal and external surface is structurally impossible.
- The envelope travels: results carry
{status, verdict}as MCP structured content alongside verbatim text; tool failures map to MCP-nativeisError.no_datais a valid answer, not an error. - Root fix included: every tool context now carries the run's data directory, so file-backed tools (path tracing, OSPF detail, security posture) work identically over the external surface.
Any MCP client — Claude Desktop, an agent framework, a gateway — now sees everything NetCopilot can do, with a machine-readable contract on every result. Read-only, as always: the network is the MCP server, and it never changes devices.
Numbers
- Tests: 931 → 963 passed (+28 skipped)
- MCP tools exposed externally: 3 → 25
- Free-form
strtool contracts: 25 → 0 - Orchestrator: −85 lines (string-convention workarounds deleted)
Full changelog: v1.1.0...v1.2.0