Give your agent a phone.
CallAgent is a remote MCP server. Your agent prepares a call and shows you the plan. Once you approve, a voice agent places the call over the normal phone network and reports back. There's no SDK to install.
Quickstart
Create an account
Sign in with Google, then finish the setup with your name, country and your own phone number. Your token appears on the dashboard.
Connect your client
Add the server with your token as a bearer header (see Client setup) and
reconnect. Call server_status to check the connection.
Ask for a call
Tell your agent what you want in plain language. It calls prepare_call, shows you the plan,
and waits for your go-ahead.
Approve and listen
On your go-ahead the agent calls approve_and_start, then loops wait_for_call
until the call is over and reports the outcome.
Client setup
Streamable HTTP transport, bearer-token auth. The endpoint is https://34.159.148.7.sslip.io/mcp.
Claude Code
claude mcp add --transport http callagent https://34.159.148.7.sslip.io/mcp \ --header "Authorization: Bearer cba_live_…"
JSON config (Claude Desktop, Cursor, others)
{
"mcpServers": {
"callagent": {
"type": "http",
"url": "https://34.159.148.7.sslip.io/mcp",
"headers": { "Authorization": "Bearer cba_live_…" }
}
}
}
Codex
[mcp_servers.callagent] url = "https://34.159.148.7.sslip.io/mcp" bearer_token_env_var = "CALLAGENT_MCP_TOKEN" tool_timeout_sec = 60
Set tool_timeout_sec above 50 s: wait_for_call long-polls for up to 50 seconds.
Authentication
Every request carries Authorization: Bearer cba_live_…. The token is the account.
Every tool resolves its caller from it, so there is no account parameter to pass, and no way to read or steer
somebody else's call. Treat it like a password: keep it out of repositories and shared configs. An
unauthenticated request gets HTTP 401.
Call lifecycle
Preparing and starting are two separate tools, and the second one needs an explicit flag. This is enforced in the protocol, not left to the client, so it holds whichever agent is driving.
prepare_call(number, brief, …) → run_id, the full plan. Nothing dials. │ show the plan to the user, get a yes approve_and_start(run_id, i_understand_this_places_a_real_call=true) │ → dials once. Repeating it never redials. wait_for_call(run_id, since_seq) → new transcript lines, pending questions, │ loop until final=true and finally the result call_review(run_id) → optional: QA review after the call
A call keeps running if your MCP client disconnects. Reconnect and keep polling with the same
run_id.
Supervising a live call
When the other side offers something the brief does not cover (a different time, another price, a question nobody planned for), the voice agent does not guess. It says something like „Moment, ich frag kurz nach“ and raises a question to you.
wait_for_callreturns immediately withpending_questions.- Answer with
nudge_call(run_id, "<answer>")within about 25 seconds; the voice agent puts it into its own words. - You can nudge without being asked, too. Normal nudges apply between replies;
urgent=trueinterrupts the current reply. - A nudge cannot widen the approved constraints.
abort_callhangs up immediately.
Writing a brief
The brief is Markdown, usually in German, and it is the voice agent's whole instruction set. Four sections carry most of the weight:
# Rolle & Ziel Du rufst bei der Praxis Dr. Huber an und vereinbarst einen Kontrolltermin für Thomas. # Ton Freundlich, knapp, natürlich. Keine Floskeln. # Ablauf 1. Begrüßen und den Grund nennen. 2. Nach freien Terminen am Donnerstag fragen. 3. Den frühesten Termin bestätigen und einmal wiederholen. # Regeln - Kein Termin vor 14 Uhr. - Bei Fragen zu Kosten: nachfragen, nicht raten. # Fertig wenn - Termin mit Datum und Uhrzeit bestätigt - Name der Praxis-Mitarbeiterin notiert
Optional directives
| Write this | Effect |
|---|---|
# Fertig wenn | A bullet checklist. The agent works through it, ticks off each item in its outcome, and it decides when the call is done. |
# Vortragstext | Text the agent must say word for word: a statement, a reference number, a quote. |
Anrede: Du | Uses du instead of Sie. |
Bekannt: ja | The callee knows the caller. Informal, and the opening becomes a personal greeting. |
Begrüßung: "…" | Your own opening line, in quotes (used with du). |
Mailbox: keine Nachricht | Hang up silently on voicemail. By default the agent leaves a short message. |
Put hard rules in constraints (up to 30 strings) instead of prose when they must never bend:
budget ceilings, earliest and latest times, things never to disclose.
Results & outcomes
When wait_for_call returns final: true it includes a result:
{
"success": true,
"outcome": { "status": "erreicht", "summary": "Termin Do 14:30 bestätigt.",
"facts": { "termin": "Do 14:30", "ansprechpartnerin": "Frau Berger" } },
"ended_by": "agent",
"ended_with_goodbye": true,
"carrier_status": "completed",
"duration_seconds": 94,
"cost": { "total_usd": 0.21, … }
}
| outcome.status | Meaning |
|---|---|
erreicht | Goal reached (success: true) |
teilweise | Partly reached |
abgelehnt | The other side declined |
voicemail · mailbox | Reached a mailbox |
ivr | Reached a phone menu |
declined_screening | Call screening did not put the call through |
unklar | Unclear; read the transcript |
ended_by is one of agent, callee, supervisor_abort,
gateway_auto_hangup or timeout_or_error.
Tools
prepare_callwriteno dialCreates a run and returns the exact plan. Nothing dials. Show the plan to your user.
| Parameter | Type | Notes |
|---|---|---|
number | string | E.164, e.g. +4989123456 |
brief | string | Markdown, 1–12,000 characters. See Writing a brief |
constraints | string[] | Optional, ≤ 30 items, ≤ 1,000 characters each |
voice | string | Default marin. See voices |
vad | string | Default server_fast. See turn detection |
max_seconds | int | Hard call limit, 30–900. Default 600 |
Returns run_id, state: "prepared", the normalised plan, and
disclosure. It may also include message_for_user, which must be shown verbatim.
approve_and_startdialsStarts a prepared call. Only call it after your user has approved the plan.
| Parameter | Type | Notes |
|---|---|---|
run_id | string | From prepare_call |
i_understand_this_places_a_real_call | bool | Must be true, or the call is refused |
Idempotent: repeating it never redials. Returns the status view and a next hint.
wait_for_callread-onlyLong-polls a call. It returns as soon as the callee asks a question, the call ends, or the timeout runs out.
| Parameter | Type | Notes |
|---|---|---|
run_id | string | |
since_seq | int | Pass the previous next_since_seq. Default 0 |
timeout_s | int | 1–50, default 45 |
Returns state, final, new_transcript[], pending_questions[],
next_since_seq and, when final, result.
nudge_callwriteSends guidance into a live call, or answers a pending question.
| Parameter | Type | Notes |
|---|---|---|
run_id | string | Must be an active call |
instruction | string | 1–3,000 characters |
urgent | bool | true interrupts the current reply. Default false |
Returns nudge_id and state: "queued". The status later shows nudge_applied
once it reached the voice session; this confirms delivery, not that the agent followed it.
abort_callhangs upHangs up immediately, and the carrier confirms it. A prepared call that never started is simply cancelled.
| Parameter | Type | Notes |
|---|---|---|
run_id | string | |
reason | string | Optional, ≤ 500 characters |
call_statusread-onlyThe full record of a call: state, events, transcript, supervisor_needed, outcome, cost and
review summary. wait_for_call is usually the better loop. Use this for a one-off look.
call_reviewread-onlyA QA review that runs automatically after each call: tasks done and missed, anything unnatural,
brief suggestions, and a score from 1 to 10. rerun=true re-evaluates. Returns call_not_finished
while the call is still running.
cost_reportread-onlyCost per call and per day for your own calls, in USD. Pass run_id for one call, or
period = 1d · 7d · 30d · all. Each value is marked actual
(carrier-reported) or estimated.
server_statusread-onlyChecks the connection and configuration without exposing secrets. Returns the model, the call limits, and whether each provider is configured. A good first call after connecting.
States
| State | Meaning |
|---|---|
prepared | Plan created, nothing dialled |
starting · dialing | Being placed, ringing |
in-progress | Connected; the conversation is running |
stopping | Hang-up requested, waiting for carrier confirmation |
start_uncertain | The carrier's answer to the dial request was lost. Never retried automatically, so it cannot ring twice |
completed | Ended normally |
busy · no-answer · failed · canceled · aborted | Ended without, or before, a conversation |
Voices & turn detection
voice is fixed for the whole call: marin (default), cedar, alloy, ash,
ballad, coral, echo, sage, shimmer or verse.
| vad | Behaviour |
|---|---|
server_fast | Default. Replies about 0.5 s after the other side stops speaking |
server | Slightly more patient, with a higher threshold for noisy lines |
semantic_high · _medium · _low | Waits for a complete thought. _low is the most patient |
Limits
| Limit | Value |
|---|---|
| Call length | 600 s default, 900 s maximum, enforced at the carrier |
| Concurrent calls | 1 during the pilot |
| Brief | 12,000 characters; ≤ 30 constraints |
| Numbers | E.164 only. Premium and service numbers (+49900, +49137, +49118) are blocked |
| Countries | EU/EEA, Switzerland, United Kingdom, USA |
| Recording | Never. Text transcripts only, deleted after 7 days |
Errors
Tools fail with a readable message, never a silent success. The common ones:
| Message | What to do |
|---|---|
Explicit real-call approval is required | Pass i_understand_this_places_a_real_call=true, after the user said yes |
Use an international E.164 phone number | Format as +49…, without spaces |
Premium/service numbers are blocked | By design |
Nudge requires an active call… | The call has already ended, or the instruction is empty |
Dein kostenloser Testanruf ist aufgebraucht… | The free call has been used. More is coming |
| HTTP 401 | Missing or wrong bearer token |
Pilot accounts
prepare_call rings your own phone
for a short call of up to 4 minutes, in which the agent asks what you would use phone calls for.
If you ask for a different number, the plan comes back with a message_for_user explaining
this. A call that never connects (busy, no answer) does not count.Questions? Read how it works or how we handle data.