Use the mods API changedplugins/mods/api
Upstream edited this page at 25 Oct 2026 14:35 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 25 Oct 2026 14:37 UTC.
Upstream edited
Recorded here
Lines+51added
Lines−5removed
From line
309
where the diff opens
First seen
1 Oct 2026
this site's first read of the page
Recorded edits15to this page, all time
### Calls refused in `claude -p` runs and Agent SDK sessions ### A `claude -p` run with your mod never exits ### Read all settings, one source, or one key
The whole hunk
from line 309, old and new numbered
/
from line 309
309309
310310#### What refused calls return
311311
312In the results, `<reason>` stands for `the session was stopped and nobody has sent input since`:
312In the results, `<reason>` stands for the reason the call was refused. After an interruption, that's `the session was stopped and nobody has sent input since`. For other reasons, see [Calls refused in `claude -p` runs and Agent SDK sessions](#calls-refused-in-non-interactive-runs).
313313
314| Some refused calls | Result until the user's next input |
314| Some refused calls | Result |
315315| :- | :- |
316316| `$.prompt.submit` | Resolves with `{ drop: '<reason>' }` and queues no prompt |
317317| `$.command.run` | Rejects with an error whose message ends with `<reason>` |
from line 320
320320| `$.session.send` to an [agent team](https://code.claude.com/docs/en/agent-teams.md) teammate | Resolves with `{ isDelivered: false, reason: 'Teammate "<name>" was not messaged: <reason>.' }`, where `<name>` is the teammate's name |
321321| [`$.tool.call`](https://code.claude.com/docs/en/plugins/mods/reference#mods-api-methods) of Bash with `run_in_background: true`, Monitor, or CronCreate | Resolves with `{ deny: '<tool> was not run: it starts background work, a schedule or a goal, and <reason>' }`, where `<tool>` is the tool's name |
322322
323Along with these `$.session.send` and `$.tool.call` results, the user can see the [`Stopped:` toast](https://code.claude.com/docs/en/plugins/mods/troubleshoot#stopped-type-a-message-to-continue).
323When one of these `$.session.send` or `$.tool.call` calls is refused after an interruption in an interactive session, the user can see the [`Stopped:` toast](https://code.claude.com/docs/en/plugins/mods/troubleshoot#stopped-type-a-message-to-continue).
324324
325325#### Work your mod started before the interruption
326326
from line 337
337337
338338When a scheduled job comes due or a monitor raises an event before the user's next input, the user can see the [`Stopped:` toast](https://code.claude.com/docs/en/plugins/mods/troubleshoot#stopped-type-a-message-to-continue).
339339
340### Calls refused in `claude -p` runs and Agent SDK sessions
341
342In a `claude -p` run or an [Agent SDK](https://code.claude.com/docs/en/agent-sdk/overview.md) session, a result such as `{ drop: 'the run reached its --max-turns limit' }` means the run refused your mod's call.
343
344A refused `$.prompt.submit` resolves with `{ drop: '<reason>' }`. Other refused calls, such as `$.command.run` and `$.agent.spawn`, give the results shown in [What refused calls return](#what-refused-calls-return) with the reason in place of `<reason>`. Find your `<reason>` below for what to do about it:
345
346* **`the run reached its --max-turns limit`**: turns from your mod's prompts and from subagents it spawns count toward [`--max-turns`](https://code.claude.com/docs/en/cli-reference#cli-flags) or the Agent SDK's [turn limit](https://code.claude.com/docs/en/agent-sdk/configuration.md#limit-turns-and-spend). For more turns, start the run with a higher limit. If you can't, have your mod stop making the refused call. If the run takes another prompt from the user or the SDK application, the turn count resets and the run accepts calls again.
347* **`the run is past its --max-budget-usd limit`**: start the run with a higher `--max-budget-usd`, or raise the Agent SDK's [spend limit](https://code.claude.com/docs/en/agent-sdk/configuration.md#limit-turns-and-spend). If you can't, have your mod stop making the refused call. The limit applies to the run's total cost so far. That total keeps adding up when the run takes another prompt, so a retry returns the same reason.
348* **`the host has ended its input`**: for example, the SDK application ended its [streaming input](https://code.claude.com/docs/en/agent-sdk/streaming-vs-single-mode.md), or stdin closed on a run started with [`--input-format stream-json`](https://code.claude.com/docs/en/cli-reference#cli-flags). The run is ending and takes no more input, so a retry returns the same reason. Have your mod stop making the refused call. If you start the run yourself, next time keep that input open until your mod has no more calls to make.
349* **`the session was stopped and nobody has sent input since`**: the run was [interrupted](#calls-and-background-work-after-the-user-interrupts-a-turn), for example with the Agent SDK's [`interrupt()`](https://code.claude.com/docs/en/agent-sdk/typescript#query-object). To make the call again after the next message from the user or the SDK application, follow [Retry a refused or dropped call](#retry-a-refused-or-dropped-call).
350
351### A `claude -p` run with your mod never exits
352
353If your mod submits a prompt at the end of every turn, each prompt starts another turn. Without [`--max-turns`](https://code.claude.com/docs/en/cli-reference#cli-flags) or `--max-budget-usd`, a `claude -p` run that loads the mod goes on until you stop it yourself.
354
355To make the run end on its own, add either flag to the command in your shell. If other people start the run, have your mod stop submitting prompts once its work is done. This command adds `--max-turns`:
356
357```bash theme={null}
358claude -p "find the TODOs" --max-turns 5
359```
360
361Once the run reaches that limit, the mod's next prompt is [refused](#calls-refused-in-non-interactive-runs) instead of starting a turn, and the run ends.
362
363If the run still goes past the limit, run `claude --version` in your shell, and [update Claude Code](https://code.claude.com/docs/en/setup.md#update-claude-code) if yours is earlier than v2.1.304. The mod's prompts are refused at the limit from v2.1.299. If your mod spawns subagents, their turns count toward `--max-turns` from v2.1.304.
364
340365## Send and receive messages between sessions
341366
342367A mod can send a plain-text message to another of your sessions, to one of this session's subagents, or to a teammate in its [agent team](https://code.claude.com/docs/en/agent-teams.md). It can also observe the messages that arrive and leave.
from line 411
386411| `$.http` | `fetch(url, init)` over `http` or `https`. It resolves to `{ status, ok, headers, text }` once the body is read. |
387412| `$.store` | A JSON key-value store of your plugin's own, kept between sessions |
388413| `$.env` | `get` and `set` environment variables. Write the name as a string literal. |
389| `$.settings` | `read` what the settings files and managed policy hold |
414| `$.settings` | `read()` resolves to all settings, merged into one object. With `{ source }` or `{ key }`, it resolves to [one source's settings or one key's value](#read-one-source-or-one-key) instead. |
390415| `$.session` | `messages()` returns the transcript as a list of `{ role, text, toolUses }`. Also the working directory, model, and more. [`usage()`](https://code.claude.com/docs/en/plugins/mods/reference#mods-api-methods) returns context window use and plan limits. |
391416| `$.mcp` | `call` a tool on a connected MCP server |
392417
from line 428
403428
404429* **`$.process.spawn started, and a plugin withheld its result:`**: the refusing mod hadn't read the command's output to the end. Claude Code stops the command if it's still running.
405430* **`$.process.spawn ran, and a plugin withheld its result:`**: the refusing mod had read the command's output to the end, so the command had exited
431
432### Read all settings, one source, or one key
433
434`$.settings.read()` resolves to one object that holds the settings from all sources in use, merged by [precedence](https://code.claude.com/docs/en/settings.md#settings-precedence). Pass `{ source }` to read one source instead, `{ key }` to read one key, or both:
435
436| Call | Resolves to |
437| :- | :- |
438| `$.settings.read()` | The merged settings as one object. Its top-level keys are those of a `settings.json` file, such as `permissions` and `hooks`. |
439| `$.settings.read({ source: 'policy' })` | That one source's settings as one object, or `{}` when that source holds no settings |
440| `$.settings.read({ key: 'spinnerTipsEnabled' })` | That key's value in the merged settings, or `undefined` when the key isn't set |
441| `$.settings.read({ key: 'spinnerTipsEnabled', source: 'user' })` | That key's value in that one source, or `undefined` when that source doesn't set it |
442
443For `source`, pass the name of the one settings source to read:
444
445* **`user`**: `~/.claude/settings.json`
446* **`project`**: `.claude/settings.json`
447* **`local`**: `.claude/settings.local.json`
448* **`flag`**: what you pass with `--settings`, and the SDK's inline settings
449* **`policy`**: [managed settings](https://code.claude.com/docs/en/managed-settings.md#how-claude-code-combines-managed-sources)
450
451For `key`, pass the exact name of one top-level key, not a dotted path into a nested object. The `key` argument requires Claude Code v2.1.304 or later.
406452
407453## Next steps
408454
No line in this hunk matches that.