guide / mac native path

WhatsApp MCP on Mac, without the Business API. And without any other API either.

Short answer: yes, you can. Install the npm package, grant Accessibility permission to your MCP host, and the server drives the WhatsApp Desktop app you are already signed in to. No Meta developer account, no API key, no webhook, no QR pairing for the MCP layer. The empty env: {} in the config below is not a typo.

The longer answer is more interesting. Every other “no Business API” WhatsApp MCP I have read still replaces Meta’s API with a different API. This one does not. It uses the operating system instead.

M
Matthew Diakonov
8 min read

Direct answer, verified 2026-05-05

Yes, the Mac path skips the Business API entirely.

whatsapp-mcp-macos is a Swift MCP server that binds to the running WhatsApp Desktop process by bundle id net.whatsapp.WhatsApp and drives it through the same accessibility framework VoiceOver uses. There is no Meta developer account in the loop, no access token, no template, no webhook, and no per-message fee. The MCP layer carries zero secrets. Your auth is whatever WhatsApp Desktop is signed in as.

Authoritative source for everything below: github.com/m13v/whatsapp-mcp-macos. Specific files cited: Sources/WhatsAppMCP/main.swift and package.json.

What “install” looks like in practice

Two commands and a config edit. No portal. No verification email. No phone code. No template review. No HTTPS endpoint to host. The postinstall step compiles the Swift binary; that is the only build step.

install on a fresh Mac

The configured env block stays empty for the lifetime of the project. There is nothing to rotate, nothing to revoke, nothing to leak. If you check ~/.claude.json into a backup the way you check most config files, you are not backing up a Meta token, because there is not one.

The two configs, side by side

The Business API setup is not just code. The bulk of it lives inside Meta’s portal: business verification, phone registration, template approval per locale, the 24-hour customer service window, and the per-conversation price sheet. The MCP path is what shows up in ~/.claude.json and that is the whole config.

WhatsApp client config

// .env for the typical Business API path
WHATSAPP_PHONE_NUMBER_ID=...           // from Meta App Dashboard
WHATSAPP_BUSINESS_ACCOUNT_ID=...       // from WABA setup
WHATSAPP_ACCESS_TOKEN=EAA...           // long-lived system user token
WHATSAPP_APP_ID=...
WHATSAPP_APP_SECRET=...
WHATSAPP_VERIFY_TOKEN=...              // for webhook handshake
WEBHOOK_URL=                           // public HTTPS endpoint you host

// Plus, separately, in Meta's UI:
//  - business verification (1 to 5 days)
//  - phone number registration + 6-digit verify code
//  - message template approval, per locale (24 to 48 hours)
//  - 24-hour customer service window per recipient
//  - per-conversation pricing by country and category
50% fewer config surfaces

The right pane is the entire client-side configuration. Everything else is the operating system handing your MCP host a process to attach to.

Where the API used to be: a 5.0 second AX timeout

On the Business API path, every send is an HTTPS POST to graph.facebook.com and every inbound message is a webhook to a public URL you host. Both fail in network-shaped ways: TLS, DNS, retries, signed payloads, signature mismatches.

On the Mac path, all of that is replaced by two lines of Swift. The server binds to the running app by bundle id, then sets a 5.0 second messaging timeout on the AX element. Every accessibility call after that, walks, clicks, attribute reads, errors out cleanly if the WhatsApp window does not respond within the ceiling. There is no retry queue and no exponential backoff because there is no network.

Sources/WhatsAppMCP/main.swift

That is the entire transport layer. AXUIElementCreateApplication(pid) returns a handle to the live WhatsApp process, and AXUIElementSetMessagingTimeout(appElement, 5.0) decides how patient every later call is.

What you do not need, line by line

For each of the things the Business API requires of you, I will tell you whether it shows up on the Mac path. Most of them do not.

Business API requirement vs Mac MCP path

  • Meta developer account: not required. The MCP never talks to graph.facebook.com.
  • Business verification: not required. There is no business profile on the other end.
  • Phone number provisioning: not required. The phone number is whatever your WhatsApp Desktop app is signed in as.
  • Message template approval: not required. There are no templates, the send tool pastes literal text into the compose textarea.
  • Public HTTPS webhook: not required. The transport is stdio, the MCP child reads JSON-RPC from stdin and writes to stdout.
  • 24-hour customer service window: not enforced. You can message anyone you can already message in WhatsApp Desktop.
  • Per-conversation pricing: zero. AXUIElementCopyAttributeValue does not bill per call.
  • Accessibility permission for the host: required. Granted to the process that forks the MCP child (Claude Code, Cursor, Terminal, Fazm).
  • WhatsApp Desktop installed and signed in: required. One-time QR pair with your phone, that login is the auth.

The single accessibility checkbox is doing a lot of work. It replaces the entire Meta-side approval surface, and it lives in System Settings.

How the auth check is enforced

