Troubleshooting Tool Execution Errors and Timeouts in aisuite: A Complete Guide

aisuite handles tool execution through a layered architecture involving MCPToolWrapper for JSON-Schema translation, MCPClient for transport management, and LocalExecutor for shell command timeouts, with specific error handling paths for HTTP and stdio transports.

aisuite is an open-source Python library that standardizes interactions with multiple AI providers and external tools through a unified interface. When integrating MCP (Model Context Protocol) servers or local shell commands, developers frequently encounter execution failures, connection errors, and timeout issues. Understanding the internal architecture of tool wrappers, client transports, and shell executors is essential for diagnosing these errors effectively.

Understanding the Tool Execution Architecture

Tool Wrappers and Schema Translation

The aisuite.mcp.tool_wrapper.MCPToolWrapper class creates Python callables that translate a tool's JSON-Schema into proper signatures, docstrings, and type annotations. When invoked, the wrapper forwards requests to the underlying MCP client via self.mcp_client.call_tool. This abstraction layer ensures that LLM-generated tool calls match the expected schema while providing clean Python interfaces for developers.

MCP Client Transport Layer

The aisuite.mcp.client.MCPClient manages transport connections via stdio or HTTP protocols. The call_tool method at line 616 determines which transport to use and delegates to async helpers _async_call_tool or _async_call_tool_http. For HTTP transport, the client handles JSON-RPC request formatting and response parsing, while stdio transport manages subprocess communication with persistent MCP servers.

Local Shell Execution Engine

For local shell tools, aisuite uses platform.coworker.tools.shell.LocalExecutor. The run method at line 166 spawns a persistent shell process, injects a completion marker (trailer), and enforces per-command timeouts. This executor maintains shell state across multiple commands, enabling operations that rely on environment variables or working directory persistence.

Timeout Enforcement Mechanisms

Timeout handling differs by operating system:

  • POSIX systems: After the deadline expires, the executor sends SIGINT to the foreground process via _interrupt. If the completion marker never arrives, the executor hard-closes the shell using the close method.
  • Windows: The shell is killed outright because PowerShell lacks a reliable interrupt mechanism that preserves the REPL state.

Timeout values are bounded by _DEFAULT_TIMEOUT (120 seconds) and _MAX_TIMEOUT (600 seconds) defined at line 41 in platform/coworker/tools/shell.py. User-provided timeouts outside these bounds are automatically clamped.

Common Failure Modes and Diagnostic Approaches

Tool not found / "nonexistent_tool_xyz_123"

  • Likely cause: The tool name is missing from the MCP server's advertised list or filtered by the allowed_tools argument.
  • Where to look: Check MCPClient.list_tools (cached after connection) and the allowed_tools parameter in MCPClient.get_callable_tools.

RuntimeError: MCP server error

  • Likely cause: The server responded with a JSON-RPC error, typically indicating invalid arguments or server-side failures.
  • Where to look: Inspect the error handling block in _async_call_tool_http at lines 88-96 in aisuite/mcp/client.py.

HTTP request to MCP server failed

  • Likely cause: Network connectivity issues, incorrect URL configuration, or missing authentication headers.
  • Where to look: Examine the exception handling in _async_send_http_request at lines 52-55 in client.py.

Shell command times out

  • Likely cause: Command execution exceeded the configured timeout limit.
  • Where to look: Review the timeout handling logic in LocalExecutor.run at lines 38-62 in platform/coworker/tools/shell.py.

Shell session becomes unusable after timeout

  • Likely cause: The shell was hard-closed due to unresponsive processes.
  • Where to look: The next call automatically triggers respawn via LocalExecutor._spawn at lines 64-67.

Background task never finishes

  • Likely cause: Tasks started with run_in_background=True remain detached without explicit cleanup.
  • Where to look: Check LocalExecutor.run_background and background_kill method calls in your implementation.

