DEV Community

Cover image for piwapp - A WhatsApp client written from scratch in pure Python with Model Context Protocol (MCP) that you can use with your favorite agents
Naing Oo
Naing Oo Subscriber

Posted on

piwapp - A WhatsApp client written from scratch in pure Python with Model Context Protocol (MCP) that you can use with your favorite agents

What I built

Logo

piwapp is a WhatsApp client written from scratch in pure Python plus a Model Context Protocol (MCP) server bolted on top so an LLM can actually read and send your WhatsApp messages for you.

You link it to your phone the same way you'd link WhatsApp Web: scan a QR code once, and from then on your Python code (or your assistant) can send and receive texts, media, and group messages. The part I'm proud of is what's not in it. There's no headless browser being puppeted in the background, no Node.js, no Go whatsmeow binary wrapped in a thin Python shim. The whole stack: the WebSocket transport, the Noise handshake, WhatsApp's binary protocol, and the Signal end-to-end encryption (Double Ratchet for DMs, Sender Keys for groups) — is implemented natively in Python, all the way down to the crypto.

That means you can read it, audit it, and hack on it without crossing a language boundary. And because I was tired of every other library treating groups as an afterthought, group chat is a first-class feature here: list your groups, send to them, and decrypt everyone's messages out of the box.

Demo

A 60-second, no-code run looks like this:

pip install piwapp            # core library
pip install "piwapp[mcp]"     # …or with the MCP server for LLMs
Enter fullscreen mode Exit fullscreen mode

Scan it with WhatsApp → Linked Devices → Link a Device, and you'll see ✓ Online as <you> with incoming messages printing live. Next launch, it logs straight back in.

The fun part is the MCP server. Once it's registered with Claude or Copilot, you just talk:

"Text Mom I'm running late"
"What did the team group say today?"
"Search my messages for 'invoice'"
"Watch the Test group and reply to whoever messages me"

Under the hood the server exposes a clean set of tools — send_message, send_file, wait_for_messages, list_chats, get_messages, search_messages, list_groups, download_media, and a start_pairing flow so you can even link a device from inside the chat.

Repo

👉 https://github.com/n-92/piwapp (MIT licensed)

👉 https://pypi.org/project/piwapp/0.1.0/ (Ready to install)

The README has copy-paste examples for the Python API and ready-to-go MCP configs for Claude Code, Claude Desktop, and VS Code / Copilot.

How I built it

The honest answer is that it was a long fight with a protocol that has no official public spec. WhatsApp Web speaks a custom binary format over a WebSocket, wrapped in two encryption layers; a Noise XX handshake to secure the connection, then the Signal protocol on top of that for the actual messages. So the build went in layers, each one useless until the one below it worked:

  1. Transport + Noise — get a raw WebSocket talking, then complete the Noise handshake so the server stops hanging up on you.
  2. WABinary codec — encode/decode WhatsApp's tokenized binary nodes (this is where the property-based fuzzing tests earned their keep).
  3. Signal — X3DH key agreement, the Double Ratchet for 1:1 chats, and Sender Keys for group fan-out. Group sending was the hairy one; I tested it fanning out to 46 devices.
  4. The product layer — QR pairing, auto-reconnect/keepalive, media encrypt-upload-download, and history sync into a local SQLite archive you can query offline.
  5. The MCP server — wrapping all of the above into tools an LLM can call, including a live "listen and reply" loop.

I leaned hard on tests to stay sane, since you can't exactly set breakpoints inside someone else's encrypted server. There are ~147 tests that all run offline against a mock server — covering the codec, the full handshake, Signal 1:1 and group crypto (including out-of-order and tamper-rejection cases), the login + reconnect flow, and the MCP tools.

What I learned

Implementing the Signal protocol by hand (with some help from Copilot) gives you a real, gut-level appreciation for it. Reading "Double Ratchet" in a blog post and actually getting message keys to line up after an out-of-order delivery are very different experiences. I also learned how much of "it works on real WhatsApp" lives in the unglamorous details — keepalives, pre-key uploads so you can even receive messages, retrying the right thing at the right time.

The MCP piece changed how I think about these clients. Once the protocol work was done, exposing it as tools meant the interface became plain English. Telling an assistant "watch the group and reply to anyone who messages" and having it just work felt like the point of all the low-level grinding.

What's next

The headline feature I'm building toward is rich group-management APIs: persistent group state with change history, batch member management, a join-request approval workflow, and an activity feed — plus retry receipts and app-state sync. Everything verified so far (login, DMs, groups, media, history sync, the MCP server) has been live-tested against the real service, so the foundation is solid enough to build the fun stuff on.

Top comments (0)