Metadata-Version: 2.4
Name: ai-prishtina-whatsapp-mcp
Version: 0.1.1
Summary: Professional WhatsApp Business API library with AI provider integration and MCP support
Author-email: "Alban Maxhuni, PhD" <alban.maxhuni@gmail.com>
Maintainer-email: "Alban Maxhuni, PhD" <alban.maxhuni@gmail.com>
License: AGPL-3.0-or-later OR Commercial
Project-URL: Homepage, https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp-server
Project-URL: Documentation, https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp-server#readme
Project-URL: Repository, https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp-server
Project-URL: Issues, https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp-server/issues
Project-URL: Changelog, https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp-server/releases
Project-URL: Bug Reports, https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp-server/issues
Project-URL: Feature Requests, https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp-server/discussions
Project-URL: Security, https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp-server/security
Keywords: whatsapp,mcp,ai,chatbot,business-api,webhook,fastmcp,async,redis,translation,sentiment-analysis,intent-detection
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Framework :: AsyncIO
Classifier: Framework :: FastAPI
Classifier: Environment :: Web Environment
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp<4.0.0,>=3.10.0
Requires-Dist: python-dotenv<2.0.0,>=1.0.0
Provides-Extra: mcp
Requires-Dist: mcp>=1.2.0; extra == "mcp"
Provides-Extra: ai
Requires-Dist: openai<2.0.0,>=1.30.0; extra == "ai"
Requires-Dist: anthropic<2.0.0,>=0.26.0; extra == "ai"
Requires-Dist: langchain<1.0.0,>=0.2.0; extra == "ai"
Requires-Dist: langdetect<2.0.0,>=1.0.9; extra == "ai"
Requires-Dist: textblob<1.0.0,>=0.17.1; extra == "ai"
Requires-Dist: numpy<3.0.0,>=1.26.0; extra == "ai"
Requires-Dist: scikit-learn<2.0.0,>=1.4.0; extra == "ai"
Provides-Extra: translation
Requires-Dist: googletrans<5.0.0,>=4.0.0; extra == "translation"
Requires-Dist: deep-translator<2.0.0,>=1.11.0; extra == "translation"
Requires-Dist: langdetect<2.0.0,>=1.0.9; extra == "translation"
Provides-Extra: dev
Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio<1.0.0,>=0.23.0; extra == "dev"
Requires-Dist: pytest-cov<6.0.0,>=5.0.0; extra == "dev"
Requires-Dist: pytest-mock<4.0.0,>=3.14.0; extra == "dev"
Requires-Dist: pytest-xdist<4.0.0,>=3.5.0; extra == "dev"
Requires-Dist: factory-boy<4.0.0,>=3.3.0; extra == "dev"
Requires-Dist: black<25.0.0,>=24.0.0; extra == "dev"
Requires-Dist: ruff<1.0.0,>=0.4.0; extra == "dev"
Requires-Dist: mypy<2.0.0,>=1.10.0; extra == "dev"
Requires-Dist: pre-commit<4.0.0,>=3.7.0; extra == "dev"
Requires-Dist: bandit<2.0.0,>=1.7.9; extra == "dev"
Requires-Dist: mkdocs<2.0.0,>=1.6.0; extra == "dev"
Requires-Dist: mkdocs-material<10.0.0,>=9.5.0; extra == "dev"
Requires-Dist: mkdocstrings[python]<1.0.0,>=0.25.0; extra == "dev"
Provides-Extra: performance
Requires-Dist: psutil<6.0.0,>=5.9.0; extra == "performance"
Requires-Dist: memory-profiler<1.0.0,>=0.61.0; extra == "performance"
Requires-Dist: py-spy<1.0.0,>=0.3.14; extra == "performance"
Requires-Dist: locust<3.0.0,>=2.17.0; extra == "performance"
Provides-Extra: docker
Requires-Dist: gunicorn<22.0.0,>=21.2.0; extra == "docker"
Requires-Dist: supervisor<5.0.0,>=4.2.5; extra == "docker"
Requires-Dist: gevent<24.0.0,>=23.9.0; extra == "docker"
Provides-Extra: monitoring
Requires-Dist: sentry-sdk[fastapi]<2.0.0,>=1.38.0; extra == "monitoring"
Requires-Dist: opentelemetry-api<2.0.0,>=1.21.0; extra == "monitoring"
Requires-Dist: opentelemetry-sdk<2.0.0,>=1.21.0; extra == "monitoring"
Requires-Dist: opentelemetry-instrumentation-fastapi<1.0.0,>=0.42b0; extra == "monitoring"
Requires-Dist: opentelemetry-instrumentation-redis<1.0.0,>=0.42b0; extra == "monitoring"
Provides-Extra: server
Requires-Dist: fastapi<1.0.0,>=0.111.0; extra == "server"
Requires-Dist: uvicorn[standard]<1.0.0,>=0.30.0; extra == "server"
Provides-Extra: storage
Requires-Dist: redis<7.0.0,>=5.0.0; extra == "storage"
Provides-Extra: all
Requires-Dist: ai-prishtina-whatsapp-mcp[ai,dev,mcp,storage,translation]; extra == "all"
Dynamic: license-file

