Skip to content

v0.7.0: The monolith splits

Choose a tag to compare

@marc-shade marc-shade released this 01 Sep 21:59
· 10 commits to main since this release

The monolith splits. No behavior changes.

server.py was 2,890 lines: a 128-entry tool registry plus an 800-line match/case dispatcher. It is now a 158-line shell over 12 domain modules in world_intel_mcp/tools/ (markets, hazards, conflict, infrastructure, society, intelligence, geospatial, synthesis, finance, vector, aoi, system - largest module 444 lines), with shared infrastructure (cache, circuit breaker, AOI store, vector store, fetcher) in world_intel_mcp/runtime.py.

The registry/dispatch parity invariant got stronger in the move: each module exports TOOLS and HANDLERS, and tools.aggregate() refuses to import a registry whose tools and handlers disagree or whose names collide across modules. Drift that the old text-scan test could only catch in CI now prevents the server from starting at all - and a falsifiability test proves the guard actually rejects.

Pure move, verified three ways:

  • AST comparison of every tool name/description/inputSchema against the pre-split registry: 127/127 byte-equal
  • Full suite: 814 passed, 90.8% statement coverage (CI gate at 89)
  • Live MCP stdio session against the real entry point: initialize handshake, tools/list returning all 128 in order, and a real tools/call round-trip

If you extend the server: add your Tool(...) and its handler to the matching tools/<domain>.py - the import-time guard fails loudly if they disagree. See CLAUDE.md's updated "Adding a New Tool".

Full detail in CHANGELOG.md.