Command Palette

Search for a command to run...

UnylyUnyly
Browse all

NotebookLM Curator

FreeNot checked

An MCP server that audits and prunes Google NotebookLM libraries, enabling source listing, removal, and freshness-based audits through browser automation.

GitHubEmbed

About

An MCP server that audits and prunes Google NotebookLM libraries, enabling source listing, removal, and freshness-based audits through browser automation.

README

An MCP server that audits and prunes Gemini Notebook (formerly NotebookLM) libraries.

Not an official Google or Anthropic integration. This drives NotebookLM through your own signed-in Chrome profile — it is not affiliated with, endorsed by, or connected to Google in any way. Consider using a separate Google account rather than your primary one; the account parameter supports multiple profiles. See Known limitations for the full picture before pointing it at anything you care about.

Off-the-shelf NotebookLM MCPs give you add_source and ask_question. None of them give you these three:

Tool What it does In other NotebookLM MCPs
nlm_list_sources Lists the sources in a notebook ✗ missing
nlm_remove_source Deletes a source ✗ missing
nlm_audit Flags stale sources by shelf life ✗ missing

Also included: nlm_auth, nlm_list_notebooks, nlm_create_notebook, nlm_rename_notebook, nlm_add_source, nlm_ask.

nlm_audit output inside Claude Desktop

(Titles above are representative examples; the counts reflect a real run against a 56-source notebook.)


Why browser automation

NotebookLM has no public API on the consumer side. Source lists, publish dates, and deletion are not reachable over HTTP. The Gemini Notebook Enterprise API on Google Cloud is a separate product that needs an enterprise account.

So the only way in is a persistent Chrome profile. Every selector lives in one file, src/notebooklm.js — when Google ships a UI change, that's the only file that needs an update.

Selectors were verified live against notebooklm.google.com on 2026-07-29.


Requirements

  • Node.js 20+
  • Google Chrome installed (Windows or macOS — the standard desktop app from google.com/chrome). This tool automates your real Chrome install on purpose, not a downloaded Chromium build: patchright's anti-detection patches are far more effective against NotebookLM's bot checks when running inside an actual Chrome, so we don't trade that away for a "works anywhere" default. If Chrome isn't found, nlm_auth/any tool call fails immediately with a clear message instead of a cryptic browser-launch error. Because real Chrome is used by default, npm install does not download a separate Chromium build — nothing extra to fetch, nothing extra on disk. (If you deliberately opt into NLM_BROWSER_CHANNEL=chromium, run npx patchright install chromium once yourself first.)
  • Tested on Windows and macOS. Linux is not a current target.

Setup

git clone https://github.com/furkancakmakcreative/notebooklm-curator.git
cd notebooklm-curator
npm install

Copy .env.example to .env if you want nlm_audit's YouTube date resolution:

YOUTUBE_API_KEY=your_own_key

The key stays on your machine. The server only sends it to googleapis.com and never logs or forwards it elsewhere.

Connecting to Claude Desktop / Claude Code

%APPDATA%\Claude\claude_desktop_config.json (Windows) or the equivalent config on macOS/Linux:

{
  "mcpServers": {
    "notebooklm-curator": {
      "command": "node",
      "args": ["/full/path/to/notebooklm-curator/src/index.js"],
      "env": { "YOUTUBE_API_KEY": "your_own_key" }
    }
  }
}

Fully restart Claude Desktop. On first use, call nlm_auth: a visible Chrome window opens, you sign in with your Google account once, and the session is saved to a persistent local profile. Every run after that is headless.


Getting a YouTube API key

Google Cloud Console → APIs & Services

  1. Library → search "YouTube Data API v3" → Enable
  2. CredentialsCreate credentialsAPI key
  3. Click the key → API restrictionsRestrict key → select only YouTube Data API v3 → Save

Don't skip step 3. If a restricted key ever leaks, it can only read public YouTube data — it can't touch your account or spend money.

Quota — this matters

The free daily quota is 10,000 units:

  • videos.list1 unit per call (up to 50 videos)
  • search.list100 units per call (one title)

NotebookLM never exposes a source's URL, so the first audit has to search by title: 83 sources ≈ 8,300 units, most of the daily quota. But that search returns a videoId. Save it and pass it back to nlm_audit as knownIds — every later audit then uses the cheap videos.list path, ~2 units total.

The searchBudget parameter caps this cost (default 60).


Shelf-life policy

A single global "40 days" threshold gets the wrong answer both ways: a model announcement is dead in three weeks; a typography video is still useful in three years. Shelf life is therefore a function of category:

Category Days What lands here
news 30 announcements, release notes, weekly roundups
tactics 45 rate limits, model ranking/picking advice tied to a moving target
tool 60 tool usage, workflow tied to a specific release
official 150 Anthropic/Claude official product-feature videos
tutorial 120 courses, walkthroughs, technical deep-dives
principle 1095 theory, strategy, timeless craft

