Building AI Agents from Scratch

Giving Your Agent MCP Tools

Connect the agent to an MCP server and turn that server’s tools into calls your loop can run.

You already wrap functions you wrote. MCP is a standard way to wrap someone else’s server — the same tools a Cursor host would see. Your loop does not change. The registry grows at startup.

If you have not built a server yet, use Building Your Own MCP Server for AI through the first tool, then come back.

Client, not host

Here you are the MCP client. You spawn or connect to a server, call list_tools, and for each tool build a Chat Completions schema plus a dispatch that calls call_tool.

import asyncio
import json

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client


async def load_mcp_tools(command: str, args: list[str]) -> tuple[list, dict]:
    params = StdioServerParameters(command=command, args=args)
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            listed = await session.list_tools()
            tools = []
            registry = {}
            for tool in listed.tools:
                name = f"mcp_{tool.name}"
                schema = tool.inputSchema or {"type": "object", "properties": {}}
                tools.append({
                    "type": "function",
                    "function": {
                        "name": name,
                        "description": (tool.description or tool.name)[:500],
                        "parameters": schema,
                    },
                })

                def make(tool_name: str):
                    def call(**kw):
                        result = asyncio.run(_call(command, args, tool_name, kw))
                        return {"ok": True, "mcp": result}
                    return call

                registry[name] = make(tool.name)
            return tools, registry


async def _call(command, args, tool_name, arguments):
    params = StdioServerParameters(command=command, args=args)
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool(tool_name, arguments or {})
            return str(result.content)

Spawning a server per tool call is fine for class. A long-lived session (one process, many calls) belongs in production. Prefix names with mcp_ so they cannot collide with list_dir.

Check it

Point the loader at the hello server from the MCP course. Your agent’s tool list should include mcp_ping. Ask it to ping; the observation should contain pong. Next: attach more than one server.