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}