Skip to content

MCP slice 4: read tools — flocks, stock, egg lots, daily entries #807

Closed

Description

@mforce

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.

Activity

  1. added
    sliceThin vertical work item
    area:apiAPI/endpoint layer
    priority:tier3Real product weight, real cost
    size:MA day or two; migration or a multi-state UI
    epic-789MCP server support (#789)
    on Sep 13, 2026
  2. added this to the MCP server support milestone on Sep 13, 2026
  3. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Note from #514's re-plan: this slice's repository injection is a known, accepted crossing

    Recorded 2026-09-14 while re-planning #514 (modular monolith). No change is requested here. This
    is context so nobody rediscovers the interaction at review.

    These tools inject repositories directly, per docs/plans/770-mcp-server/01-design.md:112
    (WaterTools(McpCallContext call, IWaterUsageRepository water)), and that is this design's sanctioned
    path. #514's proposed adapter rule says the opposite: an adapter "may call a module contract or
    approved workflow, not handlers/repositories/context" (its design §8 rule 4).

    Both cannot hold at once, so #514 is resolving it on its side rather than asking this slice to
    change. It declares Mcp/ an adapter tier with DirectRepository privilege. That is a named, dated
    exemption carrying a review issue, instead of a guard that everyone later waters down.

    What this means for you: nothing changes in how you write these tools. The only ask is ordering.
    #514 wants that tier row committed before this slice merges, so its guard is not red on merged code
    the day it is written. That is 1-2 days of work with no production code, and the planning is under way now.

    Related and more consequential: #809 carries a separate note about flock name resolution, which
    does affect what gets built.

  4. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Follow-up: the Mcp/ adapter-tier row described above is now filed as #843. Nothing changes for this slice. #514 absorbs the crossing on its side.

  5. mforce commented on Oct 6, 2026

    @mforce
    OwnerAuthor

    Updated in the 2026-10-06 re-evaluation. Size unchanged.

    Tools may call module contracts only (CW1004), and every read here has one:

    • flocks: IFlockModule.SearchAsync, IFlockLookup.GetAsync / GetDisplayNamesAsync. Birds alive is not a field; derive it from InitialCount and GetBirdsRemovedAsync.
    • stock: IEggOperationsModule.GetStockByGradeAsync.
    • egg lots: ListLotsAsync, ListLotMovementsAsync.
    • daily entries: ListDailyEntriesAsync, GetDailyEntryAsync.

    The page caps still live in the endpoints (StockEndpoints.cs, DailyEntryEndpoints.cs), not the contracts, so each tool sets its own bound and completeness fields. The "known, accepted crossing" note from 2026-09-14 is out of date: injecting a repository is now a build error.

  6. mforce commented on Oct 10, 2026

    @mforce
    OwnerAuthor

    Re-check after milestone 10, 2026-10-10. Every tool here requires the farm:read scope (OAuthScopes.cs:7) as well as its role policy. #806 adds the named scope policies. Nothing else in this slice changed.

  7. mforce commented on Oct 10, 2026

    @mforce
    OwnerAuthor

    Note from #806 (PR #1195). Guard rows 13 to 15 in docs/plans/770-mcp-server/02-guards.md (route parity: [MirrorsRoute] or [MirrorsNoRoute] on every tool, tool roles a subset of the mirrored route's, route names that resolve) did not land with #806. No tool existed, so there was no route to mirror. They land with this slice's first tool.

    What #806 did leave in place for the first tool:

    • /mcp is mapped, stateless, OAuth only, and WithToolsFromAssembly picks up any [McpServerToolType] in Cluckwork.Api.
    • Each tool must carry a role [Authorize] and a scope [Authorize(Policy = AuthPolicies.FarmReadScope)] (or DailyEntriesWriteScope), and never [AllowAnonymous]. McpToolSurfaceTests.RealTree_EveryToolCarriesARolePolicyAndAScopePolicy fails otherwise.
    • A tool class reaching a module needs its AdapterClaim row in RealModuleLedger.Adapters (tests/Cluckwork.Application.Tests/Architecture/RealModuleLedger.Adapters.cs). MCP slice 3: map /mcp, wire RBAC and tools/list filtering #806 added none because there were no tool classes.
    • McpAuthorizationTests shows the pattern for driving a tool through the real OAuth flow and the SDK client (McpConnection in the same folder).
  8. added a commit that references this issue on Oct 11, 2026
  9. added a commit that references this issue on Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:apiAPI/endpoint layerepic-789MCP server support (#789)priority:tier3Real product weight, real costsize:MA day or two; migration or a multi-state UIsliceThin vertical work item

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions