9  MCP Client

Model Context Protocol (MCP) — standardized tool servers for agents.

Note

Code Reference: code/v0.6/src/agentsilex/mcp.py

9.1 What is MCP?

MCP is a protocol (by Anthropic) for tool servers that agents can connect to:

Agent ←→ MCP Client ←→ MCP Server ←→ External Service

Benefits:

  • Standardized — One protocol, many tools
  • Reusable — Same server works with any MCP client
  • Isolated — Tools run in separate processes
  • Ecosystem — Growing library of pre-built servers

9.2 Converting MCP Tools to FunctionTool

First, we need to convert MCP tools to our FunctionTool format (mcp.py):

from mcp import types as mcp_types
from agentsilex.function_tool import FunctionTool


def mcp_tool_as_function_tool(mcp_tool: mcp_types.Tool, func: Callable) -> FunctionTool:
    return FunctionTool(
        name=mcp_tool.name,
        description=mcp_tool.description,
        function=func,
        parameters_specification=mcp_tool.inputSchema,
    )

MCP tools already have name, description, and JSON schema — we just wrap them.

9.3 The MCPTools Class

import asyncio
from typing import List
import json

from mcp import ClientSession, StdioServerParameters
from mcp import types as mcp_types
from mcp.client.stdio import stdio_client


class MCPTools:
    def __init__(self, server_parameters: dict):
        self.server_params = StdioServerParameters(**server_parameters)
        self.tools = None

    def get_tools(self) -> List[FunctionTool]:
        if not self.tools:
            mcp_tools = self._get_mcp_tools()
            self.tools = self._build_mcp_func(mcp_tools)
        return self.tools

    def _get_mcp_tools(self) -> List[mcp_types.Tool]:
        async def run():
            async with stdio_client(self.server_params) as (read, write):
                async with ClientSession(read, write) as session:
                    await session.initialize()
                    tools = await session.list_tools()
                    return tools.tools

        return asyncio.run(run())

    def _build_mcp_func(self, mcp_tools: List[mcp_types.Tool]) -> List[FunctionTool]:
        def func_maker(mcp_tool: mcp_types.Tool) -> FunctionTool:
            def wrapper(**kwargs):
                async def run():
                    async with stdio_client(self.server_params) as (read, write):
                        async with ClientSession(read, write) as session:
                            await session.initialize()
                            result = await session.call_tool(mcp_tool.name, kwargs)
                            return result

                return asyncio.run(run())

            return mcp_tool_as_function_tool(mcp_tool, wrapper)

        return [func_maker(tool) for tool in mcp_tools]

Key methods:

  1. get_tools() — Returns list of FunctionTool objects
  2. _get_mcp_tools() — Connects to MCP server, lists available tools
  3. _build_mcp_func() — Creates wrapper functions that call the MCP server

9.4 Helper Functions

def mcp_tools(server_params) -> List[FunctionTool]:
    """Quick way to get tools from server parameters."""
    mcp_tools_instance = MCPTools(server_params)
    return mcp_tools_instance.get_tools()


def mcp_tool_from_config(config_path: str):
    """Load tools from a config file (supports Claude Desktop format)."""
    with open(config_path) as f:
        config = json.load(f)

    # Support both direct format and Claude Desktop format
    if "mcpServers" in config:
        config = config["mcpServers"]

    return mcp_tools(config)

9.5 Usage Example

from agentsilex import Agent, Runner, Session
from agentsilex.mcp import mcp_tools

# Define MCP server parameters
server_params = {
    "command": "npx",
    "args": ["-y", "@anthropic/mcp-server-filesystem", "/tmp"],
}

# Get tools from MCP server
fs_tools = mcp_tools(server_params)

# Create agent with MCP tools
agent = Agent(
    name="file_assistant",
    model="gpt-4o",
    instructions="You can read and write files. Use the available tools.",
    tools=fs_tools,
)

# Run
session = Session()
runner = Runner(session)
result = runner.run(agent, "List files in /tmp")

9.6 Config File Format

You can use a JSON config file:

{
  "command": "npx",
  "args": ["-y", "@anthropic/mcp-server-filesystem", "/allowed/path"]
}

Or the Claude Desktop format:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-filesystem", "/allowed/path"]
    }
  }
}
from agentsilex.mcp import mcp_tool_from_config

tools = mcp_tool_from_config("mcp_config.json")

9.8 How It Works

sequenceDiagram
    participant A as Agent
    participant M as MCPTools
    participant S as MCP Server

    A->>M: get_tools()
    M->>S: Connect via stdio
    S->>M: List of tools
    M->>A: FunctionTool objects

    Note over A: Agent runs...

    A->>M: Call tool(args)
    M->>S: Connect + call_tool
    S->>M: Result
    M->>A: String result

Note: Each tool call creates a new connection. This is simple but not optimized for high-frequency calls.

9.9 Combining with Regular Tools

from agentsilex import tool
from agentsilex.mcp import mcp_tools

# Regular tool
@tool
def calculate(expression: str) -> str:
    """Evaluate a math expression."""
    return str(eval(expression))

# MCP tools
fs_tools = mcp_tools({"command": "npx", "args": ["..."]})

# Combine
agent = Agent(
    name="assistant",
    tools=[calculate] + fs_tools,  # Mix both types
    # ...
)

9.10 Key Design Decisions

Decision Why
Sync wrapper around async AgentSilex is sync-first
New connection per call Simple, stateless
Claude Desktop config support Easy migration
Returns FunctionTool Seamless integration
TipCheckpoint
cd code/v0.6

MCP client integration complete! Connect to any MCP server.