From 918b8cddf9013c20943310e0c02a724aecb3fe94 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Mon, 29 Jun 2026 00:36:20 +0900 Subject: fix(mcp): support newline-delimited JSON framing + timeouts + abort signal propagation --- packages/session-orchestrator/src/orchestrator.ts | 5 +++++ packages/session-orchestrator/src/tools-filter.ts | 10 ++++++++++ 2 files changed, 15 insertions(+) (limited to 'packages/session-orchestrator/src') diff --git a/packages/session-orchestrator/src/orchestrator.ts b/packages/session-orchestrator/src/orchestrator.ts index aaf418a..616129b 100644 --- a/packages/session-orchestrator/src/orchestrator.ts +++ b/packages/session-orchestrator/src/orchestrator.ts @@ -760,6 +760,11 @@ export function createSessionOrchestrator( conversationId, ...(effectiveCwd !== undefined ? { cwd: effectiveCwd } : {}), ...(effectiveComputerId !== undefined ? { computerId: effectiveComputerId } : {}), + // Thread the turn's abort signal into the filter chain so a filter + // awaiting slow I/O (the MCP tools filter connecting to MCP servers) + // can be interrupted by POST /conversations/:id/stop instead of + // blocking the turn until its own timeout fires. + signal: controller.signal, }); const dispatch = deps.resolveDispatch?.() ?? defaultDispatchPolicy(); const turnLogger = deps.logger?.child({ conversationId, turnId }); diff --git a/packages/session-orchestrator/src/tools-filter.ts b/packages/session-orchestrator/src/tools-filter.ts index 28e82bf..22bc5cd 100644 --- a/packages/session-orchestrator/src/tools-filter.ts +++ b/packages/session-orchestrator/src/tools-filter.ts @@ -15,6 +15,15 @@ export interface ToolAssembly { readonly computerId?: string; /** The conversation this turn belongs to. */ readonly conversationId: string; + /** + * The turn's abort signal, threaded through the filter chain so a filter that + * awaits slow I/O (e.g. the MCP tools filter connecting to MCP servers) can be + * interrupted by `POST /conversations/:id/stop` instead of blocking the turn + * until its own timeout fires. Optional: omitted by paths that have no turn + * controller (e.g. the cache-warm probe), in which case filters fall back to + * their own timeouts. Filters that return a fresh assembly MUST preserve it. + */ + readonly signal?: AbortSignal; } /** Filter chain run once per turn to transform the tool set before it reaches runTurn. */ @@ -55,5 +64,6 @@ export function filterRemoteIncompatibleTools(assembly: ToolAssembly): ToolAssem ...(assembly.cwd !== undefined ? { cwd: assembly.cwd } : {}), ...(assembly.computerId !== undefined ? { computerId: assembly.computerId } : {}), conversationId: assembly.conversationId, + ...(assembly.signal !== undefined ? { signal: assembly.signal } : {}), }; } -- cgit v1.2.3