Viber MCP Server
Enables an AI assistant to send and read Viber messages by automating the Viber Desktop app on macOS through UI automation and OCR.
README
Viber MCP (macOS)
An MCP server that lets an AI assistant (e.g. Claude) send and read Viber messages by driving your already-running, signed-in Viber Desktop app on macOS.
Viber has no local/public API and its message database is encrypted, so this works entirely through UI automation:
- Send — open the chat via the
viber://URL scheme, paste the text, and click the send button. - Receive — capture the visible Viber window and OCR it with Apple's Vision framework.
⚠️ Unofficial. Not affiliated with, endorsed by, or supported by Viber / Rakuten. It automates the desktop UI on your own machine and account. UI automation is inherently version-sensitive and may break when Viber updates. Use at your own risk and review your account's terms of service.
Why it works this way
Three hard constraints shaped the design (all verified on Viber v28.2.0):
- The message DB is unreadable.
~/Library/Application Support/ViberPC/<number>/viber.dbis encrypted with a Botan page codec (random header, notSQLite format 3), with no public key-derivation recipe. So history can't be read from disk. - The accessibility tree is unusable. Viber is a Qt app; AX queries hang, so there's no element tree to read or click.
- Qt ignores ordinary synthetic input. Every click/keystroke must be generated from a real HID
event source (
CGEventSourceCreate(kCGEventSourceStateHIDSystemState)) — plainCGEvents and System Events keystrokes are silently dropped by Qt's input widgets.
What's left — and what this uses — is the viber:// URL scheme, HID-sourced input, and screen OCR.
Tools
| Tool | Description |
|---|---|
send_message(to, text, dry_run=False) |
Open the chat for to, confirm it, paste text, click send, and OCR-verify it landed. dry_run pastes into the box but doesn't send. |
reply(text) |
Send text into the currently-open chat — no navigation. Pair with read_open_chat. |
open_chat(to) |
Bring a conversation to the foreground without sending. |
open_and_read(to) |
Open a conversation and return its visible text in one step. |
read_open_chat() |
OCR the currently-visible Viber window and return its text. |
save_to_my_notes(text) |
Save a note to Viber's private "My Notes" self-chat (opens it via search, then sends). |
open_my_notes() |
Open the "My Notes" self-chat without saving. |
list_chats() |
List the sidebar chats (names, previews, times, unread counts) via OCR — a quick inbox overview. |
put_draft(text) |
Paste text into the currently-open chat's compose box — no navigation, no send. For drafting a reply to review. |
search_and_open(name) |
Search Viber by name and open the best-matching contact/chat (exact/prefix beats substring). Returns matched + low_confidence; verify before sending if low_confidence. |
list_contacts() |
Return the name → number aliases from contacts.json. |
to is either an E.164 number (+639171234567) or a name defined in contacts.json.
Unknown names error out rather than guess a recipient. send_message returns verified: true when
the sent message is confirmed on screen (false = not seen, null = couldn't check).
Requirements
- macOS (tested on macOS 15/26, Apple Silicon)
- Viber Desktop installed, running, and signed in
uv(Python is managed byuv)
Install
git clone <your-fork-url> viber-mcp
cd viber-mcp
uv sync
cp contacts.example.json contacts.json # then edit with your real contacts
macOS permissions (required)
Grant these to the app that launches the server (your terminal — iTerm/Terminal — or Claude), in System Settings → Privacy & Security:
- Accessibility — synthetic clicks/keystrokes (sending).
- Screen Recording — window capture (reading + send verification).
The first send/read fails with a clear message until these are granted. After granting, fully quit and reopen the host app so the permission binds.
If you get
not allowed to send keystrokes(error 1002) while the toggle already looks on, reset the stuck TCC entry and relaunch:tccutil reset Accessibility <your-terminal-bundle-id>(e.g.com.googlecode.iterm2,com.apple.Terminal).
Configure contacts
contacts.json maps friendly names to E.164 numbers (it is git-ignored — it holds real numbers):
{
"Me": "+639171234567",
"Mom": "+639181234567"
}
Names that aren't in the file must be given as full E.164 numbers.
Use it from Claude Code
Register the server (user scope):
claude mcp add --transport stdio --scope user viber -- \
uv --directory "$(pwd)" run server.py
Check with claude mcp list (should show viber … ✓ Connected). After editing the server code,
reconnect so Claude reloads it (/mcp → reconnect viber, or restart Claude Code). Remove with
claude mcp remove viber.
/viber-reply slash command (optional convenience)
Copy commands/viber-reply.md into ~/.claude/commands/ (user scope) or your project's
.claude/commands/. It reads the open chat, drafts a reply in your voice, and pastes it into the
compose box without sending — you review and send.
/viber-reply
/viber-reply keep it short and warm; say I'll call tonight
Use it standalone (no Claude)
A small MCP client is included for testing:
uv run client.py list # advertise tools
uv run client.py contacts # show alias map
uv run client.py open "Mom" # open a chat
uv run client.py send "Mom" "hello" --dry-run # fill box, don't send
uv run client.py send "Mom" "hello" # actually send
uv run client.py draft "a reply to review" # drop draft in open chat (not sent)
uv run client.py read # OCR the open chat
How it works (details)
- Send:
open viber://chat?number=<E164>opens the chat (desktop navigation takes a few seconds, so the window is polled via OCR until the recipient's chat is confirmed — and the send is refused if the number "isn't on Viber"). Then: click the compose box, clear any draft, paste the clipboard text (emoji/unicode/newlines survive), click the send button, and OCR-verify the bubble appeared. The clipboard is saved and restored around the paste. - Receive: the largest on-screen window owned by "Rakuten Viber" is captured with
screencapture -l <id>and OCR'd byVNRecognizeTextRequest. It only sees what's on screen.
Automation constraints
Because there's no API — only synthetic input + OCR against a Qt app — a few non-obvious rules make
or break reliability. Keep these in mind when running the tools or editing viber_send.py:
- Synthetic input must use a real HID event source. Qt silently drops plain
CGEvents and System Events keystrokes; the code posts every click/keystroke fromCGEventSourceCreate(kCGEventSourceStateHIDSystemState). - Sidebar, search box, send button, and panel-close ✕ are targeted by fixed PIXEL offsets from the window edges — not fractions. The sidebar and side panels have fixed pixel widths, so fraction-based clicks miss on wide/maximized windows.
- Any right-side panel (chat-info or sticker "Settings") must be closed before sending — it shifts the send button. The code detects and closes it automatically.
- Sending clicks the send button; it does not press Return ("Send message on Enter" may be off).
viber://chat navigation is slow and mobile-first — the code polls via OCR until the target chat is confirmed, and refuses to send if the number "isn't on Viber". You cannot message your own number ("cannot add yourself"); My Notes is a separate chat.- Keep Viber in a normal window on the main desktop — not macOS fullscreen. Fullscreen puts it on a separate Space, causing window-ID churn and blank captures. Don't resize it mid-operation, and prefer keeping side panels closed.
- OCR sees only on-screen text and can garble small/emoji text or interleave the sidebar with the
open conversation; the returned
verifiedflag is best-effort (false negatives are common).
Project layout
| File | Purpose |
|---|---|
server.py |
FastMCP stdio server; defines the tools |
viber_send.py |
platform-neutral recipient resolution, send pipeline, search scoring |
viber_ocr.py |
platform-neutral formatting of OCR observations into read results |
backends/base.py |
Backend interface (semantic: observe, clicks, keys, clipboard) + OCRError |
backends/macos.py |
macOS backend — the only file with OS APIs (Quartz/Vision/AppKit) + Viber layout |
backends/__init__.py |
get_backend() selects a backend by platform |
client.py |
standalone MCP client for testing |
contacts.json |
your name → number map (git-ignored) |
contacts.example.json |
template to copy |
commands/viber-reply.md |
optional Claude Code slash command |
Porting: add backends/<os>.py implementing Backend, wire it into get_backend(), and
re-tune that platform's UI-element locations. All domain logic and tools are shared. (Windows can
likely target elements via UI Automation instead of coordinates; Linux/Wayland restricts capture and
synthetic input, so X11 is easier.)
Limitations & caveats
- OCR sees only on-screen text — scroll to bring older messages into view; it can garble text or interleave the sidebar's chat previews with the conversation.
- No message history from disk (encrypted DB).
- Names resolve from
contacts.json— Viber's own contact list can't be read. - Version-sensitive: a Viber UI/layout change can require small coordinate tweaks.
- Messaging your own number is blocked by Viber ("cannot add yourself"); "My Notes" is separate.
Privacy & security
- Runs entirely locally; it does not send your messages or contacts anywhere except into Viber.
contacts.jsonis git-ignored so real numbers don't get committed.- Requires powerful macOS permissions (Accessibility, Screen Recording) for the host app — grant only if you understand and trust that.
License
MIT — see LICENSE.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.