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.
Files changed (208) hide show
  1. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.gitignore +3 -0
  2. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/CHANGELOG.md +29 -0
  3. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/CLAUDE.md +6 -2
  4. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/PKG-INFO +11 -3
  5. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/README.md +10 -2
  6. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/desktop-extension/manifest.json +1 -1
  7. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/AUTHENTICATION.md +7 -2
  8. notebooklm_mcp_cli-0.7.2/docs/CHATGPT_CONNECTOR_SETUP.md +162 -0
  9. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/pyproject.toml +1 -1
  10. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/__init__.py +3 -1
  11. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/auth.py +49 -17
  12. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/base.py +5 -3
  13. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/AGENTS_SECTION.md +1 -1
  14. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/SKILL.md +1 -1
  15. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/server.py +49 -4
  16. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/_utils.py +59 -6
  17. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/auth.py +11 -8
  18. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/downloads.py +40 -2
  19. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/server.py +24 -28
  20. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/sources.py +111 -4
  21. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/studio.py +22 -9
  22. notebooklm_mcp_cli-0.7.2/src/notebooklm_tools/services/auth.py +625 -0
  23. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/downloads.py +71 -0
  24. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/sources.py +262 -0
  25. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/cdp.py +10 -4
  26. notebooklm_mcp_cli-0.7.2/src/notebooklm_tools/utils/env_sanitize.py +20 -0
  27. notebooklm_mcp_cli-0.7.2/tests/services/test_auth_health.py +564 -0
  28. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_auth_service.py +12 -3
  29. notebooklm_mcp_cli-0.7.2/tests/test_e2e_chatgpt_upload_polling.py +68 -0
  30. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_mcp_auth_studio_failures.py +65 -21
  31. notebooklm_mcp_cli-0.7.2/tests/test_mcp_chatgpt_file_download.py +130 -0
  32. notebooklm_mcp_cli-0.7.2/tests/test_mcp_chatgpt_file_upload.py +93 -0
  33. notebooklm_mcp_cli-0.7.2/tests/test_profile_aware_headless_auth.py +84 -0
  34. notebooklm_mcp_cli-0.7.2/tools/run-chatgpt-tunnel.ps1 +38 -0
  35. notebooklm_mcp_cli-0.7.2/tools/run-cloudflare-chatgpt.ps1 +34 -0
  36. notebooklm_mcp_cli-0.7.0/src/notebooklm_tools/services/auth.py +0 -146
  37. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.github/workflows/lint-test.yml +0 -0
  38. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.github/workflows/publish.yml +0 -0
  39. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.github/workflows/release-gate.yml +0 -0
  40. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/.github/workflows/version-check.yml +0 -0
  41. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/AGENTS.md +0 -0
  42. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/CONTRIBUTING.md +0 -0
  43. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/GEMINI.md +0 -0
  44. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/LICENSE +0 -0
  45. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/desktop-extension/run_server.py +0 -0
  46. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/API_REFERENCE.md +0 -0
  47. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/CLI_GUIDE.md +0 -0
  48. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/FILE_UPLOAD_IMPLEMENTATION.md +0 -0
  49. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/GETTING_STARTED.md +0 -0
  50. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/KNOWN_ISSUES.md +0 -0
  51. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/MCP_CLI_TEST_PLAN.md +0 -0
  52. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/MCP_GUIDE.md +0 -0
  53. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/MULTI_USER_ANALYSIS.md +0 -0
  54. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/WSL_SETUP.md +0 -0
  55. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/docs/media/header.jpg +0 -0
  56. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/run_log.txt +0 -0
  57. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/scripts/build_mcpb.py +0 -0
  58. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/scripts/inject_cookies_and_inspect.py +0 -0
  59. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/scripts/inspect_upload_dom.py +0 -0
  60. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/scripts/test_label_rpcs.py +0 -0
  61. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/__init__.py +0 -0
  62. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/ai_docs.py +0 -0
  63. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/__init__.py +0 -0
  64. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/alias.py +0 -0
  65. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/batch.py +0 -0
  66. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/chat.py +0 -0
  67. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/config.py +0 -0
  68. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/cross.py +0 -0
  69. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/doctor.py +0 -0
  70. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/download.py +0 -0
  71. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/export.py +0 -0
  72. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/label.py +0 -0
  73. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/note.py +0 -0
  74. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/notebook.py +0 -0
  75. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/pipeline.py +0 -0
  76. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/repl.py +0 -0
  77. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/research.py +0 -0
  78. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/setup.py +0 -0
  79. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/share.py +0 -0
  80. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/skill.py +0 -0
  81. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/source.py +0 -0
  82. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/studio.py +0 -0
  83. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/tag.py +0 -0
  84. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/commands/verbs.py +0 -0
  85. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/formatters.py +0 -0
  86. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/main.py +0 -0
  87. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/cli/utils.py +0 -0
  88. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/__init__.py +0 -0
  89. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/alias.py +0 -0
  90. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/client.py +0 -0
  91. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/constants.py +0 -0
  92. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/conversation.py +0 -0
  93. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/data_types.py +0 -0
  94. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/download.py +0 -0
  95. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/errors.py +0 -0
  96. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/exceptions.py +0 -0
  97. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/exports.py +0 -0
  98. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/labels.py +0 -0
  99. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/models.py +0 -0
  100. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/notebooks.py +0 -0
  101. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/notes.py +0 -0
  102. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/research.py +0 -0
  103. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/retry.py +0 -0
  104. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/sharing.py +0 -0
  105. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/sources.py +0 -0
  106. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/studio.py +0 -0
  107. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/core/utils.py +0 -0
  108. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/command_reference.md +0 -0
  109. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/studio-prompt-examples.md +0 -0
  110. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/studio-prompting-guide.md +0 -0
  111. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/troubleshooting.md +0 -0
  112. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/data/references/workflows.md +0 -0
  113. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/__init__.py +0 -0
  114. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/__init__.py +0 -0
  115. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/batch.py +0 -0
  116. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/chat.py +0 -0
  117. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/cross_notebook.py +0 -0
  118. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/exports.py +0 -0
  119. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/labels.py +0 -0
  120. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/notebooks.py +0 -0
  121. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/notes.py +0 -0
  122. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/pipeline.py +0 -0
  123. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/research.py +0 -0
  124. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/sharing.py +0 -0
  125. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/smart_select.py +0 -0
  126. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/mcp/tools/studio_advanced.py +0 -0
  127. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/__init__.py +0 -0
  128. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/_compat.py +0 -0
  129. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/batch.py +0 -0
  130. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/chat.py +0 -0
  131. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/cross_notebook.py +0 -0
  132. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/errors.py +0 -0
  133. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/exports.py +0 -0
  134. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/labels.py +0 -0
  135. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/notebooks.py +0 -0
  136. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/notes.py +0 -0
  137. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/pipeline.py +0 -0
  138. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/research.py +0 -0
  139. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/sharing.py +0 -0
  140. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/smart_select.py +0 -0
  141. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/services/studio.py +0 -0
  142. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/__init__.py +0 -0
  143. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/auth_browser.py +0 -0
  144. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/browser.py +0 -0
  145. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/config.py +0 -0
  146. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/io_encoding.py +0 -0
  147. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/src/notebooklm_tools/utils/wsl.py +0 -0
  148. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_formatters.py +0 -0
  149. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_login.py +0 -0
  150. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_repl.py +0 -0
  151. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_setup_github_copilot.py +0 -0
  152. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_setup_opencode.py +0 -0
  153. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_source_add.py +0 -0
  154. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_studio_cli.py +0 -0
  155. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_verbs_defaults.py +0 -0
  156. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/cli/test_verbs_parity.py +0 -0
  157. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_alias.py +0 -0
  158. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_auth_check.py +0 -0
  159. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_auth_guard.py +0 -0
  160. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_base.py +0 -0
  161. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_conversation.py +0 -0
  162. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_data_types.py +0 -0
  163. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_download.py +0 -0
  164. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_errors.py +0 -0
  165. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_notebooks.py +0 -0
  166. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_notes.py +0 -0
  167. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_research.py +0 -0
  168. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_retry.py +0 -0
  169. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_sharing.py +0 -0
  170. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_source_reconciliation.py +0 -0
  171. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_sources.py +0 -0
  172. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_studio.py +0 -0
  173. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_thread_safety.py +0 -0
  174. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_url_source_fallback.py +0 -0
  175. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/core/test_utils.py +0 -0
  176. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/manual/test_fastmcp_stdio.py +0 -0
  177. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/manual/test_revise_slide_deck.py +0 -0
  178. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/__init__.py +0 -0
  179. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_batch.py +0 -0
  180. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_chat.py +0 -0
  181. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_cross_notebook.py +0 -0
  182. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_downloads.py +0 -0
  183. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_errors.py +0 -0
  184. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_exports.py +0 -0
  185. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_notebooks.py +0 -0
  186. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_notes.py +0 -0
  187. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_pipeline.py +0 -0
  188. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_research.py +0 -0
  189. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_sharing.py +0 -0
  190. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_smart_select.py +0 -0
  191. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_sources.py +0 -0
  192. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/services/test_studio.py +0 -0
  193. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_api_client.py +0 -0
  194. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_auth_browser.py +0 -0
  195. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_auth_migration.py +0 -0
  196. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_cdp_proxy_bypass.py +0 -0
  197. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_cdp_url_check.py +0 -0
  198. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_coerce_list.py +0 -0
  199. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_cookie_parsing.py +0 -0
  200. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_download_integration.py +0 -0
  201. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_e2e.py +0 -0
  202. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_file_upload.py +0 -0
  203. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_io_encoding_windows.py +0 -0
  204. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_mcp_e2e.py +0 -0
  205. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_mcp_file_upload.py +0 -0
  206. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_mcp_studio.py +0 -0
  207. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_research_polling.py +0 -0
  208. {notebooklm_mcp_cli-0.7.0 → notebooklm_mcp_cli-0.7.2}/tests/test_snap.py +0 -0
