# Directory Structure ``` ├── .gitignore ├── assets │ └── demo.gif ├── LICENSE ├── package-lock.json ├── package.json ├── README.md ├── src │ └── index.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release build/ # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp .cache # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # Twilio MCP Server A Model Context Protocol (MCP) server that enables Claude and other AI assistants to send SMS and MMS messages using Twilio. ## Demo  ## Features - Send SMS messages 📱 - Pre-built prompts for common messaging scenarios 📝 - Secure handling of Twilio credentials 🔒 ## Requirements - Node.js >= 18 - If you need to update Node.js, we recommend using `nvm` (Node Version Manager): ```bash nvm install 18.14.2 nvm alias default 18.14.2 ``` - If you encounter any errors in Claude Desktop, try running the following command in your terminal to verify the installation: ```bash npx -y @yiyang.1i/sms-mcp-server ``` ## Configuration The server requires three environment variables: - `ACCOUNT_SID`: Your Twilio account SID - `AUTH_TOKEN`: Your Twilio auth token - `FROM_NUMBER`: Your Twilio phone number (in E.164 format, e.g., +11234567890) ### Claude Desktop Configuration To use this server with Claude Desktop, add the following to your configuration file: **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` ```json { "mcpServers": { "twilio": { "command": "npx", "args": [ "-y", "@yiyang.1i/sms-mcp-server" ], "env": { "ACCOUNT_SID": "your_account_sid", "AUTH_TOKEN": "your_auth_token", "FROM_NUMBER": "your_twilio_number" } } } } ``` After that, restart Claude Desktop to reload the configuration. If connected, you should see Twilio under the 🔨 menu. ## Example Interactions with Claude Here are some natural ways to interact with the server through Claude: 1. Simple SMS: ``` Send a text message to the number +11234567890 saying "Don't forget about dinner tonight!" ``` 2. Creative SMS: ``` Write a haiku about autumn and send it to my number +11234567890 ``` ## Important Notes 1. **Phone Number Format**: All phone numbers must be in E.164 format (e.g., +11234567890) 2. **Rate Limits**: Be aware of your Twilio account's rate limits and pricing 3. **Security**: Keep your Twilio credentials secure and never commit them to version control ## Troubleshooting Common error messages and solutions: 1. "Phone number must be in E.164 format" - Make sure the phone number starts with "+" and the country code 2. "Invalid credentials" - Double-check your ACCOUNT_SID and AUTH_TOKEN. You can copy them from the [Twilio Console](https://console.twilio.com) ## Contributing Contributions are welcome! Please read our contributing guidelines before submitting pull requests. ## License This project is licensed under the MIT License - see the LICENSE file for details. ## Security Please do not include any sensitive information (like phone numbers or Twilio credentials) in GitHub issues or pull requests. ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "./build", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*"], "exclude": ["node_modules"] } ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "@yiyang.1i/sms-mcp-server", "version": "2025.2.24", "description": "A Model Context Protocol (MCP) server that enables Claude and other AI assistants to send SMS and MMS messages using Twilio.", "license": "MIT", "author": "Yiyang Li", "type": "module", "bin": { "sms-mcp-server": "build/index.js" }, "files": [ "build" ], "scripts": { "build": "tsc && shx chmod +x build/*.js", "prepare": "npm run build", "watch": "tsc --watch" }, "dependencies": { "@modelcontextprotocol/sdk": "1.5.0", "twilio": "^5.4.5", "zod": "^3.24.2" }, "devDependencies": { "@types/node": "^22", "shx": "^0.3.4", "typescript": "^5.3.3" } } ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript #!/usr/bin/env node import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import twilio from "twilio"; // Environment variables validation const requiredEnvVars = ["ACCOUNT_SID", "AUTH_TOKEN", "FROM_NUMBER"]; for (const envVar of requiredEnvVars) { if (!process.env[envVar]) { console.error(`Error: ${envVar} environment variable is required`); process.exit(1); } } // Initialize Twilio client const client = twilio(process.env.ACCOUNT_SID, process.env.AUTH_TOKEN); // Create MCP server const server = new McpServer({ name: "twilio-sms", version: "1.0.0", }); server.prompt( "send-greeting", { to: z.string().describe("Recipient's phone number in E.164 format (e.g., +11234567890)"), occasion: z.string().describe("The occasion for the greeting (e.g., birthday, holiday)") }, ({ to, occasion }) => ({ messages: [{ role: "user", content: { type: "text", text: `Please write a warm, personalized greeting for ${occasion} and send it as a text message to ${to}. Make it engaging and friendly.` } }] }) ); server.prompt( "send-haiku", { theme: z.string().describe("The theme of the haiku"), to: z.string().describe("Recipient's phone number in E.164 format (e.g., +11234567890)") }, ({ to, theme }) => ({ messages: [{ role: "user", content: { type: "text", text: `Please write a warm, personalized greeting for ${theme} and send it as a text message to ${to}. Make it engaging and friendly.` } }] }) ); // Add send message tool server.tool( "send-message", "Send an SMS message via Twilio", { to: z.string().describe("Recipient phone number in E.164 format (e.g., +11234567890)"), message: z.string().describe("Message content to send") }, async ({ to, message }) => { try { // Validate phone number format if (!to.startsWith("+")) { return { content: [{ type: "text", text: "Error: Phone number must be in E.164 format (e.g., +11234567890)" }], isError: true }; } // Send message via Twilio const response = await client.messages.create({ body: message, from: process.env.FROM_NUMBER, to: to }); return { content: [{ type: "text", text: `Message sent successfully! Message SID: ${response.sid}` }] }; } catch (error) { console.error("Error sending message:", error); return { content: [{ type: "text", text: `Error sending message: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true }; } } ); // Start server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Twilio SMS MCP Server running on stdio"); } main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); }); ```