The MCP’s only authority check happens once per tool call, before any AX call is made. If the host process is not in the macOS Accessibility allowlist, the call returns a structured JSON error pointing at the right System Settings pane, instead of silently doing nothing.

Sources/WhatsAppMCP/main.swift

There is also a functional probe a few lines later that actually tries to read the AX tree, because AXIsProcessTrustedWithOptions can return true while the TCC database is stale and AX calls silently fail. That distinction shows up in whatsapp_status as accessibilityWorking.

Business API vs the Mac MCP path

The shapes of the two contracts are opposite.

FeatureWhatsApp Business APIwhatsapp-mcp-macos
Auth modelMeta-issued access token tied to a verified Business Account. Token rotation, system users, app secrets.Whatever WhatsApp Desktop is already signed in as. The MCP layer has no credentials. The env block is literally {}.
TransportHTTPS to graph.facebook.com plus a public HTTPS webhook the caller has to host for inbound messages and delivery state.stdio. The MCP host forks the binary and pipes JSON-RPC over stdin and stdout. No ports, no TLS, no inbound URL.
What the call actually doesPOST a JSON payload referencing a pre-approved template ID. Meta routes it.Walks AXUIElement nodes inside the running net.whatsapp.WhatsApp process, finds the AXTextArea, posts a Cmd+V CGEvent, presses Return. The OS does the routing.
Sender identityA registered business phone number with a verified profile.The human's own personal account. The same one that shows up in their friends' chats.
Approval surfaceBusiness verification, phone registration, template review, app review for advanced permissions.One macOS Accessibility checkbox in System Settings.
Per-message costPer-conversation pricing set by Meta, varying by country and category.Zero. The accessibility framework is part of the OS.
What 'opt in' meansEnd user must have given the business explicit opt-in for marketing categories.There is no business in the loop. The user is messaging from their own logged-in account, the same way they would in the app.

But what about the other “no Business API” MCPs?

Fair question. There are a few. The most popular ones I know about are lharries/whatsapp-mcp (Go bridge speaking the WhatsApp Web multidevice protocol via whatsmeow), msaelices/whatsapp-mcp-server (which connects to GreenAPI, a paid third-party WhatsApp gateway), and a handful of Playwright-driven WhatsApp Web automations.

All of them avoid the Business API. None of them avoid having an API in the path. The Mac one does.

Featureother ‘no Business API’ MCPswhatsapp-mcp-macos
What replaces Meta's APISome other API. whatsmeow speaks the WhatsApp Web multidevice protocol. GreenAPI is a paid third-party WhatsApp gateway. Playwright drives WhatsApp Web in a real browser.Nothing replaces an API, because no API is in the path. The server reads the AX tree of the WhatsApp Desktop app the same way VoiceOver does.
Pairing stepQR scan inside the MCP server itself, or a logged-in browser session that has to be kept warm. Both can drop unpredictably.QR pair was done once, in WhatsApp Desktop, the day the user installed it. The MCP layer never sees a QR code.
Where the session livesA SQLite file or a Chromium user-data dir owned by the MCP project. Lose it and you re-pair.Inside Apple's Keychain and WhatsApp Desktop's own Group Container. Lose your laptop and you re-pair WhatsApp itself, not the MCP.
Failure modeProtocol-level disconnects, rate limits, captcha, multi-device session expiry. Often silent.If the AX tree does not respond inside 5.0 seconds, the call errors cleanly and the model can decide what to do next.
What 'this works on Mac' meansSame code runs anywhere the protocol or browser runs. Mac is incidental.Mac is the entire point. The Catalyst app exposes a stable AX tree under macOS 13+. No Linux or Windows port is possible because there is no Catalyst app to drive there.

None of those projects are wrong. They are the right answer for different problems. If you want a single MCP that runs on Linux, Windows, and Mac and does not depend on a desktop app being installed, whatsmeow is the path. If you want a hosted gateway with a SaaS billing model, GreenAPI does that. If you want an MCP that does not require any of those things and treats your existing Mac WhatsApp install as the substrate, this is that one.

Why this is Mac only and probably will be

Three macOS-specific pieces hold the design together. AXUIElement is Apple’s accessibility framework, the same surface VoiceOver and Switch Control use. The WhatsApp Catalyst app exposes a stable AX tree under macOS 13 and later because Catalyst inherits UIKit’s accessibility surface for free. And CGEvent posts low-level mouse and keyboard events into the same input stream a human would use.

Windows has UI Automation, which is a different shape and would need a fresh implementation. Linux has AT-SPI, but WhatsApp does not ship a Linux desktop client at all, so there is no app to drive there. A “cross-platform port” would have to use the WhatsApp Web protocol or a browser, and at that point you are running one of the other MCPs above. The Mac path is not a stepping stone. It is the destination.

Want a hand wiring this into your agent?

If you are building an AI agent that needs WhatsApp on a Mac and you would rather not climb the Business API tree, book a 30-minute call. Bring your stack, leave with a working stdio config and a clear sense of where this path stops fitting.

Common questions

