v0.7.0: The monolith splits
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/listreturning all 128 in order, and a realtools/callround-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.