@@ -83,6 +83,9 @@ notebooklm-mcp-*.mcpb
83
83
  # GitHub CLI temp files
84
84
  .gh_comment_body.md
85
85
 
86
+ # Local ad-hoc test scripts
87
+ scratch/
88
+
86
89
  # PR diff/patch scratch files
87
90
  *.diff
88
91
  *.patch
@@ -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.0
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
  [![Total downloads](https://static.pepy.tech/badge/notebooklm-mcp-cli)](https://pepy.tech/projects/notebooklm-mcp-cli)
38
38
  [![Python](https://img.shields.io/pypi/pyversions/notebooklm-mcp-cli)](https://pypi.org/project/notebooklm-mcp-cli/)
39
39
  [![License](https://img.shields.io/pypi/l/notebooklm-mcp-cli)](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
+ [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-FFDD00?style=flat-square&logo=buy-me-a-coffee&logoColor=black)](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
  [![Total downloads](https://static.pepy.tech/badge/notebooklm-mcp-cli)](https://pepy.tech/projects/notebooklm-mcp-cli)
8
8
  [![Python](https://img.shields.io/pypi/pyversions/notebooklm-mcp-cli)](https://pypi.org/project/notebooklm-mcp-cli/)
9
9
  [![License](https://img.shields.io/pypi/l/notebooklm-mcp-cli)](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
+ [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-FFDD00?style=flat-square&logo=buy-me-a-coffee&logoColor=black)](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.0",
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 after a best-effort *live* check against NotebookLM.
274
- Knowing the difference matters: a `stale` status means you must re-auth, but
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.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "notebooklm-mcp-cli"
7
- version = "0.7.0"
7
+ version = "0.7.2"
8
8
  description = "Unified CLI and MCP server for Google NotebookLM"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -4,7 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any
6
6
 
7
- __version__ = "0.7.0"
7
+ import notebooklm_tools.utils.env_sanitize as _env_sanitize # noqa: F401
8
+
9
+ __version__ = "0.7.2"
8
10
 
9
11
  __all__ = ["NotebookLMClient", "__version__"]
10
12
 
@@ -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
- extracted_at=profile.last_validated.timestamp()
99
- if profile.last_validated
100
- else time.time(),
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=data.get("cookies", [])
330
- if isinstance(data.get("cookies"), list)
331
- else data.get("cookies", {}),
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 AuthenticationError, ProfileNotFoundError
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=datetime.fromisoformat(metadata["last_validated"])
392
- if metadata.get("last_validated")
393
- else None,
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, live=False, profile=profile, checked_at=p.last_validated.timestamp()
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.cdp import run_headless_auth
937
+ from notebooklm_tools.utils.auth_browser import run_headless_auth
938
+ from notebooklm_tools.utils.config import get_config
938
939
 
939
- tokens = run_headless_auth()
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,5 +1,5 @@
1
1
  <!-- nlm-skill-start -->
2
- <!-- nlm-version: 0.7.0 -->
2
+ <!-- nlm-version: 0.7.2 -->
3
3
  ## NLM - NotebookLM CLI Expert
4
4
 
5
5
  **Triggers:** "nlm", "notebooklm", "notebook lm", "podcast", "audio overview", "research"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: nlm-skill
3
- version: "0.7.0"
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 *default*; otherwise ``false|0|no|off`` (case-insensitive) False, anything else True."""
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 binding externally exposes\n"
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,