Skip to content

Releases

v2026.5.0 — security and correctness release

Choose a tag to compare

@rosschurchill rosschurchill released this 03 Sep 06:15

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_data no longer deletes optional categories unprompted (FX-018)
  • Adopted notebook.google.com after 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

Choose a tag to compare

@rosschurchill rosschurchill released this 01 Jun 06:22

v2026.4.0 — Security & Reliability Hardening

Choose a tag to compare

@rosschurchill rosschurchill released this 01 Jun 05:18

v2026.3.3 — Fix bin shim and npx entry-point detection

Choose a tag to compare

@rosschurchill rosschurchill released this 11 May 07:10

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)

Choose a tag to compare

@rosschurchill rosschurchill released this 25 Apr 14:38

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:


Security & Validation

  • Notebook ID validation centralised — validateNotebookId() used everywhere (I107, I108)
  • delete_document confirm guard — destructive tool requires confirm: 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.goto timeout retries once before throwing (I126)
  • Startup tool coverage check — phantom TOOLS_REQUIRING_AUTH entries removed (I313)
  • Secrets scanner — Bearer token pattern broadened to any opaque token ≥ 20 chars (I189)
  • Audit hash chain — previousHash advanced only after write succeeds (I228)
  • File lock liveness — forceUnlock treats EPERM as "process alive" (I296)

Protocol & API

  • All error paths include data: null for consistent shape (I095, I330)
  • Duplicate title fields removed from all annotations blocks (I041)
  • Empty arguments: [] removed from prompt definitions (I109)
  • parseArray now 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, or network; DNS failures skip retries; errorKind in all log messages (I281)

Code Quality

  • Repeated notebook URL resolution deduplicated into resolveNotebookUrl() (I093, I094)
  • clickAddSource and addFileSource split into focused helpers (I166, I167)
  • findElement/waitForElement relocated to src/utils/page-utils.ts (I149)
  • Discovery scripts relocated — run-discovery.ts and selector-discovery.ts moved to scripts/; npm run discover-selectors added (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: null shape, delete_document guard, 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)
  • .gitignore extended to cover tooling artifacts and internal document patterns

v2026.3.0 — The Security Audit Release

Choose a tag to compare

@rosschurchill rosschurchill released this 25 Apr 10:23

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() forceAuth path allowed unauthenticated calls to filesystem tools (add_folder, cleanup_data, export_library). Fixed with forceValidation flag.
  • 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/isError shapes; error responses use isError: true; transport tags stripped before delivery; server sends notifications/cancelled on shutdown
  • Annotation correctness — readOnlyHint, idempotentHint, destructiveHint accurate 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.ts decomposed 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_PINNING env 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@latest

Full documentation: SECURITY.md • CHANGELOG.md

v2026.2.11 — UI Selector Hardening & Compliance Clarification

Choose a tag to compare

@rosschurchill rosschurchill released this 28 Mar 18:21

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 .green CSS 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'] to ensureStudioPanelOpen — 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

Choose a tag to compare

@rosschurchill rosschurchill released this 15 Mar 10:29

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 test step 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. Set NLMCP_AUTH_DISABLED=true if you explicitly want no auth (not recommended).
  • CONFIG.loginPassword is now blanked. Code that reads CONFIG.loginPassword directly will get an empty string. Use getSecureLoginPassword() 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:

  1. forceAuth bypass when auth was globally disabled (any token accepted)
  2. 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

Choose a tag to compare

@rosschurchill rosschurchill released this 01 Mar 16:14

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() from performSetup — Chrome re-login overwrites cookies without needing to delete first. For account switching, call clearAllAuthData() explicitly before invoking setup_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

Choose a tag to compare

@rosschurchill rosschurchill released this 01 Mar 16:14

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_data no longer includes browser_state or chrome_profile in any cleanup path — these contain Google auth cookies that require interactive login to recreate
  • get_health troubleshooting tip updated to say setup_auth(show_browser:true) and explicitly warns not to call cleanup_data for auth issues