phone-mcp-server
Allows placing phone calls and sending SMS messages through a Twilio account, with plan-then-confirm safety, dry-run mode, destination allowlists, calling time windows, and rate limiting.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@phone-mcp-serverSend an SMS to +14155552671 saying I'm running 10 minutes late."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Recommended: run it on RizzDial + Beam
RizzDial is a commercial platform for AI voice agents and AI calling, with MCP for Claude and Codex. Pair it with Beam for iMessage on supported devices, with SMS fallback where configured. SMS fallback is subject to carrier A2P requirements; consent and opt-out rules still apply.
Create a RizzDial account and pick a plan on the signup page, or book a call and have the team set it up for you.
Open Connect MCP in your RizzDial dashboard. Follow the public MCP guide: copy the command for Claude or Codex, run it locally, and authorize in your browser. Claude Code uses
claude mcp login rizzdialafter adding the connection; Codex can usecodex mcp login rizzdialexplicitly.Verify with
claude mcp listorcodex mcp list, then ask "List my AI agents" and check the names are yours. You can ask "Create a new outbound agent for lead follow-up." Confirm before deleting anything, bulk contact edits, buying numbers, or starting a live campaign. For script setup or a test call, book a call.For Beam, Text our team to try it. Create a workspace with your work email and business name to explore a private preview; nothing sends. Choose a plan in Billing when ready; a dedicated line is assigned before live sending unlocks.
No terminal? RizzDial's MCP page can open claude.ai's connector setup. If Connect MCP is missing, or you use ChatGPT, book a call. Never paste tokens or keys into chat.
Follow the full RizzDial + Beam walkthrough for connection steps and Beam MCP permissions. The DIY alternative below uses this repository's own tools.
Related MCP server: PhoneBooth MCP Server
Demo
What it does
Who this is for
Developers connecting a personal Claude or ChatGPT workflow to their own Twilio account. This is a single-owner starter, not a multi-tenant service or a conversational voice agent. The message_or_goal argument is spoken literally: write a finished script before planning the call.
Or build it yourself (DIY Twilio path)
For numbered setup steps, follow the DIY quickstart.
This is the self-hosted alternative: you run this MCP server yourself against your own Twilio account, with the plan-then-confirm safety described below.
Try the 60-second offline demo, no keys
From this directory, with Python 3.11 or later installed:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements-dev.txt
python -m phone_mcp.demo
python -m phone_mcp.checkOn Windows, activate with .venv\Scripts\Activate.ps1. The demo uses the SDK's in-memory client/session utility and temporary storage. It fixes the clock inside the allowed window, simulates a human confirmation, and demonstrates an allowlist rejection. It ignores your .env, so it cannot place a real call.
Place real phone calls or send real SMS
You need a Twilio account, an appropriate Twilio sender number, and a recipient who has given the required consent. No OpenAI API key is used by this server. Your MCP client supplies the assistant. No audio bridge is present.
cp .env.example .env
# Edit .env locally:
# DRY_RUN=false
# ALLOWED_NUMBERS=your consenting recipient in E.164
# TWILIO_ACCOUNT_SID=your account SID
# TWILIO_AUTH_TOKEN=your auth token
# TWILIO_FROM_NUMBER=your Twilio number in E.164
# CALLER_NAME=your actual name or organization
python -m phone_mcp.check
python -m phone_mcpConnect a client using the instructions below. Ask it to plan a call, review the exact destination and spoken script, then explicitly approve that plan. A fresh plan is required if the script changes. Trial accounts and messaging registration can impose additional provider restrictions; consult Twilio's trial guide.
Calls use inline TwiML and Say, without a callback URL or <Gather>. No Twilio webhook is exposed, so webhook signature validation is not applicable. If you add an inbound webhook, validate it with twilio.request_validator.RequestValidator before handling data. A tunnel is needed only for a remote MCP client, not for Twilio to speak the script.
Claude Desktop
Install the package into the virtual environment:
python -m pip install -e .Merge this into claude_desktop_config.json, replacing all paths with absolute paths to your checkout and environment:
{
"mcpServers": {
"phone": {
"command": "/absolute/path/phone-mcp-server/.venv/bin/python",
"args": ["-m", "phone_mcp"],
"env": {
"POLICY_FILE": "/absolute/path/phone-mcp-server/policy.yaml",
"AUDIT_FILE": "/absolute/path/phone-mcp-server/data/audit.jsonl",
"STATE_FILE": "/absolute/path/phone-mcp-server/data/state.sqlite3",
"DRY_RUN": "true",
"ALLOWED_NUMBERS": "+12125550123"
}
}
}
}Create that policy.yaml first using the example. On Windows use the absolute .venv\Scripts\python.exe path. Restart Claude Desktop after updating its configuration. Keep credentials in your local environment or private local configuration. Do not assume a desktop client's working directory will load this checkout's .env.
Claude Code
In this checkout with the virtual environment active:
python -m pip install -e .
claude mcp add phone -- python -m phone_mcpIf launching from another directory, configure absolute storage and policy paths as above. Ask Claude to read phone://policy and use outbound_call_checklist before planning a call.
Streamable HTTP and MCP Inspector
HTTP listens on loopback at http://127.0.0.1:8000/mcp:
# Generate locally; store the result privately as MCP_HTTP_TOKEN in .env.
python -c 'import secrets; print(secrets.token_urlsafe(32))'
python -m phone_mcp --http --port 8000Never expose HTTP without auth. Startup warns if both bearer and OAuth auth are absent. Keep tokens out of URLs, browser history, source control, and screenshots. Use HTTPS for remote traffic. Run a single server process so confirmation codes stay in one memory store.
For stdio inspection:
npx @modelcontextprotocol/inspector python -m phone_mcpFor HTTP, start Inspector without a server command, choose Streamable HTTP and /mcp, and configure the Authorization header as Bearer YOUR_LOCAL_TOKEN:
npx @modelcontextprotocol/inspectorTo create an HTTPS tunnel after configuring auth:
ngrok http 8000
# Alternative:
# cloudflared tunnel --url http://127.0.0.1:8000Set MCP_PUBLIC_URL=https://YOUR-TUNNEL-HOST/mcp and restart the server so the SDK accepts that host. A tunnel provides transport, not user authentication. The HTTP implementation retains the SDK's host/origin checks.
ChatGPT remote MCP with OAuth
ChatGPT's remote MCP connection uses OAuth for user authentication; do not assume it accepts a custom static API key. See the official authentication guide and connection quickstart.
This repo implements the OAuth resource server. You must supply an external authorization server with OAuth metadata, authorization-code flow with PKCE, and client registration compatible with ChatGPT. It is not an identity provider.
Configure that provider to issue RS256 JWT access tokens with an
audequal to your exact public/mcpURL,issequal to the issuer URL,sub,exp, andscopecontainingphone:use. Restrict authorization to the intended owner; that scope grants access to this Twilio account's tools.Set
OAUTH_ISSUER,OAUTH_JWKS_URL, andMCP_PUBLIC_URLin.env. LeaveMCP_HTTP_TOKENempty. Start withDRY_RUN=true.Run
python -m phone_mcp --http --port 8000andngrok http 8000. Use a stable tunnel hostname or update the identity provider audience and server URL when it changes.In ChatGPT's developer-mode MCP/app settings, add the public HTTPS
/mcpURL and select OAuth. Register the callback URI shown by ChatGPT with your identity provider, then sign in as the authorized owner.Inspect
phone://policy, plan an SMS, and explicitly approve the dry-run plan before enabling live mode.
The SDK publishes /.well-known/oauth-protected-resource/mcp and advertises it in unauthenticated challenges. The server validates signature, issuer, audience, expiry, and required scope. OAuth setup depends on your provider and ChatGPT account/workspace access. Static bearer auth remains useful for Inspector and other clients supporting custom Authorization headers.
Use it with Claude Code or Codex
This repo ships an agent skill at .claude/skills/phone-mcp-setup/SKILL.md and a root AGENTS.md. In Claude Code or Codex, ask for help setting up your AI receptionist and the agent will first ask whether you want RizzDial + Beam or DIY. For DIY, it will ask about your niche and business, copy the closest example config, walk you through GET_YOUR_KEYS.md (you edit .env yourself; it will not ask you to paste secrets into chat), run the doctor and offline demo, then help you place your first real test call.
How it works
Claude or ChatGPT calls
plan_callorplan_sms, which checks destination, timing, length, and rate guards before returning a plan and a one-time code.A human reviews the exact spoken script or SMS body in the client and approves it.
place_callorsend_smsaccepts only that code, re-checks the guards, and consumes the code so it cannot be replayed.In dry-run mode the server stops there. In live mode it calls Twilio to speak the script or send the message.
Every plan and execution writes a masked JSONL audit record, and rate reservations persist in SQLite.
PhoneService accepts an injected PhoneProvider and clock, so tests use a fake provider instead of the network. The SDK's installed API was inspected during development, including FastMCP.streamable_http_app() and create_connected_server_and_client_session; the dependency floor is the verified SDK version.
How does confirmation prevent accidental calls?
plan_call(to, message_or_goal, voice="alice") and plan_sms(to, body) return a human-readable plan, a short random code, expiry, and SHA-256 payload hash. The exact payload includes action, destination, final text, voice, and dry-run mode. place_call(confirmation_code) and send_sms(confirmation_code) accept only the code. Codes expire after five minutes and are consumed even on a failed execution attempt. The payload is immutable and its hash is verified before use.
The server instructs the assistant to obtain human approval, but a code is a capability, not proof that a human clicked approve. A client can invoke both tools. Keep client approval controls enabled and treat prompt injection as a risk. For enforcement independent of the model, add an out-of-band approval UI before deploying beyond a trusted owner.
Rate limits count execute attempts across calls and SMS, including dry-runs and uncertain provider failures. They persist across restarts in SQLite. Plans check the limit but do not reserve slots. Execution reserves atomically before dispatch. Pending plans exist only in memory; restarting invalidates them. Failed provider requests are never automatically retried because the delivery outcome may be unknown.
How do I inspect delivery or troubleshoot setup?
Use get_call_status(call_sid), list_recent_calls(limit=10), or list_recent_messages(limit=10). The list limit is bounded from 1 to 100. Live reads query your Twilio account, returning SIDs/statuses without bodies or destinations. Dry-run reads return no remote records and make no requests.
Run python -m phone_mcp.check to validate configuration, timezone availability, blocklist, and writable audit/state storage without contacting Twilio. It does not verify credential validity, number ownership, consent, or delivery. Runtime audit data stays under ignored data/; records omit bodies, codes, and credentials and keep only the destination's final digits. Protect the directory and define your own retention policy.
Configuration
Copy policy.example.yaml to policy.yaml if desired. Configuration loads .env without overriding existing process variables, reads YAML, then applies environment variables over YAML. YAML keys use the lowercase names shown in the example. Unknown keys and invalid values stop startup. Relative paths resolve from the server working directory; use absolute paths for desktop clients.
Environment variable | Default | Purpose |
|
| No Twilio requests, including read tools |
| empty, deny all | Comma-separated exact E.164 numbers or starred prefixes such as |
| unset | Text file of exact numbers/starred prefixes; comments start with |
|
| Rolling shared call/SMS attempt limit, persisted in SQLite |
|
| Character limit including the spoken disclosure |
|
| Identity in the mandatory automated-call disclosure |
|
| Masked plan/execute audit records |
|
| Persistent rate reservations |
| empty | Live Twilio account |
| empty | Live credential, never include in source control |
| empty | E.164 Twilio sender |
| empty | Static bearer token; mutually exclusive with OAuth |
| empty | Public HTTPS resource URL including |
| empty | External OAuth issuer URL, exact value including trailing slash if applicable |
| empty | HTTPS signing-key endpoint for RS256 tokens |
|
| Optional YAML path; an explicitly configured missing file is an error |
The blocklist is read again at execution. Other policy changes require a restart, which invalidates pending codes. Blank allowlists deny everything. Blocklists take precedence. Prefixes require a trailing *; an unstarred number matches only itself.
Calls require all candidate recipient zones to be within 08:00 inclusive to 21:00 exclusive. The bundled area-code map covers selected US geographic NPAs and rejects unknown, toll-free, and non-US destinations for calls. SMS still requires E.164, allowlist, blocklist, size, confirmation, and rate checks, but does not use the calling window. See map maintenance and limitations. An area code cannot prove a mobile recipient's current location.
The disclosure is always prepended: This is an automated call from .... Supported voice values are alice, man, and woman. The requested alice default uses Twilio's compatibility handling; see Twilio's voice documentation.
Testing
python -m pip install -r requirements-dev.txt
python -m pytest -q
python -m phone_mcp.demo
python scripts/make_demo_gif.py100 tests pass offline with no network access and no API keys. They cover MCP registration/schemas/resources/prompts, confirmation binding, expiry, replay and concurrency, destination and timing guards, rate persistence, dry-run/live behavior with a fake, HTTP authentication, OAuth verification, audit masking, the demo, and that every example niche config loads and validates. CI runs the same offline checks on Python 3.11, 3.12, and 3.13.
Compliance note (not legal advice)
Calling real people with automated or AI voices is regulated. The FCC has confirmed that AI-generated voices fall within the TCPA's artificial or prerecorded voice restrictions; see the FCC ruling. Required consent, identification, opt-out handling, messaging rules, and applicable federal/state requirements depend on the situation. These software guards do not establish consent, perform DNC screening, or guarantee legal compliance. Consult qualified counsel before live outreach.
How does this compare with other approaches?
Approach | What you get | What you maintain |
This MIT starter | MCP tools, explicit plans, local guards, tests | Twilio account, hosting, auth, policies, compliance |
Build from scratch | Your chosen behavior and architecture | MCP transport, provider integration, guards, testing, operations |
Hosted platform | Managed product and supported workflows | Vendor selection, configuration, consent and business process |
Want this done for you?
This starter is built for a developer running their own Twilio account for themselves. If you run an agency, a local business, or a sales team and want AI calling set up and managed instead of self-hosted, the RizzDial team can set that up for you on a commercial platform.
On RizzDial, the team sets up AI voice agents and AI calling for agencies and GoHighLevel users, including predictive, power, and parallel dialing, answering machine detection, a built-in CRM plus GoHighLevel, HubSpot, and Salesforce integrations, and MCP for Claude and Codex.
See the RizzDial MCP product page or book a call to talk it through.
FAQ
Is this free?
Yes. This starter is MIT licensed and free to run on your own Twilio account. You pay Twilio for the calls and messages you send, not for the code.
Is RizzDial open source?
No. RizzDial is a separate commercial platform. This starter is the MIT-licensed part; RizzDial is not open source.
Do I need RizzDial or Beam to use this?
No. This starter works on its own with your own Twilio account, or locally with the offline demo. RizzDial and Beam are the managed option if you would rather not self-host: see Recommended: run it on RizzDial + Beam.
How do I text leads from an iMessage number?
That is what Beam
is for: iMessage on supported devices, with SMS fallback where configured. SMS fallback
is still subject to carrier A2P requirements, and consent and opt-out rules still apply. See the
Beam docs or docs/RIZZDIAL_AND_BEAM.md.
Can it hold a conversation on the phone?
No. It speaks a fixed script using Twilio's text-to-speech. It does not listen, record, gather replies, or connect an audio model.
Can I call any international number?
Calls require a supported US geographic area code. SMS can target E.164 destinations allowed by policy and Twilio, subject to applicable messaging rules.
Does dry-run bypass the safety guards?
No. It uses the same validation and confirmation flow and consumes a rate reservation, but never creates a Twilio client or sends a provider request.
What if the provider times out?
The confirmation code remains consumed and the attempt still counts. Check the Twilio console or recent-call/message tools before deciding whether a new plan is appropriate.
Can multiple people share one HTTP server?
This is a single-owner starter. A bearer token or authorized OAuth identity can access the shared Twilio account and pending codes. Add per-user ownership, authorization, isolated state, and operational controls before multi-user deployment.
How do I get help?
Ask in the Evolving AI Hub, James Hill's free Skool community, or open an issue on this repo.
Going further
License
MIT, starter code only. RizzDial is a separate commercial platform.
Built by James Hill (The AI Guy).
More free starters:
This server cannot be deployed
Maintenance
Related MCP Connectors
Give AI agents a phone layer for consent-based calls, transcripts, summaries, and outcomes.
- DialMCPOAuthcom.dialmcp
Let AI agents place real phone calls from your verified number, with transcripts and recordings.
Give your AI agent a phone. Place outbound calls to US businesses to ask, book, or confirm.
Give your AI a real phone: place calls, send SMS, fetch recordings and transcripts. Local or hosted.
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceEnables AI assistants to interact with all Twilio APIs through the Model Context Protocol. Supports SMS, voice, messaging, and other Twilio services with secure authentication and configurable API filtering.-
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to make real-world phone calls with AI voice technology and provides tools to track call status, transcripts, and summaries. It supports automated communication with both live numbers and simulated businesses for testing and demonstration purposes.-
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to manage phone numbers, send/receive SMS, and place voice calls through natural language, connecting to the phone network via the AgentPhone API.281 npm124MIT
- AlicenseBqualityBmaintenanceEnables MCP-compatible agents to make safe, consented phone calls using Twilio and Deepgram.4Apache 2.0