How aisuite Integrates with MCP Servers for Tool Calling: A Complete Technical Guide
aisuite integrates with MCP servers through the MCPClient class, which supports both stdio and HTTP transports, automatically discovers tools, and wraps them as native Python callables that LLMs can invoke directly.
The andrewyng/aisuite library provides seamless MCP server integration for tool calling through a dedicated client that abstracts transport complexity. This enables language models to access external capabilities—filesystems, APIs, databases—as if they were native Python functions. This guide walks through the architecture, implementation details, and practical usage patterns based on the actual source code.
MCPClient: The Core Integration Point
All MCP server integration flows through [aisuite/mcp/client.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/mcp/client.py). The MCPClient class manages the full lifecycle: connection establishment, tool discovery, callable generation, and execution routing.
Transport Selection: stdio vs HTTP
The client enforces mutually exclusive transport options via a XOR check in the constructor (lines 101‑104):
- stdio transport: Spawns a local MCP server as a subprocess using a command and arguments
- HTTP transport: Connects to a remote MCP endpoint via
httpx.AsyncClient
Only one transport may be configured. Attempting to provide both raises a ValueError.
Connection Setup and Handshake
Each transport path implements its own connection sequence in _async_connect methods.
stdio Connection
For local MCP servers, the client:
- Spawns the process via
stdio_clientwith the providedcommandandargs - Creates a
ClientSessionand runs the MCP protocol handshake - Caches the available tools in
_tools_cache
HTTP Connection
For remote endpoints, the _async_connect_http method:
- Instantiates
httpx.AsyncClientwith optional headers - Sends an
initializeJSON‑RPC request - Dispatches an
initializednotification - Fetches tools via the
tools/listendpoint
HTTP connections automatically extract and reuse Mcp-Session-Id headers for session affinity.
Tool Discovery and Caching
After the handshake completes, the client populates _tools_cache with structured tool metadata (lines 29‑33):
name: The tool identifierdescription: Human‑readable purposeinputSchema: JSON‑Schema defining parameters
The list_tools() method exposes this cache, returning a list of tool definitions ready for inspection or filtering.
Callable Tool Wrappers
The bridge between MCP schemas and Python functions happens in get_callable_tools(). This method delegates to [aisuite/mcp/tool_wrapper.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/mcp/tool_wrapper.py) to generate MCPToolWrapper instances.
Schema‑Driven Signature Generation
Each wrapper performs three transformations using utilities from [aisuite/mcp/schema_converter.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/mcp/schema_converter.py):
mcp_schema_to_annotations: Maps JSON‑Schema types to Python type hintsextract_parameter_descriptions: Pulls field documentation for docstring constructionbuild_docstring: Assembles a complete function docstring with parameter details
The resulting wrapper exposes a proper inspect.signature() interface, enabling aisuite's Tools class to introspect arguments without special‑casing MCP tools.
Execution Forwarding
When invoked, the wrapper's __call__ method (lines 27‑29) filters out None values and delegates to MCPClient.call_tool(). This preserves Python's calling conventions while translating to MCP's JSON‑RPC protocol.
Tool Execution: Routing and Response Handling
The call_tool() method routes requests based on transport type:
_async_call_tool: stdio transport via JSON‑RPC over stdin/stdout_async_call_tool_http: HTTP transport withtools/callendpoint
HTTP Response Processing
The HTTP path handles two response formats through _parse_sse_response:
- Plain JSON: Direct result extraction
- Server‑Sent Events (SSE): Stream parsing for progressive responses
Results are normalized consistently: if a content array exists, the first text or data element is returned; otherwise the raw response is stringified.
Session Management and Cleanup
MCPClient implements Python's context manager protocol via __enter__/__exit__ and provides _async_close for explicit async cleanup. This ensures:
- Subprocess termination for stdio connections
- HTTP client closure
- Session ID cleanup
Always use mcp.close() or the context manager to prevent resource leaks.
Practical Code Examples
Connecting to a Local Filesystem MCP Server
from aisuite.mcp.client import MCPClient
mcp = MCPClient(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/my/docs"],
name="fs"
)
# Retrieve callable tools and use them in a chat request
import aisuite as ai
client = ai.Client()
response = client.chat.completions.create(
model="openai:gpt-4o",
messages=[{"role": "user", "content": "List the files in the current directory"}],
tools=mcp.get_callable_tools(),
max_turns=2,
)
print(response.choices[0].message.content)
mcp.close()
This example spawns the official Model Context Protocol filesystem server via npx, exposes its tools, and passes them to a chat completion call. The LLM can then request filesystem operations which aisuite executes through the MCP wrapper.
Remote HTTP MCP Server with Tool Filtering
from aisuite.mcp.client import MCPClient
config = {
"type": "mcp",
"name": "cloud-api",
"server_url": "https://mcp.example.com",
"headers": {"Authorization": "Bearer <TOKEN>"},
}
mcp = MCPClient.from_config(config)
# Only expose the `read_file` tool, prefixing its name with the client name
tools = mcp.get_callable_tools(allowed_tools=["read_file"], use_tool_prefix=True)
# The wrapper will be called `cloud-api__read_file`
result = tools[0]("/path/to/file.txt")
print(result)
mcp.close()
The from_config factory method enables declarative setup. The use_tool_prefix=True option namespaces tools to prevent collisions when multiple MCP servers provide identically named functions.
Configuration Patterns and Examples
The repository includes two reference implementations:
| Example | Location | Purpose |
|---|---|---|
| HTTP MCP usage | [aisuite/examples/mcp_http_example.py](https://github.com/andrewyng/aisuite/blob/main/examples/mcp_http_example.py) |
Remote server connection and tool invocation |
| Config dict pattern | [aisuite/examples/mcp_config_dict_example.py](https://github.com/andrewyng/aisuite/blob/main/examples/mcp_config_dict_example.py) |
Building configs and using MCPClient.get_tools_from_config |
These demonstrate production patterns for both transport types and configuration management approaches.
Summary
MCPClientin [aisuite/mcp/client.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/mcp/client.py) is the sole entry point for aisuite MCP server integration for tool calling- Transport is strictly stdio XOR HTTP, enforced at construction time
- Tool discovery caches name, description, and JSON‑Schema metadata
MCPToolWrapperinstances expose Python‑native callables with proper signatures and docstrings- Execution routes through transport‑specific async methods with SSE support for HTTP
- Session persistence and resource cleanup are handled automatically via context managers
Frequently Asked Questions
How does aisuite choose between stdio and HTTP transport for MCP?
The MCPClient constructor checks that exactly one of command (for stdio) or server_url (for HTTP) is provided. Lines 101‑104 enforce this XOR constraint: providing both or neither raises a ValueError. This design prevents ambiguous configurations and ensures predictable transport selection.
Can I filter which MCP tools are exposed to the LLM?
Yes. The get_callable_tools() method accepts an allowed_tools list to whitelist specific tools by name. Additionally, use_tool_prefix=True prepends the client name (e.g., cloud-api__read_file) to prevent naming collisions when multiple MCP servers define identically named functions.
What happens to MCP session state across multiple tool calls?
For HTTP transports, the client automatically extracts Mcp-Session-Id from response headers and includes it in subsequent requests, maintaining server‑side session continuity. stdio transports maintain state through the persistent subprocess. Both patterns support multi‑turn conversations with stateful MCP servers.
How does aisuite handle streaming responses from MCP tools?
The HTTP implementation includes _parse_sse_response to handle Server‑Sent Events. When the MCP server streams results, the client parses SSE frames and extracts the final payload. Non‑streaming JSON responses pass through directly. The result normalization layer ensures consistent return types regardless of transport or response format.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →