Skip to content
BLOG / AI AGENTSFoundations

What is function calling? How LLM tool calling works

Function calling lets a model request a tool call while your code runs it. How the loop works, how to design tool schemas, handle errors and keep it safe.

Learn AI Engineering Editorial October 31, 2026 10 min readLast reviewed

Topics:AI agents in productionMCP and tool use

A language model cannot check the weather, query your database or send an email. It can only produce text. Function calling is the pattern that turns that text into action: the model writes a structured request, and your own code decides whether to carry it out.

TL;DR

In function calling, the model does not run anything. You describe your tools in the request, and the model replies with a structured tool call: a tool name and a set of arguments. Your application validates that call, runs the tool, and sends the result back so the model can write the final answer. Tool calling and function calling are two names for the same pattern. The model proposes, your code disposes.

How function calling works, step by step

The loop has five steps, and every provider's API follows the same shape even though the field names differ. Check your provider's current documentation for the exact request and response format, because those details change between versions.

  1. Step 1: Describe the tools

    Your request includes each tool's name, a plain-language description and a JSON schema for its arguments

  2. Step 2: The model decides

    It either answers in text or returns a tool call naming a tool and its arguments

  3. Step 3: Your code validates and runs

    You check the call against the schema, apply your own permission rules, then execute the real function

  4. Step 4: Return the result

    You send the tool's output (or its error) back to the model as a new message

  5. Step 5: Final answer

    The model reads the result and writes the reply, or asks for another tool call

The function calling loop

Two consequences follow from this shape. First, the model never sees your code, your network or your credentials. It sees only the schemas you chose to expose and the results you chose to return. Second, every tool call is a decision point where your application can say no.

A runnable minimal example

The example below runs with no API key and no provider SDK. A stub function plays the model and returns a hard-coded tool call, so you can see the loop, the validation step and the step cap in about sixty lines. To adapt it, replace fake_model with a call to your provider's SDK, pass the tool schemas in the format that provider documents, and read the tool call from its response. Use a model name placeholder such as <model> in your own code and check the provider's documentation for the current identifiers.

import json

# 1. The tool: a plain function plus a schema that describes it to the model.
def get_weather(city: str, unit: str = "celsius") -> dict:
    data = {"Sydney": 24, "Oslo": 6}  # stand-in for a real lookup
    if city not in data:
        return {"error": f"unknown city '{city}'. Known cities: {sorted(data)}"}
    temp = data[city] if unit == "celsius" else round(data[city] * 9 / 5 + 32)
    return {"city": city, "temperature": temp, "unit": unit}

TOOLS = {
    "get_weather": {
        "fn": get_weather,
        "schema": {
            "name": "get_weather",
            "description": "Current temperature for one city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                },
                "required": ["city"],
            },
        },
    }
}

# 2. A stub "model". A real model reads the messages and the tool schemas, then
#    returns either a tool call or final text. This one is hard-coded so the
#    example runs with no API key.
def fake_model(messages, tools):
    last = messages[-1]
    if last["role"] == "user":
        return {"type": "tool_call", "name": "get_weather",
                "arguments": {"city": "Sydney", "unit": "celsius"}}
    result = json.loads(last["content"])
    return {"type": "text", "text": f"It is {result['temperature']} degrees {result['unit']} in {result['city']}."}

# 3. Your code validates the call, runs the tool, and feeds the result back.
def validate(call):
    tool = TOOLS.get(call["name"])
    if tool is None:
        return f"no such tool '{call['name']}'"
    schema = tool["schema"]["parameters"]
    args = call["arguments"]
    for field in schema["required"]:
        if field not in args:
            return f"missing required argument '{field}'"
    for name, value in args.items():
        spec = schema["properties"].get(name)
        if spec is None:
            return f"unexpected argument '{name}'"
        if "enum" in spec and value not in spec["enum"]:
            return f"'{name}' must be one of {spec['enum']}"
    return None

def run(question, max_steps=5):
    messages = [{"role": "user", "content": question}]
    for _ in range(max_steps):  # a hard cap, so a confused model cannot loop forever
        reply = fake_model(messages, [t["schema"] for t in TOOLS.values()])
        if reply["type"] == "text":
            return reply["text"]
        error = validate(reply)
        result = {"error": error} if error else TOOLS[reply["name"]]["fn"](**reply["arguments"])
        messages.append({"role": "tool", "content": json.dumps(result)})
    return "stopped: step limit reached"

print(run("What is the weather in Sydney?"))

Run it and it prints the temperature for Sydney. The point is the structure, not the weather. The validate function sits between the model and your tool, and the max_steps cap sits around the whole loop. Those two guards are the difference between a demo and something you can put in production.

Designing good tool schemas

The model chooses tools by reading their names and descriptions, so a schema is part of your prompt. Treat it that way.

  • Name tools by what they do. get_order_status is clearer than lookup or orders_tool.
  • Describe when to use the tool, not only what it does. One sentence on when it applies and when it does not helps the model avoid unnecessary calls.
  • Keep parameters narrow. Use enums for fixed choices, mark required fields as required, and avoid free-form strings where a structured value would do.
  • One job per tool. A single tool with a mode argument that switches between five behaviours is harder for a model to call correctly than five small tools.
  • Prefer idempotent tools. If a call is retried, running it twice should not charge a customer twice. Where a side effect is unavoidable, accept an idempotency key.

Vague

  • Name: data_tool
  • Description: "Gets data"
  • Parameters: one free-text query string
  • Result: raw rows, any shape

Well designed

  • Name: get_order_status
  • Description: "Returns the shipping status for one order. Use when the customer asks where an order is."
  • Parameters: order_id (string, required)
  • Result: a small object with status and estimated_delivery
Vague tool versus well-designed tool

