Repository navigation
feat(mcp): submit daily entries and record feed, water, purchases and expenses - #1325
Merged
Merged
Conversation
Adds five MCP write tools behind three new OAuth scopes: daily-entries:submit, operations:write and expenses:write. Each tool calls its REST route's validator and handler under that route's role policy, and runs through McpWrite, the #809 idempotency protocol lifted out of the record tool so all six writes share it. Feed use, water use and purchases now write an audit event in the handler's transaction, so each record names the person and the app. Refs #1311, #1313
MCP_CLIENT_FILE keeps one registration across connects, so a reconnect shows which permissions consent marks New.
Measured: Claude Code asks for every scope the resource metadata lists. Also moves the at-once flag into the consent scope table, and adds the write-tool steps to the live MCP check.
The tools convert a decimal with a currency read before the handler takes its account lock. A currency change committed in between made the handler stamp the new currency on the old figure. Each tool now compares the stored currency and fails, which rolls the record back; the expense reply reports the stored amount and currency.
The purchase tool read every lot of the item to find the one it had just inserted. RecordPurchaseHandler now returns the stored lot; the REST route still answers with its LotId.
…chases A flock-scoped person's consent said the app records only for their flocks, but inventory purchases are farm-wide. Details now names what the limit covers and says purchases add to the whole farm's inventory, in en, es and tl.
Owner
Author
|
Screenshots, at 1:1 from a stack rebuilt at ae160b3. The "before" frames come from main at 39228ed. Consent, English. Before, an app asking for every scope saw two permissions. After, it sees five. Reconnect. An app first allowed only Record daily entries asks again. The three new permissions are marked New. Restricted Worker, with Details open. Record expenses is marked Not allowed for your role. The flock limit names daily entries, feed and water, and purchases are described as farm-wide. Consent, Spanish. Help, connected-app items, English and Spanish. |
The spec's Connecting an app entry gained the reconnect rule; the in-app definition now says it too, in en, es and tl, with each locale's consent labels.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.











A connected AI app can now submit a daily entry, and record feed use, water use, inventory purchases and expenses. Each action sits behind its own permission on the consent screen. Each one calls the REST route's own validator and handler, under that route's role policy.
Closes #1311
Closes #1313
Permissions
farm:readdaily-entries:writecluckwork_record_daily_entry(unchanged)daily-entries:submitcluckwork_submit_daily_entryoperations:writecluckwork_record_feed_usage,cluckwork_record_water_usage,cluckwork_record_inventory_purchaseexpenses:writecluckwork_record_expenseThe scope names follow the existing
<resource>:<verb>style. Submit shares thedaily-entriesresource with record because both act on the same record. A read tool requires exactlyfarm:read, and a write tool requires exactly one write scope.tools/listshows a tool only when the person's role and the token's scopes both allow it. The protected-resource metadata advertises all five scopes. The reasoning is indocs/decisions/1311-mcp-write-tools.md.What changes for a farm
correctInApp, which names the screen and control where an Admin or Manager corrects the record. One example: "Feed records are never edited. An Admin or Manager corrects the stock with Correct stock on Inventory." Consent says these records count at once.DailyEntry.NotDraft.FeedUsage.Create,WaterUsage.Create,InventoryItem.Purchase), so each record names the person and the connected app. The REST routes write it too, because they share the handler. The Audit page's filters and labels gain the three actions in en, es and tl.Reviewer guide
Start with these files:
src/Cluckwork.Api/Mcp/McpWrite.cs: the MCP slice 6: write tool — record daily entry #809 claim, execute and publish protocol, moved out of the record tool. All six write tools use it. The record tool keeps its operation key, fingerprint and replay check.src/Cluckwork.Api/Mcp/DailyEntryTools.cs(submit),InventoryTools.cs(feed, water, purchase) andExpenseTools.cs.McpNames.cs: items and expense categories match an active record exactly, ignoring case, or any record by id. A name shared by two farms is refused with both ids.web/src/auth/oauthScopes.ts: one table that drives both the consent screen and Connected apps.The currency race (fixed in review). The expense and purchase tools convert a decimal using a currency read taken before the handler locks the account. If a currency change commits in between, the handler would stamp the new currency on the old figure. Each tool now compares the stored currency and precision and refuses with
Money.CurrencyChanged. That failure rolls the whole record back throughIdempotentUnit. The expense reply reports the stored amount.Mechanical or guard files:
tests/.../Architecture/Data/coupling-matrix.mdis generated.RealModuleLedger.Adapters.csgains two adapter rows.McpToolSurface.cs,McpToolSurfaceTests.csandMcpRouteParityTests.cshold the scope rule and the reviewed reply-text list.tools/oauth/mutation-check.shgains 16 rows. It also repairs 2 anchors: one my change moved, andrpc-429-as-problem, which was already stale on main.Tests
McpWriteToolsTests(22 tests). They cover:Live run
I used the TypeScript MCP SDK client against an isolated stack behind TLS, with real consent and step-up.
tools/listshowed one tool. It then reconnected with the same client id. Consent marked Submit daily entries, Record farm operations and Record expenses New, and the five new tools appeared.replayed: truewith the same lot ids, and REST still shows one set of lots. A new key was refused withDailyEntry.NotDraft.Screenshots follow in a comment.