Problem
ToolHandlerContext exposes two booleans that aren't actually used by tools and leak a wire-layer concern into the public tool contract:
// src/rendering/types.ts
interface ToolHandlerContext {
emit: (fragment: AnyFragment) => void
attach: (image: ImageAttachment) => void
liveProgressEnabled: boolean // ← effectively unread
streamingFragmentsEnabled: boolean // ← wire-layer leak
nextStepParams?: NextStepParamsMap
nextSteps?: NextStep[]
structuredOutput?: StructuredToolOutput
}
Evidence
No tool branches on either flag. Searched all of src/mcp/ for reads of these fields:
src/mcp/tools/xcode-ide/shared.ts:19 — declares readonly liveProgressEnabled = false as a class member, never reads it.
src/mcp/resources/devices.ts:25-26, src/mcp/resources/simulators.ts:25-26 — pass-through wiring that sets both to false, never reads them.
- Zero other tool-side reads.
liveProgressEnabled is dead in DefaultStreamingExecutionContext too:
// src/utils/execution/tool-execution-context.ts
constructor(options) {
this.liveProgressEnabled = options.liveProgressEnabled ?? true; // stored
this.fragmentCallback = options.onFragment;
}
emitFragment(fragment) {
this.fragmentCallback?.(fragment); // does NOT branch on liveProgressEnabled
}
The class stores liveProgressEnabled and exposes it publicly but never reads its own copy.
streamingFragmentsEnabled does one thing, in one place:
// src/utils/tool-execution-compat.ts
onFragment: ctx.streamingFragmentsEnabled ? (fragment) => ctx.emit(fragment) : undefined
A single wire-layer decision: subscribe to fragments, or don't. The tool never needs to know.
Why this matters
The boundary already has all the information it needs to decide what to do with fragments:
- Runtime kind (
cli / mcp / daemon) is set at session creation in src/runtime/types.ts:19.
- Output format (
text / json / jsonl / raw) is known at the CLI command boundary in src/cli/register-tool-commands.ts.
- Render strategy (
text / cli-text / raw) is selected per session in src/rendering/render.ts.
Tools should always emit fragments. Whether those fragments are forwarded, recorded, or dropped is a render-session/wire-boundary concern, not a tool-contract concern.
Proposed change
Remove both fields from ToolHandlerContext. Push the wire decision down into the layers that already know:
src/rendering/types.ts — drop liveProgressEnabled and streamingFragmentsEnabled from ToolHandlerContext.
src/utils/tool-execution-compat.ts — collapse createStreamingExecutionContext so onFragment is always wired; let the render session decide what to do with fragments based on its strategy.
src/utils/execution/tool-execution-context.ts — drop liveProgressEnabled from DefaultStreamingExecutionContext and StreamingExecutionContextOptions. (StreamingExecutionContext in src/types/tool-execution.ts:14 would lose it too.)
src/cli/register-tool-commands.ts — replace the per-call liveProgressEnabled/streamingFragmentsEnabled wiring (around lines 76-80, 362) with a single render-session strategy selection.
src/utils/tool-registry.ts — drop the liveProgressEnabled: false / streamingFragmentsEnabled: false initialization (around lines 309-310). MCP-side handlers will just have their fragments captured by the render session as today.
src/runtime/tool-invoker.ts — drop the duplicated wiring at lines 465-466, 511-512, 537-538.
src/daemon/daemon-server.ts — the divergent liveProgressEnabled: false / streamingFragmentsEnabled: true case (lines 181-182) becomes a daemon-side render strategy choice rather than a tool-context flag.
src/mcp/resources/devices.ts, simulators.ts and src/mcp/tools/xcode-ide/shared.ts — remove the now-dead initializations.
- Test fixtures in
src/test-utils/test-helpers.ts and any __tests__/*.ts that set these fields manually — drop the field passes.
After this, the resulting ToolHandlerContext is:
interface ToolHandlerContext {
emit: (fragment: AnyFragment) => void
attach: (image: ImageAttachment) => void
nextStepParams?: NextStepParamsMap
nextSteps?: NextStep[]
structuredOutput?: StructuredToolOutput
}
Migration impact
External impact is minimal. These fields aren't part of any public type-author API; they're internal to the runtime layer. Tool authors don't read them (verified above). Test fixtures will need a small mechanical update.
The daemon's "forward fragments to client without rendering them locally" behavior is preserved by selecting a daemon-appropriate render strategy instead of toggling two booleans on the tool context.
Background
Surfaced during a docs accuracy audit of xcodebuildmcp.com/app/docs/_content/architecture.mdx. Full investigation report at /Volumes/Developer/xcodebuildmcp.com/docs/investigations/architecture-doc-accuracy-audit-2026-04-25.md (Finding 6). The architecture doc has been pre-emptively updated to omit these two fields from the documented ToolHandlerContext shape, on the assumption that this issue lands.
Acceptance criteria
Problem
ToolHandlerContextexposes two booleans that aren't actually used by tools and leak a wire-layer concern into the public tool contract:Evidence
No tool branches on either flag. Searched all of
src/mcp/for reads of these fields:src/mcp/tools/xcode-ide/shared.ts:19— declaresreadonly liveProgressEnabled = falseas a class member, never reads it.src/mcp/resources/devices.ts:25-26,src/mcp/resources/simulators.ts:25-26— pass-through wiring that sets both tofalse, never reads them.liveProgressEnabledis dead inDefaultStreamingExecutionContexttoo:The class stores
liveProgressEnabledand exposes it publicly but never reads its own copy.streamingFragmentsEnableddoes one thing, in one place:A single wire-layer decision: subscribe to fragments, or don't. The tool never needs to know.
Why this matters
The boundary already has all the information it needs to decide what to do with fragments:
cli/mcp/daemon) is set at session creation insrc/runtime/types.ts:19.text/json/jsonl/raw) is known at the CLI command boundary insrc/cli/register-tool-commands.ts.text/cli-text/raw) is selected per session insrc/rendering/render.ts.Tools should always emit fragments. Whether those fragments are forwarded, recorded, or dropped is a render-session/wire-boundary concern, not a tool-contract concern.
Proposed change
Remove both fields from
ToolHandlerContext. Push the wire decision down into the layers that already know:src/rendering/types.ts— dropliveProgressEnabledandstreamingFragmentsEnabledfromToolHandlerContext.src/utils/tool-execution-compat.ts— collapsecreateStreamingExecutionContextsoonFragmentis always wired; let the render session decide what to do with fragments based on its strategy.src/utils/execution/tool-execution-context.ts— dropliveProgressEnabledfromDefaultStreamingExecutionContextandStreamingExecutionContextOptions. (StreamingExecutionContextinsrc/types/tool-execution.ts:14would lose it too.)src/cli/register-tool-commands.ts— replace the per-callliveProgressEnabled/streamingFragmentsEnabledwiring (around lines 76-80, 362) with a single render-session strategy selection.src/utils/tool-registry.ts— drop theliveProgressEnabled: false/streamingFragmentsEnabled: falseinitialization (around lines 309-310). MCP-side handlers will just have their fragments captured by the render session as today.src/runtime/tool-invoker.ts— drop the duplicated wiring at lines 465-466, 511-512, 537-538.src/daemon/daemon-server.ts— the divergentliveProgressEnabled: false/streamingFragmentsEnabled: truecase (lines 181-182) becomes a daemon-side render strategy choice rather than a tool-context flag.src/mcp/resources/devices.ts,simulators.tsandsrc/mcp/tools/xcode-ide/shared.ts— remove the now-dead initializations.src/test-utils/test-helpers.tsand any__tests__/*.tsthat set these fields manually — drop the field passes.After this, the resulting
ToolHandlerContextis:Migration impact
External impact is minimal. These fields aren't part of any public type-author API; they're internal to the runtime layer. Tool authors don't read them (verified above). Test fixtures will need a small mechanical update.
The daemon's "forward fragments to client without rendering them locally" behavior is preserved by selecting a daemon-appropriate render strategy instead of toggling two booleans on the tool context.
Background
Surfaced during a docs accuracy audit of
xcodebuildmcp.com/app/docs/_content/architecture.mdx. Full investigation report at/Volumes/Developer/xcodebuildmcp.com/docs/investigations/architecture-doc-accuracy-audit-2026-04-25.md(Finding 6). The architecture doc has been pre-emptively updated to omit these two fields from the documentedToolHandlerContextshape, on the assumption that this issue lands.Acceptance criteria
ToolHandlerContextandStreamingExecutionContext.