MCPMCP Python SDK 2.2 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
24 small wins to finish your pathNext lesson

From MCP tools to a model's tools

A host sits between the model and your server. Its first job is turning the tools the server lists into the tool definitions a model API expects.

Example
def to_model_tools(tools):
    return [
        {
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "parameters": tool.input_schema,
            },
        }
        for tool in tools
    ]

OpenAI's Chat Completions API takes tools in exactly this shape, and many other APIs accept it. Anthropic's Messages API takes name, description and input_schema at the top level, which maps even more directly. Either way, the MCP tool list already holds everything needed: no schema written by hand, unlike lesson 1.

Example
import asyncio
import json

from mcp import Client
from shop import mcp

def to_model_tools(tools):
    return [
        {
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "parameters": tool.input_schema,
            },
        }
        for tool in tools
    ]


async def main():
    async with Client(mcp) as client:
        listed = await client.list_tools()
        model_tools = to_model_tools(listed.tools)
        print(json.dumps(model_tools[0], indent=2))


asyncio.run(main())

This goes into the tools field of every model request. When the model answers with a tool call, the host sends that name and those arguments to client.call_tool, and puts the result back in the conversation. That loop is lesson 21.

Every tool costs tokens

Example
import asyncio
import json

from mcp import Client
from shop import mcp

def to_model_tools(tools):
    return [
        {
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "parameters": tool.input_schema,
            },
        }
        for tool in tools
    ]


async def main():
    async with Client(mcp) as client:
        listed = await client.list_tools()
        text = json.dumps(to_model_tools(listed.tools))
        print(len(listed.tools), "tools,", len(text), "characters sent with every request")


asyncio.run(main())

Tool definitions are sent with every request, and paid for as input tokens, as LLM Fundamentals showed. A server with forty tools makes every message expensive and gives the model more ways to choose wrong. Offer the tools a task needs, with short, exact descriptions.

Try it yourself
  • Convert the tools to Anthropic's shape: a list of dictionaries with name, description and input_schema.
  • Filter to_model_tools to read-only tools using tool.annotations.
  • Shorten search_help's description and count the characters again.

This is what real progress feels like.