Skip to content

v1.2.0 — From Tools to Contracts

Choose a tag to compare

@charlesaspe charlesaspe released this 03 Jul 17:09
9e895d0

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_review on 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_datanot_foundambiguouserror. 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-native isError. no_data is 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 str tool contracts: 25 → 0
  • Orchestrator: −85 lines (string-convention workarounds deleted)

Full changelog: v1.1.0...v1.2.0