notebooklm-mcp-cli 0.7.0__tar.gz → 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.gitignore +3 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/CHANGELOG.md +29 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/CLAUDE.md +6 -2
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/PKG-INFO +11 -3
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/README.md +10 -2
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/desktop-extension/manifest.json +1 -1
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/AUTHENTICATION.md +7 -2
- notebooklm_mcp_cli-0.7.2/docs/CHATGPT_CONNECTOR_SETUP.md +162 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/pyproject.toml +1 -1
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/__init__.py +3 -1
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/auth.py +49 -17
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/base.py +5 -3
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/AGENTS_SECTION.md +1 -1
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/SKILL.md +1 -1
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/server.py +49 -4
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/_utils.py +59 -6
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/auth.py +11 -8
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/downloads.py +40 -2
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/server.py +24 -28
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/sources.py +111 -4
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/studio.py +22 -9
- notebooklm_mcp_cli-0.7.2/src/notebooklm_tools/services/auth.py +625 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/downloads.py +71 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/sources.py +262 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/cdp.py +10 -4
- notebooklm_mcp_cli-0.7.2/src/notebooklm_tools/utils/env_sanitize.py +20 -0
- notebooklm_mcp_cli-0.7.2/tests/services/test_auth_health.py +564 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_auth_service.py +12 -3
- notebooklm_mcp_cli-0.7.2/tests/test_e2e_chatgpt_upload_polling.py +68 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_mcp_auth_studio_failures.py +65 -21
- notebooklm_mcp_cli-0.7.2/tests/test_mcp_chatgpt_file_download.py +130 -0
- notebooklm_mcp_cli-0.7.2/tests/test_mcp_chatgpt_file_upload.py +93 -0
- notebooklm_mcp_cli-0.7.2/tests/test_profile_aware_headless_auth.py +84 -0
- notebooklm_mcp_cli-0.7.2/tools/run-chatgpt-tunnel.ps1 +38 -0
- notebooklm_mcp_cli-0.7.2/tools/run-cloudflare-chatgpt.ps1 +34 -0
- notebooklm_mcp_cli-0.7.0/src/notebooklm_tools/services/auth.py +0 -146
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.github/workflows/lint-test.yml +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.github/workflows/publish.yml +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.github/workflows/release-gate.yml +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.github/workflows/version-check.yml +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/AGENTS.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/CONTRIBUTING.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/GEMINI.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/LICENSE +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/desktop-extension/run_server.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/API_REFERENCE.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/CLI_GUIDE.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/FILE_UPLOAD_IMPLEMENTATION.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/GETTING_STARTED.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/KNOWN_ISSUES.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/MCP_CLI_TEST_PLAN.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/MCP_GUIDE.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/MULTI_USER_ANALYSIS.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/WSL_SETUP.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/media/header.jpg +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/run_log.txt +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/scripts/build_mcpb.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/scripts/inject_cookies_and_inspect.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/scripts/inspect_upload_dom.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/scripts/test_label_rpcs.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/__init__.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/ai_docs.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/__init__.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/alias.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/batch.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/chat.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/config.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/cross.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/doctor.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/download.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/export.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/label.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/note.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/notebook.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/pipeline.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/repl.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/research.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/setup.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/share.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/skill.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/source.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/studio.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/tag.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/verbs.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/formatters.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/main.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/utils.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/__init__.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/alias.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/client.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/constants.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/conversation.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/data_types.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/download.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/errors.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/exceptions.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/exports.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/labels.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/models.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/notebooks.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/notes.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/research.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/retry.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/sharing.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/sources.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/studio.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/utils.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/command_reference.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/studio-prompt-examples.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/studio-prompting-guide.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/troubleshooting.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/workflows.md +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/__init__.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/__init__.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/batch.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/chat.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/cross_notebook.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/exports.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/labels.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/notebooks.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/notes.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/pipeline.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/research.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/sharing.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/smart_select.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/studio_advanced.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/__init__.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/_compat.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/batch.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/chat.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/cross_notebook.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/errors.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/exports.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/labels.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/notebooks.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/notes.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/pipeline.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/research.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/sharing.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/smart_select.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/studio.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/__init__.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/auth_browser.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/browser.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/config.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/io_encoding.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/wsl.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_formatters.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_login.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_repl.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_setup_github_copilot.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_setup_opencode.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_source_add.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_studio_cli.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_verbs_defaults.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_verbs_parity.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_alias.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_auth_check.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_auth_guard.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_base.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_conversation.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_data_types.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_download.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_errors.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_notebooks.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_notes.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_research.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_retry.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_sharing.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_source_reconciliation.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_sources.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_studio.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_thread_safety.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_url_source_fallback.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_utils.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/manual/test_fastmcp_stdio.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/manual/test_revise_slide_deck.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/__init__.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_batch.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_chat.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_cross_notebook.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_downloads.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_errors.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_exports.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_notebooks.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_notes.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_pipeline.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_research.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_sharing.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_smart_select.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_sources.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_studio.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_api_client.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_auth_browser.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_auth_migration.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_cdp_proxy_bypass.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_cdp_url_check.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_coerce_list.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_cookie_parsing.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_download_integration.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_e2e.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_file_upload.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_io_encoding_windows.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_mcp_e2e.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_mcp_file_upload.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_mcp_studio.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_research_polling.py +0 -0
- {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_snap.py +0 -0
|
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.2] - 2026-06-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **ChatGPT File Parameter Upload Bridge (PR #220)** — Enables direct upload of files provided by ChatGPT App/Action clients by resolving and downloading from the client's temporary HTTPS URL before uploading to Google NotebookLM. Thanks to **@insane66613**!
|
|
15
|
+
- **ChatGPT Artifact Download Bridge (PR #220)** — Allows copying generated artifacts to the local server's public directory and returning public download links when the server runs behind secure tunnels. Exposes a new `/artifacts/{filename}` route in the MCP HTTP server. Thanks to **@insane66613**!
|
|
16
|
+
- **Transient Source Content Polling** (`poll_source_content` service function) — Automatically handles Google NotebookLM indexing states (processing, indexing, try again) via robust polling with exponential backoff when fetching raw source contents immediately after creation.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **Sanitize `no_proxy` environment variable (PR #221)** — Fixed package import crashes on Windows systems by cleaning up the `no_proxy` environment variable on initialization before `httpx` parses it. Thanks to **@insane66613**!
|
|
21
|
+
- **Modern Chrome Cookie DB path detection (PR #222)** — Improved the SQLite cookie database location search logic for Google Chrome to work correctly with modern Chrome setups. Thanks to **@insane66613**!
|
|
22
|
+
- **Profile-aware headless refresh (PR #223)** — Fixed headless login refresh to use the default/active profile configured in the CLI configuration file instead of falling back. Thanks to **@insane66613**!
|
|
23
|
+
- **`AuthHealthChecker` API fallback passed wrong cookie format, causing false `stale` on semi-stale sessions (PR #225)** — The API fallback flattened `profile.cookies` to a dict, dropping domain-specific duplicates and omitting `session_id` / `build_label`. The API probe now passes `profile.cookies` unchanged with all session fields, matching `nlm login --check`. Thanks to **@insane66613**!
|
|
24
|
+
- **`server_info`, `refresh_auth`, and `studio_create` disagreed on semi-stale auth (PR #225, Fixes #224)** — These three MCP paths each ran independent auth checks, so `server_info` could report `stale` and `studio_create` refuse to run while `notebook_list` and CLI tools worked fine. All MCP auth gates now share `credentials_are_usable()` (`AuthHealthChecker` + live API confirmation), eliminating split-brain results. Thanks to **@insane66613**!
|
|
25
|
+
|
|
26
|
+
## [0.7.1] - 2026-06-06
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **`server_info` reported `"stale"` for valid semi-stale cookies (PR #219)** — Google enforces different session lifecycles for the NotebookLM homepage (a navigation endpoint) and the RPC API endpoints. Cookies that are "semi-stale" — rejected by the homepage with a redirect to `accounts.google.com`, but still 100% accepted by the RPC API — were causing the MCP `server_info` tool (and `nlm login --check`) to falsely report `"stale"` even though every actual API tool would work fine. The new `AuthHealthChecker` runs a homepage probe first and, on `expired` / `http_401` / `http_403`, falls back to a live `NotebookLMClient.list_notebooks()` call before deciding. The homepage headers were also upgraded to use the full browser-like `_PAGE_FETCH_HEADERS` (including `Sec-Fetch-Dest` / `Sec-Fetch-Mode` / `Sec-Fetch-Site` / `Sec-Fetch-User`) — without them Google was bot-detecting the homepage check and redirecting even fresh cookies, producing false `"stale"` reports on first probe. Results are cached for 30 seconds with mtime-based bypass, so an external `nlm login` is reflected without waiting for the TTL. The CLI and MCP now share the cache via the `get_auth_health_checker()` singleton in `services/auth.py`. Thanks to **@SERDAR-AKIN** for the original multi-probe design and PR #219!
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **`AuthHealthChecker` lives in `services/auth.py`, not `core/auth.py`** — the multi-probe orchestration, 30-second cache, and verdict aggregation are business logic and belong in the services layer per the layering rule in `CLAUDE.md` / `AGENTS.md` / `GEMINI.md`. `core/auth.py` stays focused on the low-level auth primitives (`AuthManager`, `AuthTokens`, `check_auth`, `save_tokens_to_cache`, `load_cached_tokens`, `_fetch_notebooklm_homepage`).
|
|
35
|
+
- **`server_info` docstring no longer calls `auth_status` a "live check"** — it is cached for 30 seconds with mtime-based bypass. `docs/AUTHENTICATION.md` documents the same contract for `nlm login --check` (always live) vs `server_info` (cached).
|
|
36
|
+
- **`AuthHealthReport.valid` is now `verdict == "configured"` (was `!=`)** — the field was inverted: it returned `True` for `stale` / `unverified` / `not_configured` reports. Pinned by `TestReportValid` in `tests/services/test_auth_health.py`.
|
|
37
|
+
- **API-probe exceptions are classified as transport errors when they are** — `_probe_api` now catches `httpx.TimeoutException` and `httpx.RequestError` explicitly and emits the `"network_error:"` prefix so `_determine_verdict` can route them to `"unverified"`. Previously all exceptions emitted `f"{type(e).__name__}: {e}"`, which meant a real transport error on the API path was misclassified as an auth failure (`"stale"`). Pinned by `TestProbeApiErrorClassification` in `tests/services/test_auth_health.py`.
|
|
38
|
+
|
|
10
39
|
## [0.7.0] - 2026-06-03
|
|
11
40
|
|
|
12
41
|
### Added
|
|
@@ -11,6 +11,9 @@ Tested with personal/free tier accounts. May work with Google Workspace accounts
|
|
|
11
11
|
## Development Commands
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
+
# Fetch latest from remote (run before git operations)
|
|
15
|
+
git fetch
|
|
16
|
+
|
|
14
17
|
# Install dependencies
|
|
15
18
|
uv tool install .
|
|
16
19
|
|
|
@@ -142,7 +145,8 @@ src/notebooklm_tools/
|
|
|
142
145
|
| `notebook_get` | Get notebook details |
|
|
143
146
|
| `notebook_describe` | Get AI-generated summary of notebook content with keywords |
|
|
144
147
|
| `source_describe` | Get AI-generated summary and keyword chips for a source |
|
|
145
|
-
| `source_get_content` | Get raw text content from a source (no AI processing) |
|
|
148
|
+
| `source_get_content` | Get raw text content from a source (no AI processing). Supports `wait`, `wait_timeout`, `poll_interval` params and returns `download_url` when MCP HTTP transport is active. |
|
|
149
|
+
| `source_add_chatgpt_file` | Add a ChatGPT-uploaded file to a notebook (supports `openai/fileParams`). Supports `wait`, `wait_timeout`, `cleanup` params. |
|
|
146
150
|
| `notebook_rename` | Rename a notebook |
|
|
147
151
|
| `chat_configure` | Configure chat goal/style and response length |
|
|
148
152
|
| `notebook_delete` | Delete a notebook (REQUIRES confirmation) |
|
|
@@ -156,7 +160,7 @@ src/notebooklm_tools/
|
|
|
156
160
|
| `research_status` | Check research progress and get results |
|
|
157
161
|
| `research_import` | Import discovered sources into notebook |
|
|
158
162
|
| `studio_create` | Generate unified content (audio, video, infographic, slides, etc.) |
|
|
159
|
-
| `download_artifact` | Download any artifact (audio, video, pdf, markdown, json) |
|
|
163
|
+
| `download_artifact` | Download any artifact (audio, video, pdf, markdown, json). Supports `wait`, `wait_timeout`, `poll_interval` params and returns `download_url` when MCP HTTP transport is active. |
|
|
160
164
|
| `export_artifact` | Export Data Tables to Google Sheets or Reports to Google Docs |
|
|
161
165
|
| `studio_status` | Check studio artifact generation status |
|
|
162
166
|
| `studio_delete` | Delete studio artifacts (REQUIRES confirmation) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: notebooklm-mcp-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Unified CLI and MCP server for Google NotebookLM
|
|
5
5
|
Project-URL: Homepage, https://github.com/jacob-bd/notebooklm-mcp-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/jacob-bd/notebooklm-mcp-cli
|
|
@@ -37,8 +37,7 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
[](https://pepy.tech/projects/notebooklm-mcp-cli)
|
|
38
38
|
[](https://pypi.org/project/notebooklm-mcp-cli/)
|
|
39
39
|
[](https://github.com/jacob-bd/notebooklm-mcp-cli/blob/main/LICENSE)
|
|
40
|
-
|
|
41
|
-
> 🎉 **January 2026 — Major Update!** This project has been completely refactored to unify **NotebookLM-MCP** and **NotebookLM-CLI** into a single, powerful package. One install gives you both the CLI (`nlm`) and MCP server (`notebooklm-mcp`). See the [CLI Guide](docs/CLI_GUIDE.md) and [MCP Guide](docs/MCP_GUIDE.md) for full documentation.
|
|
40
|
+
[](https://buymeacoffee.com/jacobbd)
|
|
42
41
|
|
|
43
42
|
**Programmatic access to Google NotebookLM** — via command-line interface (CLI) or Model Context Protocol (MCP) server.
|
|
44
43
|
|
|
@@ -603,6 +602,15 @@ Special thanks to:
|
|
|
603
602
|
- **codepiano** ([@codepiano](https://github.com/codepiano)) for the configurable DevTools timeout for the auth CLI.
|
|
604
603
|
- **Tony Hansmann** ([@997unix](https://github.com/997unix)) for contributing the `nlm setup` and `nlm doctor` commands and CLI Guide documentation.
|
|
605
604
|
- **Fabiana Furtado** ([@fabianafurtadoff](https://github.com/fabianafurtadoff)) for batch operations, cross-notebook query, pipelines, and smart select/tagging (PR #90).
|
|
605
|
+
- **Amy-Ra-lph** ([@Amy-Ra-lph](https://github.com/Amy-Ra-lph)) for security hardening: TOCTOU-safe credential storage, sensitive cookie redaction from debug logs, and pinning all CI actions to full commit SHAs (PRs #205–207).
|
|
606
|
+
- **Kyle Brodeur** ([@kylebrodeur](https://github.com/kylebrodeur)) for WSL2 authentication support with Windows Chrome integration (PR #138).
|
|
607
|
+
- **Robiton** ([@Robiton](https://github.com/Robiton)) for enterprise NotebookLM support via configurable base URL (PR #114).
|
|
608
|
+
- **pjeby** ([@pjeby](https://github.com/pjeby)) for connection pooling and fast startup improvements (PR #54).
|
|
609
|
+
- **beausea** ([@beausea](https://github.com/beausea)) for making the interface language configurable via the `NOTEBOOKLM_HL` environment variable (PR #59).
|
|
610
|
+
- **JumpLao** ([@JumpLao](https://github.com/JumpLao)) for extended audio, video, and image format support (PR #82).
|
|
611
|
+
- **cbruyndoncx** ([@cbruyndoncx](https://github.com/cbruyndoncx)) for including `cited_text` passages in query output (PR #81).
|
|
612
|
+
- **zxyasfas** ([@zxyasfas](https://github.com/zxyasfas)) for cited-only research import (PR #188).
|
|
613
|
+
- **Serdar Akın** ([@SERDAR-AKIN](https://github.com/SERDAR-AKIN)) for the multi-probe `AuthHealthChecker` that fixes false `"stale"` reports for semi-stale cookies (PR #219).
|
|
606
614
|
|
|
607
615
|
|
|
608
616
|
## Star History
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
[](https://pepy.tech/projects/notebooklm-mcp-cli)
|
|
8
8
|
[](https://pypi.org/project/notebooklm-mcp-cli/)
|
|
9
9
|
[](https://github.com/jacob-bd/notebooklm-mcp-cli/blob/main/LICENSE)
|
|
10
|
-
|
|
11
|
-
> 🎉 **January 2026 — Major Update!** This project has been completely refactored to unify **NotebookLM-MCP** and **NotebookLM-CLI** into a single, powerful package. One install gives you both the CLI (`nlm`) and MCP server (`notebooklm-mcp`). See the [CLI Guide](docs/CLI_GUIDE.md) and [MCP Guide](docs/MCP_GUIDE.md) for full documentation.
|
|
10
|
+
[](https://buymeacoffee.com/jacobbd)
|
|
12
11
|
|
|
13
12
|
**Programmatic access to Google NotebookLM** — via command-line interface (CLI) or Model Context Protocol (MCP) server.
|
|
14
13
|
|
|
@@ -573,6 +572,15 @@ Special thanks to:
|
|
|
573
572
|
- **codepiano** ([@codepiano](https://github.com/codepiano)) for the configurable DevTools timeout for the auth CLI.
|
|
574
573
|
- **Tony Hansmann** ([@997unix](https://github.com/997unix)) for contributing the `nlm setup` and `nlm doctor` commands and CLI Guide documentation.
|
|
575
574
|
- **Fabiana Furtado** ([@fabianafurtadoff](https://github.com/fabianafurtadoff)) for batch operations, cross-notebook query, pipelines, and smart select/tagging (PR #90).
|
|
575
|
+
- **Amy-Ra-lph** ([@Amy-Ra-lph](https://github.com/Amy-Ra-lph)) for security hardening: TOCTOU-safe credential storage, sensitive cookie redaction from debug logs, and pinning all CI actions to full commit SHAs (PRs #205–207).
|
|
576
|
+
- **Kyle Brodeur** ([@kylebrodeur](https://github.com/kylebrodeur)) for WSL2 authentication support with Windows Chrome integration (PR #138).
|
|
577
|
+
- **Robiton** ([@Robiton](https://github.com/Robiton)) for enterprise NotebookLM support via configurable base URL (PR #114).
|
|
578
|
+
- **pjeby** ([@pjeby](https://github.com/pjeby)) for connection pooling and fast startup improvements (PR #54).
|
|
579
|
+
- **beausea** ([@beausea](https://github.com/beausea)) for making the interface language configurable via the `NOTEBOOKLM_HL` environment variable (PR #59).
|
|
580
|
+
- **JumpLao** ([@JumpLao](https://github.com/JumpLao)) for extended audio, video, and image format support (PR #82).
|
|
581
|
+
- **cbruyndoncx** ([@cbruyndoncx](https://github.com/cbruyndoncx)) for including `cited_text` passages in query output (PR #81).
|
|
582
|
+
- **zxyasfas** ([@zxyasfas](https://github.com/zxyasfas)) for cited-only research import (PR #188).
|
|
583
|
+
- **Serdar Akın** ([@SERDAR-AKIN](https://github.com/SERDAR-AKIN)) for the multi-probe `AuthHealthChecker` that fixes false `"stale"` reports for semi-stale cookies (PR #219).
|
|
576
584
|
|
|
577
585
|
|
|
578
586
|
## Star History
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": "0.2",
|
|
3
3
|
"name": "notebooklm-mcp",
|
|
4
4
|
"display_name": "NotebookLM MCP Server",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.2",
|
|
6
6
|
"description": "Connect Claude to Google NotebookLM. Create podcasts, research topics, generate quizzes, flashcards, mind maps, and more from your notebooks.",
|
|
7
7
|
"long_description": "This extension connects Claude Desktop to Google NotebookLM via the Model Context Protocol. It provides 29 tools for managing notebooks, sources, and generating AI content.\n\n**Prerequisites:**\n1. Install the package: `pip install notebooklm-mcp-cli` or `uv tool install notebooklm-mcp-cli`\n2. Authenticate: Run `nlm login` to set up your Google account\n\n**Features:**\n- Create and manage notebooks\n- Add sources (URLs, YouTube, Google Drive, text, files)\n- Generate podcasts (Audio Overview)\n- Create quizzes, flashcards, mind maps, reports\n- Research topics with web or Drive search",
|
|
8
8
|
"author": {
|
|
@@ -270,10 +270,15 @@ When you start seeing authentication errors, simply run `nlm login` again to ref
|
|
|
270
270
|
## Understanding `auth_status`
|
|
271
271
|
|
|
272
272
|
The MCP `server_info` tool and `nlm login --check` report one of five
|
|
273
|
-
`auth_status` values
|
|
274
|
-
|
|
273
|
+
`auth_status` values from the multi-probe AuthHealthChecker. Knowing the
|
|
274
|
+
difference matters: a `stale` status means you must re-auth, but
|
|
275
275
|
an `unverified` status is a network problem, not a credential problem.
|
|
276
276
|
|
|
277
|
+
> **Caching note:** the `server_info` result is cached for 30 seconds
|
|
278
|
+
> (the checker's `CACHE_TTL`) and bypassed on the next call if any auth
|
|
279
|
+
> file on disk is rewritten, so an external `nlm login` is reflected
|
|
280
|
+
> without waiting for the TTL. `nlm login --check` is always live.
|
|
281
|
+
|
|
277
282
|
| Status | Meaning | What to do |
|
|
278
283
|
|--------|---------|------------|
|
|
279
284
|
| `configured` | Live check passed. Credentials are good. | Nothing. |
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# ChatGPT connector setup
|
|
2
|
+
|
|
3
|
+
This guide covers using `notebooklm-mcp-cli` from ChatGPT with normal ChatGPT file uploads and downloadable NotebookLM outputs.
|
|
4
|
+
|
|
5
|
+
## Capabilities
|
|
6
|
+
|
|
7
|
+
- Add a normal ChatGPT-uploaded file to NotebookLM with `source_add_chatgpt_file`.
|
|
8
|
+
- Retrieve source text with `source_get_content` and get a downloadable `/artifacts/...` URL.
|
|
9
|
+
- Download already-generated NotebookLM artifacts with `download_artifact`, including audio, video, slide decks, reports, mind maps, infographics, quizzes, flashcards, and data tables.
|
|
10
|
+
- Serve downloaded files from the MCP HTTP server at `/artifacts/{filename}`.
|
|
11
|
+
|
|
12
|
+
## Recommended flow for existing artifacts
|
|
13
|
+
|
|
14
|
+
1. Call `studio_status` with a `notebook_id`.
|
|
15
|
+
2. Pick an artifact whose `status` is `completed` and note its `artifact_id` and `type`.
|
|
16
|
+
3. Call `download_artifact` with:
|
|
17
|
+
- `notebook_id`
|
|
18
|
+
- `artifact_type`
|
|
19
|
+
- `artifact_id`
|
|
20
|
+
- `output_path`
|
|
21
|
+
4. Use the returned `download_url` from ChatGPT.
|
|
22
|
+
|
|
23
|
+
Example result:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"status": "success",
|
|
28
|
+
"artifact_type": "audio",
|
|
29
|
+
"path": "C:\\Users\\you\\Downloads\\overview.m4a",
|
|
30
|
+
"download_url": "https://your-public-host.example/artifacts/overview.m4a"
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Local authentication
|
|
35
|
+
|
|
36
|
+
Before exposing the MCP server to ChatGPT, verify NotebookLM auth locally:
|
|
37
|
+
|
|
38
|
+
```powershell
|
|
39
|
+
uv run nlm login --profile default --check
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If expired, run:
|
|
43
|
+
|
|
44
|
+
```powershell
|
|
45
|
+
uv run nlm login --profile default --clear
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Option A: OpenAI/ChatGPT secure tunnel
|
|
49
|
+
|
|
50
|
+
Use this when ChatGPT is configured in tunnel mode.
|
|
51
|
+
|
|
52
|
+
Start the local MCP server:
|
|
53
|
+
|
|
54
|
+
```powershell
|
|
55
|
+
cd X:\Code\notebooklm-mcp-cli
|
|
56
|
+
pwsh -File .\tools\run-chatgpt-tunnel.ps1 -Profile default -NoOpenAITunnel
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Then create/connect the ChatGPT secure MCP tunnel using target URL:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
http://127.0.0.1:8811/mcp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The same server exposes files at:
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
http://127.0.0.1:8811/artifacts/<filename>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
When ChatGPT connects through a secure tunnel, the server captures forwarded host headers and returns public artifact links using the tunnel host.
|
|
72
|
+
|
|
73
|
+
## Option B: Cloudflare tunnel
|
|
74
|
+
|
|
75
|
+
Use this when you want a stable public hostname.
|
|
76
|
+
|
|
77
|
+
Start the MCP server:
|
|
78
|
+
|
|
79
|
+
```powershell
|
|
80
|
+
cd X:\Code\notebooklm-mcp-cli
|
|
81
|
+
uv run notebooklm-mcp --transport http --host 127.0.0.1 --port 8811 --query-timeout 600
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Create a Cloudflare tunnel ingress rule pointing your hostname to the local origin:
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
hostname: notebooklm-mcp.example.com
|
|
88
|
+
service: http://127.0.0.1:8811
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then configure ChatGPT to use:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
https://notebooklm-mcp.example.com/mcp
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Downloads will be returned as:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
https://notebooklm-mcp.example.com/artifacts/<filename>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
For a quick local reverse-proxy test:
|
|
104
|
+
|
|
105
|
+
```powershell
|
|
106
|
+
pwsh -File .\tools\run-cloudflare-chatgpt.ps1 -PublicHostname notebooklm-mcp.example.com -Profile default
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Tool usage examples
|
|
110
|
+
|
|
111
|
+
### Download an existing audio artifact
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"notebook_id": "<notebook-id>",
|
|
116
|
+
"artifact_type": "audio",
|
|
117
|
+
"artifact_id": "<completed-audio-artifact-id>",
|
|
118
|
+
"output_path": "C:\\Users\\you\\Downloads\\notebooklm-audio.m4a",
|
|
119
|
+
"wait": true,
|
|
120
|
+
"wait_timeout": 180,
|
|
121
|
+
"poll_interval": 5
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Download an existing slide deck
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"notebook_id": "<notebook-id>",
|
|
130
|
+
"artifact_type": "slide_deck",
|
|
131
|
+
"artifact_id": "<completed-slide-deck-artifact-id>",
|
|
132
|
+
"output_path": "C:\\Users\\you\\Downloads\\notebooklm-slides.pdf",
|
|
133
|
+
"slide_deck_format": "pdf"
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Retrieve source text as a downloadable file
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"source_id": "<source-id>",
|
|
142
|
+
"wait": true,
|
|
143
|
+
"wait_timeout": 120,
|
|
144
|
+
"poll_interval": 3
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Delay handling
|
|
149
|
+
|
|
150
|
+
NotebookLM may need time to index new sources or propagate generated media URLs. The download tools now poll transient states by default:
|
|
151
|
+
|
|
152
|
+
- `source_get_content`: waits for source text indexing.
|
|
153
|
+
- `download_artifact`: waits for existing generated artifacts whose media URL is still propagating.
|
|
154
|
+
|
|
155
|
+
For already-generated artifacts, this usually returns immediately.
|
|
156
|
+
|
|
157
|
+
## Security notes
|
|
158
|
+
|
|
159
|
+
- `/artifacts/{filename}` serves files from the configured `public_artifacts` directory only.
|
|
160
|
+
- Path traversal is neutralized by reducing requests to the basename.
|
|
161
|
+
- Do not expose the MCP server publicly without the same access controls you use for other private NotebookLM tooling.
|
|
162
|
+
- Cloudflare Access or ChatGPT's secure tunnel is preferred over an unauthenticated public hostname.
|
|
@@ -95,9 +95,10 @@ def load_cached_tokens() -> AuthTokens | None:
|
|
|
95
95
|
cookies=profile.cookies,
|
|
96
96
|
csrf_token=profile.csrf_token or "",
|
|
97
97
|
session_id=profile.session_id or "",
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
build_label=profile.build_label or "",
|
|
99
|
+
extracted_at=(
|
|
100
|
+
profile.last_validated.timestamp() if profile.last_validated else time.time()
|
|
101
|
+
),
|
|
101
102
|
)
|
|
102
103
|
except Exception as e:
|
|
103
104
|
logger.debug(f"Failed to load default profile: {e}")
|
|
@@ -311,7 +312,7 @@ class Profile:
|
|
|
311
312
|
"session_id": self.session_id,
|
|
312
313
|
"email": self.email,
|
|
313
314
|
"build_label": self.build_label,
|
|
314
|
-
"last_validated": self.last_validated.isoformat() if self.last_validated else None,
|
|
315
|
+
"last_validated": (self.last_validated.isoformat() if self.last_validated else None),
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
@classmethod
|
|
@@ -326,9 +327,11 @@ class Profile:
|
|
|
326
327
|
|
|
327
328
|
return cls(
|
|
328
329
|
name=data.get("name", "default"),
|
|
329
|
-
cookies=
|
|
330
|
-
|
|
331
|
-
|
|
330
|
+
cookies=(
|
|
331
|
+
data.get("cookies", [])
|
|
332
|
+
if isinstance(data.get("cookies"), list)
|
|
333
|
+
else data.get("cookies", {})
|
|
334
|
+
),
|
|
332
335
|
csrf_token=data.get("csrf_token"),
|
|
333
336
|
session_id=data.get("session_id"),
|
|
334
337
|
email=data.get("email"),
|
|
@@ -368,7 +371,10 @@ class AuthManager:
|
|
|
368
371
|
"""Load the current profile from disk."""
|
|
369
372
|
from datetime import datetime
|
|
370
373
|
|
|
371
|
-
from notebooklm_tools.core.exceptions import
|
|
374
|
+
from notebooklm_tools.core.exceptions import (
|
|
375
|
+
AuthenticationError,
|
|
376
|
+
ProfileNotFoundError,
|
|
377
|
+
)
|
|
372
378
|
|
|
373
379
|
if self._profile is not None and not force_reload:
|
|
374
380
|
return self._profile
|
|
@@ -388,9 +394,11 @@ class AuthManager:
|
|
|
388
394
|
csrf_token=metadata.get("csrf_token"),
|
|
389
395
|
session_id=metadata.get("session_id"),
|
|
390
396
|
email=metadata.get("email"),
|
|
391
|
-
last_validated=
|
|
392
|
-
|
|
393
|
-
|
|
397
|
+
last_validated=(
|
|
398
|
+
datetime.fromisoformat(metadata["last_validated"])
|
|
399
|
+
if metadata.get("last_validated")
|
|
400
|
+
else None
|
|
401
|
+
),
|
|
394
402
|
build_label=metadata.get("build_label"),
|
|
395
403
|
)
|
|
396
404
|
return self._profile
|
|
@@ -588,6 +596,21 @@ class AuthCheckResult:
|
|
|
588
596
|
details: dict[str, Any] | None = None # e.g. extracted csrf on success
|
|
589
597
|
|
|
590
598
|
|
|
599
|
+
# Browser-like headers required for the NotebookLM homepage fetch.
|
|
600
|
+
# These match _PAGE_FETCH_HEADERS in BaseClient (core/base.py).
|
|
601
|
+
# The Sec-Fetch-* headers are critical: without them Google may redirect
|
|
602
|
+
# even valid cookies to the login page, causing false "expired" results.
|
|
603
|
+
_PAGE_FETCH_HEADERS = {
|
|
604
|
+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
|
|
605
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
|
|
606
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
607
|
+
"Sec-Fetch-Dest": "document",
|
|
608
|
+
"Sec-Fetch-Mode": "navigate",
|
|
609
|
+
"Sec-Fetch-Site": "none",
|
|
610
|
+
"Sec-Fetch-User": "?1",
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
|
|
591
614
|
def _fetch_notebooklm_homepage(
|
|
592
615
|
cookies: dict[str, str] | list[dict],
|
|
593
616
|
*,
|
|
@@ -598,6 +621,9 @@ def _fetch_notebooklm_homepage(
|
|
|
598
621
|
|
|
599
622
|
Returns the final response after redirects. Callers decide what the
|
|
600
623
|
final URL / status means.
|
|
624
|
+
|
|
625
|
+
Note: uses proper browser-like _PAGE_FETCH_HEADERS (including Sec-Fetch-*)
|
|
626
|
+
to avoid false redirects to Google login that occur with minimal headers.
|
|
601
627
|
"""
|
|
602
628
|
import httpx
|
|
603
629
|
|
|
@@ -608,11 +634,7 @@ def _fetch_notebooklm_homepage(
|
|
|
608
634
|
else:
|
|
609
635
|
cookie_dict = cookies
|
|
610
636
|
|
|
611
|
-
headers =
|
|
612
|
-
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
|
|
613
|
-
"Accept": "text/html,application/xhtml+xml",
|
|
614
|
-
"Accept-Language": "en-US,en;q=0.9",
|
|
615
|
-
}
|
|
637
|
+
headers = _PAGE_FETCH_HEADERS.copy()
|
|
616
638
|
|
|
617
639
|
cookie_header = cookies_to_header(cookie_dict)
|
|
618
640
|
if cookie_header:
|
|
@@ -686,7 +708,10 @@ def check_auth(
|
|
|
686
708
|
age = (time.time() - p.last_validated.timestamp()) / 3600
|
|
687
709
|
if age <= 168:
|
|
688
710
|
return AuthCheckResult(
|
|
689
|
-
valid=True,
|
|
711
|
+
valid=True,
|
|
712
|
+
live=False,
|
|
713
|
+
profile=profile,
|
|
714
|
+
checked_at=p.last_validated.timestamp(),
|
|
690
715
|
)
|
|
691
716
|
return AuthCheckResult(valid=False, reason="stale_heuristic", live=False, profile=profile)
|
|
692
717
|
|
|
@@ -743,3 +768,10 @@ def check_auth(
|
|
|
743
768
|
profile=profile,
|
|
744
769
|
details={"exception": str(exc)},
|
|
745
770
|
)
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
# Note: AuthHealthChecker, AuthProbeResult, AuthHealthReport live in
|
|
774
|
+
# notebooklm_tools.services.auth — they are business logic (multi-probe
|
|
775
|
+
# orchestration, caching, verdict aggregation) and belong in the services
|
|
776
|
+
# layer, not here. The thin re-export shim in services.auth makes them
|
|
777
|
+
# available to cli/ and mcp/ without breaking the layering rule.
|
|
@@ -932,11 +932,13 @@ class BaseClient:
|
|
|
932
932
|
self._session_id = "" # Force re-extraction of session ID
|
|
933
933
|
return True
|
|
934
934
|
|
|
935
|
-
# Try headless auth if Chrome profile exists
|
|
935
|
+
# Try headless auth if the configured default Chrome profile exists.
|
|
936
936
|
try:
|
|
937
|
-
from notebooklm_tools.utils.
|
|
937
|
+
from notebooklm_tools.utils.auth_browser import run_headless_auth
|
|
938
|
+
from notebooklm_tools.utils.config import get_config
|
|
938
939
|
|
|
939
|
-
|
|
940
|
+
profile_name = get_config().auth.default_profile
|
|
941
|
+
tokens = run_headless_auth(profile_name=profile_name)
|
|
940
942
|
if tokens:
|
|
941
943
|
with self._state_lock:
|
|
942
944
|
self.cookies = tokens.cookies
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nlm-skill
|
|
3
|
-
version: "0.7.
|
|
3
|
+
version: "0.7.2"
|
|
4
4
|
description: "Expert guide for the NotebookLM CLI (`nlm`) and MCP server - interfaces for Google NotebookLM. Use this skill when users want to interact with NotebookLM programmatically, including: creating/managing notebooks, adding sources (URLs, YouTube, text, Google Drive), generating content (podcasts, reports, quizzes, flashcards, mind maps, slides, infographics, videos, data tables), conducting research, chatting with sources, or automating NotebookLM workflows. Triggers on mentions of \"nlm\", \"notebooklm\", \"notebook lm\", \"podcast generation\", \"audio overview\", or any NotebookLM-related automation task."
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -20,18 +20,23 @@ Tool Modules:
|
|
|
20
20
|
import argparse
|
|
21
21
|
import logging
|
|
22
22
|
import os
|
|
23
|
+
from pathlib import Path
|
|
23
24
|
|
|
24
25
|
from fastmcp import FastMCP
|
|
26
|
+
from starlette.middleware import Middleware
|
|
27
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
25
28
|
from starlette.requests import Request
|
|
26
|
-
from starlette.responses import JSONResponse
|
|
29
|
+
from starlette.responses import FileResponse, JSONResponse, Response
|
|
27
30
|
|
|
28
31
|
from notebooklm_tools import __version__
|
|
29
32
|
|
|
33
|
+
from .tools._utils import PUBLIC_DIR, reset_mcp_base_url, set_mcp_base_url
|
|
34
|
+
|
|
30
35
|
_FALSY = frozenset({"false", "0", "no", "off"})
|
|
31
36
|
|
|
32
37
|
|
|
33
38
|
def _env_bool(name: str, default: bool = False) -> bool:
|
|
34
|
-
"""Read a boolean from an environment variable. Unset/empty
|
|
39
|
+
"""Read a boolean from an environment variable. Unset/empty → *default*; otherwise ``false|0|no|off`` (case-insensitive) → False, anything else → True."""
|
|
35
40
|
raw = os.environ.get(name, "")
|
|
36
41
|
if not raw:
|
|
37
42
|
return default
|
|
@@ -73,6 +78,46 @@ async def health_check(request: Request) -> JSONResponse:
|
|
|
73
78
|
)
|
|
74
79
|
|
|
75
80
|
|
|
81
|
+
class BaseUrlMiddleware(BaseHTTPMiddleware):
|
|
82
|
+
async def dispatch(self, request: Request, call_next):
|
|
83
|
+
host = request.headers.get(
|
|
84
|
+
"x-forwarded-host", request.headers.get("host", "127.0.0.1:8000")
|
|
85
|
+
)
|
|
86
|
+
proto = request.headers.get("x-forwarded-proto", "http")
|
|
87
|
+
base_url = f"{proto}://{host}"
|
|
88
|
+
|
|
89
|
+
token = set_mcp_base_url(base_url)
|
|
90
|
+
try:
|
|
91
|
+
return await call_next(request)
|
|
92
|
+
finally:
|
|
93
|
+
reset_mcp_base_url(token)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# Ensure FastMCP applies the HTTP middleware to the actual served Starlette app.
|
|
97
|
+
_original_http_app = mcp.http_app
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _http_app_with_base_url(*args, middleware=None, **kwargs):
|
|
101
|
+
merged_middleware = [Middleware(BaseUrlMiddleware), *(middleware or [])]
|
|
102
|
+
return _original_http_app(*args, middleware=merged_middleware, **kwargs)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
mcp.http_app = _http_app_with_base_url
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Custom route to serve public artifacts
|
|
109
|
+
@mcp.custom_route("/artifacts/{filename}", methods=["GET"])
|
|
110
|
+
async def get_public_artifact(request: Request) -> Response:
|
|
111
|
+
"""Serve downloaded artifacts and source files via the public URL."""
|
|
112
|
+
filename = request.path_params["filename"]
|
|
113
|
+
# Clean the path to prevent directory traversal
|
|
114
|
+
safe_name = Path(filename).name
|
|
115
|
+
filepath = PUBLIC_DIR / safe_name
|
|
116
|
+
if not filepath.exists() or not filepath.is_file():
|
|
117
|
+
return Response("File not found", status_code=404)
|
|
118
|
+
return FileResponse(str(filepath))
|
|
119
|
+
|
|
120
|
+
|
|
76
121
|
def _register_tools() -> None:
|
|
77
122
|
"""Import and register all tools from the modular tools package."""
|
|
78
123
|
# Import all tool modules to populate the registry
|
|
@@ -169,7 +214,7 @@ Examples:
|
|
|
169
214
|
default=_env_bool("NOTEBOOKLM_MCP_STATELESS", default=True),
|
|
170
215
|
help=(
|
|
171
216
|
"Stateless HTTP sessions (default: true). Avoids MCP SDK double-response crash "
|
|
172
|
-
"(python-sdk#2416). NOTE: this affects the MCP HTTP transport layer only
|
|
217
|
+
"(python-sdk#2416). NOTE: this affects the MCP HTTP transport layer only  "
|
|
173
218
|
"it does NOT control the in-process conversation history cache. To bound the "
|
|
174
219
|
"conversation cache (e.g. for long-lived servers), set "
|
|
175
220
|
"NOTEBOOKLM_CONVERSATION_MAX_TURNS / NOTEBOOKLM_CONVERSATION_MAX_CONVS."
|
|
@@ -241,7 +286,7 @@ Examples:
|
|
|
241
286
|
if not _env_bool("NOTEBOOKLM_ALLOW_EXTERNAL_BIND"):
|
|
242
287
|
print(
|
|
243
288
|
f"SECURITY ERROR: Refusing to bind to non-loopback address '{args.host}'.\n"
|
|
244
|
-
"There is no built-in authentication
|
|
289
|
+
"There is no built-in authentication  binding externally exposes\n"
|
|
245
290
|
"your Google cookies to anyone on the network.\n\n"
|
|
246
291
|
"To override, set: NOTEBOOKLM_ALLOW_EXTERNAL_BIND=1",
|
|
247
292
|
file=sys.stderr,
|