ag-ui-protocol / ag-ui-protocol/ag-ui
[Feature]: @ag-ui/claude-agent-sdk: expose a handle for out-of-band per-run control (AbortController + active Query)
- Dominant language
- Python
- Stars
- 15.9k
- Forks
- 1.4k
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 163
Description
### Pre-flight Checklist
- [x] I have searched existing issues and this hasn't been requested yet.
### Problem or Motivation
`ClaudeAgentAdapter` gives a caller no way to influence a run from outside the
Observable returned by `run()`. Two concrete needs:
**(a) Cancel a run from outside and trigger the SDK's canonical abort cleanup.**
`run()` constructs an `AbortController` only when `config.queryTimeoutMs` is set,
and never lets a caller supply one. `interrupt()` is not a substitute — the SDK
documents it as requiring streaming input/output mode, and it does not run
`Options.abortController`'s cleanup path. A caller that needs to cancel a run in
response to an out-of-band signal (e.g. a pub/sub cancellation message) has no way
to reach the SDK's own teardown.
**(b) Reach the live `Query` mid-run.** After a transient failure it can be
necessary to re-register MCP servers via the SDK's raw control request
`request({ subtype: "mcp_set_servers" })`. The adapter already holds the `Query`
in its internal `activeQueries` map but does not expose it, and the SDK's `Query`
interface has no `setMcpServers` method — so a caller needs the whole `Query`
object, not a narrow proxy.
### Proposed Solution
1. **Caller-supplied `AbortController`** — accept one through a documented,
whitelisted forwarded prop, honored by `run()` and falling back to the
`queryTimeoutMs`-only controller when absent (the timeout wiring stands down
when a caller controller is supplied).
2. **`getActiveQuery(threadId): Query | undefined`** — a public accessor over the
existing `activeQueries` map, returning the live `Query` while a run is in
flight and `undefined` once it settles.
Both are additive. I'm happy to align on the exact shape before opening PRs.
### Alternatives Considered
- Abort via a per-adapter `config.abortController`: rejected — cancellation is
per-run, not per-adapter.
- Abort via `interrupt()`: insufficient — it is gated on streaming input/output
mode and does not run `Options.abortController` cleanup.
- A narrow `setMcpServers` proxy: the SDK exposes no such method, so callers need
the whole `Query`.
- A forwarded callback handing back the `Query`: the accessor is preferable — it
reuses state the adapter already keeps and adds no per-run prop.
### Additional Context
These would land as two independent PRs once the shapes are acknowledged.
Contributor guide
Assessment
This issue has not been assessed yet.