{
  "run_id": "finereli--whatsapp-mcp-claude-haiku-5-5-1792785224818035339",
  "date": "2026-10-23T20:05:07.920189+00:00",
  "repo": "finereli/whatsapp-mcp",
  "kind": "mcp",
  "source": "https://github.com/finereli/whatsapp-mcp",
  "commit": "64f1b3017931e4f6fe33eec4f95999a10f839b99",
  "model": "claude-haiku-5-5",
  "model_version": "claude-haiku-5-5",
  "test_version": "readme-bdc1b9b3c0d2673aef87e076d14084a8123ddcf09d520c1ee2e364a37c3f4bb9",
  "pass": null,
  "outcome": "needs_credentials",
  "time_to_working_s": null,
  "total_s": 683.1021301746368,
  "steps": [
    "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'\nimport base64, hashlib, html, http.cookiejar, json, os, re, secrets, sys, urllib.parse, urllib.request\n\nBASE = \"http://127.0.0.1:8787\"\nREDIRECT = \"http://127.0.0.1:9999/cb\"\nPW = None\nfor line in open(\"<guest-path>\"):\n    if line.startswith(\"OWNER_PASSWORD=\"):\n        PW = line.strip().split(\"=\", 1)[1]\n\njar = http.cookiejar.CookieJar()\n\nclass NoRedirect(urllib.request.HTTPRedirectHandler):\n    def redirect_request(self, *a, **k):\n        return None\n\nopener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar), NoRedirect)\n\ndef req(url, data=None, headers=None, method=None):\n    r = urllib.request.Request(url, data=data, headers=headers or {}, method=method)\n    try:\n        resp = opener.open(r)\n        return resp.status, dict(resp.headers), resp.read().decode()\n    except urllib.error.HTTPError as e:\n        return e.code, dict(e.headers), e.read().decode()\n\nclient_id = json.loads(open(\"<guest-path>\").read())[\"client_id\"]\nverifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode().rstrip(\"=\")\nchallenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).decode().rstrip(\"=\")\nq = urllib.parse.urlencode({\"response_type\": \"code\", \"client_id\": client_id, \"redirect_uri\": REDIRECT,\n    \"code_challenge\": challenge, \"code_challenge_method\": \"S256\", \"state\": \"s1\", \"scope\": \"whatsapp\"})\nst, h, body = req(f\"{BASE}/authorize?{q}\")\nprint(\"authorize\", st, file=sys.stderr)\nm = re.search(r'<form[^>]*action=\"([^\"]+)\"', body)\naction = html.unescape(m.group(1))\nif action.startswith(\"/\"):\n    action = BASE + action\nprint(\"signin action\", action[:120], file=sys.stderr)\nst, h, body = req(action)\nprint(\"signin GET\", st, file=sys.stderr)\nform = urllib.parse.urlencode({\"password\": <secret>}).encode()\nst, h, body = req(action, data=form, headers={\"content-type\": \"application/x-www-form-urlencoded\"})\nprint(\"password POST\", st, h.get(\"Location\", \"\")[:120], file=sys.stderr)\nloc = h.get(\"Location\")\nif not loc:\n    sys.exit(\"no redirect after password: \" + body[:300])\n# follow redirects manually until we reach the client redirect with code\nfor _ in range(6):\n    if loc.startswith(REDIRECT):\n        break\n    if loc.startswith(\"/\"):\n        loc = BASE + loc\n    st, h, body = req(loc)\n    loc = h.get(\"Location\")\n    if not loc:\n        sys.exit(\"chain ended: %s %s\" % (st, body[:300]))\nqs = urllib.parse.parse_qs(urllib.parse.urlparse(loc).query)\ncode = qs[\"code\"][0]\ntok_body = urllib.parse.urlencode({\"grant_type\": \"authorization_code\", \"code\": code, \"code_verifier\": verifier,\n    \"client_id\": client_id, \"redirect_uri\": REDIRECT}).encode()\nst, h, body = req(f\"{BASE}/token\", data=tok_body, headers={\"content-type\": \"application/x-www-form-urlencoded\"})\nprint(\"token\", st, file=sys.stderr)\ntok = json.loads(body)\nprint(tok[\"access_token\"])\nEOF\ncd <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'\nimport json, os\nW=\"<guest-path>\"\nREADME=open(os.path.join(W,\"README.md\"),encoding=\"utf-8\").read()\nq1=\"It can't send anything.\"\nq2=\"You need a Cloudflare account on the Workers Paid plan (see the note below), Node 20 or later, and your phone.\"\nq3=\"Every read tool first tops up from WhatsApp if the stored copy is more than ten minutes old.\"\nfor q in (q1,q2,q3): print(q in README, q)\nclaims=[\n {\"kind\":\"claim\",\"claim\":\"The server is read-only and cannot send WhatsApp messages\",\n  \"source\":q1,\"input\":\"none (no send tool in the MCP tool list; no authenticated call possible here)\",\n  \"expected\":\"cannot be checked without an authenticated MCP session\",\n  \"testable\":False,\n  \"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.\"},\n {\"kind\":\"claim\",\"claim\":\"Setup needs a Cloudflare Workers Paid account, Node 20+, and a phone\",\n  \"source\":q2,\"input\":\"none\",\n  \"expected\":\"deploy and link with a real account and phone\",\n  \"testable\":False,\n  \"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.\"},\n {\"kind\":\"claim\",\"claim\":\"Read tools top up from WhatsApp when the stored copy is older than ten minutes\",\n  \"source\":q3,\"input\":\"none\",\n  \"expected\":\"a read tool returns messages after a pull\",\n  \"testable\":False,\n  \"reason\":\"Needs a linked WhatsApp account and an authenticated MCP session; both require a phone scan and a Cloudflare deployment, neither available here.\"},\n]\njson.dump(claims,open(os.path.join(W,\".coldpass/claims.json\"),\"w\"),indent=2,ensure_ascii=False)\nprobe={\"checks\":[{\"check\":\"credentials_error\",\"kind\":\"mcp_http\",\n  \"argv\":[\"npm\",\"run\",\"dev\",\"--\",\"--port\",\"8787\",\"--ip\",\"127.0.0.1\"],\n  \"url\":\"http://127.0.0.1:8787/mcp\",\"cwd\":\"<guest-path>\"}]}\njson.dump(probe,open(os.path.join(W,\".coldpass/probe.json\"),\"w\"),indent=2)\nexamples=[{\"kind\":\"example\",\"claim\":\"The repo's own offline test suite passes\",\n  \"source\":\"package.json scripts.test\",\n  \"input\":\"none\",\"expected\":\"all tests pass\",\"argv\":[\"npm\",\"test\"],\n  \"cwd\":\"<guest-path>\",\"env\":{},\"expected_text\":\"all passed\"}]\njson.dump(examples,open(os.path.join(W,\".coldpass/examples.json\"),\"w\"),indent=2)\ncycles={\"detected\":True,\"checks\":[{\"claim\":\"Daily pull of WhatsApp messages at 04:00 UTC\",\n  \"source\":\"wrangler.jsonc\",\n  \"quote\":\"\\\"triggers\\\": { \\\"crons\\\": [\\\"0 4 * * *\\\"] }\",\n  \"schedule\":\"cron 0 4 * * * (04:00 UTC daily)\",\n  \"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\",\n  \"input\":\"none\",\"expected\":\"a daily pull of stored messages\",\n  \"reason\":\"Missing prerequisite: the account must be linked to WhatsApp before the cron pulls anything; no Cloudflare deployment and no phone here.\"}]}\njson.dump(cycles,open(os.path.join(W,\".coldpass/cycles.json\"),\"w\"),indent=2,ensure_ascii=False)\nreport={\"outcome\":\"needs_credentials\",\"fault_owner\":\"prerequisite\",\n \"operation\":\"Run the whatsapp-mcp server and have an MCP client read WhatsApp messages through its tools\",\n \"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.\",\n \"steps\":[\"Install Node 20+ (Ubuntu apt nodejs is 18; installed Node 22 via npm i -g n && n 22)\",\n   \"npm install (includes scripts/vendor-bridge.mjs, postinstall: OK)\",\n   \"npm test (offline suite: all passed)\",\n   \"npm run typecheck (tsc --noEmit: OK)\",\n   \"npx wrangler kv namespace create OAUTH_KV (not run: needs Cloudflare login)\",\n   \"npx wrangler secret put OWNER_PASSWORD (not run: needs Cloudflare login)\",\n   \"npm run deploy (not run: needs Cloudflare account)\",\n   \"Open deployed /, link WhatsApp by QR (not run: needs phone)\",\n   \"claude mcp add --transport http whatsapp <url>/mcp (not run: no deployed URL)\"],\n \"deviations\":[\"apt nodejs 18.19 failed the Baileys and wrangler engine checks; installed Node 22.23.4 with npm i -g n\",\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\",\n   \"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).\",\n   \"No browser used; no external services contacted except the local dev server.\"],\n \"stuck_step\":\"Deploy to Cloudflare and link WhatsApp (README 'Set it up', steps 1-3 and 'Link WhatsApp')\",\n \"main_blocker\":\"No Cloudflare account and no phone for the WhatsApp QR link in this disposable guest.\",\n \"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.\",\n \"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.\"}\nos.makedirs(os.path.join(W,\".coldpass\"),exist_ok=True)\njson.dump(report,open(os.path.join(W,\".coldpass/report.json\"),\"w\"),indent=2,ensure_ascii=False)\nprint(\"written\")\nEOF\nls -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'))\""
  ],
  "stuck_step": "Deploy to Cloudflare and link WhatsApp (README 'Set it up', steps 1-3 and 'Link WhatsApp')",
  "error_excerpt": "No Cloudflare account and no phone for the WhatsApp QR link in this disposable guest.",
  "what_it_did": "Run the whatsapp-mcp server and have an MCP client read WhatsApp messages through its tools",
  "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.",
  "tokens_in": 759431,
  "tokens_out": 23662,
  "cost_usd": 0.0301595,
  "usage_complete": true,
  "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
    }
  ],
  "safety": {
    "version": "z2x-1",
    "rules_version": "z2x-1",
    "summary": "Review signals: 1 connection review, 6 known vulnerability.",
    "expected": "Reviewed as expected for a test run: resolver address lookups that send no data.",
    "findings": [
      {
        "kind": "connection-review",
        "count": 1,
        "note": "Repository processes attempted these non-loopback connections; proxies can hide the final hostname.",
        "location_count": 1,
        "path": "104.18.2.57:443",
        "process_count": 3
      },
      {
        "kind": "known-vulnerability",
        "count": 6,
        "note": "OSV reports a known vulnerability in this dependency version.",
        "location_count": 2
      }
    ],
    "context": [],
    "limitations": [
      "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."
    ],
    "static": {
      "dependency_note": "OSV scanned supported lockfiles; unpinned dependencies and unsupported formats are outside this check."
    },
    "evidence_url": "/safety/?run=finereli--whatsapp-mcp-claude-haiku-5-5-1792785224818035339",
    "evidence_file": "/data/safety/finereli--whatsapp-mcp-claude-haiku-5-5-1792785224818035339.json.gz"
  },
  "agent_execution": "native_agent",
  "agent_runtime": "claude-code",
  "agent_runtime_version": "2.1.300 (Claude Code)",
  "tool_transport": "mcp-assigned-vm",
  "platform": "linux",
  "reasoning": "low",
  "service_tier": null,
  "resources": {
    "cpus": 2,
    "memory_mib": 4096,
    "disk_mib": 32768
  },
  "native_tool_calls": 25,
  "native_bundle": null,
  "architecture": null,
  "backend": "hyperv",
  "platform_details": {
    "backend": "hyperv",
    "os": "linux"
  },
  "platform_conflicts": null,
  "sandbox_source": null,
  "source_pin": {
    "commit": "64f1b3017931e4f6fe33eec4f95999a10f839b99",
    "url": "https://github.com/finereli/whatsapp-mcp"
  },
  "purpose": "initial",
  "profile": {
    "model": "claude-haiku-5-5",
    "reasoning": "low"
  },
  "fault": {
    "owner": "prerequisite",
    "status": "diagnosed",
    "reason": "No Cloudflare account and no phone for the WhatsApp QR link in this disposable guest."
  },
  "fix_id": null,
  "verifies_run_id": null,
  "hosting": null,
  "operation_scope": "partial checks",
  "tool_calls": 25,
  "guest_setup": null,
  "runtime": null,
  "runtime_version": null,
  "runtime_revision": null,
  "source_revision": null,
  "evidence_version": null,
  "failure_origin": null,
  "evidence_strength": "nothing_checked",
  "works": "partly",
  "install_s": null,
  "first_use_s": null,
  "mcp": "unknown",
  "needs": [
    "credentials"
  ],
  "needs_which": "an account or key",
  "agent_interface": [],
  "license": "MIT",
  "repo_url": "https://github.com/finereli/whatsapp-mcp",
  "cost_to_try": "needs an account or key",
  "hardware": null,
  "what_failed": "No Cloudflare account and no phone for the WhatsApp QR link in this disposable guest.",
  "local_or_hosted": "both",
  "cycle_checks": [
    {
      "kind": "cycle",
      "claim": "Daily pull of WhatsApp messages at 04:00 UTC",
      "source": "wrangler.jsonc",
      "input": "none",
      "expected": "a daily pull of stored messages",
      "command": "",
      "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",
      "verdict": "blocked",
      "outcome": "Missing prerequisite: the account must be linked to WhatsApp before the cron pulls anything; no Cloudflare deployment and no phone here.",
      "evidence": "Missing prerequisite: the account must be linked to WhatsApp before the cron pulls anything; no Cloudflare deployment and no phone here."
    }
  ],
  "claims": [
    {
      "kind": "claim",
      "claim": "The server is read-only and cannot send WhatsApp messages",
      "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",
      "command": "",
      "verdict": "blocked",
      "outcome": "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.",
      "evidence": "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_checked": true,
      "source_file": "README.md"
    },
    {
      "kind": "claim",
      "claim": "Setup needs a Cloudflare Workers Paid account, Node 20+, and a phone",
      "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",
      "command": "",
      "verdict": "blocked",
      "outcome": "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.",
      "evidence": "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_checked": true,
      "source_file": "README.md"
    },
    {
      "kind": "claim",
      "claim": "Read tools top up from WhatsApp when the stored copy is older than ten minutes",
      "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",
      "command": "",
      "verdict": "blocked",
      "outcome": "Needs a linked WhatsApp account and an authenticated MCP session; both require a phone scan and a Cloudflare deployment, neither available here.",
      "evidence": "Needs a linked WhatsApp account and an authenticated MCP session; both require a phone scan and a Cloudflare deployment, neither available here.",
      "source_checked": true,
      "source_file": "README.md"
    }
  ],
  "claims_note": null,
  "example_checks": [
    {
      "kind": "example",
      "claim": "The repo's own offline test suite passes",
      "source": "package.json scripts.test",
      "input": "none",
      "command": "npm test",
      "evidence": " comes first\nok   last thing THEY said, not me\nok   unnamed count\nok   save reports saved and which have chats\nok   named chat leaves the unnamed list\nok   new name shows in chat list\nok   sender name used in messages\nok   empty name removes it\nok   group message sender resolves to custom name\nok   toJid(\"+972 52-421-9320\", undefined)\nok   toJid(\"052-421-9320\", 972)\nok   toJid(\"052-421-9320\", undefined)\nok   toJid(\"00972524219320\", undefined)\nok   toJid(\"524219320\", 972)\nok   toJid(\"<email>\", undefined)\nok   toJid(\"abc\", 972)\nok   toJid(\"+1 (818) 917-7834\", 972)\n\nall passed",
      "deferred": false,
      "expected": "all tests pass",
      "returncode": 0,
      "verdict": "yes",
      "outcome": "command completed successfully"
    }
  ],
  "does_what_it_says": null,
  "maintenance": {
    "last_commit_at": "2026-10-18T22:20:41Z",
    "commits_90d": 6,
    "merged_prs_90d": 0,
    "open_issues": 1,
    "open_prs": 1,
    "reply_share": null,
    "reply_threads": 0,
    "median_reply_hours": null,
    "last_release_at": null,
    "archived": false,
    "verdict": "maintained",
    "note": "actively maintained: last commit 4 days ago; 6 commits and 0 merged PRs in 90 days; 1 open issues, 1 open PRs; no releases"
  },
  "maintenance_verdict": "maintained",
  "maintenance_note": "actively maintained: last commit 4 days ago; 6 commits and 0 merged PRs in 90 days; 1 open issues, 1 open PRs; no releases",
  "maintained": "active",
  "maintained_note": "last commit 4 days ago; 1 open issues, 0 answered or closed",
  "last_commit_at": "2026-10-19T17:08:34Z",
  "last_release_at": null,
  "open_issues": 1,
  "answered_issues": 0,
  "platforms_claimed": [
    "linux"
  ],
  "topic": "mcp-server",
  "agent_interfaces": [],
  "agent_interfaces_note": "README metadata is a discovery hint, not pinned documentation proof. Observed interfaces were exercised in this run. An empty list means unknown.",
  "mcp_context": null,
  "tested_at": "2026-10-23T20:05:07.920189+00:00",
  "tested_commit": "64f1b3017931e4f6fe33eec4f95999a10f839b99",
  "tested_with": {
    "agent": "claude-code",
    "agent_version": "2.1.300 (Claude Code)",
    "model": "claude-haiku-5-5",
    "model_route": "claude-haiku-5-5",
    "reasoning": "low",
    "os": "linux",
    "backend": "hyperv",
    "transport": "mcp-assigned-vm"
  },
  "setup_identity": {
    "repo": "finereli/whatsapp-mcp",
    "source": "https://github.com/finereli/whatsapp-mcp",
    "commit": "64f1b3017931e4f6fe33eec4f95999a10f839b99",
    "source_revision": null,
    "os": "linux",
    "architecture": null,
    "backend": "hyperv",
    "model": "claude-haiku-5-5",
    "model_version": "claude-haiku-5-5",
    "agent_execution": "native_agent",
    "agent_runtime": "claude-code",
    "agent_runtime_version": "2.1.300 (Claude Code)",
    "tool_transport": "mcp-assigned-vm",
    "runtime": null,
    "runtime_version": null,
    "runtime_revision": null,
    "sandbox_source": null,
    "guest_setup": null,
    "source_pin": {
      "commit": "64f1b3017931e4f6fe33eec4f95999a10f839b99",
      "url": "https://github.com/finereli/whatsapp-mcp"
    },
    "platform_details": {
      "os": "linux",
      "backend": "hyperv"
    },
    "platform_conflicts": null,
    "reasoning": "low",
    "service_tier": null,
    "resources": {
      "cpus": 2,
      "memory_mib": 4096,
      "disk_mib": 32768
    },
    "operation": "Run the whatsapp-mcp server and have an MCP client read WhatsApp messages through its tools",
    "operation_scope": "partial checks",
    "hosting": null,
    "kind": "mcp",
    "test_version": "readme-bdc1b9b3c0d2673aef87e076d14084a8123ddcf09d520c1ee2e364a37c3f4bb9",
    "evidence_version": null
  },
  "other_model": null,
  "other_run_id": null,
  "models_agree": null,
  "furthest_model": null,
  "transcript_url": null,
  "report_url": "/reports/finereli--whatsapp-mcp-claude-haiku-5-5-1792785224818035339.json"
}
