Tools and MCP
How a model asks an app to run a tool and reads the result back, and how the Model Context Protocol lets any app connect to any tool server.
On this page
You ask a friend whether it will rain this afternoon. They do not know from memory. They take out their phone, open the weather app, and read you the answer. Without the phone, the best they could do is look at the sky and guess.
A model has no phone. Everything it knows comes from training or from its context. Notes and retrieved documents are prepared before the call. Tools let the model ask for fresh information, or ask for an action, in the middle of answering.
How a tool call works
The model cannot run anything. It can only write text. Tool calling is a protocol between the model and the app around it:
- Describe. The app puts a short description of each tool in the context: its name, what it does, and what inputs it takes.
- Request. When the model decides a tool would help, it writes a structured request instead of an answer: "call
getWeatherwithcity = Bristol". - Run. The app reads that request, checks it, and runs the real code: an API call, a database query, a file read.
- Return. The app adds the result to the context and calls the model again.
- Answer. The model reads the result and writes its reply, or requests another tool.
The app is in charge at every step. It chooses which tools exist, whether a request is allowed, and what goes back into the context. That is where safety checks live.
context = [instructions + tool descriptions, user message]out = model(context)while out is a tool request: result = app runs the tool context += [out, result] out = model(context)show out to the user- context0tools: getWeather1user: rain in Bristol at 3?
- model calls
- 0
- tool runs
- 0
1The context starts with the instructions and a description of one tool,
getWeather(city), followed by the question. - context0tools: getWeather1user: rain in Bristol at 3?
- model calls
- 1
- tool runs
- 0
- out
- getWeather(Bristol)
2Today's weather is not in its training or its context, but a tool that fetches it is described there. So the model writes a tool request instead of an answer.
- context0tools: getWeather1user: rain in Bristol at 3?
- model calls
- 1
- tool runs
- 1
- result
- 80% rain, 15:00
3The app sees a tool request, checks that the tool exists and the input is valid, and runs the real weather lookup. The model is not involved in this step at all.
- context0tools: getWeather1user: rain in Bristol at 3?2call: getWeather(Bristol)3result: 80% rain, 15:00
- model calls
- 1
- tool runs
- 1
4The app appends the request and its result to the context. The weather is now text the model can read, just like a pasted document.
- context0tools: getWeather1user: rain in Bristol at 3?2call: getWeather(Bristol)3result: 80% rain, 15:00
- model calls
- 2
- tool runs
- 1
- out
- answer
5The model is called again with the longer context and writes a normal answer: "Likely, yes: an 80% chance around 3pm. Take an umbrella."
- context0tools: getWeather1user: rain in Bristol at 3?2call: getWeather(Bristol)3result: 80% rain, 15:00
- model calls
- 2
- tool runs
- 1
6Not a tool request, so the loop ends and the user sees the answer. One question cost two model calls and one tool run.
The loop can go around many times: search, read a page, search again, then answer. A model running that loop with real freedom is the basis of an agent, covered in The Agent Loop.
What the app writes
A tool description is usually a small block of structured data. The exact field names differ between providers, but they all carry the same three things: a name, a description in plain words, and the inputs with their types.
{
"name": "getWeather",
"description": "Get the hourly forecast for a city for today. Use for questions about rain, temperature or wind today. Not for past weather or forecasts beyond today.",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name, e.g. \"Bristol\"" }
},
"required": ["city"]
}
}The description is not for a compiler. It is for the model, which reads it to decide when to use the tool and what to pass. Write it like instructions to a colleague, including when not to use it.
On the app's side, running a request is a lookup and a function call. Here it is with a fake weather function, so you can run it.
const tools = {
getWeather: ({ city }) => ({ city, rainChance: 0.8, at: "15:00" }), // fake data
};
function runToolCall(call) {
const tool = tools[call.name];
if (!tool) return { error: `Unknown tool: ${call.name}` };
if (typeof call.arguments?.city !== "string") return { error: "city must be a string" };
return tool(call.arguments);
}
// The model wrote this instead of an answer:
const call = { name: "getWeather", arguments: { city: "Bristol" } };
console.log(runToolCall(call)); // { city: 'Bristol', rainChance: 0.8, at: '15:00' }
console.log(runToolCall({ name: "bookFlight", arguments: {} })); // { error: 'Unknown tool: bookFlight' }tools = {
"getWeather": lambda args: {"city": args["city"], "rain_chance": 0.8, "at": "15:00"}, # fake data
}
def run_tool_call(call):
tool = tools.get(call["name"])
if tool is None:
return {"error": f"Unknown tool: {call['name']}"}
if not isinstance(call.get("arguments", {}).get("city"), str):
return {"error": "city must be a string"}
return tool(call["arguments"])
# The model wrote this instead of an answer:
call = {"name": "getWeather", "arguments": {"city": "Bristol"}}
print(run_tool_call(call)) # {'city': 'Bristol', 'rain_chance': 0.8, 'at': '15:00'}
print(run_tool_call({"name": "bookFlight", "arguments": {}})) # {'error': 'Unknown tool: bookFlight'}Note the checks. The model's request is text it generated, so it can name a tool that does not exist or pass a bad input. The app validates before running, and an error goes back into the context so the model can try again.
MCP: one plug for every tool
Every app that wants a calendar, a code repository, a database and a ticket tracker could write its own connector for each. With 3 apps and 3 services, that is 3 × 3 = 9 custom connectors, and every new app or service adds a whole row or column.
The Model Context Protocol (MCP) is an open standard that removes that multiplication. A service is wrapped once as an MCP server. An AI app that speaks MCP can connect to any such server, ask what it offers, and pass that on to its model. Each side implements the protocol once: 3 + 3 pieces of work instead of 9.
An MCP server can offer three kinds of things:
- Tools: actions the model can request, like "create a ticket" or "search the repository".
- Resources: data the app can read and put into the context, like a file or a database record.
- Prompts: ready-made templates for common tasks, which a user can pick.
MCP does not change the tool loop above. It standardizes how the app finds out which tools exist and how it calls them. The model still only writes requests, and the app still decides what runs.
Common mistakes
Try it yourself
A team's assistant keeps calling this tool for the wrong things, like general knowledge questions, and passes whole sentences as the query. Rewrite the description so the model knows when to use it and what to pass.
Show a sample answer
One better version:
{
"name": "searchTeamWiki",
"description": "Search the team's internal wiki (runbooks, onboarding guides, design docs). Use when the question is about our own systems, processes or decisions. Do not use for general programming or world knowledge. Returns up to 5 page titles with a short excerpt each.",
"parameters": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "2 to 6 keywords, e.g. \"deploy rollback runbook\". Not a full sentence." }
},
"required": ["query"]
}
}Why it works: the name says what is searched. The description says when to use it, when not to, and what comes back, so the model can plan its next step. The parameter description shows the expected format with an example.
Check yourself
When a model "uses a tool", who actually runs the tool's code?
What problem does MCP mainly solve?
An assistant has 60 tools connected and often calls the wrong one. What is the first thing to try?
What to practise next
Tools add to the context on every turn, and long sessions pile up. Context Rot covers what happens when a context grows stale and how to recover. For tool design in depth, continue with Tool Use in the Agents module.