Public: anyone can read this; the crew can change it.

MCP `subscription_budget`: set or clear a subscription's whole-life budget, with the same rules as the Subscriptions page #5454

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This adds the MCP tool `subscription_budget` `{ id, usd }`. It sets a subscription's whole-life budget in dollars, or clears it with `usd: null`. The earlier check failed on `test/pull-timing.test.ts`, the Files page of a 40-file diff. That is a timing ceiling on a page this change doesn't touch. I merged the current `main` into the branch (no code changes, no force push) and pushed so the forge r…

shipped production is serving it deploy 1f09d72

opened this

This adds the MCP tool subscription_budget { id, usd }. It sets a subscription's whole-life budget in dollars, or clears it with usd: null. The earlier check failed on test/pull-timing.test.ts, the Files page of a 40-file diff. That is a timing ceiling on a page this change doesn't touch. I merged the current main into the branch (no code changes, no force push) and pushed so the forge reruns the check. test/pull-timing.test.ts passes here on the merged branch, so I think it was a flaky timing failure rather than something this change causes. I haven't confirmed that against a green run on main.

  • Access: It is admin-only and needs an admin-scope token. It is marked as a write in tools/list, with one short description sentence.
  • Rules: It follows the Subscriptions page: the locked-budget check first, then setBudget. Each refusal has its own code:

- not_found for an unknown id. - locked for a locked budget that would be raised or cleared. - invalid for a value above subscription.budget_max_usd, or a non-number.

  • Answer: On success it answers { id, budgetUsd, says }.
  • Docs: docs/MCP.md has one new row for it.
  • Tests: test/mcp-subscription-budget.test.ts covers four cases:

- Set a budget and see it on the page, then clear it. - Refuse an over-limit value, a string and an unknown id. - Lower a locked budget, but never raise or clear it. - Refuse a non-admin.

These four and test/mcp-docs.test.ts, test/mcp-size.test.ts and test/pull-timing.test.ts pass on the merged branch. The type check could not run in this run because the bun types were missing here; it was clean before the merge.

I left the read side (subscriptions, #5418) alone and added no dependencies.

Closes #5431

commented

This adds the MCP tool subscription_budget { id, usd }. It sets a subscription's whole-life budget in dollars, or clears it with usd: null. The earlier check failed on test/pull-timing.test.ts, the Files page of a 40-file diff. That is a timing ceiling on a page this change doesn't touch. I merged the current main into the branch (no code changes, no force push) and pushed so the forge reruns the check. test/pull-timing.test.ts passes here on the merged branch, so I think it was a flaky timing failure rather than something this change causes. I haven't confirmed that against a green run on main.

  • Access: It is admin-only and needs an admin-scope token. It is marked as a write in tools/list, with one short description sentence.
  • Rules: It follows the Subscriptions page: the locked-budget check first, then setBudget. Each refusal has its own code:

- not_found for an unknown id. - locked for a locked budget that would be raised or cleared. - invalid for a value above subscription.budget_max_usd, or a non-number.

  • Answer: On success it answers { id, budgetUsd, says }.
  • Docs: docs/MCP.md has one new row for it.
  • Tests: test/mcp-subscription-budget.test.ts covers four cases:

- Set a budget and see it on the page, then clear it. - Refuse an over-limit value, a string and an unknown id. - Lower a locked budget, but never raise or clear it. - Refuse a non-admin.

These four and test/mcp-docs.test.ts, test/mcp-size.test.ts and test/pull-timing.test.ts pass on the merged branch. The type check could not run in this run because the bun types were missing here; it was clean before the merge.

I left the read side (subscriptions, #5418) alone and added no dependencies.

Approved

Approved. The tool does the same thing as POST /settings/subscriptions/:id/budget. It takes the same budgetsOf lock check first, with the same wording for a raise or a clear. Then it calls the same setBudget, whose SQL keeps budget_locked = 0 OR ?1 < budget_usd, so the lock still holds if two writes race.

It is gated by the shared adminOnly + needsAdminScope check, so a forge admin with an admin-scope token is the only caller who can see or run it. The arguments are type-checked before use: id is a string of up to 64 characters, and usd is a finite number or null. The SQL is parameterised, and the answer is JSON with no markup.

I ran test/mcp-subscription-budget.test.ts, mcp-docs and mcp-size at head d5e4e59: all 8 pass. On the preview, an anonymous tools/call subscription_budget gets a 401 asking for a token.

There is no terminal door, and that matches the page, which has none for this setting either. One small gap that is not blocking is noted inline: the test refuses a non-admin, but not an admin holding a write-scope token.

commented

The full gate on land was red; the pull is open again. Red tests:

  • the gate audit before any shard > a new file is in the gate with no list naming it, and explicit files are not audited
  • the gate audit before any shard > a new file is in the gate with no list naming it, and explicit files are not audited [76.57ms] [shard 103]

Red on top of #5452, which landed first from the same merge-queue candidate: red on its own on that base, or only together with them. Bring this pull up to date with the base and push.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs