Skip to main content
Glama
sebastienfi

MCP Vapi Caller

by sebastienfi

MCP Vapi Caller

MCP server that lets Claude make AI-powered outbound phone calls via Vapi. 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.

Related MCP server: Vapi MCP Server

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 — Python package manager (curl -LsSf https://astral.sh/uv/install.sh | sh)

  • A Vapi account

  • An ElevenLabs voice ID (Vapi's default TTS provider here)

2. Get Vapi credentials

From the Vapi dashboard:

  • API keySettings → API Keys

  • Phone number IDPhone 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.

3. Install

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):

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 for all optional variables (default language, caller name, LLM provider/model, voice tuning).

5. Run (optional smoke test)

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:

{
  "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 /mcpvapi-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:

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.

Available Tools

2 tools
get_call_resultA

Check the outcome of a call initiated by make_call().

Returns the call status, transcript, AI-generated summary, success evaluation, structured extracted data, and recording URL.

USAGE:

  • For a quick status check: get_call_result(call_id="...")

  • To wait for completion: get_call_result(call_id="...", wait_seconds=180) This polls every 5 seconds until the call ends or the timeout is reached.

TYPICAL CALL DURATION: 1-3 minutes. Use wait_seconds=180 for most calls.

RETURNED FIELDS (when call has ended):

  • call_id: The call identifier

  • status: "ended" when complete

  • ended_reason: Why the call ended (e.g. "assistant-ended", "customer-ended")

  • summary: AI-generated summary of the conversation

  • success: Success evaluation based on the call_goal

  • structured_data: Extracted data matching the schema from make_call (if provided)

  • transcript: Full conversation transcript

  • recording: URL to the call recording audio file

If the call is still in progress, returns the current status without the analysis fields. Call again with wait_seconds to poll.

Args: call_id: The call ID returned by make_call wait_seconds: If > 0, poll every 5s up to this duration waiting for the call to end. 0 means check once and return immediately.

ParametersJSON Schema
NameRequiredDescriptionDefault
call_idYes
wait_secondsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, but the description fully discloses polling behavior, timeout, and the conditional return of fields. It shows exactly what is returned when the call has ended versus in progress, adding rich behavioral context beyond the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with sections (USAGE, TYPICAL CALL DURATION, RETURNED FIELDS, Args). It is detailed but every sentence contributes, with no redundancy or fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers all key aspects: status check, waiting, return fields, polling behavior, and conditional responses. Despite having an output schema, the description adds essential context about the tool's runtime behavior, making it complete for an agent to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has no property descriptions (0% coverage), but the description explains both parameters thoroughly: call_id as the ID returned by make_call, and wait_seconds with its polling semantics and default behavior. This compensates fully for the schema gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Check the outcome of a call initiated by make_call()', which is a specific verb+resource statement. It clearly distinguishes from the sibling make_call tool by focusing on result retrieval rather than initiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit usage patterns: quick status check with call_id only, or waiting via wait_seconds=180 for typical 1-3 minute calls. Explains polling interval (5 seconds) and timeout behavior, plus what to do if the call is still in progress.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

make_callA

Make an outbound phone call with an AI voice agent via Vapi.

The voice agent will call the given phone number, follow your script, and extract structured data from the conversation. After initiating the call, use get_call_result() to retrieve the outcome.

WORKFLOW:

  1. Call make_call() with your parameters → get back a call_id

  2. Wait 1-3 minutes (typical call duration)

  3. Call get_call_result(call_id=..., wait_seconds=180) to get results

REQUIRED PARAMETERS:

  • phone_number: E.164 format ("+33142000000" for France, "+15551234567" for US)

  • call_goal: One clear sentence describing what the call should achieve. This is used for automatic success evaluation. Example: "Prendre un rendez-vous coiffeur pour mardi prochain après-midi"

  • call_script: Detailed behavioral instructions for the voice agent. Write this as if briefing a human assistant before a phone call. Be specific about what to say, what to ask, how to respond to common scenarios, and when to end the call. Example: "Appelle le salon et demande un rendez-vous coiffure pour mardi prochain après-midi. Si mardi est complet, demande mercredi. Accepte tout créneau entre 14h et 18h. Refuse les créneaux du matin. Réserve au nom de Jean Dupont."

RECOMMENDED PARAMETERS:

  • first_message: The exact opening line. If omitted, one is auto-generated, but providing your own gives much better results. Example: "Bonjour ! J'appelle pour prendre un rendez-vous coiffure s'il vous plaît."

  • caller_name: Who the agent represents. Example: "Jean Dupont"

  • structured_data_schema: JSON string of a JSON Schema defining what to extract from the conversation. Without this, you still get a transcript and summary but no structured fields. Example: '{"type":"object","properties":{"appointment_date":{"type":"string","description":"YYYY-MM-DD"},"appointment_time":{"type":"string","description":"HH:MM 24h"},"confirmed":{"type":"boolean"}}}'

  • language: BCP-47 code. Default: "fr". Use "en" for English, "es" for Spanish, etc. The voice agent will speak exclusively in this language.

OPTIONAL PARAMETERS:

  • caller_context: Additional identity context. Example: "Tu es l'assistant personnel de Jean Dupont"

  • structured_data_prompt: Instructions for the extraction model. Example: "Aujourd'hui c'est le 2026-04-11. Résoudre les dates relatives."

  • end_call_phrases: Comma-separated phrases that signal the call should end. Default per language (e.g. "au revoir,bonne journée" for French).

  • voice_id: ElevenLabs voice ID override (default from env VAPI_VOICE_ID).

  • customer_name: Label for the person/business being called (Vapi dashboard).

  • system_prompt_override: Replaces the entire auto-generated system prompt. Use only when you need full control over the voice agent's instructions.

TIPS FOR GOOD CALLS:

  • Keep call_script focused and concise — voice agents work best with clear, simple instructions

  • Always provide a first_message — auto-generated ones are generic

  • Include fallback behavior in call_script (what to do if the main goal fails)

  • For data extraction, keep schemas simple with clear field descriptions

Args: phone_number: Phone number in E.164 format (e.g. "+33142000000") call_goal: One sentence describing what the call should achieve call_script: Detailed behavioral instructions for the voice agent language: BCP-47 language code (default: "fr") caller_name: Who the agent calls on behalf of caller_context: Additional identity context for the voice agent first_message: Opening line when the call connects structured_data_schema: JSON string of a JSON Schema for data extraction structured_data_prompt: Instructions for the extraction model end_call_phrases: Comma-separated end-call phrases voice_id: ElevenLabs voice ID override customer_name: Name of the person/business being called system_prompt_override: Full system prompt (replaces auto-generated one)

ParametersJSON Schema
NameRequiredDescriptionDefault
languageNo
voice_idNo
call_goalYes
call_scriptYes
caller_nameNo
phone_numberYes
customer_nameNo
first_messageNo
caller_contextNo
end_call_phrasesNo
structured_data_promptNo
structured_data_schemaNo
system_prompt_overrideNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full behavioral burden. It discloses async behavior (wait 1-3 minutes), indicates what happens when optional params are omitted (auto-generated first_message, no structured fields without a schema), specifies defaults (language='fr', voice_id from env), and notes the call duration. This is rich behavioral detail that helps the agent anticipate outcomes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-organized with clear sections and front-loads the main purpose. However, the final 'Args' block redundantly repeats parameter descriptions already covered in the REQUIRED/RECOMMENDED/OPTIONAL sections. The TIPS section adds value but also lengthens the text. Overall structured, but slightly overlong due to redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (13 params, 0% schema coverage, no annotations), the description is remarkably complete. It covers all parameters, workflow, timing, tool dependencies, and best-practice tips. The existence of an output schema reduces the need to detail return values, and the description still notes the call_id response. No critical gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but the description compensates exhaustively: it groups parameters into required/recommended/optional, provides format examples (E.164, BCP-47, JSON Schema string), explains each parameter's purpose (e.g., call_goal for automatic success evaluation), and includes usage examples for call_script and first_message. This is far beyond what the schema offers.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Make an outbound phone call with an AI voice agent via Vapi', clearly stating a specific verb, resource, and service. It differentiates itself from the sibling tool 'get_call_result' by framing it as the initiating step that returns a call_id.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The WORKFLOW section explicitly lays out when to call make_call (step 1) and when to call get_call_result (step 3), including a recommended wait time. This provides clear usage context and names the alternative tool for result retrieval, satisfying the 'when/when-not/alternatives' criterion.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 2 tool updatesv0.1.0
    • First observedget_call_result
    • First observedmake_call

TDQS

A4.8/5.0
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

ActivitySlowing
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    F
    maintenance
    Enables AI assistants to make real phone calls on your behalf using VoIP, handling conversations automatically through OpenAI's Real-Time Voice API. Simply tell Claude what you want to accomplish and it will call and manage the entire conversation for you.
    24
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables Claude to make and manage real phone calls through Twilio, allowing it to think, search, and respond in real-time conversations.
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sebastienfi/mcp-vapi-generic-caller'

If you have feedback or need assistance with the MCP directory API, please join our Discord server