Checks
Strands Version
1.35.0
Python Version
3.14.3 (CPython, macOS arm64, installed via uv)
Operating System
macOS 14.4.0 (Darwin 25.4.0, arm64 / Apple Silicon)
Installation Method
uv (PyPI)
Steps to Reproduce
When an Agent that has an MCPClient tool is garbage-collected at interpreter shutdown on Python 3.14, the cleanup path in Agent.__del__ raises PythonFinalizationError: cannot join thread at interpreter shutdown. The error is swallowed (Exception ignored while calling deallocator) but Strands also emits its own log lines and a traceback to stderr, which is visible to users.
Minimal pattern (excerpted and simplified from a real codebase using the public AWS Knowledge MCP endpoint):
from mcp.client.streamable_http import streamable_http_client
from rich import print as rprint
from rich.text import Text
from strands import Agent
from strands.hooks import HookProvider, HookRegistry
from strands.hooks.events import BeforeToolCallEvent
from strands.models import BedrockModel, CacheConfig
from strands.tools.executors import SequentialToolExecutor
from strands.tools.mcp import MCPClient
class ToolLoggingHook(HookProvider):
def register_hooks(self, registry: HookRegistry) -> None:
registry.add_callback(BeforeToolCallEvent, self.on_tool_start)
def on_tool_start(self, event: BeforeToolCallEvent) -> None:
rprint(Text(f"{event.agent.name} : {event.tool_use['name']}"))
mcp_client = MCPClient(
lambda: streamable_http_client("https://knowledge-mcp.global.api.aws")
)
agent = Agent(
name="RetrievalAgent",
model=BedrockModel(
model_id="us.anthropic.claude-haiku-4-5-20251001-v1:0",
cache_config=CacheConfig(strategy="auto"),
),
system_prompt="Search AWS docs and summarize.",
tools=[mcp_client],
tool_executor=SequentialToolExecutor(),
callback_handler=None,
hooks=[ToolLoggingHook()],
)
# Trigger multiple MCP tool calls (search + read) then exit.
result = agent("Explain AWS Bedrock AgentCore briefly.")
print(str(result)[:200])
Environment:
uv init --python 3.14
uv add "strands-agents==1.35.0" "strands-agents-tools[rss]==0.4.1" "mcp==1.27.0" "rich==14.3.3" "boto3[crt]==1.42.90"
AWS_PROFILE=... AWS_DEFAULT_REGION=us-east-1 uv run python repro.py
Notes on narrowing the repro:
- A single agent with a single MCP tool call that returns quickly does not always trigger the warning.
- The issue reliably appears when the agent performs multiple MCP tool calls in one turn (e.g.
aws___search_documentation then aws___read_documentation), then the interpreter exits. It also reproduces inside a Swarm that routes through an MCP-owning agent.
- Python 3.13 does not raise this error; the underlying cause is cpython#113964, Python 3.14's stricter
PythonFinalizationError around joining non-daemon threads at shutdown.
Expected Behavior
On Python 3.14, Agent.__del__ / MCPClient.remove_consumer should cleanly shut down the MCP transport's background thread without raising PythonFinalizationError, so that no traceback appears on stderr during interpreter finalization.
Actual Behavior
Stderr at program exit (even when the agent's response has already been produced successfully):
error=<cannot join thread at interpreter shutdown> | failed to cleanup MCP client
provider=<MCPClient>, error=<Failed to cleanup MCP client: cannot join thread at interpreter shutdown> | failed to remove provider consumer
Exception ignored while calling deallocator <function Agent.__del__ at 0x...>:
Traceback (most recent call last):
File ".../strands/agent/agent.py", line 742, in __del__
File ".../strands/tools/registry.py", line 731, in cleanup
File ".../strands/tools/registry.py", line 722, in cleanup
File ".../strands/tools/mcp/mcp_client.py", line 316, in remove_consumer
strands.types.exceptions.ToolProviderException: Failed to cleanup MCP client: cannot join thread at interpreter shutdown
The agent's response is complete and correct; the traceback is purely noise from shutdown. However, the Traceback / Exception ignored / Failed to cleanup wording makes end users (especially beginners working through tutorials) think the run has failed.
Additional Context
This surfaced while validating a technical book's sample code under Python 3.14 (AWS AgentCore CLI v0.8.2 bumped its default runtime to PYTHON_3_14 on 2026-04-16). The book's readers run MCP-enabled Strands agents in handson chapters, and the shutdown traceback is likely to cause unnecessary confusion.
Related upstream context:
Possible Solution
Two options worth exploring:
- Mark the MCP background thread as a daemon thread (in
strands/tools/mcp/mcp_client.py) so the interpreter does not attempt to join it at shutdown. Combine with best-effort stop() in Agent.__del__.
- Guard
remove_consumer / __del__ against PythonFinalizationError: if sys.is_finalizing() is True, skip the thread-join path and just let the runtime reclaim resources, avoiding the traceback entirely.
Option 2 is less invasive and should fully silence the shutdown noise without changing thread lifetime semantics during normal operation.
Related Issues
Checks
Strands Version
1.35.0
Python Version
3.14.3 (CPython, macOS arm64, installed via uv)
Operating System
macOS 14.4.0 (Darwin 25.4.0, arm64 / Apple Silicon)
Installation Method
uv (PyPI)
Steps to Reproduce
When an
Agentthat has anMCPClienttool is garbage-collected at interpreter shutdown on Python 3.14, the cleanup path inAgent.__del__raisesPythonFinalizationError: cannot join thread at interpreter shutdown. The error is swallowed (Exception ignored while calling deallocator) but Strands also emits its own log lines and a traceback to stderr, which is visible to users.Minimal pattern (excerpted and simplified from a real codebase using the public AWS Knowledge MCP endpoint):
Environment:
Notes on narrowing the repro:
aws___search_documentationthenaws___read_documentation), then the interpreter exits. It also reproduces inside aSwarmthat routes through an MCP-owning agent.PythonFinalizationErroraround joining non-daemon threads at shutdown.Expected Behavior
On Python 3.14,
Agent.__del__/MCPClient.remove_consumershould cleanly shut down the MCP transport's background thread without raisingPythonFinalizationError, so that no traceback appears on stderr during interpreter finalization.Actual Behavior
Stderr at program exit (even when the agent's response has already been produced successfully):
The agent's response is complete and correct; the traceback is purely noise from shutdown. However, the
Traceback/Exception ignored/Failed to cleanupwording makes end users (especially beginners working through tutorials) think the run has failed.Additional Context
This surfaced while validating a technical book's sample code under Python 3.14 (AWS AgentCore CLI v0.8.2 bumped its default runtime to
PYTHON_3_14on 2026-04-16). The book's readers run MCP-enabled Strands agents in handson chapters, and the shutdown traceback is likely to cause unnecessary confusion.Related upstream context:
Possible Solution
Two options worth exploring:
strands/tools/mcp/mcp_client.py) so the interpreter does not attempt to join it at shutdown. Combine with best-effortstop()inAgent.__del__.remove_consumer/__del__againstPythonFinalizationError: ifsys.is_finalizing()is True, skip the thread-join path and just let the runtime reclaim resources, avoiding the traceback entirely.Option 2 is less invasive and should fully silence the shutdown noise without changing thread lifetime semantics during normal operation.
Related Issues