# How aisuite Integrates with MCP Servers for Tool Calling: A Complete Technical Guide

> Learn how aisuite integrates with MCP servers for tool calling using MCPClient. Discover automatic tool discovery and seamless LLM invocation for your applications.

- Repository: [Andrew Ng/aisuite](https://github.com/andrewyng/aisuite)
- Tags: how-to-guide
- Published: 2026-08-03

---

**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](https://github.com/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)](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:

1. Spawns the process via `stdio_client` with the provided `command` and `args`
2. Creates a `ClientSession` and runs the MCP protocol handshake
3. Caches the available tools in `_tools_cache`

### HTTP Connection

For remote endpoints, the `_async_connect_http` method:

1. Instantiates `httpx.AsyncClient` with optional headers
2. Sends an `initialize` JSON‑RPC request
3. Dispatches an `initialized` notification
4. Fetches tools via the `tools/list` endpoint

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 identifier
- `description`: Human‑readable purpose
- `inputSchema`: 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)](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)](https://github.com/andrewyng/aisuite/blob/main/aisuite/mcp/schema_converter.py):

1. **`mcp_schema_to_annotations`**: Maps JSON‑Schema types to Python type hints
2. **`extract_parameter_descriptions`**: Pulls field documentation for docstring construction
3. **`build_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 with `tools/call` endpoint

### 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

```python
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

```python
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/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/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

- **`MCPClient`** in [[`aisuite/mcp/client.py`](https://github.com/andrewyng/aisuite/blob/main/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
- **`MCPToolWrapper`** instances 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.