MCP Vapi Caller
by sebastienfi
README.md
# MCP Vapi Caller
MCP server that lets Claude make AI-powered outbound phone calls via [Vapi](https://vapi.ai). Works with **any type of call** — customer service, sales, surveys, scheduling, etc. — in any language.
## How It Works
```
Claude → make_call(phone, goal, script) → Vapi API → AI voice agent calls the number
Claude → get_call_result(call_id) → transcript, summary, structured data, recording
```
The caller (Claude) provides domain-specific building blocks — goal, script, caller identity. The server wraps them in a voice-optimized system prompt (language enforcement, natural-speech style, goodbye handling) and sends the call to Vapi.
## Tools
### `make_call`
Initiate an outbound call. Required parameters:
- `phone_number` — E.164 format (e.g. `+33142000000`)
- `call_goal` — What the call should achieve (one sentence, used for automatic success evaluation)
- `call_script` — Behavioral instructions for the voice agent
Optional: `language`, `caller_name`, `caller_context`, `first_message`, `structured_data_schema`, `structured_data_prompt`, `end_call_phrases`, `voice_id`, `customer_name`, `system_prompt_override`.
Returns a `call_id`.
### `get_call_result`
Poll for the call outcome. Pass `wait_seconds` (e.g. `180`) to poll every 5s until the call ends. Returns status, transcript, AI summary, success evaluation, structured extracted data, and recording URL.
## Supported Languages
Built-in voice tuning (end-call phrases, goodbye rules, natural-speech style) for: **French (`fr`, default), English (`en`), Spanish (`es`), German (`de`), Italian (`it`), Portuguese (`pt`)**. Any other BCP-47 code still works — it falls back to English conversation rules while instructing the agent to speak the requested language.
## Setup
### 1. Prerequisites
- [uv](https://docs.astral.sh/uv/) — Python package manager (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
- A [Vapi](https://vapi.ai) account
- An [ElevenLabs](https://elevenlabs.io) voice ID (Vapi's default TTS provider here)
### 2. Get Vapi credentials
From the [Vapi dashboard](https://dashboard.vapi.ai/):
- **API key** → *Settings → API Keys*
- **Phone number ID** → *Phone Numbers*. Buy a Vapi number or import a Twilio one, then copy its ID (a UUID, not the phone number itself).
Pick a **voice ID** from the [ElevenLabs voice library](https://elevenlabs.io/voice-library).
### 3. Install
```bash
git clone git@github.com:sebastienfi/mcp-vapi-generic-caller.git
cd mcp-vapi-generic-caller
uv sync
```
### 4. Configure secrets
Create `~/.config/mcp/secrets.env` (and `chmod 600` it):
```bash
VAPI_API_KEY=your-vapi-api-key
VAPI_PHONE_NUMBER_ID=your-phone-number-id
VAPI_VOICE_ID=your-elevenlabs-voice-id
```
These three are required. See [`.env.example`](.env.example) for all optional variables (default language, caller name, LLM provider/model, voice tuning).
### 5. Run (optional smoke test)
```bash
uv run python server.py # stdio transport; Ctrl-C to stop
# or, using PEP 723 inline metadata:
uv run --script server.py
```
The server communicates over stdio and is normally launched by Claude, not run manually — this just confirms it starts without errors.
### 6. Register with Claude
#### Claude Code
Add to `~/.claude.json` under `mcpServers`:
```json
{
"mcpServers": {
"vapi-caller": {
"command": "/bin/bash",
"args": ["-c", "set -a; source ~/.config/mcp/secrets.env; set +a; exec uv run --directory /path/to/mcp-vapi-generic-caller python server.py"]
}
}
}
```
Replace `/path/to/mcp-vapi-generic-caller` with the absolute clone path. Restart Claude Code and verify with `/mcp` — `vapi-caller` should list two tools.
#### Claude Desktop
Add the same block to `claude_desktop_config.json`
(macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`).
Use **absolute paths** for both `bash` and `uv` (e.g. `/opt/homebrew/bin/uv`) — Claude Desktop is an Electron app and does not inherit your shell `PATH`. The `source secrets.env` wrapper is what makes env vars available at runtime.
## Usage Example
```
You: Call +33142000000 and book a haircut for next Tuesday afternoon.
My name is Jean Dupont.
Claude: [make_call with goal, script, language="fr"]
Call initiated, waiting for result...
[get_call_result(call_id=..., wait_seconds=180)]
The salon confirmed a haircut appointment for Tuesday at 15:00
with stylist Marie. Recording: [link]
```
## Docker (HTTP / cloud deployment)
For non-local deployments, run the server over Streamable HTTP instead of stdio:
```bash
cp .env.example .env # fill in your secrets
docker compose up --build
```
This exposes the MCP server on `http://localhost:8000` (`MCP_TRANSPORT=streamable-http`). Point an HTTP-capable MCP client at it. For local Claude Code/Desktop use, the stdio setup above is simpler.
## Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
| `VAPI_API_KEY` | ✅ | — | Vapi API key |
| `VAPI_PHONE_NUMBER_ID` | ✅ | — | Vapi phone number ID (UUID) |
| `VAPI_VOICE_ID` | ✅ | — | ElevenLabs voice ID |
| `DEFAULT_LANGUAGE` | | `fr` | Default BCP-47 call language |
| `DEFAULT_CALLER_NAME` | | — | Who the agent calls on behalf of |
| `VAPI_LLM_PROVIDER` | | `anthropic` | Vapi LLM provider |
| `VAPI_LLM_MODEL` | | `claude-sonnet-4-6` | LLM model (validated against Vapi's Anthropic list) |
| `VAPI_VOICE_PROVIDER` | | `11labs` | TTS provider |
| `VAPI_VOICE_MODEL` | | `eleven_flash_v2_5` | ElevenLabs TTS model |
| `VAPI_VOICE_STABILITY` | | `0.5` | Voice stability (0.0–1.0) |
| `VAPI_VOICE_SIMILARITY_BOOST` | | `0.75` | Voice similarity boost (0.0–1.0) |
| `VAPI_VOICE_SPEED` | | `1.0` | Speech speed |
| `MCP_TRANSPORT` | | `stdio` | `stdio` or `streamable-http` |
## Project Structure
```
server.py # MCP server (single file, PEP 723 inline metadata)
pyproject.toml # Project metadata & dependencies
uv.lock # Locked dependency versions
.env.example # Environment variable template
Dockerfile # Multi-stage Docker build
docker-compose.yml # HTTP/cloud deployment
CLAUDE.md # Claude Code guidance
```
No test suite or linter is configured.
TDQS
A4.8/5.0
Scored across 2 tools
Disambiguation5/5
The two tools have entirely distinct purposes: one initiates a call, the other retrieves its result. There is no overlap or ambiguity between them.
Naming Consistency5/5
Both tools follow the verb_noun pattern: make_call and get_call_result. The naming is consistent and predictable.
Tool Count3/5
With only two tools, the set feels thin but the scope is narrow. The two tools cover the primary workflow, though additional utilities (like cancelling a call) could be expected.
Completeness4/5
The core lifecycle of initiating a call and retrieving its result is well covered. A minor gap exists in lack of cancellation or management of calls, but agents can work around this by waiting for completion.
Maintenance
ActivityStale
ResponsivenessNo issues