Skip to content

feat(mcp): an optional Usage domain so agents can read quota without root management - #1451

Merged
Juliusolsson05 merged 8 commits into
integration/batch-2026-09-27-ofrom
feat/usage-mcp-domain
Sep 27, 2026
Merged

Juliusolsson05 merged 8 commits into
integration/batch-2026-09-27-ofrom
feat/usage-mcp-domain

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Fixes #1339

Why

Orchestrating agents (the manager/worker fleet, review pipelines) spawn child agents across providers with no view of quota. Only root management's ac_usage_read exposed usage, and that domain grants full app control and is confirmation-gated. The fleet found Grok at 100% only by accident.

What

A new optional built-in MCP domain, usage, with one read-only tool:

  • usage_read returns the same sanitized snapshot the Usage screen and ac_usage_read show: per provider, the window, percent used, severity, reset time and fetch time, plus per-provider errors. It covers Claude, Codex, Grok and OpenCode (z.ai).
  • One reader for both tools: usageService.readUsageSnapshotForTools (the JSON round trip is the sanitization). Root management's usage.read calls it too, so the two can never disagree.
  • No force input. A fleet polling with force would bypass the 30 s cache and hit every provider's quota endpoint on every call.
  • Errors are curated: "Usage is unavailable." / "Usage could not be read." Never provider URLs or OS text (q22). A provider error stays an error row, never zero usage. Only the EXACT sentences Agent Code itself throws pass through (steering q132, security); every other provider or filesystem message becomes fixed text, so no path, token or provider-supplied tail reaches an agent. It is pinned at the sanitizer, through the real Usage reader's error row, and through the real usage_read tool (usageErrorBoundary.test.ts).
  • The domain is configurable on every provider, off in the shipped defaults, and not confirmation-gated. It is parent-held-only (review a): an orchestrating agent can grant it to a child only when it holds it itself. Settings → MCP lists it as "Usage".

Tests

  • src/mcp/runtime/usageTools.test.ts runs through a real MCP client against a recorded ac_usage_read answer (testing/fixtures/usage/, fetched by B6 from the owner's machine and read in full: no credentials). It checks:
    • an agent WITHOUT root management gets that exact snapshot, all four sources;
    • the recorded Grok error reaches it as an error row;
    • there is no force input;
    • there is no tool or instructions without the domain;
    • failures are curated;
    • the domain is configurable on every provider.
  • persistence.test.ts: usage is off in the shipped defaults for every provider. That lives beside the renderer's list, so the node program doesn't import renderer files.
  • src/main/usage/usageSnapshotForTools.test.ts (review c) drives the REAL readUsageSnapshotForTools over the real Usage service, with only the provider reads replaced by the recorded rows:
    • the Grok failure stays an error row;
    • two default reads hit the providers once, so the default never forces.
      Main passes the reader itself rather than a lambda, so there is no unpinned wrapper where force could be added.
  • createBuiltInMcpServer.test.ts: a child cannot be granted usage by a parent that does not hold it (review a).
  • Mutations caught:
    • registering the tools without the domain gate (1 red);
    • the reader dropping error rows (6 red);
    • a force: true default (1 red);
    • instructions not shipped with the domain (1 red);
    • any prefix-shaped provider message passing whole (10 red before the q132 fix).
  • npx tsc -b clean at b8b7506. The MCP, usage and settings suites pass 229/229 across 25 files.

Merge notes

  • origin/main was merged in: conflicts in src/main/index.ts (imports) and BuiltInMcpHttpHost.ts (the dependency type) were resolved by keeping both sides.
  • Open feat(titles): add opt-in automatic agent titles #1216 edits the same four MCP/index files on adjacent lines; whichever lands second resolves a trivial conflict.

🤖 Generated with Claude Code

Juliusolsson05 and others added 4 commits September 26, 2026 18:53
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…root management (#1339)

usage_read returns the same sanitized snapshot as the Usage screen and
ac_usage_read (one shared readUsageSnapshotForTools, no force, the 30 s
cache). Configurable on every provider, off by default, not gated. The
tool-boundary test reads a recorded ac_usage_read answer that the manager
supplies (testing/fixtures/usage, pending).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1339)

The manager's raw answer from the owner's machine (read in full: plan names,
percentages, labels; no credentials). Pins that the recorded Grok error row
reaches the agent as an error, not zero usage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	src/main/index.ts
#	src/mcp/runtime/BuiltInMcpHttpHost.ts
@Juliusolsson05 Juliusolsson05 added type:feature New capability (out of quality-loop fix scope) class:C7-new-feature Bug cluster of a recently shipped feature sev:P1 Core flow broken or badly degraded labels Sep 27, 2026
Juliusolsson05 and others added 4 commits September 27, 2026 10:56
 review a)

An agent holding only Orchestration could mint the off-by-default usage
domain for a child and read the quota back; usage now joins the parent-held-
only set. sanitizeUsageError passed any message naming auth.json verbatim,
including an fs error's absolute credential path; only Agent Code's own
fixed auth.json messages keep their words, the rest is one curated sentence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew a)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…q132, security)

sanitizeUsageError passed messages by prefix/substring (Keychain, '<Provider>
auth.json ...', 'Grok login expired'), so a prefix-shaped error with a path
or token tail passed whole to the usage MCP domain. It now keeps only the
exact sentences Agent Code throws; everything else is fixed text. Pinned at
the sanitizer, the real Usage reader's error row and the real usage_read tool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tructions with the domain (#1451 review c)

- usageSnapshotForTools.test.ts drives the real readUsageSnapshotForTools over
  the real Usage service (only the provider reads replaced, with the recorded
  rows): the Grok error stays an error row, and two default reads hit the
  providers once. Dropping error rows (6 red) or a force default (1 red) fail.
- main passes the reader itself, not a lambda, so no unpinned wrapper can add
  force.
- usageTools.test.ts asserts the instructions ship WITH the domain (dropping
  them: 1 red) and types the fixture rows as recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Round 1 disposition at b8b7506 (a: FIX-BEFORE-MERGE, b: MERGE-READY, c: FIX-BEFORE-MERGE), plus steering q132.

a

  • Major, fixed (16cdb0f): a default agent could hand usage to a child it spawned. usage is now in PARENT_HELD_ONLY_BUILT_IN_MCP_DOMAINS, and the child clamp is pinned in createBuiltInMcpServer.test.ts. The plan's gating ruling is updated (604e2b6).
  • Minor, fixed and widened by q132 (f8fc043, security): the absolute credential path in auth-file errors.
    • sanitizeUsageError now passes only the exact first-party sentences in FIRST_PARTY_USAGE_MESSAGES.
    • Everything else becomes fixed text, including Keychain and auth-file errors.
    • usageErrorBoundary.test.ts pins five prefix-shaped messages (path, token, URL tails) at three points: the sanitizer, the real Usage reader's error row, and the real usage_read tool. All 10 were red before the fix.

b

  • MERGE-READY.
  • Its surviving mutation (main's wiring with force: true) is closed by c's fix below.
  • The deleted Settings row stays a residual: a UI coverage gap, not a defect.

c (each fixed fail-first in b8b7506)

  • Major, never-zero unpinned at the reader: fixed. The new usageSnapshotForTools.test.ts runs the real reader and service, with only the provider reads replaced by the recorded Codex row and the Grok expiry. Filtering error rows out: 6 red.
  • Major, no-force unpinned outside the mock: fixed. The same test shows two default reads hit the providers once and the second is a cache hit, so a force: true default is 1 red. Main now passes readUsageSnapshotForTools itself instead of () => readUsageSnapshotForTools(). usage_read calls it with no argument (already pinned), so no wrapper is left where force could slip in.
  • Minor, instructions shipped with the domain: now asserted. Dropping them: 1 red.
  • Minor, fixture type annotation: fixed to the recorded { provider, status, message?, rows? } shape, and the cast is gone.
  • Minor, stale count in the body: fixed (229/229 across 25 files at this head).
  • Suspicion, instructions without a wired reader: accepted, as c judged it. The tool degrades to "Usage is unavailable."
  • Not pinned by design: exact curated wording (mutation 5), which c also would not pin.

npx tsc -b clean. Next: a capped verification pass for a and c.

🤖 Generated with Claude Code

@Juliusolsson05

Copy link
Copy Markdown
Owner Author

B6: the q132 security hold is lifted. sanitizeUsageError keeps only exact first-party sentences (FIRST_PARTY_USAGE_MESSAGES) and maps everything else to fixed text. It is batched in 1461.

@Juliusolsson05
Juliusolsson05 changed the base branch from main to integration/batch-2026-09-27-o September 27, 2026 19:16
@Juliusolsson05
Juliusolsson05 merged commit e03147a into integration/batch-2026-09-27-o Sep 27, 2026
2 checks passed
@Juliusolsson05
Juliusolsson05 deleted the feat/usage-mcp-domain branch September 27, 2026 19:16
Juliusolsson05 added a commit that referenced this pull request Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

class:C7-new-feature Bug cluster of a recently shipped feature sev:P1 Core flow broken or badly degraded type:feature New capability (out of quality-loop fix scope)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(mcp): optional built-in Usage MCP so agents can check provider quota before spawning work

1 participant