Can I really run a WhatsApp MCP on Mac without the Business API?

Yes. whatsapp-mcp-macos installs from npm, registers a stdio child process in your MCP host config, and drives the official WhatsApp Desktop app on your Mac through the macOS accessibility framework. There is no Meta developer account, no app review, no phone number provisioning, no access token, no webhook, and no template approval. The bundle id net.whatsapp.WhatsApp is hardcoded at line 57 of Sources/WhatsAppMCP/main.swift, and every accessibility call routes through that PID with a 5.0 second AXUIElementSetMessagingTimeout (line 120). The env block in your MCP config is literally an empty object.

How is this different from other WhatsApp MCP servers that also avoid the Business API?

Every other 'no Business API' WhatsApp MCP I have looked at still routes through some API substitute. lharries/whatsapp-mcp embeds a Go bridge that speaks the WhatsApp Web multidevice protocol via the whatsmeow library, so you scan a QR inside the MCP itself and it maintains its own session. msaelices/whatsapp-mcp-server connects to GreenAPI, a paid third-party WhatsApp gateway. A handful of others drive WhatsApp Web in a Playwright Chromium. whatsapp-mcp-macos does not replace Meta's API with another API. It binds to the running net.whatsapp.WhatsApp Catalyst process and walks the AXUIElement tree, the same accessibility surface VoiceOver and other assistive tech use.

Do I need to scan a QR code to set up the MCP?

No. The MCP layer never sees a QR code. The QR pair was already done, once, when you installed WhatsApp Desktop from the Mac App Store and signed in with your phone. That pairing lives inside WhatsApp Desktop's own session storage, not inside the MCP. The MCP just drives whatever account the desktop app is currently signed in as. If you sign out of WhatsApp Desktop, the MCP loses its target. If you sign in to a different account, the MCP starts driving that one. Pairing is the operating system's problem, not the MCP's.

What permissions does the MCP actually need?

Exactly one: macOS Accessibility, granted to the host process that forks the MCP child. If you launch Claude Code from /Applications/Claude.app, you grant Accessibility to Claude. If you run an MCP test from Terminal, you grant it to Terminal. The check is at line 562 of main.swift, and on a denied call it returns a JSON error pointing at System Settings > Privacy & Security > Accessibility. There is also a functional probe at line 576 that actually tries to read the AX tree, because AXIsProcessTrustedWithOptions can return true while the TCC database is stale and AX calls silently fail.

What does the install actually do?

npm install -g whatsapp-mcp-macos pulls the package and runs xcrun swift build -c release as a postinstall script (declared in package.json). That builds a Swift binary at .build/release/whatsapp-mcp. The npm bin script wires whatsapp-mcp on your PATH to that binary. From there you add an mcpServers.whatsapp entry to ~/.claude.json (or your client's equivalent) with type stdio, command whatsapp-mcp, args [], env {}. Restart your MCP host or run /mcp to reconnect. The first call surfaces the Accessibility prompt if it is not granted.

What can the model actually do once it is connected?

Eleven tools, assembled into the allTools array at line 1110 of main.swift: whatsapp_status, whatsapp_start, whatsapp_quit, whatsapp_get_active_chat, whatsapp_list_chats, whatsapp_search, whatsapp_open_chat, whatsapp_scroll_search, whatsapp_read_messages, whatsapp_send_message, and whatsapp_navigate. The recommended workflow is search, open_chat, get_active_chat (verify), then send_message. The send tool pastes the text via the clipboard, posts a Return CGEvent, then re-reads the AX tree to confirm the bubble appeared. There is no whatsapp_create_chat. If a recipient is not already in your sidebar, the agent has nothing to click, which is intentional.

Why is this Mac only? Can it ever run on Windows or Linux?

No, and this is by design. The path depends on three Apple-specific pieces. First, AXUIElement, which is Apple's accessibility framework, used by VoiceOver. Second, the WhatsApp Catalyst app, which is the macOS port of the iPad WhatsApp build, with a stable accessibility tree under macOS 13 and later. Third, CGEvent for posting clicks and key events. Windows has UI Automation, which is a different shape. Linux has AT-SPI, but WhatsApp does not ship a Linux desktop app at all. A Linux or Windows version would need a different transport entirely, probably whatsmeow, which is the path the Mac version was specifically designed to avoid.

What are the honest limitations of going via accessibility instead of an API?

The server can only see what is rendered. If a chat has not been opened, its messages are not in the AX tree, so whatsapp_read_messages cannot fetch arbitrary history. Text only. The send tool pastes text into the compose textarea; it does not handle images, files, voice, or video. Single window assumption. The accessibility tree parsing assumes the standard WhatsApp window layout, with x-coordinate thresholds for sidebar versus chat panel. Visible cursor and clipboard side effects are minimised but real, the cursor moves and the clipboard is briefly overwritten before being restored. And it is genuinely macOS only. If any of those constraints are deal breakers, the Business API or whatsmeow paths are the right choice instead.