Errors, retries and limits

Models make mistakes with arguments: a missing field, a value outside an enum, an ID that does not exist. Plan for it.

1. Validate before you execute. Check required fields, types and allowed values against the schema, and apply your own business rules on top. 2. Return errors the model can act on. "unknown city 'Sidney'. Known cities: Oslo, Sydney" lets the model correct itself on the next step. "Error 500" does not. 3. Cap steps, time and cost. Set a maximum number of tool calls per request and a timeout per tool. A confused model will otherwise keep calling tools. Our guide to agent loops, stopping conditions and guardrails covers the limits worth setting. 4. Retry the transport, not the decision. Retry a failed network call, but do not silently re-run a tool the model has already seen fail without telling it.

Parallel and multi-step calls

Some providers let a model request several tool calls in one turn, and a model can also chain calls across turns, using the result of one as the input to the next.

Parallel calls suit independent lookups, such as fetching three customers' records at once. Run them concurrently, then return all results together. Sequential calls suit dependent steps, such as finding a customer ID and then fetching that customer's orders. Do not allow parallel calls for tools with side effects that depend on order, and decide explicitly what happens when one call in a batch fails.

Security: treat tool output as untrusted

A tool result goes back into the model's context, so anything an attacker can write into that result can try to steer the model. A web page, an email body or a database field can contain text that looks like an instruction. This is prompt injection, and it is the main security risk of tool use. Our explainer on prompt injection attacks and defences goes through the patterns that hold up.

Three habits reduce the risk:

  • Least privilege. Give each tool only the access its job needs: read-only where possible, scoped to one tenant, with short-lived credentials.
  • Approvals for side effects. Sending money, deleting data or emailing customers should pause for a human. See approval design for AI agents for when to ask.
  • Never execute what the model wrote as code or SQL without a boundary. Pass arguments as parameters to fixed functions, not as strings to run.

Function calling vs MCP vs agents

These three terms describe different layers, and mixing them up causes most of the confusion.

Function calling

  • The mechanism: how a model asks for a tool call
  • Lives in the model API request and response
  • Tools are defined per application

MCP

  • The packaging: a standard protocol for tool servers
  • Lets one tool server work with many clients
  • Still uses tool calls underneath

Agent

  • The behaviour: a loop that plans, calls tools and observes results
  • Built from function calling plus state, limits and stopping rules
  • Needs evaluation, not only a schema
Three layers, three questions

If you have one application and a handful of tools, plain function calling is usually enough. If you want the same tools available to several applications or assistants, MCP removes the duplicated wrappers. Once the model is deciding what to do next across many steps, you are building an agent, and the agentic AI engineering learning path is the place to go next.

Testing tool use

Function calling fails in quiet ways: the model picks the wrong tool, passes a plausible but wrong argument, or calls a tool it did not need. Final-answer checks alone miss these. Measure the calls themselves:

  • Right tool: did it choose the tool a correct solution needs?
  • Right arguments: are the argument values correct, not just well-formed?
  • No unnecessary calls: did it avoid calling tools when the answer was already in context?
  • Recovery: after an error result, did it correct itself or repeat the mistake?

Start with about twenty realistic tasks and run each several times, since tool choice can vary between runs. The posts on evaluating AI agents beyond final-answer accuracy and starting agent evals small with pass@k show how.

Common mistakes

  • Writing one huge tool with a mode switch instead of several small ones.
  • Vague descriptions, so the model guesses when to call the tool.
  • Returning entire database rows or documents when a small summary would do, which wastes context and leaks data.
  • Trusting arguments because "the model wrote them".
  • No step cap, no timeout, no budget.
  • Giving the same credentials to every tool.

Frequently asked questions

What is function calling in an LLM? Function calling is a pattern where you describe tools to a model and the model replies with a structured request to call one of them, with arguments. The model does not execute anything. Your application validates the request, runs the tool and returns the result to the model.

Is function calling the same as tool calling? In practice, yes. Providers use both terms for the same idea. Some APIs name the fields "functions", others "tools", and the exact request and response shapes differ, so read your provider's current documentation before you write the integration.

Does the model actually run my function? No. The model produces text that names a tool and its arguments. Your code is the only thing that runs the function, which is why validation, permissions and approvals belong in your code.

What is the difference between function calling and MCP? Function calling is how a model asks for a tool call. The Model Context Protocol is a standard way to package tools so many clients can use the same tool server. MCP clients still pass tool calls to the model underneath.

How many tools can I give a model at once? There is no single safe number, and limits depend on the provider and model. The practical rule is that more tools mean more descriptions in context and more chances to pick the wrong one, so expose only the tools a task needs and test tool selection as you add them.

How do I stop a model from calling tools forever? Cap the number of steps per request, set a timeout on each tool and a cost budget for the whole run, and return clear errors so the model can correct itself instead of retrying blindly.

MCP and tool use

Your next step

Reading is the start. Turn it into skill: read the chapter, practise on something real, check the numbers, then follow a path.

  1. Read · ChapterModel Context Protocol (MCP)
  2. Practise · Debug challengeTool error cascade
  3. Use a free toolModel Explorer
  4. Follow a roadmapAgentic AI engineer roadmap
All articles on AI agents in production
#function calling#tool calling#agents#MCP#LLM tools

Discuss this article

Got a question, a counter-example or a correction? Bring it to the community.

Join the discussion

Email updates

Enjoyed this? Get the next guide by email.

One email when something substantial is published. No digests, no daily drip. Unsubscribe in one click.

We will email you a confirmation link first, then send an occasional email when a substantial guide is published. Unsubscribe in one click. Stored with our email provider (Resend), never sold or shared. See the privacy policy.

Prefer a feed reader? Follow by RSS.