0002. Register tools explicitly, not with decorators

  • Status: Accepted

  • Date: 2026-09-19

  • Deciders: maintainer

Context

FastMCP tools are conventionally declared with @server.tool(...), and the sixteen tools, three resources and one prompt of this server were. mutmut 3 never mutates a decorated function, so every handler sat outside mutation testing: a mutation score over this module described the private helpers and nothing an agent calls.

Options considered

  1. Keep the decorators and accept that the handlers are untested by mutation; the score describes the helpers only.

  2. Register the same functions by explicit server.tool(...)(fn), server.resource(...)(fn) and server.prompt(...)(fn) calls in one block after the definitions; the registered object, docstring, signature, title, annotations and listing order are identical.

Decision

Option 2, as the sibling pain001-mcp did (its ADR 0003). The handlers are what an agent calls; a mutation score that excludes them measures the wrong thing. The catalogue an MCP client sees (tools/list, prompts/list, resources/list, resources/templates/list) was dumped before and after the change and is byte-identical.

Consequences

A new tool is a plain function plus one registration line in the block before main(); EXPECTED_TOOLS in the tests must follow. The mutation workflow gates the handlers at the floor recorded in the Makefile, and the property tests in tests/test_properties.py state the invariants that block’s handlers keep for any input their schemas admit.