Self-Hosted Claude.ai compatible WASenderAPI MCP with minimalist OAuth implementation.
WASenderAPI → Claude.ai MCP Connector
[!WARNING] Unofficial, unaffiliated, use-at-your-own-risk. This is a personal, third-party project. It is not built, endorsed, sponsored, or supported by WASenderAPI, by Anthropic / Claude, or by Cloudflare. "WASenderAPI", "Claude", and "Cloudflare" are the trademarks of their respective owners and are used here only to describe what this proxy talks to. No warranty of any kind — see LICENSE. You are responsible for your own credentials, deployment, and compliance with each upstream service's terms.
WASenderAPI's hosted MCP (https://wasenderapi.com/mcp) only accepts a static
Bearer token, which Claude.ai's custom-connector UI refuses (it requires
OAuth 2.1 + PKCE). This is a tiny Dockerized OAuth 2.1 facade proxy that
sits in front of it: Claude talks OAuth to this proxy, and the proxy injects your
WASenderAPI Personal Access Token (PAT) and forwards traffic upstream.
It is exposed publicly via a free Cloudflare Quick Tunnel (*.trycloudflare.com).
Claude.ai --OAuth 2.1 + PKCE--> this proxy --Bearer PAT--> wasenderapi.com/mcp
Requirements
- Docker + Docker Compose
- A paid WASenderAPI plan + Personal Access Token (MCP needs a paid plan)
- Node 24+ if you want to run the proxy or tests outside Docker (it relies on
the built-in
node:sqlite, still experimental — Node prints a one-lineExperimentalWarningon startup, which is expected)
Setup
-
Copy the env template and fill in your secrets:
cp .env.example .envSet in
.env:WASENDER_PAT— your WASenderAPI Personal Access Token.ADMIN_PASSPHRASE— the password you'll type on the consent screen. You can leave this empty:scripts/up.shgenerates a strong random passphrase, writes it into.env, and prints it once — keep it safe. If you set your own value, the script leaves it untouched. LeavePUBLIC_BASE_URLempty — the script fills it in.
-
Bring everything up:
bash scripts/up.shThis builds the image, starts the proxy + Cloudflare tunnel, detects the public
https://<random>.trycloudflare.comURL, writes it into.env, restarts the proxy, and prints the exact connector URL. -
In Claude.ai → Settings → Connectors → Add custom connector, paste:
https://<random>.trycloudflare.com/mcpLeave the OAuth Client ID/Secret fields blank (Claude auto-registers via DCR).
-
When Claude opens the consent page, enter your
ADMIN_PASSPHRASE. Done — the WASenderAPI WhatsApp tools appear in Claude.
Important: Quick Tunnel URLs change on restart
The free *.trycloudflare.com URL is ephemeral. If cloudflared restarts, the
URL changes and you must re-run bash scripts/up.sh and re-add the new /mcp URL
in Claude. For a stable URL, switch cloudflared to a named tunnel on a
Cloudflare-managed domain.
Operations
docker compose ps # status
docker compose logs -f oauth-proxy # proxy logs
docker compose logs -f cloudflared # tunnel logs / current URL
docker compose down # stop
bash scripts/up.sh # (re)start + refresh tunnel URL
Both services use restart: unless-stopped, so they survive reboots/crashes.
How auth works
/.well-known/oauth-protected-resource[/mcp]— RFC 9728 resource metadata/.well-known/oauth-authorization-server— RFC 8414 AS metadata (S256 PKCE, DCR)/register— RFC 7591 Dynamic Client Registration/authorize— passphrase-gated consent → authorization code/token—authorization_code(PKCE S256) +refresh_tokengrants/mcp— validates the issued token, swaps in yourWASENDER_PAT, streams to upstream
State (clients/codes/tokens) is stored in SQLite at ./data/oauth.db. Only the
SHA-256 hashes of access/refresh tokens are persisted, never the raw tokens.
Upgrade note: older builds stored raw tokens. On first start after this change the proxy drops the legacy token table (a tolerant migration that leaves the rest of
oauth.dbintact), so any tokens issued before the upgrade stop working — reconnect once from Claude.ai to get fresh ones.
Security notes
ADMIN_PASSPHRASEis the only gate on consent. Use a strong value. The passphrase check is constant-time, and repeated failures from one IP trigger a temporary backoff.- Authorization is PKCE-only (public client, no
client_secret).client_idandredirect_uriare validated against the registered client on every/authorizeand/tokencall. - Refresh tokens rotate on every use; a replayed old refresh token is rejected.
- Your
WASENDER_PATlives in.envand is injected server-side; Claude never sees it. .envanddata/are git-ignored.- To report a vulnerability, see SECURITY.md.
Development
Run the test suite (no dependencies to install — Node 24 built-ins only):
npm test # node --test
node --check src/server.js
License
MIT © 2026 Gus