mcp-sms-agent
Agente de SMS con IA
Un agente de SMS autónomo con IA. Los SMS entrantes llegan a través de la pasarela SMS TextBee, un webhook de FastAPI los entrega a un agente de IA local, que puede usar herramientas MCP cuando sea necesario, y la respuesta de la IA se envía de vuelta al mismo número a través de TextBee.
Estado: Pipeline completo conectado y endurecido para producción: webhook de TextBee -> SQLite -> worker de fondo asíncrono -> IA local (Ollama) -> herramientas MCP -> división de respuestas adaptada a SMS -> envío de TextBee -> estado de entrega.
Guía de producción (despliegue, HTTPS, seguridad, copias de seguridad, resolución de problemas): docs/PRODUCTION.md
Arquitectura
User SMS
→ TextBee SMS gateway
→ FastAPI webhook
→ Local AI agent
→ MCP tools when needed
→ AI generates response in the same language
→ TextBee
→ Response SMS to the same userRequisitos
Python 3.11+
SQLite (incluido con Python)
Instalación
cd sms-ai-agent
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install -r requirements.txtConfiguración
Copia el archivo de entorno de ejemplo y rellena tus valores:
cp .env.example .envAjustes de TextBee (https://textbee.dev):
Variable | Propósito |
| URL base de la API (por defecto |
| Clave de API del panel, enviada como cabecera |
| Dispositivo desde el que enviar; omítelo para usar el dispositivo por defecto |
| Secreto de firma de tu suscripción al webhook |
Ajustes de Ollama:
Variable | Propósito |
| URL del servidor de Ollama (por defecto |
| Modelo para generar respuestas (p. ej. |
| Máximo de segundos de espera para una respuesta del modelo |
| Mensajes recientes incluidos como contexto de la conversación |
| Longitud máxima de la respuesta (se mantiene corta para SMS) |
Ajustes de MCP:
Variable | Propósito |
| URL del servidor MCP (transporte HTTP Streamable). Vacío = sin herramientas |
| Máximo de segundos de espera para conectar/listar/llamar a MCP |
| Máximo de llamadas a herramientas por turno de conversación (protección de bucle) |
El archivo de base de datos SQLite se crea automáticamente en database/sms_agent.db en el primer arranque.
Ejecución
uvicorn app.main:app --reloadA continuación, comprueba el servicio:
curl http://127.0.0.1:8000/healthLa documentación interactiva de la API está disponible en http://127.0.0.1:8000/docs.
Pasarela de SMS
Recepción de SMS
Registra un webhook en el panel de textbee.dev apuntando a https://<tu-host>/webhook/textbee, suscríbete al evento MESSAGE_RECEIVED y establece el secreto de firma como TEXTBEE_WEBHOOK_SECRET.
Cada entrega se verifica con HMAC-SHA256 sobre el cuerpo JSON sin procesar (cabecera X-Signature), se deduplica por el id de mensaje de la pasarela y se almacena en SQLite (se crean un usuario y una conversación si es necesario). El webhook responde inmediatamente y el mensaje se procesa en segundo plano.
Pruébalo localmente con una petición firmada simulada:
python scripts/mock_webhook.py "test_1" "What is 12 * 8?"Pipeline de segundo plano
El webhook pone en cola cada mensaje entrante; un worker (iniciado por el ciclo de vida de FastAPI) lo procesa:
webhook -> store -> queue -> worker -> AI agent (+ MCP tools) -> reply
-> TextBee send -> delivery status -> durable dedupe markerAlmacenar primero, responder rápido: el webhook confirma el mensaje y lo pone en cola.
Procesamiento asíncrono: una
asyncio.Queueen proceso conWORKER_COUNTworkers.Remitente correcto: la respuesta se envía solo al número de teléfono del mensaje entrante.
Estado de entrega: las filas salientes registran
status(sent/failed) ydelivery_status(accepted/error).Reintentos: los fallos de envío de TextBee se reintentan hasta
MAX_RETRIEScon retroceso (backoff).Prevención de duplicados: el id de mensaje de la pasarela es único, y un marcador
ProcessedMessagehace que el procesamiento sea idempotente entre reinicios.Fallos controlados: los fallos de IA/MCP se registran; los fallos de TextBee dejan la respuesta almacenada con
status=faileden lugar de provocar un fallo del sistema.
Ejecuta un worker independiente sin el servidor web:
python -m app.workers.workerPrueba de extremo a extremo sin un teléfono real
Simula el lado de envío de TextBee para que no se envíe ningún SMS real, pero el pipeline completo se ejecute:
# Terminal 1 - fake TextBee API (logs each accepted send)
python scripts/mock_textbee_api.py --port 9001
# Terminal 2 - demo MCP server (optional, for tool use)
python -m app.mcp.echo_server --port 8001
# Terminal 3 - the app pointing TextBee at the mock
set "TEXTBEE_API_URL=http://127.0.0.1:9001"
set "TEXTBEE_API_KEY=test-key"
set "TEXTBEE_WEBHOOK_SECRET=test_secret_at_least_20_chars"
set "MCP_SERVER_URL=http://127.0.0.1:8001/mcp"
uvicorn app.main:app --port 8000
# Terminal 4 - send a mock inbound SMS
python scripts/mock_webhook.py "e2e_1" "Hi there!"A continuación, inspecciona database/sms_agent.db: el mensaje entrante tiene status=sent, la respuesta saliente tiene delivery_status=accepted, y processed_messages contiene el id de la pasarela.
Envío de SMS
# Preview the request without sending (no API key needed)
python -m app.sms.send_test +15551234567 "Hello" --dry-run
# Send for real (requires TEXTBEE_API_KEY in .env)
python -m app.sms.send_test +15551234567 "Hello"IA local (Ollama)
El agente es independiente de la pasarela de SMS: lee el historial de conversaciones de SQLite y genera respuestas con el modelo de Ollama configurado.
Instala Ollama (https://ollama.com) y ejecútalo.
Descarga un modelo:
ollama pull llama3.2.Establece
MODEL_NAMEen.env(por defectollama3.2).
El generador de respuestas:
Detecta el idioma/escritura del mensaje entrante (urdu, árabe, cirílico, devanagari, CJK, etc.) e indica al modelo que responda en el mismo idioma.
Construye el contexto a partir de los últimos
MAX_CONTEXT_MESSAGESmensajes en SQLite.Almacena la respuesta del asistente como mensaje saliente.
Elimina artefactos del modelo (p. ej. un eco inicial de
assistant) y recorta aMAX_RESPONSE_CHARSpara SMS.Agota el tiempo de espera después de
AI_TIMEOUT_SECONDSy lanzaOllamaErroren caso de fallo.
Pruébalo en vivo:
python scripts/demo_agent.py # English demo
python scripts/demo_agent_urdu.py # Urdu language-matching demoHerramientas MCP (bucle del agente)
El agente es un bucle de decisión:
message -> LLM -> needs a tool? -> MCP tool -> tool result -> LLM -> final answerCuando MCP_SERVER_URL está establecido, el agente descubre las herramientas del servidor, las pasa al modelo y ejecuta cualquier llamada a herramientas que solicite. Los resultados de las herramientas se retroalimentan hasta que el modelo produce una respuesta de texto final. El bucle está limitado a MAX_TOOL_CALLS para que un modelo con comportamiento incorrecto no pueda repetirse indefinidamente. Cada llamada a una herramienta se registra en la tabla tool_calls.
El repositorio incluye un pequeño servidor MCP de demostración (echo + calculadora) construido con el SDK oficial mcp:
# Terminal 1 - start the MCP server (Streamable HTTP)
python -m app.mcp.echo_server --port 8001
# Terminal 2 - run the full agent flow with real LLM + MCP tool
python scripts/demo_agent_tools.py "What is 17 * 23?"Apunta MCP_SERVER_URL=http://127.0.0.1:8001/mcp en .env (o pasa la URL directamente a MCPClient).
Seguridad y endurecimiento
Autenticación del webhook: verificación HMAC-SHA256 de
X-Signature(tiempo constante).Validación de entrada: números de teléfono E.164, límite de longitud de mensaje, eliminación de caracteres de control.
Inyección de prompts: el contenido del usuario se sanitiza (
[instrucción bloqueada]) antes de llegar al modelo; el prompt del sistema prohíbe revelar secretos.Aislamiento por usuario: las respuestas se envían solo al remitente entrante; la búsqueda de conversación está limitada a la fila de usuario del remitente.
Límite de velocidad: ventana deslizante por remitente en el webhook.
Protección contra duplicados:
gateway_message_idúnico + marcadorProcessedMessagepersistente.Tiempos de espera: Ollama (
AI_TIMEOUT_SECONDS), MCP (MCP_TIMEOUT_SECONDS), TextBee (TEXTBEE_TIMEOUT_SECONDS).Reintentos: reintentos de envío de TextBee con retroceso (
MAX_RETRIES).Longitud de SMS: detección de GSM-7 (153/parte) frente a UCS-2 (67/parte) con división multiparte segura a nivel de grafema (
app/sms/encoding.py).Copias de seguridad: instantáneas de copia de seguridad en línea de SQLite al inicio/apagado +
database/backups/.Apagado controlado: el ciclo de vida detiene los workers y vacía la cola.
Secretos: todos en
.env(ignorado por git,chmod 600); nunca se registran.
Pruebas
pytestLas pruebas usan una base de datos SQLite en memoria y simulan las APIs HTTP de TextBee y Ollama; las pruebas del cliente MCP usan el transporte en proceso del SDK. Las pruebas del pipeline cubren el flujo completo webhook -> cola -> agente -> envío -> estado, incluidos reintentos, deduplicación y discrepancia de remitente. Las pruebas de integración se ejecutan contra servidores reales de Ollama / MCP cuando son accesibles (se omiten en caso contrario):
python -m pytest tests/test_integration_ollama.py -v
python -m pytest tests/test_integration_agent_tools.py -v # needs the MCP server on :8001Estructura del proyecto
app/
├── main.py # FastAPI app, lifespan (starts workers), /health
├── config.py # Environment-driven settings
├── database.py # SQLAlchemy engine + session
├── models.py # users, conversations, messages, tool_calls, processed_messages
├── sms/
│ ├── webhook.py # MESSAGE_RECEIVED webhook: verify, validate, store, enqueue
│ ├── textbee.py # TextBee httpx client (send-sms) + signature verification
│ ├── pipeline.py# async queue + worker: agent -> split -> send -> status
│ ├── encoding.py# GSM-7 vs UCS-2 detection + grapheme-safe splitting
│ └── send_test.py # CLI helper to send a test SMS
├── security.py # input validation, prompt-injection, rate limiting
├── backup.py # SQLite online backups
├── agent/
│ ├── agent.py # agent decision loop: LLM <-> MCP tools, history, storage
│ ├── llm.py # Ollama client (chat + tool calling, timeout, errors)
│ ├── prompts.py # system prompt + message builder
│ ├── language.py # script-based language detection
│ └── memory.py # conversation history helpers
├── mcp/
│ ├── client.py # MCP client wrapper (connect, list, call)
│ └── echo_server.py # demo MCP server (echo + calculate tools)
└── workers/ # standalone worker entry pointThis server cannot be installed
Maintenance
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
Phone, SMS & email for AI agents — one remote MCP endpoint, OAuth login, zero install.
Give AI agents real phone numbers, messages, and voice calls via MCP.
Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/mustafaansari4564/mcp-sms-agent'
If you have feedback or need assistance with the MCP directory API, please join our Discord server