Skip to content

Fix: honor abort signal during in-flight MCP tool calls - #2373

Open
kyungseopk1m wants to merge 1 commit into
huggingface:mainfrom
kyungseopk1m:fix/mcp-client-abort-during-tool-call
Open

Fix: honor abort signal during in-flight MCP tool calls#2373
kyungseopk1m wants to merge 1 commit into
huggingface:mainfrom
kyungseopk1m:fix/mcp-client-abort-during-tool-call

Conversation

@kyungseopk1m

@kyungseopk1m kyungseopk1m commented Aug 14, 2026

Copy link
Copy Markdown

Problem

McpClient.processSingleTurnWithTools accepts an abortSignal and checks it in a few places, but the actual MCP tool call ignores it. client.callTool was called with signal on the request params object:

await client.callTool({ name: toolName, arguments: toolArgs, signal: opts.abortSignal });

The installed @modelcontextprotocol/sdk signature is callTool(params, resultSchema?, options?), where signal lives on the third argument (RequestOptions). Putting it on params means it's never forwarded to the transport, so aborting mid-tool-call has no effect on the in-flight request. It keeps running, and cancellation only takes effect a turn later, once the result comes back and the post-call abort check fires.

Fix

Pass the signal through RequestOptions instead:

await client.callTool({ name: toolName, arguments: toolArgs }, undefined, {
	signal: opts.abortSignal,
});

Also added abort checks at the top of the tool-call loop and in the catch block (the existing check only ran right after the callTool await), so an abort landing between two tool calls in the same turn, or during a tool call that rejects for another reason, still ends the turn cleanly instead of continuing or surfacing as a normal tool-failure message.

Tests

McpClient.abort.spec.ts covers: a pending tool call actually receiving the signal through the SDK (fails without the params/options fix), abort landing between two tool calls in one turn, a tool that resolves normally after the caller already aborted, and that a real non-abort tool error still surfaces normally. Checked each of the three abort-check locations individually by reverting it in isolation: each has a test that fails without it.

pnpm vitest run in packages/mcp-client: 22/22 passed. tsc and eslint clean.


One thing this doesn't touch: when a tool call is aborted mid-turn, the assistant message with its tool_calls is already pushed to messages, but the matching tool response never gets appended, leaving the message list with a tool_calls entry that has no corresponding tool message, which most providers reject on the next turn. Is there a preferred way to handle this, e.g. drop the dangling assistant message, or push a synthetic cancelled tool message?


Note

Low Risk
Localized change to MCP client tool execution and cancellation; no auth or data-path changes, with dedicated test coverage.

Overview
Fixes cancellation during MCP tool execution by passing opts.abortSignal through the SDK’s third callTool argument (RequestOptions) instead of on the params object, so in-flight tool requests can actually be aborted.

Tightens abort handling in the tool loop: checks abortSignal before each tool call, after callTool returns, and in the catch path so aborts stop the turn cleanly (no fake tool-failure messages, no second tool in the same turn, no extra LLM turn).

Adds McpClient.abort.spec.ts with Vitest coverage for signal forwarding, mid-turn abort between tools, tools that ignore abort, and unchanged non-abort error paths.

Reviewed by Cursor Bugbot for commit 29ffca3. Bugbot is set up for automated code reviews on this repo. Configure here.

McpClient kept running a tool call to completion after the caller's
AbortSignal fired, so cancelling mid-turn only took effect on the next
LLM turn. Add abort checks at the top of the tool-call loop, right
after the SDK call resolves, and in its catch block, so an abort
during a tool call ends the turn immediately.

Also fix client.callTool() to pass the signal through RequestOptions
(the SDK's third argument), not the request params, where it was
silently ignored by the transport.
@coyotte508

Copy link
Copy Markdown
Member

let's archive tiny-agents & mcp-client @julien-c @gary149 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants