Repository navigation
feat(mcp): cap cumulative real-purchase spend per process - #80
Conversation
The per-call USD cap still let a model repeat a purchase with a fresh idempotency_nonce, region or profile and spend again. Real purchases now also need CUDLY_MCP_BUDGET_USD, a ceiling on the total USD this server process commits. An unset or invalid value refuses before credentials are resolved. After the USD cap prices a purchase, its total is reserved against the budget under a mutex, so concurrent calls cannot both pass. Once PurchaseCommitment has been invoked the reservation stays counted on every error and every non-no-op result; only a clean no-op or a commitment the library adopted is released. The budget lives in memory: it resets on restart, is not shared between processes and cannot see purchases made elsewhere, which the README states. Refs #51 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 11 billable files and costs up to $2.75.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Or wait 9 minutes for your next included review. View limit details
Comment |
|
Merge-driver verdict: CLEAN at An independent adversarial reviewer (Sonnet, high effort, cold brief, not the author) reviewed the full diff. It found no blocking findings. Verified:
Mutation probes (6), all killed:
Local verification on macOS (real runs,
Non-blocking follow-ups:
Gate items:
|
Summary
This is part 3 of 3 for #51, after #66 and #69 (both merged). It replaces #77, which closed automatically when the #69 base branch was deleted after merge.
The per-call USD cap (#69) still let a model repeat a purchase with a fresh
idempotency_nonce, region or profile and spend again. Real purchases now also requireCUDLY_MCP_BUDGET_USD, a ceiling on the total USD this server process commits.authorizeRealPurchasereads the budget, and an unset or invalid value refuses beforeResolveClient. It now returnspurchaseLimits{usd, budget}.enforceUSDCapprices the purchase, its total is reserved against the budget under a mutex, so concurrent calls cannot both pass. One stderr line is logged per reservation.purchaseAndSettlemakes thePurchaseCommitmentcall, logs, audits and settles. Once the purchase is invoked, the reservation stays counted on every error, including an unknown outcome, and on every non-no-op result. Only a clean no-op, or a commitment the library adopted (ExistingCommitment), releases it. Refusals before the purchase call never count.mcpb/manifest.json: env var in all 3 platform blocks, plus abudget_usduser setting.server.jsonBreaking/Addedentries.Closes #51. With #66, #69 and this PR, scope items 1 and 2 are implemented. Item 3 (priced RI/CUD preview) was an optional owner decision and is not done. GCP re-enable is #67, auditing refusals is #68 and a persisted budget is #74.
Verification (local, macOS)
Fake-cloud and unit evidence only. No real purchase or cloud call is made. The full suite ran at 909549f; after rebasing onto main at 8c9b39f (range-diff identical; #69 squash tree equals its reviewed head 928c95c), gofmt, build, vet and
go test -racewere re-run at 8e58a54 and pass.gofmt,go vet ./...andmake buildpass.go test -race -count=1 ./...passes in all 3 packages. golangci-lint and gosec, both run withGOTOOLCHAIN=go1.26.9, report 0 issues.gocyclo -over 10lists no non-test code. markdownlint passes and both JSON files parse.TestBudgetRepeatCallWithFreshNonceRefusedmakes two identical $60 purchases with different nonces under a $100 budget. With the reserve call disabled both execute and the test fails. With it, the second is refused and no purchase call is made for it.-race(exactly 3 of 10 reservations succeed)>changed to>=in reserveExistingCommitmentreleaseReview
An independent reviewer (Sonnet, high effort, non-author) did two passes. It walked every
(err, Success, Error, ExistingCommitment)combination and every path between reserve and settle; there are no leaks and no double settle. It also checked the singleton against parallel tests and found no race.Pass 1 found:
>=mutation survived: added, and now caught.Pass 2 found no actionable findings. CodeRabbit is not pinged because the org's CR quota is exhausted.
🤖 Generated with Claude Code