Viber MCP Server

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.

Category
Visit Server

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.db is encrypted with a Botan page codec (random header, not SQLite 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)) — plain CGEvents 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 by uv)

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 by VNRecognizeTextRequest. 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 from CGEventSourceCreate(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 verified flag 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.json is 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

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.

Official
Featured
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured