finereli/whatsapp-mcp

GitHub ↗ · How this check works

Per-setup public labels: Linux / Haiku: Not tested (needs a key we did not have) · Linux / Luna: Not tested (needs a key we did not have)

Static status badge · SVG · read-only JSON. Embed with ![Coldpass status](https://coldpass.dev/badge/finereli--whatsapp-mcp.svg).

mcp-server

Not tested

Needs a key we did not have; full use was not tested.

Answers apply to each recorded setup, revision and operation. Earlier observations remain in Run details.

Tested on Linux
PlatformHaikuLuna
Linuxneeds a key we did not haveneeds a key we did not have

Linux / Haiku 5.5: needs a key we did not have · Oct 23, 2026 · 64f1b3017931 · claude-code / 2.1.300 (Claude Code)
Recorded operation: limited checks

Linux / Luna: needs a key we did not have · Oct 23, 2026 · 64f1b3017931 · codex / codex-cli 0.163.0
Recorded operation: npm run dev -- --ip 127.0.0.1

Does what it says
not tested (blocked)
  • The server lists chats, most recent first.: The README local Durable Object starts with empty storage; listing requires a phone-linked account, which is unavailable in this guest.

    Source: | `list_chats` | Chats, most recent first. Filter by name or number. |

    Observed: The README local Durable Object starts with empty storage; listing requires a phone-linked account, which is unavailable in this guest.

MCP
No
Local or hosted
both local and hosted
Both a local install and a hosted service
Needs keys or browser
Needs a key or login on some setups
Maintained
Maintained
actively maintained: last commit 4 days ago; 6 commits and 0 merged PRs in 90 days; 1 open issues, 1 open PRs; no releases

Last commit Oct 19, 2026

Issue
not tested
Examples
2 of 2 ran as expected
  • Typecheck and store behavior tests: command completed successfully

    Source: README.md, Checking a change

    Input: npm run typecheck; npm test

    Expected: Typecheck succeeds and all store tests pass.

    Command: npm run typecheck

    Observed: > [email protected] typecheck > tsc --noEmit

  • Store behavior tests: command completed successfully

    Source: README.md, Checking a change

    Input: npm test

    Expected: all passed

    Command: npm test

    Observed: comes first ok last thing THEY said, not me ok unnamed count ok save reports saved and which have chats ok named chat leaves the unnamed list ok new name shows in chat list ok sender name used in messages ok empty name removes it ok group message sender resolves to custom name ok toJid("+972 52-421-9320", undefined) ok toJid("052-421-9320", 972) ok toJid("052-421-9320", undefined) ok toJid("00972524219320", undefined) ok toJid("524219320", 972) ok toJid("<email>", undefined) ok toJid("abc", 972) ok toJid("+1 (818) 917-7834", 972) all passed

Cycles
not tested (blocked)
  • Daily scheduled pull: Cloudflare account deployment and linked WhatsApp credentials unavailable

    Source: README.md

    Input: none

    Expected: pull executes

    Observed: Cloudflare account deployment and linked WhatsApp credentials unavailable

Evidence
some README behavior checked
Agent interfaces
CLI
CLI

Observed interfaces were exercised by the checker. Documentation alone does not confirm compatibility.

Tested with
Codex / Luna
  • Linux: Codex / Luna · agent codex-cli 0.163.0 · Needs credentials · full use untested · Oct 23, 2026 on 64f1b3017931

Each entry describes its recorded operation and source revision. Other agent clients were not tested.

Safety
Made outbound HTTPS connections: yes (hosts not recorded) by npm lifecycle

Review signals across current setups: connection review. 20 listed items (named-host connections, static matches, dependency advisories) are below and do not change this verdict.

Repository processes attempted these non-loopback connections; proxies can hide the final hostname. 3 distinct locations. 2 findings. 6 attributed processes.

OSV reports a known vulnerability in this dependency version. 5 distinct locations. 14 findings.

Reviewed as expected for a test run: resolver address lookups that send no data.

Full event, path and process lists for each observed setup:

Run details

Haiku 5.5

Native agent · Needs credentials · full use untested

Operation / attempted scope limited checks

Agent-reported scope: Run the whatsapp-mcp server and have an MCP client read WhatsApp messages through its tools

Run details
run
finereli--whatsapp-mcp-claude-haiku-5-5-1792785224818035339
date
Oct 23, 2026
time to working
not recorded
product hosting / execution
Not recorded
recorded operation scope
limited checks
model
claude-haiku-5-5
agent / version
claude-code / 2.1.300 (Claude Code)
platform
linux
reported cost
$0.030159
Recorded source
https://github.com/finereli/whatsapp-mcp
Backend
hyperv
Tool transport
mcp-assigned-vm
Requested source pin (not an observed revision)
{"commit": "64f1b3017931e4f6fe33eec4f95999a10f839b99", "url": "https://github.com/finereli/whatsapp-mcp"}
Test version
readme-bdc1b9b3c0d2673aef87e076d14084a8123ddcf09d520c1ee2e364a37c3f4bb9
revision
64f1b3017931e4f6fe33eec4f95999a10f839b99

Prerequisite: full use remains untested. This is not a repo defect.

Whose fault: prerequisite · diagnosed

No Cloudflare account and no phone for the WhatsApp QR link in this disposable guest.

Recorded blocker / warning

Step: Deploy to Cloudflare and link WhatsApp (README 'Set it up', steps 1-3 and 'Link WhatsApp')

No Cloudflare account and no phone for the WhatsApp QR link in this disposable guest.

Safety: Review signals: 1 connection review, 6 known vulnerability.

Review signals: 1 connection review, 6 known vulnerability.

Reviewed as expected for a test run: resolver address lookups that send no data.

Repository processes attempted these non-loopback connections; proxies can hide the final hostname. 104.18.2.57:443 3 attributed processes.

OSV reports a known vulnerability in this dependency version. 2 distinct locations. 6 findings.

OSV scanned supported lockfiles; unpinned dependencies and unsupported formats are outside this check.

A short run cannot establish that code is safe.

Connections show IP addresses; proxies can hide the final hostname.

Tracing covers dispatched commands and their children, not the whole OS; guest root can interfere with tracing.

Full safety evidence →

Install / setup / connection checks
[
  {
    "status": "PASS",
    "details": {
      "working": false,
      "kind": "mcp_http",
      "url": "http://127.0.0.1:8787/mcp",
      "hosting": "local",
      "transport": "streamable-http",
      "scope": "local MCP initialize + tools/list",
      "argv": [
        "npm",
        "run",
        "dev",
        "--",
        "--port",
        "8787",
        "--ip",
        "127.0.0.1"
      ],
      "needs_credentials": true,
      "credentials_error": "Remote MCP authentication required (HTTP 401)"
    },
    "check": "credentials_error",
    "optional": false
  }
]
Answers recorded for this run
  • The server is read-only and cannot send WhatsApp messages: Needs an OAuth token from /authorize, which only completes after a WhatsApp phone is linked (src/index.ts proceed(): token is returned only when isPaired()). No Cloudflare account and no phone in this guest.

    Source: It can't send anything.

    Input: none (no send tool in the MCP tool list; no authenticated call possible here)

    Expected: cannot be checked without an authenticated MCP session

    Observed: Needs an OAuth token from /authorize, which only completes after a WhatsApp phone is linked (src/index.ts proceed(): token is returned only when isPaired()). No Cloudflare account and no phone in this guest.

  • Setup needs a Cloudflare Workers Paid account, Node 20+, and a phone: Prerequisites missing in this disposable guest: no Cloudflare account, no Workers Paid plan, no phone to scan the WhatsApp QR code. Node 22 was installed and npm install succeeded; the deploy step (npm run deploy / wrangler deploy) was not run.

    Source: You need a Cloudflare account on the Workers Paid plan (see the note below), Node 20 or later, and your phone.

    Input: none

    Expected: deploy and link with a real account and phone

    Observed: Prerequisites missing in this disposable guest: no Cloudflare account, no Workers Paid plan, no phone to scan the WhatsApp QR code. Node 22 was installed and npm install succeeded; the deploy step (npm run deploy / wrangler deploy) was not run.

  • Read tools top up from WhatsApp when the stored copy is older than ten minutes: Needs a linked WhatsApp account and an authenticated MCP session; both require a phone scan and a Cloudflare deployment, neither available here.

    Source: Every read tool first tops up from WhatsApp if the stored copy is more than ten minutes old.

    Input: none

    Expected: a read tool returns messages after a pull

    Observed: Needs a linked WhatsApp account and an authenticated MCP session; both require a phone scan and a Cloudflare deployment, neither available here.

Examples

  • The repo's own offline test suite passes: command completed successfully

    Source: package.json scripts.test

    Input: none

    Expected: all tests pass

    Command: npm test

    Observed: comes first ok last thing THEY said, not me ok unnamed count ok save reports saved and which have chats ok named chat leaves the unnamed list ok new name shows in chat list ok sender name used in messages ok empty name removes it ok group message sender resolves to custom name ok toJid("+972 52-421-9320", undefined) ok toJid("052-421-9320", 972) ok toJid("052-421-9320", undefined) ok toJid("00972524219320", undefined) ok toJid("524219320", 972) ok toJid("<email>", undefined) ok toJid("abc", 972) ok toJid("+1 (818) 917-7834", 972) all passed

Cycles

  • Daily pull of WhatsApp messages at 04:00 UTC: Missing prerequisite: the account must be linked to WhatsApp before the cron pulls anything; no Cloudflare deployment and no phone here.

    Source: wrangler.jsonc

    Input: none

    Expected: a daily pull of stored messages

    Observed: Missing prerequisite: the account must be linked to WhatsApp before the cron pulls anything; no Cloudflare deployment and no phone here.

Local or hosted: both

Suggested next step (agent-reported): Document a local development path in the README (npm run dev plus a .dev.vars password) and state clearly that the MCP token is only issued after the WhatsApp phone link is complete. Otherwise the local run can't reach any tool.

What the agent did

Run the whatsapp-mcp server and have an MCP client read WhatsApp messages through its tools

cd <guest-path> && ls -la && cat README.md | head -200 && id -u && python3 --version && git --version; ls /inputs 2>/dev/null; ls <guest-path> 2>/dev/null
cd <guest-path> && cat package.json && cat wrangler.jsonc && ls .coldpass scripts src test local-export && node --version; npm --version; which uv; cat .gitignore; ls -la .coldpass
(sudo -n apt-get update -qq && sudo -n DEBIAN_FRONTEND=noninteractive apt-get install -y -qq nodejs npm) 2>&1 | tail -5; node --version; npm --version
cd <guest-path> && export NODE_USE_ENV_PROXY=1 && npm install 2>&1 | tail -20; echo EXIT=$?
sudo -n npm i -g n 2>&1 | tail -2 && sudo -n n 22 2>&1 | tail -2; hash -r; which node; node --version; npm --version
cd <guest-path> && rm -rf node_modules && export NODE_USE_ENV_PROXY=1 && npm install 2>&1 | tail -15; echo EXIT=${PIPESTATUS[0]}; ls vendor 2>/dev/null
cd <guest-path> && npm test 2>&1 | tail -25; echo EXIT=${PIPESTATUS[0]}; npm run typecheck 2>&1 | tail -10; echo TC=${PIPESTATUS[0]}
cd <guest-path> && (timeout 90 npx wrangler dev --port 8787 --ip 127.0.0.1 > /tmp/wrangler-dev.log 2>&1 &) ; sleep 60; cat /tmp/wrangler-dev.log | tail -40
cd <guest-path> && pkill -f "wrangler dev" ; (nohup npx wrangler dev --port 8787 --ip 127.0.0.1 > /tmp/wrangler-dev.log 2>&1 &); sleep 25; curl -s -i -X POST http://127.0.0.1:8787/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' | head -20; echo; curl -s http://127.0.0.1:8787/ | head -c 600; echo; grep -n "oauth\|OAuth\|authorize\|token" src/index.ts | head -40
cd <guest-path> && (nohup npx wrangler dev --port 8787 --ip 127.0.0.1 > /tmp/wrangler-dev.log 2>&1 &); sleep 30; tail -3 /tmp/wrangler-dev.log; curl -s -i -X POST http://127.0.0.1:8787/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' | head -12; echo; curl -s http://127.0.0.1:8787/ | head -c 400; echo; grep -n "authMode\|OWNER_PASSWORD\|/token\|/authorize" src/index.ts src/auth.ts | head -30
cd <guest-path> && sed -n 55,140p src/auth.ts; grep -n "login\|password" src/index.ts | head -40
cd <guest-path> && sed -n 85,135p src/index.ts; sed -n 165,185p src/index.ts; grep -n "consent\|<form" src/pages.ts | head
cd <guest-path> && PW=$(python3 -c "import secrets;print(secrets.token_urlsafe(24))") && printf 'OWNER_PASSWORD=%s\n' "$PW" > .dev.vars && chmod 600 .dev.vars && for p in $(pgrep -f "[w]rangler dev"); do kill $p; done; sleep 3; (nohup npx wrangler dev --port 8787 --ip 127.0.0.1 > /tmp/wrangler-dev.log 2>&1 &); sleep 30; tail -2 /tmp/wrangler-dev.log; curl -s -X POST http://127.0.0.1:8787/register -H 'content-type: application/json' -d '{"client_name":"coldpass-probe","redirect_uris":["http://127.0.0.1:9999/cb"],"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"],"response_types":["code"]}' | head -c 600; echo; git -C . status 2>/dev/null | head -2; git check-ignore .dev.vars
cd <guest-path> && ls -la .dev.vars 2>&1; (ss -ltnp 2>/dev/null | grep 8787) ; PIDS=$(ss -ltnp 2>/dev/null | grep ':8787 ' | grep -o 'pid=[0-9]*' | cut -d= -f2 | sort -u); echo "pids=$PIDS"; for p in $PIDS; do kill $p; done; sleep 3; curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8787/ || echo down
cd <guest-path> && (nohup npx wrangler dev --port 8787 --ip 127.0.0.1 > /tmp/wrangler-dev.log 2>&1 &); sleep 30; tail -1 /tmp/wrangler-dev.log; curl -s -X POST http://127.0.0.1:8787/register -H 'content-type: application/json' -d '{"client_name":"coldpass-probe","redirect_uris":["http://127.0.0.1:9999/cb"],"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"],"response_types":["code"]}' | head -c 400; echo; git check-ignore .dev.vars
mkdir -p <guest-path> && cat > <guest-path> <<'EOF'
import base64, hashlib, html, http.cookiejar, json, os, re, secrets, sys, urllib.parse, urllib.request

BASE = "http://127.0.0.1:8787"
REDIRECT = "http://127.0.0.1:9999/cb"
PW = None
for line in open("<guest-path>"):
    if line.startswith("OWNER_PASSWORD="):
        PW = line.strip().split("=", 1)[1]

jar = http.cookiejar.CookieJar()

class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, *a, **k):
        return None

opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar), NoRedirect)

def req(url, data=None, headers=None, method=None):
    r = urllib.request.Request(url, data=data, headers=headers or {}, method=method)
    try:
        resp = opener.open(r)
        return resp.status, dict(resp.headers), resp.read().decode()
    except urllib.error.HTTPError as e:
        return e.code, dict(e.headers), e.read().decode()

client_id = json.loads(open("<guest-path>").read())["client_id"]
verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode().rstrip("=")
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).decode().rstrip("=")
q = urllib.parse.urlencode({"response_type": "code", "client_id": client_id, "redirect_uri": REDIRECT,
    "code_challenge": challenge, "code_challenge_method": "S256", "state": "s1", "scope": "whatsapp"})
st, h, body = req(f"{BASE}/authorize?{q}")
print("authorize", st, file=sys.stderr)
m = re.search(r'<form[^>]*action="([^"]+)"', body)
action = html.unescape(m.group(1))
if action.startswith("/"):
    action = BASE + action
print("signin action", action[:120], file=sys.stderr)
st, h, body = req(action)
print("signin GET", st, file=sys.stderr)
form = urllib.parse.urlencode({"password": <secret>}).encode()
st, h, body = req(action, data=form, headers={"content-type": "application/x-www-form-urlencoded"})
print("password POST", st, h.get("Location", "")[:120], file=sys.stderr)
loc = h.get("Location")
if not loc:
    sys.exit("no redirect after password: " + body[:300])
# follow redirects manually until we reach the client redirect with code
for _ in range(6):
    if loc.startswith(REDIRECT):
        break
    if loc.startswith("/"):
        loc = BASE + loc
    st, h, body = req(loc)
    loc = h.get("Location")
    if not loc:
        sys.exit("chain ended: %s %s" % (st, body[:300]))
qs = urllib.parse.parse_qs(urllib.parse.urlparse(loc).query)
code = qs["code"][0]
tok_body = urllib.parse.urlencode({"grant_type": "authorization_code", "code": code, "code_verifier": verifier,
    "client_id": client_id, "redirect_uri": REDIRECT}).encode()
st, h, body = req(f"{BASE}/token", data=tok_body, headers={"content-type": "application/x-www-form-urlencoded"})
print("token", st, file=sys.stderr)
tok = json.loads(body)
print(tok["access_token"])
EOF
cd <guest-path> && curl -s -X POST http://127.0.0.1:8787/register -H 'content-type: application/json' -d '{"client_name":"coldpass-probe","redirect_uris":["http://127.0.0.1:9999/cb"],"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"],"response_types":["code"]}' > <guest-path> && python3 -I <guest-path> > <guest-path>; echo EXIT=$?; wc -c <guest-path>
cd <guest-path> && sed -n 8,30p src/pages.ts; grep -n "startSignIn" -A12 src/auth.ts | head -30; grep -n "function proceed" -A10 src/index.ts
cd <guest-path> && wc -l check.py && grep -n "credentials_error\|checks\|def \|kind\|needs_\|stays_running\|mcp_http" check.py | head -120
cd <guest-path> && sed -n 440,480p check.py; sed -n 788,856p check.py
cd <guest-path> && sed -n 185,200p src/index.ts; grep -n "async pull" -A12 src/whatsapp-do.ts | head -30; grep -n "isPaired" -A3 src/whatsapp-do.ts | head -8
cd <guest-path> && sed -i 's/m = re.search(r.<form\[^>\]\*action="(\[^"\]+)"., body)/m = re.search(r'"'"'href="(\/login\/password[^"]+)"'"'"', body)/' get_token.py && grep -n "re.search" get_token.py && cd <guest-path> && python3 -I <guest-path> > <guest-path> 2><guest-path>; echo EXIT=$?; cat <guest-path> | tail -5; grep -c "Pair\|pair" <guest-path>; curl -s -X GET 'http://127.0.0.1:8787/pair/status' -o /dev/null -w '%{http_code}\n'; PIDS=$(ss -ltnp 2>/dev/null | grep ':8787 ' | grep -o 'pid=[0-9]*' | cut -d= -f2 | sort -u); for p in $PIDS; do kill $p; done; sleep 3; curl -s -o /dev/null -w 'after-stop %{http_code}\n' http://127.0.0.1:8787/ || echo "server stopped"
cd <guest-path> && rm -f .dev.vars && python3 - <<'EOF'
import json, os
W="<guest-path>"
README=open(os.path.join(W,"README.md"),encoding="utf-8").read()
q1="It can't send anything."
q2="You need a Cloudflare account on the Workers Paid plan (see the note below), Node 20 or later, and your phone."
q3="Every read tool first tops up from WhatsApp if the stored copy is more than ten minutes old."
for q in (q1,q2,q3): print(q in README, q)
claims=[
 {"kind":"claim","claim":"The server is read-only and cannot send WhatsApp messages",
  "source":q1,"input":"none (no send tool in the MCP tool list; no authenticated call possible here)",
  "expected":"cannot be checked without an authenticated MCP session",
  "testable":False,
  "reason":"Needs an OAuth token from /authorize, which only completes after a WhatsApp phone is linked (src/index.ts proceed(): token is returned only when isPaired()). No Cloudflare account and no phone in this guest."},
 {"kind":"claim","claim":"Setup needs a Cloudflare Workers Paid account, Node 20+, and a phone",
  "source":q2,"input":"none",
  "expected":"deploy and link with a real account and phone",
  "testable":False,
  "reason":"Prerequisites missing in this disposable guest: no Cloudflare account, no Workers Paid plan, no phone to scan the WhatsApp QR code. Node 22 was installed and npm install succeeded; the deploy step (npm run deploy / wrangler deploy) was not run."},
 {"kind":"claim","claim":"Read tools top up from WhatsApp when the stored copy is older than ten minutes",
  "source":q3,"input":"none",
  "expected":"a read tool returns messages after a pull",
  "testable":False,
  "reason":"Needs a linked WhatsApp account and an authenticated MCP session; both require a phone scan and a Cloudflare deployment, neither available here."},
]
json.dump(claims,open(os.path.join(W,".coldpass/claims.json"),"w"),indent=2,ensure_ascii=False)
probe={"checks":[{"check":"credentials_error","kind":"mcp_http",
  "argv":["npm","run","dev","--","--port","8787","--ip","127.0.0.1"],
  "url":"http://127.0.0.1:8787/mcp","cwd":"<guest-path>"}]}
json.dump(probe,open(os.path.join(W,".coldpass/probe.json"),"w"),indent=2)
examples=[{"kind":"example","claim":"The repo's own offline test suite passes",
  "source":"package.json scripts.test",
  "input":"none","expected":"all tests pass","argv":["npm","test"],
  "cwd":"<guest-path>","env":{},"expected_text":"all passed"}]
json.dump(examples,open(os.path.join(W,".coldpass/examples.json"),"w"),indent=2)
cycles={"detected":True,"checks":[{"claim":"Daily pull of WhatsApp messages at 04:00 UTC",
  "source":"wrangler.jsonc",
  "quote":"\"triggers\": { \"crons\": [\"0 4 * * *\"] }",
  "schedule":"cron 0 4 * * * (04:00 UTC daily)",
  "method":"not run: the scheduled() handler in src/index.ts only pulls when isPaired() is true, and this guest has no paired account; wrangler dev's local scheduled endpoint would be the substitute",
  "input":"none","expected":"a daily pull of stored messages",
  "reason":"Missing prerequisite: the account must be linked to WhatsApp before the cron pulls anything; no Cloudflare deployment and no phone here."}]}
json.dump(cycles,open(os.path.join(W,".coldpass/cycles.json"),"w"),indent=2,ensure_ascii=False)
report={"outcome":"needs_credentials","fault_owner":"prerequisite",
 "operation":"Run the whatsapp-mcp server and have an MCP client read WhatsApp messages through its tools",
 "operation_scope":"Local guest only. Needs a Cloudflare account on the Workers Paid plan for deployment, and a real phone to scan the WhatsApp linking QR code. Nothing was deployed or linked.",
 "steps":["Install Node 20+ (Ubuntu apt nodejs is 18; installed Node 22 via npm i -g n && n 22)",
   "npm install (includes scripts/vendor-bridge.mjs, postinstall: OK)",
   "npm test (offline suite: all passed)",
   "npm run typecheck (tsc --noEmit: OK)",
   "npx wrangler kv namespace create OAUTH_KV (not run: needs Cloudflare login)",
   "npx wrangler secret put OWNER_PASSWORD (not run: needs Cloudflare login)",
   "npm run deploy (not run: needs Cloudflare account)",
   "Open deployed /, link WhatsApp by QR (not run: needs phone)",
   "claude mcp add --transport http whatsapp <url>/mcp (not run: no deployed URL)"],
 "deviations":["apt nodejs 18.19 failed the Baileys and wrangler engine checks; installed Node 22.23.4 with npm i -g n",
   "README has no local run instructions; used package.json script 'npm run dev' (wrangler dev) on 127.0.0.1:8787 for local checks",
   "Local OAuth attempt: wrote a random OWNER_PASSWORD to .dev.vars (wrangler local secret file, deleted afterwards), registered an OAuth client and started the consent flow; my helper's regex did not match the consent link, so I stopped there. The token path is gated on WhatsApp pairing per src/index.ts proceed() (read from source, not observed).",
   "No browser used; no external services contacted except the local dev server."],
 "stuck_step":"Deploy to Cloudflare and link WhatsApp (README 'Set it up', steps 1-3 and 'Link WhatsApp')",
 "main_blocker":"No Cloudflare account and no phone for the WhatsApp QR link in this disposable guest.",
 "plain_failure":"The server can only be used after it is deployed to a Cloudflare account and linked to a real WhatsApp phone. This guest has neither, so I could run the code locally and confirm it refuses unauthenticated requests, but I could not read any messages.",
 "suggested_fix":"Document a local development path in the README (npm run dev plus a .dev.vars password) and state clearly that the MCP token is only issued after the WhatsApp phone link is complete. Otherwise the local run can't reach any tool."}
os.makedirs(os.path.join(W,".coldpass"),exist_ok=True)
json.dump(report,open(os.path.join(W,".coldpass/report.json"),"w"),indent=2,ensure_ascii=False)
print("written")
EOF
ls -la .coldpass; <env-var>=<guest-path> python3 -I <guest-path> --validate --replay 2>&1 | tail -60; echo CHECK_EXIT=${PIPESTATUS[0]}
ss -ltnp 2>/dev/null | grep -E ':8787|wrangler' || echo "no server listening on 8787"; pgrep -af "[w]rangler|workerd" | head -3 || true; ls -la <guest-path> | head -3; test -f <guest-path> && echo "BAD root report" || echo "no root report.json"
for p in $(ps -eo pid,args | awk '$2 ~ /^(npm|sh|node|workerd)$/ && /wrangler|workerd/ {print $1}'); do echo "kill $p: $(ps -o args= -p $p | cut -c1-80)"; kill $p 2>/dev/null; done; sleep 3; ps -eo pid,args | grep -E "[w]rangler dev|[w]orkerd" | cut -c1-120 || true; echo done
cd <guest-path> && <env-var>=<guest-path> python3 -I <guest-path> --validate --replay > <guest-path> 2>&1; echo EXIT=$?; python3 -I -c "import json;d=json.load(open('<guest-path>'));print('valid',d.get('valid'),'problems',d.get('problems'),'replay',d.get('replay',{}).get('status'))"
Earlier attempts (1)

