Skip to content

MCP tool-calling in practice

Practical notes on exposing and consuming tools through the Model Context Protocol.

Defining a tool

from mcp.server import Server
from mcp.types import Tool

server = Server("internal-tools")

@server.tool()
async def lookup_account(account_id: str) -> dict:
    """Fetch account details by ID."""
    return await db.get_account(account_id)

Client-side discovery

MCP clients call list_tools() to discover what’s available at runtime — no hardcoded tool registry required on the client side. This is what makes tools portable across agent frameworks.

Error handling

Return structured errors, not raw exceptions — a tool call failure should give the calling agent enough information to decide whether to retry, fall back, or surface the error to a human.

@server.tool()
async def lookup_account(account_id: str) -> dict:
    try:
        return await db.get_account(account_id)
    except NotFoundError:
        return {"error": "account_not_found", "account_id": account_id}