npx skills add ...
npx skills add magicseek/nblm --skill nblm
Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses.
npx skills add magicseek/nblm --skill nblm
Query Google NotebookLM for source-grounded, citation-backed answers.
All dependencies and authentication are handled automatically by run.py:
.venv and installs Python/Node.js dependencies/nblm <command> [args]
| Command | Description |
|---|---|
login | Authenticate with Google |
status | Show auth and library status |
accounts | List all Google accounts |
accounts add | Add a new Google account |
accounts switch <id> | Switch active account (by index or email) |
accounts remove <id> | Remove a Google account |
accounts use <id> | Set agent-specific active account (OpenClaw isolation) |
accounts clear | Clear agent-specific account override |
local | List notebooks in local library |
remote | List all notebooks from NotebookLM API |
create <name> | Create a new notebook |
delete [--id ID] | Delete a notebook |
rename <name> [--id ID] | Rename a notebook |
summary [--id ID] | Get AI-generated summary |
describe [--id ID] | Get description and suggested topics |
add <url-or-id> | Add notebook to local library (auto-detects URL vs notebook ID) |
activate <id> | Set active notebook |
| Command | Description |
|---|---|
sources [--id ID] | List sources in notebook |
upload <file> | Upload a single file |
upload <folder> | Sync a folder of files to NotebookLM |
upload-zlib <url> | Download from Z-Library and upload |
upload-url <url> | Add URL as source |
upload-youtube <url> | Add YouTube video as source |
upload-text <title> [--content TEXT] | Add text as source |
source-text <source-id> | Get full indexed text |
source-guide <source-id> | Get AI summary and keywords |
source-rename <source-id> <name> | Rename a source |
source-refresh <source-id> | Re-fetch URL content |
source-delete <source-id> | Delete a source |
Upload options:
--use-active - Upload to the currently active notebook--create-new - Create a new notebook named after the file/folder--notebook-id <id> - Upload to a specific notebook--dry-run - Show sync plan without executing (folder sync)--rebuild - Force rebuild tracking file (folder sync)Important: When user runs upload without specifying a target, ASK them first:
"Would you like to upload to the active notebook, or create a new notebook?" Then pass the appropriate flag (
--use-activeor--create-new).
| Command | Description |
|---|---|
ask <question> | Query NotebookLM |
podcast [--instructions TEXT] | Generate audio podcast |
podcast-status <task-id> | Check podcast generation status |
podcast-download [output-path] | Download latest podcast |
briefing [--instructions TEXT] | Generate brief audio summary |
debate [--instructions TEXT] | Generate debate-style audio |
slides [--instructions TEXT] | Generate slide deck |
slides-download [output-path] | Download slide deck as PDF |
infographic [--instructions TEXT] | Generate infographic |
infographic-download [output-path] | Download infographic |
media-list [--type TYPE] | List generated media (audio/video/slides/infographic) |
media-delete <id> | Delete a generated media item |
Based on $ARGUMENTS, execute the appropriate command:
$IF($ARGUMENTS, Parse the command from: "$ARGUMENTS"
login → python scripts/run.py auth_manager.py setup --service google
accounts → python scripts/run.py auth_manager.py accounts list
accounts add → python scripts/run.py auth_manager.py accounts add
accounts switch → python scripts/run.py auth_manager.py accounts switch "<id>"
accounts remove → python scripts/run.py auth_manager.py accounts remove "<id>"
accounts use → python scripts/run.py auth_manager.py accounts use "<id>"
accounts clear → python scripts/run.py auth_manager.py accounts clear
status → Run both:
python scripts/run.py auth_manager.py statuspython scripts/run.py notebook_manager.py listlocal → python scripts/run.py notebook_manager.py list
remote → python scripts/run.py nblm_cli.py notebooks
create → python scripts/run.py nblm_cli.py create "<name>"
delete [--id ID] → python scripts/run.py nblm_cli.py delete <args>
rename [--id ID] → python scripts/run.py nblm_cli.py rename "<name>" <args>
summary [--id ID] → python scripts/run.py nblm_cli.py summary <args>
describe [--id ID] → python scripts/run.py nblm_cli.py describe <args>
add → Smart add workflow (auto-detects URL vs notebook ID)
activate → python scripts/run.py notebook_manager.py activate --id "<id>"
sources [--id ID] → python scripts/run.py nblm_cli.py sources <args>
upload → First ASK user: "Upload to active notebook or create new?" Then:
- Active: python scripts/run.py source_manager.py add --file "<file>" --use-active
- New: python scripts/run.py source_manager.py add --file "<file>" --create-new
upload → Sync a folder:
- First ASK user: "Sync to active notebook, create new, or specify notebook?"
- Active: python scripts/run.py source_manager.py sync "<folder>" --use-active
- New: python scripts/run.py source_manager.py sync "<folder>" --create-new
- Specific: python scripts/run.py source_manager.py sync "<folder>" --notebook-id ID
- Dry-run: python scripts/run.py source_manager.py sync "<folder>" --dry-run
- Rebuild: python scripts/run.py source_manager.py sync "<folder>" --rebuild
upload-zlib → First ASK user: "Upload to active notebook or create new?" Then:
- Active: python scripts/run.py source_manager.py add --url "<url>" --use-active
- New: python scripts/run.py source_manager.py add --url "<url>" --create-new
upload-url → python scripts/run.py nblm_cli.py upload-url "<url>"
upload-youtube → python scripts/run.py nblm_cli.py upload-youtube "<url>"
upload-text → python scripts/run.py nblm_cli.py upload-text "<title>" <args>
source-text → python scripts/run.py nblm_cli.py source-text "<id>"
source-guide → python scripts/run.py nblm_cli.py source-guide "<id>"
source-rename → python scripts/run.py nblm_cli.py source-rename "<id>" "<name>"
source-refresh → python scripts/run.py nblm_cli.py source-refresh "<id>"
source-delete → python scripts/run.py nblm_cli.py source-delete "<id>"
ask → python scripts/run.py nblm_cli.py ask "<question>"
podcast → python scripts/run.py artifact_manager.py generate --format DEEP_DIVE <args>
podcast-status → python scripts/run.py artifact_manager.py status --task-id "<task-id>"
podcast-download [output-path] → python scripts/run.py artifact_manager.py download "<output-path>"
briefing → python scripts/run.py artifact_manager.py generate --format BRIEF <args>
debate → python scripts/run.py artifact_manager.py generate --format DEBATE <args>
slides → python scripts/run.py artifact_manager.py generate-slides <args>
slides-download [output-path] → python scripts/run.py artifact_manager.py download "<output-path>" --type slide-deck
infographic → python scripts/run.py artifact_manager.py generate-infographic <args>
infographic-download [output-path] → python scripts/run.py artifact_manager.py download "<output-path>" --type infographic
media-list [--type TYPE] → python scripts/run.py artifact_manager.py list <args>
media-delete → python scripts/run.py artifact_manager.py delete "<id>"
If command not recognized, show usage help.,
Show available commands with /nblm (no arguments)
)
| Option | Values |
|---|---|
--length | SHORT, DEFAULT, LONG |
--instructions | Custom instructions for the content |
--wait | Wait for generation to complete |
--output | Download path (requires --wait) |
| Option | Values |
|---|---|
--format | DETAILED_DECK, PRESENTER_SLIDES |
--length | SHORT, DEFAULT |
--instructions | Custom instructions for the content |
--wait | Wait for generation to complete |
--output | Download path (requires --wait) |
| Option | Values |
|---|---|
--orientation | LANDSCAPE, PORTRAIT, SQUARE |
--detail-level | CONCISE, STANDARD, DETAILED |
--instructions | Custom instructions for the content |
--wait | Wait for generation to complete |
--output | Download path (requires --wait) |
| Command | Description | Output |
|---|---|---|
/nblm podcast | Deep-dive audio discussion | MP3 |
/nblm briefing | Brief audio summary | MP3 |
/nblm debate | Debate-style audio | MP3 |
/nblm slides | Slide deck presentation | |
/nblm infographic | Visual infographic | PNG |
Trigger when user:
https://notebooklm.google.com/notebook/...)The add command now automatically discovers metadata from the notebook:
What Smart Add does:
Supported input formats:
5fd9f36b-8000-401d-a7a0-7aa3f7832644https://notebooklm.google.com/notebook/5fd9f36b-8000-401d-a7a0-7aa3f7832644NEVER manually specify --name, --description, or --topics unless the user explicitly provides them.
NEVER call scripts directly. ALWAYS use python scripts/run.py [script]:
The run.py wrapper automatically:
.venv if neededIf not authenticated, proceed to setup.
Important:
python scripts/run.py notebook_manager.py listpython scripts/run.py ask_question.py --question "..." --notebook-id IDEvery NotebookLM answer ends with: "EXTREMELY IMPORTANT: Is that ALL you need to know?"
Required Claude Behavior:
auth_manager.py)notebook_manager.py)ask_question.py)source_manager.py)Folder Sync:
--use-active, --create-new, or --notebook-id is REQUIRED.
Uploads wait for NotebookLM processing and print progress as Ready: N/T. Press Ctrl+C to stop waiting.
Local file uploads use browser automation and require Google authentication.
If browser automation is unavailable, set NOTEBOOKLM_UPLOAD_MODE=text to upload extracted text instead (PDFs require pypdf).cleanup_manager.py)auth_manager.py)The virtual environment is automatically managed:
.venv automaticallyAGENT_BROWSER_OWNER_PID to auto-stop when the agent process exitsscripts/run.py sets AGENT_BROWSER_OWNER_PID to its parent PID by defaultManual setup (only if automatic fails):
All data stored in ~/.claude/skills/notebooklm/data/:
library.json - Notebook metadata (with account associations)auth/google/ - Multi-account Google auth
index.json - Account index (active account, list)<n>-<email>.json - Per-account credentialsauth/zlibrary.json - Z-Library auth stateagent_browser/session_id - Current daemon session IDagent_browser/last_activity.json - Last activity timestamp for idle shutdownagent_browser/watchdog.pid - Idle watchdog process IDSecurity: Protected by .gitignore, never commit to git.
Optional .env file in skill directory:
| Problem | Solution |
|---|---|
| ModuleNotFoundError | Use run.py wrapper |
| Authentication fails | Browser must be visible for setup! --show-browser |
| DAEMON_UNAVAILABLE | Ensure Node.js/npm installed, run npm install, retry |
| AUTH_REQUIRED | Run python scripts/run.py auth_manager.py setup |
| ELEMENT_NOT_FOUND | Verify notebook URL and re-run with fresh page load |
| Rate limit (50/day) | Wait or add another Google account with accounts add |
| Browser crashes | python scripts/run.py cleanup_manager.py --preserve-library |
| Notebook not found | Check with notebook_manager.py list |
Important directories and files:
scripts/ - All automation scripts (ask_question.py, notebook_manager.py, etc.)data/ - Local storage for authentication and notebook libraryreferences/ - Extended documentation:
api_reference.md - Detailed API documentation for all scriptstroubleshooting.md - Common issues and solutionsusage_patterns.md - Best practices and workflow examples.venv/ - Isolated Python environment (auto-created on first run).gitignore - Protects sensitive data from being committed/nblm podcast --wait --output ./deep-dive.mp3
/nblm briefing --instructions "Focus on chapter 3" --wait
/nblm debate --length LONG --wait --output ./debate.mp3
/nblm slides --instructions "Include key diagrams" --format DETAILED_DECK --wait --output ./presentation.pdf
/nblm infographic --orientation LANDSCAPE --detail-level DETAILED --wait --output ./summary.png/nblm podcast-download ./my-podcast.mp3
/nblm slides-download ./presentation.pdf
/nblm infographic-download ./visual.png
/nblm media-list # List all generated media
/nblm media-list --type audio # List only audio
/nblm media-delete <id> # Delete a media item# Smart Add (auto-discovers name, description, topics)
python scripts/run.py notebook_manager.py add <notebook-id-or-url>
# With optional overrides
python scripts/run.py notebook_manager.py add <id> --name "Custom Name" --topics "custom,topics"# ✅ CORRECT - Always use run.py:
python scripts/run.py auth_manager.py status
python scripts/run.py notebook_manager.py list
python scripts/run.py ask_question.py --question "..."
# ❌ WRONG - Never call directly:
python scripts/auth_manager.py status # Fails without venv!python scripts/run.py auth_manager.py status# Browser MUST be visible for manual Google login
python scripts/run.py auth_manager.py setup# List all notebooks
python scripts/run.py notebook_manager.py list
# BEFORE ADDING: Ask user for metadata if unknown!
# "What does this notebook contain?"
# "What topics should I tag it with?"
# Add notebook to library (ALL parameters are REQUIRED!)
python scripts/run.py notebook_manager.py add \
--url "https://notebooklm.google.com/notebook/..." \
--name "Descriptive Name" \
--description "What this notebook contains" \ # REQUIRED - ASK USER IF UNKNOWN!
--topics "topic1,topic2,topic3" # REQUIRED - ASK USER IF UNKNOWN!
# Search notebooks by topic
python scripts/run.py notebook_manager.py search --query "keyword"
# Set active notebook
python scripts/run.py notebook_manager.py activate --id notebook-id
# Remove notebook
python scripts/run.py notebook_manager.py remove --id notebook-id# Basic query (uses active notebook if set)
python scripts/run.py ask_question.py --question "Your question here"
# Query specific notebook
python scripts/run.py ask_question.py --question "..." --notebook-id notebook-id
# Query with notebook URL directly
python scripts/run.py ask_question.py --question "..." --notebook-url "https://..."
# Show browser for debugging
python scripts/run.py ask_question.py --question "..." --show-browser