Mastering LLM Agent Tool Use: From Function Calling to MCP
What makes an agent an agent is tool use. It is the mechanism that takes LLMs beyond mere text generation and lets them call external APIs, query databases, and execute code.
How Tool Use Works
LLMs only accept text as input and only produce text as output. Tool use bypasses that constraint with the following flow.
1. User message + tool spec → LLM
2. LLM → "Call this tool with these arguments" (JSON output)
3. Runtime executes the actual function
4. Execution result → passed back to the LLM
5. LLM → generates the final natural-language answerThe key point is that the LLM does not execute functions itself. The runtime does, then hands the results back to the LLM.
OpenAI Function Calling
from openai import OpenAI
import json
import os
client = OpenAI()
tools = [
{
"type": "function",
"function": {
"name": "get_stock_price",
"description": " ",
"parameters": {
"type": "object",
"properties": {
"symbol": {
"type": "string",
"description": " (: AAPL, TSLA)"
},
"currency": {
"type": "string",
"enum": ["USD", "KRW"]
}
},
"required": ["symbol"]
}
}
}
]
def get_stock_price(symbol, currency="USD"):
return {"symbol": symbol, "price": 192.5, "currency": currency}
def run_agent(user_message):
messages = [{"role": "user", "content": user_message}]
while True:
response = client.chat.completions.create(
model=os.environ["OPENAI_MODEL"], # pick one from platform.openai.com/docs/models
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = response.choices[0].message
messages.append(msg)
if msg.tool_calls is None:
return msg.content
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
result = get_stock_price(**args)
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(result)
})Anthropic Tool Use
Claude's tool use issues calls as tool_use content blocks and returns results as tool_result.
import anthropic, json
client = anthropic.Anthropic()
tools = [{
"name": "search_docs",
"description": " ",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"},
"max_results": {"type": "integer", "default": 5}
},
"required": ["query"]
}
}]
def search_docs(query, max_results=5):
return [{"title": "API ", "content": "Bearer method ..."}]
messages = [{"role": "user", "content": " API method ?"}]
while True:
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
tools=tools,
messages=messages
)
if response.stop_reason == "end_turn":
text_block = next(b for b in response.content if b.type == "text")
print(text_block.text)
break
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = search_docs(**block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result, ensure_ascii=False)
})
messages.append({"role": "user", "content": tool_results})Model Context Protocol (MCP)
MCP is a tool standardization protocol proposed by Anthropic. It is not tied to any individual LLM SDK; tools are provided independently through a server–client architecture.
# MCP
from mcp.server import Server
import mcp.types as types
server = Server("my-tools-server")
@server.list_tools()
async def handle_list_tools():
return [
types.Tool(
name="query_database",
description="SQL run Result ",
inputSchema={
"type": "object",
"properties": {
"sql": {"type": "string", "description": "run SELECT "}
},
"required": ["sql"]
}
)
]
@server.call_tool()
async def handle_call_tool(name, arguments):
if name == "query_database":
results = execute_query(arguments["sql"])
return [types.TextContent(type="text", text=str(results))]MCP's advantage is reusability. An MCP server you build once can be connected from Claude Desktop, Claude Code, your own agents, and more.
Tool Design Principles
The difference between a good tool and a bad one is in the description.
| Bad example | Good example |
|---|---|
| "Fetches data" | "Looks up status, amount, and shipping info for a specific order by order ID. Canceled orders can also be retrieved." |
| No parameter descriptions | Explicit type, allowed values, and defaults for each parameter |
The LLM reads the description to choose a tool. If the description is inaccurate, it will call the wrong tool or pass the wrong arguments.
Parallel Tool Calls
Running multiple tools sequentially is slow. Both the OpenAI and Anthropic APIs support parallel tool calls, where the model calls several tools in one turn. On OpenAI, setting parallel_tool_calls to false limits it to at most one tool per turn (OpenAI, Anthropic).
import asyncio
async def execute_tools_parallel(tool_calls):
tasks = [dispatch_tool(tc.function.name, json.loads(tc.function.arguments))
for tc in tool_calls]
return await asyncio.gather(*tasks)In the next installment, we cover memory system design—how to let agents maintain conversation context and carry out long-running tasks.
Editor's Note — From the Field
It feels like attaching more tools should make an agent smarter, but in practice, once you go past five or six tools, the LLM frequently misjudges which tool to use and when. What helped most was cutting the number of tools and writing each description so specifically that a human wouldn't get confused either—search_internal_docs (search the internal wiki) rather than search. Tool design is an extension of prompt engineering.
Sources · checked 2026-10-04
- OpenAI — Function calling guide —
parallel_tool_calls - Anthropic — Tool use docs — parallel tool use
- Anthropic — Models overview — current model ID
claude-sonnet-5-5 - Model Context Protocol (MCP)
Model IDs change, so check the current ID on each provider’s model page before running the code.
Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.