What is tool calling (function calling)?
Tool calling, also called function calling, is the feature that lets a large language model (LLM) take actions through your software. You describe a set of tools — each with a name, a description and a JSON Schema for its arguments — alongside the conversation. When the model decides it needs one, it returns a structured call, such as check_order_status with {"order_id": "48213"}, instead of a text reply. Your code runs the function, sends the result back, and the model writes its answer from real data.
The model only proposes; your application disposes. Nothing is executed by the model, which is what makes tool calling safe enough to build on: your code can validate the arguments, check that this WhatsApp user may perform the action, ask for confirmation, rate-limit, log or refuse. One turn can involve several calls, in sequence or in parallel, and an AI agent is essentially this loop run repeatedly until a task is done.
Tool calling is often confused with two neighbours. Structured output (JSON mode) also returns JSON, but as the final answer rather than as a request for your code to act. The Model Context Protocol (MCP) sits a layer above: an open standard for packaging tools in servers so any compatible application can discover and use them, while tool calling is the model-level mechanism that actually invokes a tool, wherever it came from.
Key takeaways
- Tool calling lets an LLM ask your code to run a named function with JSON arguments, then answer from the result.
- The model never executes anything itself; your code decides whether and how each proposed call runs.
- Authorise every call against the verified WhatsApp sender, never against identifiers the model supplies.
- Make side-effecting tools idempotent and confirm irreversible actions with the customer first.
- MCP standardises how tools are packaged and shared; tool calling is the model-level mechanism that invokes them.
Inspect one tool call, from definition to reply
{"tools": [{"name": "check_order_status","description": "Get delivery status for one of this customer's orders.","parameters": {"type": "object","properties": {"order_id": {"type": "string"}},"required": ["order_id"]}}],"customer_message": "48213 kab tak aayega?","model_turn": {"tool_calls": [{"id": "call_01","name": "check_order_status","arguments": {"order_id": "48213"}}]},"tool_result": {"tool_call_id": "call_01","content": {"status": "out_for_delivery","eta": "today, 6–8 pm"}},"final_reply": {"text": "Aapka order 48213 out for delivery hai — aaj 6–8 pm ke beech pahunch jayega. 🚚"}}
Field 8: final_reply.text — The model's answer, written from real data, sent to the customer through the Cloud API.
Tool calling (function calling) in code
const tools = [{name: "check_order_status",description: "Get delivery status for one of THIS customer's orders.",parameters: {type: "object",properties: { order_id: { type: "string", pattern: "^[0-9]{5,10}$" } },required: ["order_id"],},}];async function reply(conv, waId) {for (let round = 0; round < 5; round++) { // hard cap on tool roundsconst res = await llm.generate({ system: SYSTEM_PROMPT, messages: conv, tools });if (!res.toolCalls?.length) return res.text; // final answer for WhatsAppconv.push(res.assistantMessage);for (const call of res.toolCalls) {const result = await runTool(call.name, call.arguments, waId);conv.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) });}}return "Sorry, I couldn't finish that. Connecting you to our team.";}async function runTool(name, args, waId) {if (name !== "check_order_status") return { error: "unknown_tool" };const order = await orders.find(args.order_id);if (!order || order.waId !== waId) return { error: "order_not_found" }; // same error for bothreturn { status: order.status, eta: order.eta }; // only what the customer may see}
A provider-neutral loop: the model proposes calls, your code authorises and runs them, and results go back tagged with the call ID.
How does tool calling (function calling) work?
Describe the tools
Send each tool's name, a description of when to use it, and a JSON Schema for its arguments with every request. Clear descriptions matter more than clever names: the model chooses tools from them.
The model returns a call
When a tool is needed, the response contains one or more calls — each with an ID, the tool name and JSON arguments — instead of, or before, any text for the customer.
Your code validates and runs it
Parse the arguments against the schema, authorise the action for the sender's
wa_idfrom the webhook, attach an idempotency key to anything that writes, and execute with a timeout.Return the result
Send the result back tagged with the matching call ID — data on success, a short machine-readable error otherwise — so the model can answer, retry with different arguments or call another tool.
Send the final reply
When the model responds with text, send it through the Cloud API. Cap the loop — say five tool rounds per message — so a confused model cannot spin forever.
Why does tool calling (function calling) matter?
Tool calling is what turns a bot that talks about orders into one that can look them up, book a service slot or raise a return — the difference between sending a customer to a website and resolving the request in the chat. It is also where most of the risk lives: every tool is a capability a manipulated or confused model might try to use, through prompt injection or a plain misunderstanding. Designing tools well — narrow, validated, authorised and idempotent — is most of the work of building a trustworthy WhatsApp bot that acts.
Common wrong turns
Common mistakes with tool calling (function calling)
Trusting the arguments
Arguments are model output, shaped by whatever the customer typed. Validate types and formats, and authorise against the verified sender — never accept a phone number or customer ID from the model as proof of identity.
Tools that do too much
A generic
run_sqlorupdate_order(fields)tool hands the model your database. Expose narrow, task-shaped tools with only the fields they need.No idempotency on writes
Retries and repeated calls can book two slots or raise two returns. Give every side-effecting call an idempotency key derived from the conversation and the request.
Silent slowness
Several tool rounds plus model time can take many seconds. Acknowledge the webhook immediately, show a typing indicator, and set a timeout on every tool.
Leaking raw errors and data
Returning stack traces or whole customer records gives the model — and through it the customer — more than they should see. Return small, purpose-built results and generic error codes.
Key facts at a glance
Tool calling (function calling): key facts and limits
- Who executes the function
- Your code — the model only proposes calls
- Tool definition
- Name + description + JSON Schema for the arguments
- Call shape
- Call ID, tool name, JSON arguments
- Calls per turn
- None, one, several in sequence or several in parallel
- Tool choice
- Let the model decide, force a tool, or disable tools
- Typical loop cap
- A few rounds per customer message (for example 5)
- WhatsApp typing indicator
- Shown until you reply or for up to 25 seconds
Example: Booking an AC service visit in Ahmedabad
An appliance-service company in Ahmedabad gives its WhatsApp bot three tools: get_open_slots(pincode, date), book_slot(slot_id) and create_ticket(summary). A customer writes "AC not cooling, can someone come tomorrow? 380015". The model calls get_open_slots with the PIN code and tomorrow's date and offers three slots as reply buttons. When the customer taps one, it calls book_slot — which the server runs only after checking the slot is still free and the booking is for the sender's own wa_id, with an idempotency key so a double tap books once. The bot then confirms the technician's time window in the chat.
Tool Calling (Function Calling) FAQs
Does the LLM run the function itself?+
What is the difference between tool calling and MCP?+
Is function calling the same as structured output?+
How do I stop a bot from calling tools it shouldn't?+
Good practice
Best practices for tool calling (function calling)
- Write tool descriptions as instructions: when to use it, when not to, and what each argument means.
- Return errors the model can act on, such as
slot_takenororder_not_found, rather than free text. - Log every call with its arguments, result, latency and the conversation's
wamidfor audits. - Start with read-only tools, and add tools that write once your authorisation layer has been tested.