Repository navigation
Releases
v2026.5.0 — security and correctness release
Ten fixes (FX-015..FX-027), every one regression-gated. First release verified end-to-end against live NotebookLM.
Breaking: tool inputs are now validated against their declared inputSchema at dispatch. Calls relying on the previous leniency will fail — most likely add_notebook without topics.
Highlights:
- Runtime schema validation at MCP dispatch (FX-015)
- Webhook HMAC secret no longer persisted or disclosed (FX-016)
- Webhook DNS-rebinding re-checked per send attempt (FX-017)
cleanup_datano longer deletes optional categories unprompted (FX-018)- Adopted
notebook.google.comafter Google's domain migration (FX-026, FX-027)
Full detail in CHANGELOG.md.
npm: @pan-sec/notebooklm-mcp@2026.5.0
v2026.4.1 — Manager Refactors + CI Fix
v2026.4.0 — Security & Reliability Hardening
v2026.3.3 — Fix bin shim and npx entry-point detection
Bug Fix
Entry-point detection broken for bin shim and npx invocations (closes #11)
The isDirectRun guard compared import.meta.url against a raw pathToFileURL(process.argv[1]). When invoked via npm install -g bin shim or npx, argv[1] is a symlink — not the real module path — so the comparison always failed, main() never ran, and the server exited cleanly with no MCP transport registered. The failure was silent (clean exit 0, no error), making it hard to diagnose.
Fix
argv[1] is now resolved through realpathSync before the comparison, canonicalising any symlink before the equality check. The guard is wrapped in a try/catch that fails closed on any path resolution error.
Diagnostic
Set NLMCP_DEBUG=true to log the exact argv[1] vs import.meta.url values when the guard gates off main() — useful for diagnosing any future silent no-start.
Workaround (no longer needed)
The node /absolute/path/dist/index.js workaround documented in #11 is no longer required. The README npx and claude mcp add invocation patterns now work as documented.
npm: @pan-sec/notebooklm-mcp@2026.3.3
Tests: 643 passing across 52 test files · tsc --noEmit clean · npm audit 0 vulnerabilities
v2026.3.1 — Security Audit Complete (all 334 issues resolved)
Security Audit Complete — All 334 Issues Resolved
This release closes the full 334-item security audit end-to-end. v2026.3.0 closed every critical, high, and medium issue. This release closes all remaining low-severity items plus the three intentionally-deferred items, and cleans internal process documents out of the repository.
By the numbers:
- Tests: 609 → 643 across 52 test files
npx tsc --noEmit— cleannpm audit— 0 vulnerabilities- npm:
@pan-sec/notebooklm-mcp@2026.3.1
Security & Validation
- Notebook ID validation centralised —
validateNotebookId()used everywhere (I107, I108) delete_documentconfirm guard — destructive tool requiresconfirm: true(I078, I331)- Auth startup transparency — effective auth state logged on startup (I118, I314)
- Optional token rotation via
NLMCP_AUTH_ROTATION_INTERVAL_HOURS(I119) - Login retry —
page.gototimeout retries once before throwing (I126) - Startup tool coverage check — phantom
TOOLS_REQUIRING_AUTHentries removed (I313) - Secrets scanner — Bearer token pattern broadened to any opaque token ≥ 20 chars (I189)
- Audit hash chain —
previousHashadvanced only after write succeeds (I228) - File lock liveness —
forceUnlocktreatsEPERMas "process alive" (I296)
Protocol & API
- All error paths include
data: nullfor consistent shape (I095, I330) - Duplicate
titlefields removed from allannotationsblocks (I041) - Empty
arguments: []removed from prompt definitions (I109) parseArraynow splits on comma and semicolon (I028)- Raw notebook URLs never leak in thrown errors (I173, I332)
- Webhook error classification — errors classified as
timeout,dns_or_connect, ornetwork; DNS failures skip retries;errorKindin all log messages (I281)
Code Quality
- Repeated notebook URL resolution deduplicated into
resolveNotebookUrl()(I093, I094) clickAddSourceandaddFileSourcesplit into focused helpers (I166, I167)findElement/waitForElementrelocated tosrc/utils/page-utils.ts(I149)- Discovery scripts relocated —
run-discovery.tsandselector-discovery.tsmoved toscripts/;npm run discover-selectorsadded (I171)
Test Coverage
- Handler smoke tests —
session-management(6),ask-question(3),notebook-creator(+3) via injected mocks; no browser required (I305) - Webhook error classification — DNS cause-chain classification and single-attempt behaviour verified (I281)
validateNotebookId,data: nullshape,delete_documentguard, sanitized throws, log rotation, RateLimiter memory bound,clampInteger(I329–I332, I302–I304)
Repository Cleanup
- Removed internal process documents (
CODEX_BATCH*.md,ISSUES.md,OUTSTANDING.md,medusa-fp-analysis.md) .gitignoreextended to cover tooling artifacts and internal document patterns
v2026.3.0 — The Security Audit Release
The Security Audit Release
We commissioned a parallel deep-audit of v2026.2.11 using four specialised AI code reviewers, each independently focused on a different attack surface: security vulnerabilities, MCP protocol correctness, architecture quality, and testing gaps and edge cases. Operating independently so their findings wouldn't influence each other, they produced a 334-item master issue list across four severity tiers.
This release resolves every high and medium issue on that list.
By the Numbers
| Metric | Before | After |
|---|---|---|
| Tests | 139 | 609 across 50 files |
| TypeScript errors | 0 | 0 (maintained) |
| npm audit vulnerabilities | 0 | 0 (maintained) |
| MCP protocol compliance | Partial | Full |
| Audit log integrity | Basic | Hash-chain verified on read |
| Concurrent write safety | ❌ | ✅ Write-locked |
| Webhook SSRF | ❌ | ✅ Blocked |
| Auth bypass vectors | 1 (forceAuth) | 0 |
Security Fixes
- Auth bypass closed —
validateToken()forceAuthpath allowed unauthenticated calls to filesystem tools (add_folder,cleanup_data,export_library). Fixed withforceValidationflag. - Auth token salt persisted — token hash salt now survives server restart; tokens no longer invalidated on every restart
- Webhook SSRF — delivery targets validated against SSRF blocklist; HMAC signing covers all delivery attempts
- Webhook delivery persistence — failed deliveries retried with exponential backoff; results persisted across restarts
- Audit log hash chain verified on read — tampered entries now detected and rejected
- Concurrent audit write lock — interleaved JSONL entries under concurrent tool calls eliminated
- Hash chain continuity across log rotation — anchor preserved on daily boundary rotation
- Per-page mutex — concurrent tool calls on the same browser page serialised to eliminate race conditions
- Login cancellation resilience — auth flow handles user cancelling the Google login dialog without corrupting state
MCP Protocol Compliance
- Response shape — all 48 tools return correct
structuredContent/isErrorshapes; error responses useisError: true; transport tags stripped before delivery; server sendsnotifications/cancelledon shutdown - Annotation correctness —
readOnlyHint,idempotentHint,destructiveHintaccurate for all 48 tools - Schema bounds — all numeric/string parameters have explicit min/max constraints (no more out-of-range inputs accepted)
Architecture
- 3,611-line
handlers.tsdecomposed into 9 domain modules with HandlerContext dependency injection — all domain functions now unit-testable without process-level mocks - Tool registry —
Map<string, ToolHandler>replaces 500-line switch/case; O(1) dispatch - Notebook creator split into dedicated
src/notebook-creation/module with typed domain errors
Test Coverage — 50 New Test Suites
Key new suites: browser-session, shared-context-manager, prompt-injection (40+ patterns), audit-logger (hash chain, tampering, rotation), mcp-auth, webhook-dispatcher, dsar-handler, compliance, notebook-library, settings-manager, cleanup-manager, file-permissions
Security-critical module coverage: mcp-auth.ts 75.7%, webhook-dispatcher.ts 71.4%, data-erasure.ts 72.0%
Selector & Browser Reliability
- Notebook name selector no longer matches search inputs (
inSearch()exclusion) - Text source flow validates the target textarea before typing; skips search/web-context textareas
- Video tile detection locale-independent; Studio panel selector resilient to class renames
Claims Aligned with Implementation
- Certificate pinning retracted — pinning implementation was removed in the audit;
NLMCP_CERT_PINNINGenv var removed; all documentation updated - PQ encryption scope — README and architecture diagram now consistent with SECURITY.md ("local at-rest, not Harvest-Now-Decrypt-Later")
- Compliance language — uses "compliance-ready architecture (controls implemented)" throughout; does not imply formal SOC2 Type II report, GDPR registration, or CSSF submission
Install
claude mcp add notebooklm -- npx @pan-sec/notebooklm-mcp@latestFull documentation: SECURITY.md • CHANGELOG.md
v2026.2.11 — UI Selector Hardening & Compliance Clarification
What's New
Fix: UI Selector Hardening
NotebookLM's DOM can change without notice. This release makes the browser automation more resilient to class renames and UI updates.
Video tile detection (video-manager.ts)
- Added mat-icon text scan as a fallback in
clickVideoTile— finds the video tile by icon exclusion (!= "table_view") rather than relying solely on the.greenCSS class - Now uses the same locale-independent pattern already used by data-table
Studio panel detection (video-manager.ts + data-table-manager.ts)
- Added
[class*='create-artifact']toensureStudioPanelOpen— panel detection no longer breaks if Google renames.create-artifact-button-container
File upload dialog (selectors.ts)
- Added 3 new fallbacks for
chooseFileButton:[class*="file-dialog-button"],button[class*="upload"][class*="trigger"],span[class*="file-dialog"] - Resilient against Dropzone.js class renames
Close dialog button (selectors.ts)
- Added US spelling
button[aria-label="Close dialog"]alongside British"Close dialogue"— survives if Google normalises to US English
Chat input (selectors.ts)
- Removed hardcoded German
aria-label="Feld für Anfragen"fallback - Replaced with locale-agnostic chain:
textarea[aria-label],textarea[class*="query"],.chat-input textarea
Docs: Compliance Language Clarification
Updated README and package.json to accurately reflect compliance-ready architecture rather than implying formal certification. All GDPR/SOC2/CSSF controls are implemented — formal certification requires a third-party audit engagement. Added a footnote to the comparison table making this distinction clear.
Full changelog: CHANGELOG.md
v2026.2.10 — 17 Security Layers, Architecture Overhaul, Secure-by-Default Auth
v2026.2.10 — The Hardening Release
17 Security Layers | 168 Tests | 9 Domain Modules | Secure-by-Default
The biggest security and architecture update since launch. Three new security layers, a complete handler architecture overhaul, and the most secure MCP server default configuration in the ecosystem.
What's New
🔒 3 New Security Layers (14 → 17)
| Layer | What it does |
|---|---|
| Secure-by-Default Auth | MCP authentication is now ON by default — zero configuration needed. Explicit opt-out via NLMCP_AUTH_DISABLED=true required to disable. No more accidentally running without auth. |
| Exponential Backoff Lockout | Failed auth attempts now trigger escalating lockouts: 5min → 15min → 45min → 4hr cap. Brute-force attacks become exponentially expensive. Lockout state persists across lockout resets. |
| Credential Isolation | Login passwords and API keys are wrapped in SecureCredential with 30-minute TTL auto-wipe. Original values are scrubbed from process.env immediately after reading. Memory dump attacks have a 30-minute window instead of forever. |
🏗️ Architecture Overhaul — Handler Split
The monolithic 3,611-line handlers.ts has been split into 9 domain modules:
src/tools/handlers/
├── ask-question.ts — Core Q&A
├── session-management.ts — Session lifecycle
├── auth.ts — Google auth flows
├── notebook-management.ts — Library CRUD
├── notebook-creation.ts — Create/batch/sync/sources
├── system.ts — Quota, cleanup, project info
├── audio-video.ts — Audio/video/data table
├── webhooks.ts — Webhook management
├── gemini.ts — Deep research, documents
├── types.ts — HandlerContext interface
└── index.ts — Facade class
The 500-line switch/case in index.ts was replaced with a tool registry Map — built once at startup, O(1) dispatch for all 48 tools.
🛡️ Filesystem Tool Gating
add_folder, cleanup_data, and export_library now require authentication even when auth is globally disabled. These tools can modify the filesystem and must never be callable without a valid token.
🔑 Token Management CLI
Lost your token? No problem:
npx notebooklm-mcp token show # Check token status
npx notebooklm-mcp token rotate # Generate new token (invalidates old)First-run token display now shows copy-pasteable commands with your actual token value.
🐳 Multi-Stage Docker Build
New 2-stage Dockerfile keeps dev dependencies out of production images. ~40-60% smaller image size. New .dockerignore prevents test files, docs, and secrets from leaking into builds.
⚡ Reliability Improvements
- Gemini API retry: Exponential backoff on 429/500/502/503 errors (3 retries, won't retry on 400/401/403/404)
- Configurable response timeout:
NLMCP_RESPONSE_TIMEOUT_MS(default: 120s) - File permission audit trail: Permission failures now logged + audited instead of silently swallowed
- Locale-agnostic selectors: Removed hardcoded German-locale browser selectors
🧪 Testing
- 57 new tests covering security utilities, config parsing, range clamping, and browser options
- 168 total tests passing across 6 test files
- Tests now run in CI pipeline (
npm teststep added)
New Environment Variables
| Variable | Default | Description |
|---|---|---|
NLMCP_AUTH_DISABLED |
false |
Explicitly disable MCP auth (not recommended) |
NLMCP_RESPONSE_TIMEOUT_MS |
120000 |
NotebookLM response timeout |
NLMCP_FOLLOW_UP_REMINDER |
(built-in) | Custom follow-up reminder text |
NLMCP_FOLLOW_UP_ENABLED |
true |
Enable/disable follow-up reminder |
Breaking Changes
- Auth is now enabled by default. If you were running without
NLMCP_AUTH_ENABLED=true, the server will auto-generate a token on first run. SetNLMCP_AUTH_DISABLED=trueif you explicitly want no auth (not recommended). CONFIG.loginPasswordis now blanked. Code that readsCONFIG.loginPassworddirectly will get an empty string. UsegetSecureLoginPassword()instead.
Security Audit
This release was validated by a 4-agent security review team:
- The Skeptic — Edge case and failure mode analysis
- The Sentinel — Red-team security audit with exploit chain analysis
- The Architect — Architecture and design review
- The Librarian — Code quality and style compliance
Two critical vulnerabilities were found and fixed during review:
forceAuthbypass when auth was globally disabled (any token accepted)- Plaintext credentials persisting in CONFIG despite SecureCredential wrapping
Full Changelog: v2026.2.9...v2026.2.10
v2026.2.9 — Fix: performSetup No Longer Destroys Auth Before Chrome Opens
Critical Bug Fix
Root Cause
performSetup called clearAllAuthData() unconditionally at startup — before Chrome even opened. If Chrome failed to launch or the user didn't complete login within the timeout, all auth credentials were already destroyed with no recovery path.
This was the final source of recurring auth loss.
Changes
- Removed
clearAllAuthData()fromperformSetup— Chrome re-login overwrites cookies without needing to delete first. For account switching, callclearAllAuthData()explicitly before invokingsetup_auth - Added stack trace logging to
clearAllAuthData()so every call is traceable in server logs with the exact caller location
Upgrade Notes
Auth is now stable across session restarts. Use auth-now.mjs in the project root for initial authentication or re-authentication.
v2026.2.8 — Fix: cleanup_data No Longer Destroys Auth Credentials
Critical Bug Fix
Root Cause
Sessions were following get_health's troubleshooting tip which said to run cleanup_data(confirm:true). This deleted browser_state/ and chrome_profile/ — the Google auth credentials — then setup_auth ran headlessly and failed to restore them.
Changes
cleanup_datano longer includesbrowser_stateorchrome_profilein any cleanup path — these contain Google auth cookies that require interactive login to recreateget_healthtroubleshooting tip updated to saysetup_auth(show_browser:true)and explicitly warns not to callcleanup_datafor auth issues