The most useful communications agents do more than answer questions. They take action.
This Telnyx code example shows that pattern on Edge Compute. A user writes a normal sentence, GLM-5.2 decides whether the request needs an SMS, a phone call, or a status check, and a durable ToolAgent executes the selected Telnyx tool.
The code example is here:
https://github.com/team-telnyx/telnyx-code-examples/tree/main/agent-with-tool-calling
You need a Telnyx API key, a Telnyx number, Messaging setup, a Call Control application, and the code sample.
What This Example Builds
The sample is a Node.js and TypeScript app running on Telnyx Edge Compute.
It defines three tools:
send_sms(to, body)
make_call(to)
check_status(what)
The user experience is intentionally simple:
User: Text +13125550001 hi from my tool-calling agent
Agent: sends the SMS, then replies with a short confirmation
User: Call me at +13125550001
Agent: places the call, then replies with a short confirmation
User: Did the SMS send?
Agent: checks the latest send_sms result and summarizes it
That is the point of the sample. The user does not need to click a button for "SMS" or "Call." The model chooses the tool from the request.
Architecture
user message
-> edge function receives request
-> ToolAgent appends user message to history
-> Telnyx Inference receives the conversation and tool definitions
-> GLM-5.2 returns a tool call
-> ToolAgent dispatches send_sms, make_call, or check_status
-> tool result is appended with the same toolCallId
-> model is called again with the tool result
-> assistant returns a normal final message
The important detail is the second model call. A tool call is not the final answer. It is an instruction to the application.
The agent has to:
- save the assistant tool-call message
- execute the selected tool once
- append the tool result using the same
toolCallId - send the updated conversation back to the model
- stop when the model returns a normal assistant message
That gives you a clean, inspectable loop.
Why This Pattern Matters
Tool calling is where an LLM becomes useful inside a communications workflow.
Without tools, the model can say:
I can send that text for you.
With tools, the model can choose send_sms, pass the phone number and body, and let the application call Telnyx Messaging.
Without tools, the model can say:
You should call the customer now.
With tools, the model can choose make_call, pass the destination number, and let the application call Telnyx Call Control.
That is a better developer pattern because the model handles intent and argument extraction, while your code owns execution, validation, logging, and error handling.
The Tool Definitions
The sample sends tool definitions to Telnyx Inference alongside the conversation.
The SMS tool accepts a destination number and message body:
send_sms({
to: "+13125550001",
body: "hi from my tool-calling agent"
})
The call tool accepts a destination number:
make_call({
to: "+13125550001"
})
The status tool checks the agent's local ledger:
check_status({
what: "send_sms"
})
These schemas give the model a small action surface. It can choose from approved functions, not arbitrary code.
The Core Implementation
The ToolAgent extends the Telnyx Agent SDK:
export class ToolAgent extends Agent<Env, ToolState> {
protected override initialState(): ToolState {
return { from: "", to: "", iterations: 0, lastReply: "", updatedAt: 0 };
}
}
When a user message arrives, the agent saves it and queues work:
await this.messages.add("user", text);
await this.queue("process");
Inside process(), the agent calls Telnyx Inference through the Edge binding:
await this.env.TELNYX.ai.openai.chat.createCompletion({
model,
messages,
tools,
tool_choice: "auto",
});
If the model chooses send_sms, the app calls Telnyx Messaging:
await this.env.TELNYX.messages.send({
from: fromNumber,
to,
text: body,
});
If the model chooses make_call, the app calls Telnyx Call Control:
await this.env.TELNYX.calls.dial({
connection_id: callControlAppId,
from: fromNumber,
to,
command_id: toolCallId,
});
The command_id uses the tool call ID, which makes the call easy to correlate with the agent ledger.
The Tool Ledger
The sample logs each tool call with:
- tool name
toolCallId- normalized arguments
- result
- status
- timestamp
That ledger is useful in demos and in debugging. You can prove that the model chose the right tool, the app executed it, and the tool result went back into the conversation before the final assistant response.
Run The Example
Clone the repo:
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/agent-with-tool-calling
npm install
Run the checks:
npm run typecheck
npm run types
Configure your Telnyx number, Messaging Profile webhook, Call Control application, and Edge function settings. Then deploy:
npm run ship
Try commands like:
Text +13125550001 running five minutes late
Call me at +13125550001
Did the call go through?
Why This Is Useful
This same shape works for many communications agents:
- appointment reminders
- delivery updates
- support escalations
- lead response
- customer callbacks
- field service notifications
- internal operations alerts
The model understands the request. Your code validates the arguments, calls Telnyx, records the result, and gives the model enough context to answer the user clearly.
That separation is the main lesson of the sample.