Luna

Native agent · Needs credentials · full use untested

Operation / attempted scope npm run dev -- --ip 127.0.0.1

Agent-reported scope: npm run dev local app and documented store checks

Run details
run
finereli--whatsapp-mcp-gpt-6-luna-1792809664924278168
date
Oct 23, 2026
time to working
not recorded
product hosting / execution
Local command in the guest
recorded operation scope
product operation
model
gpt-6-luna
agent / version
codex / codex-cli 0.163.0
platform
linux
reported cost
not reported (not assumed free)
Recorded source
https://github.com/finereli/whatsapp-mcp
Backend
hyperv
Tool transport
mcp-assigned-vm
Requested source pin (not an observed revision)
{"commit": "64f1b3017931e4f6fe33eec4f95999a10f839b99", "url": "https://github.com/finereli/whatsapp-mcp"}
Test version
readme-f72312298d35d84837d4fc5f6f22b138d54d456600b5ed431e36e8f5db931dbf
revision
64f1b3017931e4f6fe33eec4f95999a10f839b99

Prerequisite: full use remains untested. This is not a repo defect.

Whose fault: prerequisite · diagnosed

No linked WhatsApp account or deployment credentials.

Double-checks failed run: finereli--whatsapp-mcp-gpt-6-luna-1792798369354093824.

