Content
# twilio-mcp
Hosted MCP server exposing the Twilio REST API. Lets Claude (web, Desktop, Code) configure Twilio end-to-end — send SMS, place calls, buy/configure phone numbers, run Verify, trigger Studio Flows, and hit any other Twilio endpoint via a generic escape hatch.
## Tools
Named:
- `twilio_sms_send`, `twilio_sms_list`, `twilio_sms_get`, `twilio_sms_delete`, `twilio_messaging_service_list`
- `twilio_call_create`, `twilio_call_list`, `twilio_call_get`, `twilio_call_update`, `twilio_recording_list`
- `twilio_number_search_available`, `twilio_number_buy`, `twilio_number_list`, `twilio_number_update`, `twilio_number_release`, `twilio_lookup`
Generic escape hatch:
- `twilio_request` — call any Twilio REST endpoint (Studio, Verify, Conversations, sub-accounts, TaskRouter, Flex, Video, Pay, Notify, Sync, etc) by passing `method`, `path`, optional `base_url`, and optional `query` / `form`.
## Local development
```sh
cp .env.example .env
# fill in TWILIO_ACCOUNT_SID + TWILIO_AUTH_TOKEN (or API Key pair)
# generate MCP_BEARER_TOKEN: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
npm install
npm run build
npm start
```
Then `curl http://127.0.0.1:3010/health`.
## Tests
```sh
npm test # vitest unit tests (zod→JSON, OAuth/PKCE)
npm run smoke # curl-based smoke against http://127.0.0.1:3010
npm run smoke -- https://twilio.nlma.io # smoke against production
```
`npm run smoke` reads `MCP_BEARER_TOKEN` from env or `.env`. It exercises `/health`, bearer enforcement on `/mcp`, the `WWW-Authenticate: resource_metadata=` discovery hint, header- and query-param auth, OAuth discovery + DCR, and the full PKCE authorize→token round-trip.
## Production deploy
DNS: add `A twilio.nlma.io → 178.16.141.166` (Hostinger MCP `DNS_updateDNSRecordsV1`).
First-time VPS setup (one-shot, on the VPS):
```sh
mkdir -p /opt/twilio-mcp
# copy .env.example to /opt/twilio-mcp/.env and fill it in
cp deploy/nginx.conf /etc/nginx/sites-available/twilio.nlma.io
ln -s /etc/nginx/sites-available/twilio.nlma.io /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
certbot --nginx -d twilio.nlma.io
```
Subsequent deploys (from any machine with SSH access — no local checkout needed):
```sh
./deploy/push-to-vps.sh # deploys origin/main
./deploy/push-to-vps.sh some-branch # deploys a specific ref
```
The script SSHes into the VPS, `git fetch + checkout`s the requested ref from GitHub, `npm ci && npm run build`, prunes devDeps, then restarts the systemd unit. The first run converts `/opt/twilio-mcp` into a git checkout (preserving `.env` and `tenants.json`). Health-check at `https://twilio.nlma.io/health`.
The VPS needs read access to the GitHub repo — either a deploy key on `git@github.com:NextLevelManagementAdvisors/twilio-mcp.git`, or set `GIT_REMOTE` to an HTTPS URL with an embedded PAT.
## Auth
Two ways to authenticate against `/mcp`:
1. **Owner token (env-tier)** — the static `MCP_BEARER_TOKEN` from `.env`. Dispatches to the env-configured Twilio account if `TWILIO_ACCOUNT_SID` is set. Rotate by editing `/opt/twilio-mcp/.env` and `systemctl restart twilio-mcp`.
2. **Enrolled tenant** — any user can self-enroll at `https://twilio.nlma.io/enroll`. They submit their own Twilio Account SID + Auth Token (or API Key pair); the server verifies the creds against Twilio, then issues a personal bearer token shown once on the success page. The token dispatches all subsequent `/mcp` requests to that user's Twilio account.
Tokens are passed as either:
- `Authorization: Bearer <token>` header (preferred for stdio bridges and CLI)
- `?token=<token>` query param (required for claude.ai web — see [mcp-client-setup skill](../../../.claude/skills/mcp-client-setup/SKILL.md))
For claude.ai web, the OAuth consent page has a single text field where the user pastes their enrollment token (leave blank to use the owner token).
Programmatic enrollment:
```sh
curl -s -X POST https://twilio.nlma.io/enroll.json \
-H 'Content-Type: application/json' \
-d '{"label":"Acme prod","account_sid":"ACxxxx…","auth_token":"…"}'
# → {"id":"…","label":"Acme prod","account_sid":"ACxxxx…","token":"…","created_at":"…"}
```
Gate signups by setting `ENROLLMENT_ADMIN_TOKEN` in `.env` — clients must then send `Authorization: Bearer <admin>` to `/enroll`.
## Wiring claude.ai
After the server is live at `https://twilio.nlma.io`:
1. Open https://claude.ai/settings/connectors
2. Click "Add custom connector"
3. Paste: `https://twilio.nlma.io/mcp?token=<MCP_BEARER_TOKEN>`
4. Name it "Twilio"
5. Test from a chat: "list my Twilio phone numbers"
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.