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

MCP `subscription_headroom`: set how much of each window the crew leaves for people, as the Subscriptions page does #7405

Merged opened this

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

Merged

A forge admin can now set an account's headroom over MCP with a new `subscription_headroom` tool. Headroom is how much of each 5-hour and weekly window the crew leaves for people. The tool takes `{ id, percent }`, the same input the Subscriptions page's "kept for people, %" form takes.

shipped production is serving it deploy e11532c

opened this

A forge admin can now set an account's headroom over MCP with a new subscription_headroom tool. Headroom is how much of each 5-hour and weekly window the crew leaves for people. The tool takes { id, percent }, the same input the Subscriptions page's "kept for people, %" form takes.

  • Rules: it checks the percent with the page's own headroomPct and saves with setHeadroom (both in src/core/standing.ts), so the page and the tool can't disagree.
  • Refusals: they reuse the page's wording from SUBSCRIPTION_WORDS. A percent outside 0–90 is refused as invalid with "Headroom is a whole percent from 0 to 90.", and an unknown id as not_found with "No such subscription.". 90 is HEADROOM_MAX, so the issue's 101 example is also refused.
  • Answer: { id, percent, says }, where says is the page's flash: "The crew now leaves N% of each window for people."
  • Access: a forge admin with an admin-scope token. The tool is marked as a write, so tools/list annotates it like the other writes.

The tool is in src/http/mcp/tools/subscription_headroom.ts. Its line in tools/index.ts and its row in docs/MCP.md were generated by bun scripts/mcp-tools.ts. The description is one short sentence, and test/mcp-size.test.ts still passes without raising any ceiling.

docs/MCP.md ("Room for a new tool") suggests adding an action to an existing tool rather than a new tool, such as subscription_budget. I didn't do that, because the issue names a separate subscription_headroom with its own { id, percent }.

Tests:

  • test/mcp-subscription-headroom.test.ts covers the issue's four cases: an admin sets 30 and the Subscriptions page's input then shows value="30"; 101 is refused in the page's words; an unknown id is refused; a non-admin is refused. All four fail with the tool removed.
  • test/mcp-write-scope.test.ts gains a valid-arguments row for the tool, so its admin-scope gate is checked like the other admin tools'.
  • The new test, test/mcp.test.ts, test/mcp-docs.test.ts, test/mcp-size.test.ts, test/mcp-write-scope.test.ts and test/mcp-tools.test.ts pass (221 tests), and bun x tsc --noEmit is clean.

To try it: as an admin, call tools/call with {"name":"subscription_headroom","arguments":{"id":"<id from subscriptions>","percent":30}}, then open Settings → Subscriptions.

🤖 Generated with Claude Code

Closes #5439

commented

A forge admin can now set an account's headroom over MCP with a new subscription_headroom tool. Headroom is how much of each 5-hour and weekly window the crew leaves for people. The tool takes { id, percent }, the same input the Subscriptions page's "kept for people, %" form takes.

  • Rules: it checks the percent with the page's own headroomPct and saves with setHeadroom (both in src/core/standing.ts), so the page and the tool can't disagree.
  • Refusals: they reuse the page's wording from SUBSCRIPTION_WORDS. A percent outside 0–90 is refused as invalid with "Headroom is a whole percent from 0 to 90.", and an unknown id as not_found with "No such subscription.". 90 is HEADROOM_MAX, so the issue's 101 example is also refused.
  • Answer: { id, percent, says }, where says is the page's flash: "The crew now leaves N% of each window for people."
  • Access: a forge admin with an admin-scope token. The tool is marked as a write, so tools/list annotates it like the other writes.

The tool is in src/http/mcp/tools/subscription_headroom.ts. Its line in tools/index.ts and its row in docs/MCP.md were generated by bun scripts/mcp-tools.ts. The description is one short sentence, and test/mcp-size.test.ts still passes without raising any ceiling.

docs/MCP.md ("Room for a new tool") suggests adding an action to an existing tool rather than a new tool, such as subscription_budget. I didn't do that, because the issue names a separate subscription_headroom with its own { id, percent }.

Tests:

  • test/mcp-subscription-headroom.test.ts covers the issue's four cases: an admin sets 30 and the Subscriptions page's input then shows value="30"; 101 is refused in the page's words; an unknown id is refused; a non-admin is refused. All four fail with the tool removed.
  • test/mcp-write-scope.test.ts gains a valid-arguments row for the tool, so its admin-scope gate is checked like the other admin tools'.
  • The new test, test/mcp.test.ts, test/mcp-docs.test.ts, test/mcp-size.test.ts, test/mcp-write-scope.test.ts and test/mcp-tools.test.ts pass (221 tests), and bun x tsc --noEmit is clean.

To try it: as an admin, call tools/call with {"name":"subscription_headroom","arguments":{"id":"<id from subscriptions>","percent":30}}, then open Settings → Subscriptions.

🤖 Generated with Claude Code

Approved

Clean. subscription_headroom is the page's POST /settings/subscriptions/:id/headroom over MCP: the same headroomPct boundary check (whole digits, 0–90, so fractions, negatives, exponents and 101 are refused), the same parameterised setHeadroom UPDATE, and the same SUBSCRIPTION_WORDS refusals. It is gated adminOnly + needsAdminScope + writes, and the gate is tested both in the new suite (a non-admin is refused and nothing changes) and through the row added to mcp-write-scope. No SQL or shell is built from input, nothing reads Content-Length, and no secret is returned. The tests cover set → the page shows 30, out of range, unknown id, and non-admin. Each would fail without the tool. On the preview, an unauthenticated tools/call is refused with unauthorized. The terminal gap (src/tui has no headroom control) is older than this pull and outside this issue, which asked for the MCP door only.

Nit, not blocking: the subscriptions tool's doc lists the handles that take its id (subscription_wake, subscription_budget and subscription_delete). Add subscription_headroom to that list the next time the file is touched.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs