Claude Code mod: stop paid Sume calls after N in a session
Write a Claude Code mod that counts paid Sume MCP calls with a tool.call hook, denies call N+1, and fails closed. Code, matcher, and what it cannot cap.

To stop a Claude Code session from making more than N paid Sume calls, write a mod with a tool.call hook that matches Sume's paid MCP tool names, counts each call in a variable, and returns { deny: '...' } without calling next once the count passes N. Claude Code's mods overview and events guide (read 2026-10-03) document exactly those pieces: tool.call fires for MCP tool calls, a matcher can be a regular expression over the tool name, and a hook's variables are shared across its hooks for the life of the session.
The mod below is a counter, not a wallet. It limits how many paid calls the model may try; what each call costs is still decided by Sume's admission check and your balance. Mods need Claude Code v2.1.287 or later.
What name does a Sume tool have inside Claude Code?
Claude Code names an MCP tool mcp__<server>__<tool>, and the events guide's own example matches a whole server with /^mcp__github__/. If you add Sume as claude mcp add --transport http sume https://mcp.sume.com/mcp, as the MCP quickstart does, the server name is sume and a paid image call is mcp__sume__generate_image. If you named the server anything else, change the prefix in the pattern.
The tool ids come from Sume's tools and gates page, which lists the paid set: generate_image, generate_video, music_create, tts_create, stt_create, image_upscale_create, rmbg_create, video_upscale_create, kling-motion-control_create, plus the Avatar creates. Run tools_list in a live session to confirm what your account sees; the docs say not to assume parity with the HTTP API.
What does the mod look like?
A mod is a plugin with .claude-plugin/plugin.json, hooks/hooks.json pointing at your code, and hooks/register.js, per the overview. The module exports register(on). This register.js is the whole guard:
const PAID = /^mcp__sume__(generate_image|generate_video|music_create|tts_create|stt_create|image_upscale_create|rmbg_create|video_upscale_create|avatars_create|avatar-videos_create|avatar-image-to-video_create|kling-motion-control_create|script_run)$/
const LIMIT = 5
let used = 0
async function cap($, e, next) {
if (used >= LIMIT) {
return { deny: 'Paid Sume call limit (' + LIMIT + ') reached for this session. Stop and ask the user.' }
}
used += 1
$.ui.log('paid Sume call ' + used + ' of ' + LIMIT + ': ' + e.tool)
return next(e)
}
export function register(on) {
on('tool.call', { tool: PAID }, cap).catch(async ($, e, next) => {
return { deny: 'The Sume call guard failed (' + next.error.kind + '), so this call was not run.' }
})
}Why is script_run in the list?
Sume's script_run tool runs a short JavaScript program on the Sume side that can loop over several paid tools. Claude Code sees one call to script_run, not the child calls, so a hook on the individual tool names would miss them. Sume's docs say the script itself is bounded by timeout_seconds (5 to 55), max_calls, and max_paid_calls, and that paid creates inside it still need their own idempotency_key. Counting a script_run as one paid call, as above, is the conservative choice; denying it outright is stricter.
The .catch handler matters because of how Claude Code treats a failing hook: one that throws or times out before calling next is skipped, and the tool call proceeds. Attaching .catch makes the guard answer with a deny instead. The post on mod hooks that fail open goes through that case.
What can this mod not cap?
A counter inside Claude Code is easy to over-trust. The table lists what it bounds and what it leaves open, so you can decide which other layers you still need. None of the limits below is a flaw in the hook; each is a consequence of where a mod runs and what a Claude Code session can see.
| Question | Answer | Source |
|---|---|---|
| Does the count survive a new session? | Not by itself. The count is a variable in the mod's module; nothing here writes it to disk, so assume a new session starts at zero. | Claude Code mods overview |
| Does a denied call count? | Not in this code: the check runs before the increment, so only allowed calls are counted. | This example |
| Does it see calls inside script_run? | No, only the script_run call itself. | Sume tools and gates |
| Does it know the price of a call? | No. Price comes from Sume's admission check; dry_run=true previews it. | Sume tools and gates |
| Does it bind a client that is not Claude Code? | No. Cursor, Codex, or a script using the same key are unaffected. | Claude Code mods overview |
What should I pair it with?
No single layer should carry the spend limit. These pairings use only gates that Sume documents and settings that Claude Code documents, and each one covers a gap the counter leaves.
- Connect with OAuth and leave Write off when the task is read-only: a
mcp:readsession cannot see paid tools, and a paid call returnsinsufficient_scope(OAuth and API keys). - Have the agent call
dry_run=trueorgeneration_admission_previewfirst, and sendmax_spend_usdon the real call. Sume enforcesmax_spend_usdonly when it is provided, so a mod can also deny any paid call that omits it. - Use Claude Code permission
askrules for the same tools if you want a human in the loop rather than a counter; the ask-rule post covers that route. - Treat the counter as one layer. Claude Code's docs describe mods as code that runs with your permissions and is not sandboxed, and
--safe-modeturns installed mods off.
Sources
Related posts
More in Developers
- Claude Code mod hook skipped and a paid Sume call still ran
A Claude Code mod hook that throws or times out is skipped, so the call runs anyway. Add .catch to fail closed, and know what a mod still cannot guarantee.
- Claude Code mod: ask before a paid Sume call (and claude -p)
A Claude Code mod can hold a paid Sume MCP call with $.ui.ask and show max_spend_usd in the question. In claude -p nobody answers, so it refuses. Code inside.
- Claude Code one-off routine: fire one Sume run once, rerun safely
A one-off routine auto-disables after firing. If you rerun it, send the same Sume Idempotency-Key so a repeat doesn't create a second paid run.
- Claude Code routine limit of 30 fires an hour: use a bulk run
Run now and API fires share 30 an hour per routine. For 40 videos, fire the routine once and let one Sume bulk run of up to 100 items do the fan-out.
Written by Sume