Slice 4 of #789. Blocked on #788 and slice 3 (#806).
Read tools for the production side: flocks, stock / egg lots, daily entries.
Domain questions, not route mirrors
A 1:1 tool-per-route surface would be pass-through methods with the same arguments and a renamed result. That is the red flag exactly. The caller is a model that holds names, not the SPA that already holds GUIDs.
So tools answer the question a caller actually asks. They hide name-to-id resolution, paging, and error mapping. They contain no validation rule, no authorization decision, and no tenant or flock-scope predicate. Each already exists one layer down and is already guarded.
Bounded results are required, not optional
Reusing a repository does not inherit the HTTP adapter's limits. Every read tool states a bound, and each reply carries explicit completeness and continuation fields. A silently truncated first page is worse than an error, because it is an apparently complete wrong answer and the model cannot tell.
Two separate guards are needed, and they are not the same. One checks that the reply is bounded. The other checks that the query is bounded. An implementation can fetch every page in a loop and cap the reply. That satisfies the first guard and violates the second.
Name resolution needs an ambiguity contract
Duplicate flock names are legal. FlockRepository says so, and its ThenBy(f => f.Id) tie-break exists because of it. Resolution means exactly one accessible match. Zero matches is not-found. Two or more is an ambiguity error listing candidate ids and distinguishing fields.
Done when
Each tool returns bounded results that are complete or continuable. A cross-farm bearer reads nothing. A Worker sees only assigned flocks.
Design
docs/plans/770-mcp-server/01-design.md, guards rows 25-32.
Slice 4 of #789. Blocked on #788 and slice 3 (#806).
Read tools for the production side: flocks, stock / egg lots, daily entries.
Domain questions, not route mirrors
A 1:1 tool-per-route surface would be pass-through methods with the same arguments and a renamed result. That is the red flag exactly. The caller is a model that holds names, not the SPA that already holds GUIDs.
So tools answer the question a caller actually asks. They hide name-to-id resolution, paging, and error mapping. They contain no validation rule, no authorization decision, and no tenant or flock-scope predicate. Each already exists one layer down and is already guarded.
Bounded results are required, not optional
Reusing a repository does not inherit the HTTP adapter's limits. Every read tool states a bound, and each reply carries explicit completeness and continuation fields. A silently truncated first page is worse than an error, because it is an apparently complete wrong answer and the model cannot tell.
Two separate guards are needed, and they are not the same. One checks that the reply is bounded. The other checks that the query is bounded. An implementation can fetch every page in a loop and cap the reply. That satisfies the first guard and violates the second.
Name resolution needs an ambiguity contract
Duplicate flock names are legal.
FlockRepositorysays so, and itsThenBy(f => f.Id)tie-break exists because of it. Resolution means exactly one accessible match. Zero matches is not-found. Two or more is an ambiguity error listing candidate ids and distinguishing fields.Done when
Each tool returns bounded results that are complete or continuable. A cross-farm bearer reads nothing. A Worker sees only assigned flocks.
Design
docs/plans/770-mcp-server/01-design.md, guards rows 25-32.