nlm_audit guesses a category from the title heuristically; override any threshold with the categories parameter:

{ "notebookId": "...", "categories": { "news": 14, "tool": 45 } }

Status values: freshaging (75% of shelf life burned) → stale. Sources whose date can't be resolved are unknown — a date is never guessed.

By default the response only gives you counts for fresh/pinned sources, not their full per-item detail — you rarely need to see the sources that need no action. Pass includeFresh: true to get everything, e.g. for a full-library export.


Delete safety

nlm_remove_source is irreversible and refuses to run without confirm: true. nlm_audit never deletes anything — it only produces a report.

Intended flow: nlm_audit → show the list to the user → call nlm_remove_source one title at a time for what they approve. The model bulk-deleting on its own is blocked by design.


How nlm_ask actually detects "the answer is done"

This turned out to be the hardest part of the tool, worth documenting because the wrong approach looks like it works until it silently doesn't.

The first instinct is to poll document.querySelector('main').innerText until it stops changing. That's wrong: NotebookLM's chat panel renders outside <main> entirely. The text there never changes, so the polling loop reports "stable" almost instantly and returns whatever happens to be sitting in <main> at that moment — in one observed case, hidden emoji-picker markup that had nothing to do with the question asked.

The actual reliable signal is a .thinking-message element (it carries an is-changing class while streaming) detaching from the DOM once the model finishes. Even then, the query textarea's disabled attribute clears slightly after that detachment — firing the next question too early hits a still-disabled box and hangs. ask() in src/notebooklm.js waits for both, in order, before reading the last .chat-message-pair's answer text.

If you're extending this tool and NotebookLM's DOM changes again, that's the one thing worth re-verifying live before touching anything else. If a wait times out, nlm_ask sets incomplete: true on its response rather than silently returning stale or partial text as if it were final.

nlm_ask also enforces a minimum gap (default 4s, NLM_MIN_ASK_INTERVAL_MS) between question submissions. Firing questions back-to-back is a pattern real usage never produces, and it's the most plausible trigger for NotebookLM occasionally refusing to answer ("Şu anda yanıt vermekte zorlanıyorum") — this is cheap insurance against that, not a confirmed root cause.


Known limitations

  • Source URLs are not readable. NotebookLM never puts them in the DOM — no href, no data attribute, clicking a row doesn't reveal one either. Titles are used as the identifier instead, so two sources with an identical title can't be told apart (findDuplicates reports these separately).
  • The delete confirmation dialog may or may not appear depending on rollout; the code handles both and verifies the source count afterward. removeSource re-checks the target row's title immediately before acting on it, but a full guarantee against the list reordering mid-click isn't possible with index-based targeting alone.
  • UI text matching is English/Turkish only. Menu items, buttons, and the category-guessing heuristics in policy.js match against those two languages; a notebook in a third UI language may fail to categorize or to find the "remove" menu item.
  • Freshness dates only resolve for YouTube sources today. Web pages and PDFs always come back unknown — there's no per-page date extraction yet.
  • Fixed waitForTimeout calls are used in a few places instead of polling for a DOM signal; on a slow connection they can under-wait, on a fast one they add latency. ask() uses the more robust polling pattern — anything ported from removeSource/addSource should follow that model instead.
  • If Google changes the UI, src/notebooklm.js is the only file that needs updating.
  • renameNotebook and addSource report success once the DOM action is triggered, without re-reading the page to confirm it actually applied (unlike removeSource, which re-verifies). On a slow/flaky page a rename or add could silently no-op.
  • The Chrome profile directory is created with mode: 0o700, which is a no-op on Windows NTFS (no ACL is set) — on Windows the directory's permissions are whatever the OS default is for your user folder, not actually restricted to your account alone.

Roadmap

Ideas that didn't make v0.1, roughly in order of value:

  • Export an nlm_audit report to Markdown/CSV for offline review.
  • A batch-delete tool that takes a pre-approved list of {title, occurrence} pairs (still gated by an explicit human-approved list, not autonomous).
  • Cross-notebook source search/duplicate detection (findDuplicates already generalizes to this — it just isn't wired up across notebooks yet).
  • Date resolution for web sources via Last-Modified headers or article:published_time / JSON-LD datePublished metadata.

Contributions on any of these are welcome.

License

MIT

from github.com/furkancakmakcreative/notebooklm-curator

Installing NotebookLM Curator

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/furkancakmakcreative/notebooklm-curator

FAQ

Is NotebookLM Curator MCP free?

Yes, NotebookLM Curator MCP is free — one-click install via Unyly at no cost.

Does NotebookLM Curator need an API key?

No, NotebookLM Curator runs without API keys or environment variables.

Is NotebookLM Curator hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install NotebookLM Curator in Claude Desktop, Claude Code or Cursor?

Open NotebookLM Curator on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare NotebookLM Curator with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All browse MCPs