The authentication uses a persistent browser-based session (Chrome headless) to bypass brittle cookie-only auth.
AuthManager (core/auth.py): Profile-based cookie and metadata storage.BaseClient (core/base.py): API interactions + auto-refresh (CSRF/session-id).RotateCookies (core/cookie_rotation.py): Proactive refresh of Google's ephemeral *PSIDTS cookies via accounts.google.com/RotateCookies. Non-fatal, best-effort; only works when long-lived cookies are still valid.CDP Utilities (utils/cdp.py): WebSocket control of the headless Chrome process. Uses Network.getAllCookies for extraction, Runtime.evaluate for DOM token extraction.SID, HSID, SSID, APISID, SAPISID (stored in cookies.json). Persist for weeks.__Secure-1PSIDTS, SIDCC (auto-rotated via RotateCookies). Rotate hourly/daily.SNlM0e (CSRF), FdrFJe (session_id), cfb2h (build label) (auto-extracted from page HTML).check_auth() False-Negative Trapcheck_auth(live=True) and nlm login --check can report expired when the Chrome session is perfectly valid.
This happens because:
- The CLI stores cookies in ~/.notebooklm-mcp-cli/profiles/default/cookies.json
- Chrome's own cookie store is in ~/.notebooklm-mcp-cli/chrome-profiles/default/Default/Cookies (SQLite DB)
- These are separate files. Chrome auto-refreshes short-lived cookies in its SQLite store; the CLI's JSON file does not get that update unless cookie extraction via CDP is explicitly triggered.
- When check_auth() fetches https://notebooklm.google.com/ with the CLI's stale cookies, Google may redirect to accounts.google.com → false-negative.
Solution: Always extract cookies from the running Chrome instance first:
.venv/bin/python scripts/notebooklm_refresh.py --port 9223
This uses CDP Network.getAllCookies to get Chrome's actual live cookies, then writes them into the CLI profile. Only if this fails should you attempt revive or re-login.
~/.notebooklm-mcp-cli/
├── auth.json ← Legacy flat cache (auto-migrated to profile)
├── config.toml ← CLI config (auth.browser, default_profile)
├── chrome-port-map.json ← Maps CDP ports → profile names
├── profiles/
│ └── default/
│ ├── cookies.json ← Cookie dict (can be 50+ cookies)
│ └── metadata.json ← csrf_token, session_id, email, build_label, last_validated
└── chrome-profiles/
└── default/ ← Chrome user-data-dir (real browser profile)
├── Default/Cookies ← SQLite cookie DB (36KB+)
├── Default/Login Data ← Saved logins
└── ... (SingletonLock, Cache, etc.)
rm -f SingletonLock) before relaunching.--remote-allow-origins=*.nlm login will try to launch another on 9222 and fail. Use --cdp-url to point to the existing instance.google-chrome refuses to start as root without --no-sandbox.notebooklm_refresh.py imports from the local src/ tree and MUST run via .venv/bin/python.chrome-port-map.json persists old port→PID entries. After VPS restart or Chrome crash, clean entries for dead PIDs or the CLI may try to reuse a defunct port.notebooklm_refresh.py --port <port> from running Chrome. Probe ports 9222–9232 if unsure which port Chrome is on.revive_notebooklm_auth.sh kills stale processes, removes SingletonLock, relaunches Chrome, extracts cookies, verifies auth.chrome://inspect + manual login via local browser. Always follow with refresh script to sync CLI profile.| Script | Purpose |
|---|---|
notebooklm_refresh.py |
Extract cookies from running Chrome CDP → CLI profile. --port flag to specify CDP port. |
revive_notebooklm_auth.sh |
Kill stale Chrome → remove lock → relaunch → extract → verify. One-shot fix. |
notebooklm_auth_watchdog.sh |
Cron-friendly: check auth via nlm login --check, refresh if dead, exit silently if OK. |