Async Tool Calls: How We Keep Voice AI Talking While It Works

We've written before about why Lexycall calls don't have dead air — the short, natural audio cues that play while the agent is mid-task. This post is the layer underneath that: the actual mechanism that makes those cues possible, and why "just play a sound while you wait" is harder than it sounds once tool calls are involved.

The problem: a tool call blocks the conversation

An AI voice agent isn't just a language model talking — it's a language model that stops mid-conversation to do things: check calendar availability, look up an order, create a booking. Each of those is a real network call to a real system, with real latency. A calendar API might respond in 200ms on a good day and well over a second on a bad one.

If the agent waits for that response before saying anything, the caller hears nothing for however long the tool takes. That's the dead-air problem, and it gets worse — not better — the more capable the agent's integrations are, because more integrations mean more tool calls sitting in the middle of more conversations.

The fix: async tools with in-progress messages

The pattern we build on comes from how LiveKit's agent framework models function calling: a tool call isn't a single blocking step, it's a call with distinct states — invoked, running, and completed — and the agent can react to each state separately instead of only to the final result.

That separation is what makes an in-progress message possible. The moment a tool call starts running, the agent already knows it has something to fill the gap with: a short spoken acknowledgment ("let me check that for you") that plays while the request is actually in flight, not a canned line inserted after the fact. The tool call and the spoken filler run concurrently — the agent isn't waiting to speak until the tool finishes, and it isn't waiting to call the tool until it's done speaking.

Making our own tools async — including MCP

Lexycall's integrations aren't only built as bespoke API calls per system — some run as MCP (Model Context Protocol) tools, which gives us a consistent way to expose an integration's actions to the agent. The part that mattered for latency was making sure our MCP tool calls follow the same async, stateful pattern as our native ones: a tool call reports that it has started before it reports that it has finished, so the in-progress cue fires off the start event rather than waiting on the response.

Without that, an MCP-backed action would behave differently from a native one — silent while running, sound only at the end — which is exactly the inconsistency that breaks the illusion of a natural conversation. Treating every tool call the same way, regardless of what's implementing it underneath, is what keeps the caller experience consistent as we add more integrations.

Not every tool call is a lookup the agent decides to make on its own — some, like collect_dtmf, wait on the caller instead, capturing a keypad-entered order or tracking number before the agent continues. Same invoked/running/completed pattern, just triggered by the caller's input rather than a background API call.

Why this matters more as integrations scale

A single tool call is easy to paper over. The real test is a call that needs two or three of them — checking availability, then booking, then confirming details back into a CRM. Each one is a small wait; stacked together, they're the difference between a call that flows and one that feels like it's stalling every few seconds. Building tool calls as async, stateful operations from the start is what lets that stack of steps still sound like one continuous conversation.

This is also why we treat custom voice AI integrations as more than "connect an API" — the connection working correctly and the connection sounding right on a live call are two different engineering problems, and both have to be solved for the integration to actually be usable on the phone.

Want to hear it on a call with a real integration behind it? Book a free demo.

Frequently Asked Questions

A tool call is a real network request — checking a calendar, hitting an API — that takes real time to respond. If the agent waits silently for that response before speaking, the caller hears nothing for however long it takes.
It means the tool call has distinct states — invoked, running, and completed — that the agent can react to separately, instead of only reacting once to the final result. The in-progress state is what makes a spoken filler cue possible.
This is the mechanism underneath it. The filler audio is what the caller hears; the async, stateful tool-call pattern is what lets that audio fire the moment a tool call starts rather than after it finishes.
Both. We built our MCP tool calls to follow the same invoked/running/completed pattern as native ones, so an MCP-backed action sounds identical to a caller regardless of what's implementing it underneath.
No — the calendar lookup or booking API call takes exactly as long as it takes. What changes is that the caller experiences that wait as a natural acknowledgment instead of silence.
A single tool call is easy to mask. A call that needs two or three in sequence — checking availability, booking, confirming — stacks those waits together, and that's where the difference between silent waiting and continuous conversation becomes obvious.
Get started

Stop missing calls.
Start banking them.

Join hundreds of teams running their phones on AI — without sounding like one. See a live agent on your own call flow in 30 minutes.

Live · Lexy00:01:24
AgentBooking confirmed for Thursday at 2pm.
CallerThanks, that was easy.