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.
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.
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
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.
- Convert the tools to Anthropic's shape: a list of dictionaries with
name,descriptionandinput_schema. - Filter
to_model_toolsto read-only tools usingtool.annotations. - Shorten
search_help's description and count the characters again.
This is what real progress feels like.