# ai-prishtina-whatsapp-mcp

<div align="center">

<img src="https://cdn.buymeacoffee.com/uploads/cover_images/2025/07/Z2zeOfmCLnzhA78njCsfaRNA0ebw0dlXo53HmGhq.jpg@2560w_0e.webp" alt="AI-Prishtina Logo" width="100%">

<p align="center">

> **The WhatsApp Business API library that does everything — except make coffee.**
> *(We're working on that.)*

[![PyPI version](https://badge.fury.io/py/ai-prishtina-whatsapp-mcp.svg)](https://badge.fury.io/py/ai-prishtina-whatsapp-mcp)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![License: AGPL-3.0+](https://img.shields.io/badge/License-AGPL%20v3+-blue.svg)](LICENSE)
[![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](#testing)

</div>

A production-ready Python library for WhatsApp Business API integration, featuring a full **MCP (Model Context Protocol) server** that exposes WhatsApp as AI tools for Claude, Windsurf, Cursor, and any other MCP-compatible client. Built with async-first design, immutable domain models, 9 AI providers, and enough features to make your competitors cry into their lukewarm coffee.

Think of it as the Swiss Army knife of WhatsApp integrations — but one where every blade is a production-grade, type-safe, async-native tool. Including the corkscrew.

---

## What's New

### MCP Server — WhatsApp as AI Tools

The centerpiece addition: a **FastMCP server** that exposes every WhatsApp operation as a tool, usable directly from your AI assistant. Configure it once, and suddenly your Claude/Windsurf/Cursor installation can send WhatsApp messages, analyze sentiment, and check provider health — all without writing a line of integration code.

```json
{
  "mcpServers": {
    "whatsapp": {
      "command": "whatsapp-mcp",
      "env": {
        "WHATSAPP_ACCESS_TOKEN": "your_token",
        "WHATSAPP_PHONE_NUMBER_ID": "your_phone_id"
      }
    }
  }
}
```

Once connected, your AI assistant gets 16 tools. It's like giving your AI a WhatsApp account. Use responsibly.

### MCP Tools Available

| Tool | What It Does |
|------|-------------|
| `send_text_message` | Send a text message to any WhatsApp number |
| `send_bulk_messages` | Blast to multiple recipients with built-in rate limiting |
| `send_media_message` | Images, documents, audio, video |
| `send_template_message` | Approved template messages with components |
| `send_interactive_message` | Buttons and list messages |
| `send_reaction` | React to messages with emoji |
| `mark_as_read` | Trigger read receipts programmatically |
| `parse_webhook` | Parse incoming webhook payloads |
| `verify_webhook_signature` | Validate that webhooks actually came from Meta |
| `analyze_sentiment` | AI-powered sentiment analysis |
| `detect_intent` | Intent detection from message text |
| `translate_message` | Translate to any language |
| `get_conversation_history` | Sliding-window context per phone number |
| `add_to_conversation` | Add messages to the conversation buffer |
| `health_check_providers` | Check AI provider health across all registered providers |
| `upload_media` / `download_media` | Media management |

---

## Features

- **9 AI Providers**: OpenAI, Anthropic, Google Gemini, Azure, AWS Bedrock, Ollama (local), Whisper (local), HuggingFace, spaCy — with automatic failover
- **MCP Server**: Full FastMCP server exposing everything as AI tools
- **Functional Architecture**: Immutable frozen dataclasses, `Result<T,E>` monad, no exceptions in business logic
- **Rate Limiter**: Async token-bucket rate limiter (Meta will thank you)
- **Bulk Messaging**: Multi-recipient dispatch with per-result reporting
- **Conversation Buffer**: Sliding-window context per phone number for LLM-ready history
- **Message Pipeline**: Composable async middleware chain for webhook event processing
- **Message Deduplication**: Redis/memory-backed dedup so you don't process the same webhook twice
- **Storage Abstraction**: `MemoryStorage` (dev/test) and `RedisStorage` (production)
- **Full Webhook Support**: Parse, verify HMAC signatures, handle all message types

## Installation

```bash
# Core library
pip install ai-prishtina-whatsapp-mcp

# With MCP server support (FastMCP)
pip install "ai-prishtina-whatsapp-mcp[mcp]"

# With AI provider dependencies
pip install "ai-prishtina-whatsapp-mcp[ai]"

# The whole enchilada
pip install "ai-prishtina-whatsapp-mcp[mcp,ai,storage,server]"
```

**Requirements:** Python 3.11+, Redis 6.0+ (optional, for production storage)

---

## Quick Start

```python
import asyncio
from ai_prishtina_whatsapp_mcp import WhatsAppClient, Settings
from ai_prishtina_whatsapp_mcp.config.settings import ProviderSettings

async def main():
    settings = Settings(
        whatsapp_access_token="your_token",
        whatsapp_phone_number_id="your_phone_id",
        providers=[
            ProviderSettings.openai(api_key="sk-..."),
            ProviderSettings.ollama(),  # For when the internet breaks
        ],
    )

    async with WhatsAppClient(settings=settings) as client:
        result = await client.send_text("+38344123456", "Mirë se vini! 👋")

        if result.is_ok():
            print(f"Sent: {result.unwrap().id}")
        else:
            print(f"[{result.error.code.name}]: {result.error.message}")

asyncio.run(main())
```

---

## Core Concepts

### Result Monad — No More Try/Catch Roulette

Every operation returns `Ok(value)` or `Err(DomainError)`. Your code stays clean and composable:

```python
result = await client.send_text("+38344123456", "Hello!")

if result.is_ok():
    msg = result.unwrap()
else:
    err = result.error
    print(f"[{err.code.name}] {err.message}")

# Or use expect() — raises RuntimeError if Err, returns value if Ok
msg = result.expect("send_text failed")

# Or chain
msg_id = result.map(lambda m: m.id).unwrap_or("unknown")
```

### Rate Limiter

Token-bucket rate limiting built into the client — Meta's quota enforcement becomes your problem only if you ignore it:

```python
await client.set_rate_limit(messages_per_second=5.0)
await client.send_text("+38344111", "message 1")
await client.send_text("+38344222", "message 2")  # automatically waits if needed
```

### Bulk Messaging

```python
results = await client.send_bulk_messages(
    recipients=["+38344111111", "+38344222222", "+38344333333"],
    text="Big news from Prishtina! 🦅",
    delay_between=0.1,
)

for phone, result in results:
    print(f"{'✅' if result.is_ok() else '❌'} {phone}")
```

### Conversation Buffer

Sliding-window context memory per phone number, ready to pass to any LLM:

```python
from ai_prishtina_whatsapp_mcp import ConversationBuffer

buffer = ConversationBuffer(max_messages=20)
buffer.add("+38344123456", role="user", content="What's the weather?")
buffer.add("+38344123456", role="assistant", content="Sunny with a chance of 🦅")

history = buffer.get_history("+38344123456")
# Feed directly to your LLM
```

### Message Pipeline (Middleware)

Composable async middleware chains for webhook processing:

```python
from ai_prishtina_whatsapp_mcp import MessagePipeline

pipeline = MessagePipeline()

@pipeline.use
async def log_middleware(event, ctx, next):
    print(f"Incoming: {event['type']}")
    await next(event, ctx)

@pipeline.on_text
async def handle_text(event, ctx):
    text = event.get("text", "")
    result = await client.analyze_sentiment(text)
    if result.is_ok():
        print(f"Sentiment: {result.unwrap().content}")

await pipeline.dispatch(webhook_event)
```

### Message Deduplication

WhatsApp sometimes delivers the same webhook twice. We handle that:

```python
from ai_prishtina_whatsapp_mcp.effects.storage import MemoryStorage

storage = MemoryStorage()
await storage.initialize()

if not await client.is_duplicate_message("wamid.abc123", storage, ttl=86400):
    await process_message(event)
```

---

## AI Providers

9 providers, registered once, with automatic fallback:

```python
from ai_prishtina_whatsapp_mcp.config.settings import ProviderSettings

settings = Settings(
    providers=[
        ProviderSettings.openai(api_key="sk-..."),
        ProviderSettings.anthropic(api_key="ant-..."),
        ProviderSettings.ollama(base_url="http://localhost:11434", model="llama3"),
        ProviderSettings.google(api_key="AIza..."),
        ProviderSettings.whisper(model_size="base"),
    ],
)
```

| Provider | Text | Chat | Sentiment | Translation | Embeddings | Transcription |
|----------|:----:|:----:|:---------:|:-----------:|:----------:|:-------------:|
| OpenAI | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Anthropic | ✅ | ✅ | ✅ | ✅ | — | — |
| Google Gemini | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| AWS Bedrock | ✅ | ✅ | ✅ | — | ✅ | — |
| Azure OpenAI | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Ollama (local) | ✅ | ✅ | ✅ | — | ✅ | — |
| HuggingFace | ✅ | — | ✅ | ✅ | ✅ | — |
| Whisper (local) | — | — | — | — | — | ✅ |
| spaCy (local) | — | — | ✅ | — | ✅ | — |

---

## Storage

```python
from ai_prishtina_whatsapp_mcp.effects.storage import MemoryStorage, RedisStorage

# Development / testing
storage = MemoryStorage()

# Production
storage = RedisStorage(redis_url="redis://localhost:6379")

await storage.initialize()

await storage.store("session:+38344123456", {"intent": "order_pizza"}, ttl=3600)
data = await storage.retrieve("session:+38344123456")
keys = await storage.list_keys("session:*")
exists = await storage.exists("session:+38344123456")
```

---

## Webhook Handling

```python
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/webhook")
async def webhook(request: Request):
    raw_body = await request.body()
    signature = request.headers.get("X-Hub-Signature-256", "")

    if not client.verify_webhook_signature(raw_body, signature):
        return {"status": "nice try"}

    events = client.parse_webhook(raw_body)
    for event in events:
        await pipeline.dispatch(event)

    return {"status": "ok"}
```

---

## MCP Server

### Run as CLI

```bash
# stdio transport (Claude Desktop, Windsurf, Cursor)
WHATSAPP_ACCESS_TOKEN=... WHATSAPP_PHONE_NUMBER_ID=... whatsapp-mcp

# HTTP transport
whatsapp-mcp --transport streamable-http --port 8000
```

### Programmatic use

```python
from ai_prishtina_whatsapp_mcp.mcp_server import create_server

server = create_server()
server.run()
```

---

## Environment Variables

| Variable | Description |
|----------|-------------|
| `WHATSAPP_ACCESS_TOKEN` | Meta API access token (**required**) |
| `WHATSAPP_PHONE_NUMBER_ID` | WhatsApp phone number ID (**required**) |
| `WHATSAPP_BUSINESS_ACCOUNT_ID` | Business account ID |
| `WHATSAPP_WEBHOOK_SECRET` | Webhook HMAC-SHA256 secret |
| `REDIS_URL` | Redis connection URL (default: `redis://localhost:6379`) |
| `OPENAI_API_KEY` | OpenAI API key |
| `ANTHROPIC_API_KEY` | Anthropic API key |
| `GOOGLE_API_KEY` | Google Gemini API key |
| `OLLAMA_BASE_URL` | Ollama server URL |

---

## Architecture

```
ai_prishtina_whatsapp_mcp/
├── api.py                      # Low-level HTTP client (WhatsApp Business API)
├── mcp_server.py               # FastMCP server — 16 WhatsApp tools
├── config/
│   └── settings.py             # Settings, ProviderSettings, StorageSettings
├── core/
│   ├── domain.py               # Immutable domain models (frozen dataclasses)
│   ├── types.py                # Result<T,E>, Ok, Err, ErrorCode, DomainError
│   ├── whatsapp_client.py      # High-level client (send, bulk, dedup, rate limit)
│   ├── conversation_buffer.py  # Sliding-window context per phone number
│   └── pipeline.py             # Async middleware chain for webhooks
├── providers/
│   ├── base.py                 # AIProvider ABC, ProviderCapability enum
│   ├── registry.py             # AIProviderRegistry with health + fallback
│   ├── cloud/                  # OpenAI, Anthropic, Google, AWS, Azure
│   └── local/                  # Ollama, Whisper, HuggingFace, spaCy
├── effects/
│   └── storage.py              # MemoryStorage, RedisStorage
└── utils/
    ├── rate_limiter.py         # Async token-bucket rate limiter
    └── retry.py                # Exponential backoff retry utility
```

---

## Testing

```bash
pip install -e ".[mcp,ai]"
pip install -r requirements-dev.txt

pytest tests/ -v
pytest tests/ --cov=ai_prishtina_whatsapp_mcp --cov-report=term-missing
```

---

## Contributing

```bash
git clone https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp
cd ai-prishtina-whatsapp-mcp
pip install -e ".[mcp,ai]"
pip install -r requirements-dev.txt
pytest tests/
```

PRs welcome. The library is from Prishtina 🦅 — we ship things.

---

## License

AGPL-3.0+ OR Commercial — open source with copyleft, or contact us for commercial licensing.

---

## Support

- **Issues**: [GitHub Issues](https://github.com/albanmaxhuni/ai-prishtina-whatsapp-mcp-server/issues)
- **Email**: alban.maxhuni@gmail.com

---

*Built with passion (and plenty of coffee) by AI Prishtina. Go Eagles. 🦅*
