The Pattern Holds

Three domains down, two to go. Engine, work, and the typed client all have their GraphQL + MCP modules built and tested. The pattern that emerged from the engine work — @GraphQLApi plus @McpDomain on thin resolver adapters, auto-discovered by the platform’s GraphQLModelScanner at startup — held without modification across all three.

That’s the thing about good abstractions: the second and third consumers should be boring. The engine module was the interesting one — it established the resolver shape, the DTO convention (from() factories on records), the CDI event bridge for subscriptions, and the ModelEnricher contract for MCP domain summaries. The work module is structurally identical. Same annotations, same delegation pattern, same test shape. The only differences are the domain types flowing through.

The MCP side is the part I didn’t expect to be this clean. Two fixed tools — casehub_model for navigating the operation catalog, casehub_action for dispatching — and everything else is data. The GraphQLModelScanner walks CDI beans at startup, finds @McpDomain-annotated resolvers, reflectively extracts @Query and @Mutation methods, and builds an operation catalog. Adding a new domain to MCP is literally adding an annotation to a class that already exists for GraphQL.

Ledger and qhorus are next. If the pattern holds — and I expect it to — the interesting work shifts to the scaffold module: wiring all four domains into a single CaseHub server and proving it works end-to-end.