Recorded blocker / warning

Step: Real message operation

No linked WhatsApp account or deployment credentials.

Safety: Review signals: 1 connection review, 14 known vulnerability.

Review signals: 1 connection review, 14 known vulnerability.

Reviewed as expected for a test run: resolver address lookups that send no data.

Repository processes attempted these non-loopback connections; proxies can hide the final hostname. 3 distinct locations. 3 attributed processes.

OSV reports a known vulnerability in this dependency version. 5 distinct locations. 14 findings.

OSV scanned supported lockfiles; unpinned dependencies and unsupported formats are outside this check.

A short run cannot establish that code is safe.

Connections show IP addresses; proxies can hide the final hostname.

Tracing covers dispatched commands and their children, not the whole OS; guest root can interfere with tracing.

Full safety evidence →

Separate install, startup, tool-list and missing-credential grades were not recorded for this run.

Answers recorded for this run
  • The server lists chats, most recent first.: The README local Durable Object starts with empty storage; listing requires a phone-linked account, which is unavailable in this guest.

    Source: | `list_chats` | Chats, most recent first. Filter by name or number. |

    Observed: The README local Durable Object starts with empty storage; listing requires a phone-linked account, which is unavailable in this guest.

Examples

  • Typecheck and store behavior tests: command completed successfully

    Source: README.md, Checking a change

    Input: npm run typecheck; npm test

    Expected: Typecheck succeeds and all store tests pass.

    Command: npm run typecheck

    Observed: > [email protected] typecheck > tsc --noEmit

  • Store behavior tests: command completed successfully

    Source: README.md, Checking a change

    Input: npm test

    Expected: all passed

    Command: npm test

    Observed: comes first ok last thing THEY said, not me ok unnamed count ok save reports saved and which have chats ok named chat leaves the unnamed list ok new name shows in chat list ok sender name used in messages ok empty name removes it ok group message sender resolves to custom name ok toJid("+972 52-421-9320", undefined) ok toJid("052-421-9320", 972) ok toJid("052-421-9320", undefined) ok toJid("00972524219320", undefined) ok toJid("524219320", 972) ok toJid("<email>", undefined) ok toJid("abc", 972) ok toJid("+1 (818) 917-7834", 972) all passed

Cycles

  • Daily scheduled pull: Cloudflare account deployment and linked WhatsApp credentials unavailable

    Source: README.md

    Input: none

    Expected: pull executes

    Observed: Cloudflare account deployment and linked WhatsApp credentials unavailable

Local or hosted: both

Suggested next step (agent-reported): Deploy using the README steps, link a phone, then call list_chats and read a chat.

What the agent did

npm run dev local app and documented store checks

cd <guest-path> && pwd && rg --files -g 'README*' -g 'AGENTS.md' -g 'pyproject.toml' -g 'package.json' -g 'Dockerfile' -g '*.md' | head -80 && python3 --version && id -u && ls -la
cd <guest-path> && cat README.md && cat package.json && find src scripts -maxdepth 2 -type f -print
cd <guest-path> && node --version; npm --version; git status --short; test -f <guest-path> && echo checker-found; cat .gitignore; grep -nE 'Local development|Checking a change|Node 20|Node 22|list_chats|cron 04:00' README.md
cd <guest-path> && cat > /tmp/install-node.sh <<'EOF'
#!/bin/bash
set -e
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
sudo apt-get install -y nodejs
EOF
bash /tmp/install-node.sh
cd <guest-path> && sudo -n id; find /usr/local/bin /opt <guest-path> -maxdepth 4 -type f \( -name node -o -name npm \) -print 2>/dev/null | head -20; find .coldpass -maxdepth 2 -type f -print -exec cat {} \;
cd <guest-path> && sudo -n bash /tmp/install-node.sh && node --version && npm --version
cd <guest-path> && npm install && npm run typecheck && npm test
cd <guest-path> && sed -n '1,100p' src/index.ts; sed -n '1,100p' .coldpass/* 2>/dev/null; sed -n '1,120p' <guest-path>
cd <guest-path> && mkdir -p .coldpass && cat > .coldpass/probe.json <<'EOF'
{"kind":"cli","argv":["npm","run","dev","--","--ip","127.0.0.1"],"cwd":"<guest-path>","stays_running":true,"expected_text":"Ready on"}
EOF
cat > .coldpass/claims.json <<'EOF'
[{"kind":"claim","claim":"The server lists chats, most recent first.","source":"| `list_chats` | Chats, most recent first. Filter by name or number. |","testable":false,"reason":"The README local Durable Object starts with empty storage; listing requires a phone-linked account, which is unavailable in this guest."}]
EOF
cat > .coldpass/examples.json <<'EOF'
[{"kind":"example","claim":"Typecheck and store behavior tests","source":"README.md, Checking a change","input":"npm run typecheck; npm test","expected":"Typecheck succeeds and all store tests pass.","argv":["npm","run","typecheck"],"cwd":"<guest-path>"},{"kind":"example","claim":"Store behavior tests","source":"README.md, Checking a change","input":"npm test","expected":"all passed","argv":["npm","test"],"cwd":"<guest-path>"}]
EOF
cat > .coldpass/cycles.json <<'EOF'
{"detected":true,"checks":[{"claim":"Daily scheduled pull","source":"README.md, Shape of the thing","quote":"cron 04:00 UTC","schedule":"Daily at 04:00 UTC","method":"not triggered in guest; requires Cloudflare deployment and linked credentials","input":"none","expected":"pull executes","testable":false,"reason":"Cloudflare account deployment and linked WhatsApp credentials unavailable"}]}
EOF
cat > .coldpass/report.json <<'EOF'
{"outcome":"blocked","fault_owner":"prerequisite","operation":"npm run dev local app and documented store checks","operation_scope":"Node 22 installed; dependencies, typecheck and store tests verified. Local app replay needs server startup; actual WhatsApp read tools require a linked phone and Cloudflare deployment.","steps":["Installed Node.js 22.23.4 with NodeSource setup script and sudo apt install.","npm install","npm run typecheck","npm test"],"deviations":["Initial unprivileged NodeSource setup failed apt update with permission denied; reran using sudo successfully.","MCP read calls and scheduled pull not exercised because the guest has no linked WhatsApp account or Cloudflare deployment."],"stuck_step":"Real message operation","main_blocker":"No linked WhatsApp account or deployment credentials.","plain_failure":"The local install and store checks pass, but this fresh guest has no linked phone messages to exercise the WhatsApp tools.","suggested_fix":"Deploy using the README steps, link a phone, then call list_chats and read a chat."}
EOF
<env-var>=<guest-path> python3 -I <guest-path> --validate --replay
cd <guest-path> && cat > .coldpass/cycles.json <<'EOF'
{"detected":true,"checks":[{"claim":"Daily scheduled pull","source":"README.md","quote":"cron 04:00 UTC","schedule":"Daily at 04:00 UTC","method":"not triggered in guest; requires Cloudflare deployment and linked credentials","input":"none","expected":"pull executes","testable":false,"reason":"Cloudflare account deployment and linked WhatsApp credentials unavailable"}]}
EOF
<env-var>=<guest-path> python3 -I <guest-path> --validate --replay