A public remote MCP server: connect it to any AI agent (Claude Code, Codex, claude.ai) and it dispatches an autonomous voice agent to phone your human, run the whole call itself, and report back β with an escape hatch. When the human asks something the agent wasn't briefed on, the agent phones you (the operator) live, mid-call, keeps chatting while it waits, and relays your answer when it lands. Same BYOK design as its siblings: no env vars, no server-side keys, no signup.
Three siblings, three control models:
call-my-human-no-middle-agentβ puppet: you drive every word (full control, high latency).call-my-humanβ autonomous: brief it once, it runs the call, you collect a structured report.dispatch-my-human-escape(this one) β autonomous + async escape hatch: like dispatch, plus the agent can phone home to you mid-call.
your agent (operator, any MCP client)
β dispatch_call({ objective, context, questions })
βΌ
this val ββ POST api.vapi.ai/call (your key) ββΆ Vapi ββ gpt-4o brain + Elliot voice ββΆ π± your human
β² β β
β β ask_operator(question) ββββββββββββββββ€ (off-brief? agent phones home, keeps talking)
β βββΆ /tool (records it, returns instantly) β
β β
βββ await_report ββΆ needs_answer ββΆ provide_answer(text)
βββΆ add-message into the live call ββΆ agent relays it
dispatch_call{ objective, context?, questions?, number?, first_message?, caller_identity?, max_minutes? }β hand off the call.await_report{ wait_seconds }β long-poll. Returns either:needs_answer: truewith{ escalation: { question } }β the agent hit an off-brief question. Reply withprovide_answer, then callawait_reportagain.done: truewith{ summary, objective_met, answers[], followups, transcript }β the call is over.
provide_answer{ text }β your answer is pushed into the live call and the agent announces + relays it in its own words.
Optional: listen (eavesdrop), say (barge in), get_status, end_call.
The agent has an ask_operator(question) tool. When the human asks something off-brief, it calls it. Our /tool endpoint records the question and returns instantly β so the agent doesn't go on hold; it keeps the conversation going. Meanwhile await_report surfaces the question to you. When you provide_answer, we push it into the live call via Vapi's add-message (with triggerResponseEnabled) β that lands in the agent's context (not raw TTS), so it says "oh β just heard back from my operatorβ¦" and weaves it in, staying in the loop for follow-ups.
In effect the escape hatch hands the phone agent your entire toolset: ask it "list my files" and you (the operator) run the tool and relay the result.
Two values from dashboard.vapi.ai: your private key and a phone number ID.
claude mcp add --transport http dispatch-escape \ "https://dcm31--019ec8e38fe975a29f23e932794ecdea.web.val.run/mcp?vapi_key=YOUR_KEY&phone_number_id=YOUR_PHONE_ID&human_number=%2B15551234567"
Then tell your agent "if you get stuck on the call, ask me." The operator just needs to keep calling await_report to catch escalations.
It's a standard MCP server β drive it from anything that speaks JSON-RPC over HTTP, exactly like Claude Code/Codex do:
BASE="https://dcm31--019ec8e38fe975a29f23e932794ecdea.web.val.run/mcp" CREDS="vapi_key=YOUR_KEY&phone_number_id=YOUR_PHONE_ID&human_number=%2B15551234567" curl -s "$BASE?$CREDS" -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"dispatch_call","arguments":{"objective":"Quick check-in"}}}'
Authorization: Bearer <vapi_key> is accepted in place of the vapi_key query param (and keeps the key out of the URL).
- Your Vapi key is the crown jewel. Read per request, never stored, never logged (only a hash is logged). Everything gates on it β without it, nobody can place calls or read your data. Rotate it if it ever leaks.
- BYOK rides in the URL by default. URLs leak (server/proxy logs, history, referrers), so prefer the
Authorization: Bearerheader. And on a hosted instance you don't own, the val owner can see your key and transcripts in their logs/DB β so for anything real, remix this val and host your own (zero config). This is the single biggest privacy lever. - Stored data. Transcripts and structured reports live in the val's SQLite β unencrypted, no expiry β scoped by
tenant= first 12 hex of SHA-256(your key), unguessable without the key. Remix for full custody. - Watch links are bearer capabilities.
/?t=<tenant>shows the live transcript to anyone with the link (a 48-bit unguessable id). Don't share it for a sensitive call. - Webhook/tool auth is light.
/webhookand/toolcheck a secret equal to your tenant id;/tooleven allows a missing secret. An attacker would need your tenant id (only semi-exposed via watch links) to forge events β and could then only inject fake transcript lines or spurious escalations into your own feed, never place calls or read your key. Remixing + not leaking watch links closes this. - Prompt-injection surface. Whatever the human says flows into the agent's brain (and into escape-hatch questions). You're calling your own human, so risk is low β but don't wire the operator to take destructive, irreversible actions purely on phoned-in instructions.
- Solid bits. TLS end to end; the val never touches raw audio (Vapi does the telephony, we only receive transcripts); all DB queries are tenant-scoped.
- Consent. Only call numbers you have consent to call β automated calls without consent violate TCPA/FCC rules.
main.tsβ routes, per-tenant Vapi webhook, the async/toolescape-hatch endpoint, landing + live feedtools.tsβ the 7 tools (incl.provide_answer); builds the agent's system prompt from your briefvapi.tsβ Vapi client: native-model call creation with theask_operatortool,endCall, analysis plan, andadd-messagemcp.tsβ minimal stateless streamable-HTTP MCP plumbing (no SDK)db.tsβ SQLite:calls, append-onlyevents, and theescalationstable
- Brain: your Vapi account's bundled OpenAI model (
gpt-4odefault;&model=to override). Voice: Vapi-native Elliot (tuned for telephony). No extra keys for either. - The agent ends calls itself via
endCall;max_minutes(default 10) is a hard cap. - Phone audio is narrowband (8kHz) β it sounds worse than the Vapi dashboard's web "test call." Free Vapi numbers add latency and have a daily outbound limit; import a Twilio number for reliability.