Tool and Function Calling
Let a model request actions from your code, instead of only producing text.
What you'll learn
- Explain the tool-calling request/response cycle
- Define a small typed tool registry
- Dispatch a requested tool call safely, including for an unknown tool
Prerequisites
Explanation
By default, a language model can only produce text — it can't check today's weather, look up a real order status, or update a database. Tool calling (also called function calling) bridges that gap: you describe a set of available "tools" to the model (a name, a description, and a typed schema of parameters), and instead of guessing an answer, the model can respond with a structured request like "call the getOrderStatus tool with { orderId: '12345' }."
Critically, the model never actually executes anything — it only produces a structured request. Your own code (which you fully control) receives that request, decides whether to honor it, actually runs the corresponding function, and sends the result back to the model as another message, so it can use that result to continue the conversation or produce a final answer. This request/response cycle usually looks like:
- You send the user's message plus a list of available tool definitions.
- The model replies asking to call a specific tool with specific arguments (or just replies normally with text if no tool is needed).
- Your code executes that tool (or refuses/validates first) and returns the result.
- The model incorporates the result and produces its next message — which might be a final answer, or another tool call.
Designing tools well matters as much as designing prompts. Each tool should have a narrow, clearly-described purpose, typed parameters (so malformed requests are easy to catch before execution), and should return structured results, including structured errors — never let a tool call crash your whole request pipeline; a failed lookup should come back as { error: "order not found" }, not an unhandled exception.
Because the model decides when and with what arguments to call a tool, you must treat every requested tool call as untrusted input from the model — validate arguments, enforce authorization (can this user actually access this order?), and never expose a tool that performs an irreversible or sensitive action without additional safeguards. This is the direct foundation for the agents lesson next, where multiple tool calls get chained together in a loop.
Example
A tiny mock tool registry and dispatcher (no real model call — this simulates the model 'requesting' a tool call as a plain object).
const tools = {
getOrderStatus: (args) => {
const orders = { "1001": "shipped", "1002": "processing" };
const status = orders[args.orderId];
return status ? { status } : { error: "order not found" };
},
};
function dispatchToolCall(request) {
const tool = tools[request.name];
if (!tool) {
return { error: `Unknown tool: ${request.name}` };
}
return tool(request.arguments);
}
// Pretend this object is what the model asked for.
console.log(dispatchToolCall({ name: "getOrderStatus", arguments: { orderId: "1001" } }));
console.log(dispatchToolCall({ name: "getOrderStatus", arguments: { orderId: "9999" } }));
console.log(dispatchToolCall({ name: "deleteEverything", arguments: {} }));Try it yourself
Add a new tool to the registry and dispatch a call to it.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
Complete `dispatchToolCall(tools, request)` where tools is an object mapping tool names to functions, and request is `{ name, arguments }`. Call the matching tool with request.arguments and return its result, or return `{ error: 'Unknown tool: ' + name }` if it doesn't exist.
Checks: Dispatches to an existing tool correctly · plus 1 hidden check
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
Write a tools object with a `lookupBook` tool that takes `{ title }` and returns `{ price }` for a small hardcoded catalog (at least 2 books), or `{ error: 'book not found' }` otherwise. Then write `handleRequest(request)` that dispatches to this tools object the same way as the guided exercise, additionally returning `{ error: 'invalid arguments' }` (without calling the tool) if `request.arguments` is missing or not an object.
Checks: Looks up an existing book's price · Handles an unknown book gracefully · plus 1 hidden check
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Common mistakes
- Letting a tool call execute a sensitive action without validating its arguments or checking authorization first.
- Letting a failed tool call throw an unhandled exception instead of returning a structured error the model can react to.
- Assuming the model directly executes code — it only ever requests a call; your code stays in control of what actually runs.
Knowledge check
Takeaway
The model only asks for actions in structured form; your code stays firmly in control of whether and how they run.
Summary
Tool calling lets a model request a structured function call with typed arguments instead of only producing text. Your application code dispatches, validates, and executes that request, returning structured results (including structured errors) back to the model to continue the conversation.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.