Skip to main content
Glama
sebastienfi

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