Step-by-Step Debugging Workflow

  1. Verify tool availability before invocation:

    from aisuite.mcp.client import MCPClient
    
    mcp = MCPClient.from_config(config)
    print("Available tools:", [t["name"] for t in mcp.list_tools()])
  2. Inspect HTTP traffic when using HTTP transport by enabling httpx debug logging or running the mock client tests in tests/mcp/test_http_transport.py.

  3. Check timeout boundaries to ensure your values are within supported limits:

    from platform.coworker.tools.shell import _DEFAULT_TIMEOUT, _MAX_TIMEOUT
    print(f"Default: {_DEFAULT_TIMEOUT}s, Max: {_MAX_TIMEOUT}s")
  4. Observe shell completion markers in output logs. The trailer line format is __COWORKER_DONE_<id>__ <exit_code> <cwd>. Missing markers indicate commands hung beyond the grace period.

  5. Run integration tests to isolate specific failure modes:

Practical Code Examples

Handling Tool Execution Errors

Wrap tool calls to capture MCP server errors and transport failures:

from aisuite.mcp.client import MCPClient

try:
    result = mcp.call_tool("read_file", {"path": "/nonexistent"})
except RuntimeError as err:
    # err contains the MCP error message from lines 88-96 in client.py

    print("Tool failed:", err)

Configuring Command Timeouts

Adjust timeout values for long-running operations while respecting the 600-second maximum:

from platform.coworker.tools.shell import LocalExecutor

# Initialize with 30-second default

executor = LocalExecutor(cwd=".", default_timeout=30)

# Override for specific long-running command (5 minutes)

out = executor.run("make && make test", timeout=300)
print(out["output"])

Setting Up HTTP Transport with Authentication

Configure the MCP client for HTTP endpoints with custom headers and timeout overrides:

from aisuite.mcp.client import MCPClient

config = {
    "type": "mcp",
    "name": "my-api",
    "server_url": "http://localhost:8000",
    "headers": {"Authorization": "Bearer abc123"},
    "timeout": 45.0,
}

client = MCPClient.from_config(config)
tools = client.get_callable_tools()

# Invoke first available tool

result = tools[0](arg="value")
print(result)

Key Source Files Reference

Summary

  • MCPToolWrapper translates JSON-Schema definitions into Python callables that forward requests to MCPClient.call_tool.
  • MCPClient manages stdio and HTTP transports, with error handling concentrated in _async_call_tool_http at lines 88-96.
  • LocalExecutor enforces timeout limits between 120 and 600 seconds, using SIGINT on POSIX and process termination on Windows.
  • Error propagation converts subprocess.CalledProcessError, httpx.HTTPError, and JSON-RPC errors into RuntimeError with descriptive messages.
  • Automatic recovery occurs when shells become unresponsive, with _spawn creating new sessions automatically.

Frequently Asked Questions

Why does my shell command hang indefinitely despite setting a timeout?

The executor may have sent SIGINT (POSIX) or killed the process (Windows), but the completion marker __COWORKER_DONE_<id>__ never arrived. Check if the command spawns child processes that ignore interrupt signals. On POSIX systems, the executor waits for the marker after sending SIGINT; if it never arrives, the shell closes hard. Review LocalExecutor.run at lines 38-62 in platform/coworker/tools/shell.py for the specific timeout logic.

How do I verify if an MCP tool is available before calling it?

Call MCPClient.list_tools() after establishing the connection. This method caches the server's advertised tool list. Compare your target tool name against this list, and verify it isn't filtered out by the allowed_tools parameter in MCPClient.get_callable_tools. The tool list is populated immediately after the client connects to the MCP server.

What causes "MCP server error" RuntimeError exceptions?

This error originates in _async_call_tool_http at lines 88-96 in aisuite/mcp/client.py when the MCP server returns a JSON-RPC error field. Common causes include invalid argument types, missing required parameters, or server-side execution failures. The error message includes the server's specific error description to help diagnose schema mismatches.

How does aisuite handle timeout recovery on Windows versus Linux?

On POSIX systems (Linux/macOS), the executor attempts graceful shutdown using SIGINT before forcibly closing the shell. On Windows, PowerShell lacks reliable interrupt handling that preserves the REPL state, so the executor kills the shell process immediately. In both cases, the next command invocation automatically triggers LocalExecutor._spawn to create a fresh shell session, ensuring subsequent commands execute in a clean environment.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →