@roomi-fields/notebooklm-mcp 1.3.4 → 1.3.6

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 (96) hide show
  1. package/README.md +164 -715
  2. package/dist/__tests__/cleanup-manager.test.d.ts +2 -0
  3. package/dist/__tests__/cleanup-manager.test.d.ts.map +1 -0
  4. package/dist/__tests__/cleanup-manager.test.js +341 -0
  5. package/dist/__tests__/cleanup-manager.test.js.map +1 -0
  6. package/dist/__tests__/config-parsing.test.d.ts +2 -0
  7. package/dist/__tests__/config-parsing.test.d.ts.map +1 -0
  8. package/dist/__tests__/config-parsing.test.js +338 -0
  9. package/dist/__tests__/config-parsing.test.js.map +1 -0
  10. package/dist/__tests__/config.test.d.ts +2 -0
  11. package/dist/__tests__/config.test.d.ts.map +1 -0
  12. package/dist/__tests__/config.test.js +267 -0
  13. package/dist/__tests__/config.test.js.map +1 -0
  14. package/dist/__tests__/errors.test.d.ts +2 -0
  15. package/dist/__tests__/errors.test.d.ts.map +1 -0
  16. package/dist/__tests__/errors.test.js +166 -0
  17. package/dist/__tests__/errors.test.js.map +1 -0
  18. package/dist/__tests__/logger.test.d.ts +2 -0
  19. package/dist/__tests__/logger.test.d.ts.map +1 -0
  20. package/dist/__tests__/logger.test.js +324 -0
  21. package/dist/__tests__/logger.test.js.map +1 -0
  22. package/dist/__tests__/page-utils.test.d.ts +2 -0
  23. package/dist/__tests__/page-utils.test.d.ts.map +1 -0
  24. package/dist/__tests__/page-utils.test.js +349 -0
  25. package/dist/__tests__/page-utils.test.js.map +1 -0
  26. package/dist/__tests__/setup-verification.test.d.ts +2 -0
  27. package/dist/__tests__/setup-verification.test.d.ts.map +1 -0
  28. package/dist/__tests__/setup-verification.test.js +15 -0
  29. package/dist/__tests__/setup-verification.test.js.map +1 -0
  30. package/dist/__tests__/stealth-utils.test.d.ts +2 -0
  31. package/dist/__tests__/stealth-utils.test.d.ts.map +1 -0
  32. package/dist/__tests__/stealth-utils.test.js +413 -0
  33. package/dist/__tests__/stealth-utils.test.js.map +1 -0
  34. package/dist/__tests__/types.test.d.ts +2 -0
  35. package/dist/__tests__/types.test.d.ts.map +1 -0
  36. package/dist/__tests__/types.test.js +461 -0
  37. package/dist/__tests__/types.test.js.map +1 -0
  38. package/dist/auth/auth-manager.d.ts +2 -2
  39. package/dist/auth/auth-manager.d.ts.map +1 -1
  40. package/dist/auth/auth-manager.js +147 -145
  41. package/dist/auth/auth-manager.js.map +1 -1
  42. package/dist/auto-discovery/auto-discovery.d.ts.map +1 -1
  43. package/dist/auto-discovery/auto-discovery.js +17 -17
  44. package/dist/auto-discovery/auto-discovery.js.map +1 -1
  45. package/dist/cli/de-auth.js +19 -19
  46. package/dist/cli/help.js +60 -60
  47. package/dist/cli/setup-auth.js +39 -39
  48. package/dist/config.d.ts +1 -1
  49. package/dist/config.d.ts.map +1 -1
  50. package/dist/config.js +35 -20
  51. package/dist/config.js.map +1 -1
  52. package/dist/errors.d.ts.map +1 -1
  53. package/dist/errors.js +3 -3
  54. package/dist/errors.js.map +1 -1
  55. package/dist/http-wrapper.js +36 -30
  56. package/dist/http-wrapper.js.map +1 -1
  57. package/dist/index.js +104 -102
  58. package/dist/index.js.map +1 -1
  59. package/dist/library/notebook-library.d.ts +2 -2
  60. package/dist/library/notebook-library.d.ts.map +1 -1
  61. package/dist/library/notebook-library.js +24 -24
  62. package/dist/library/notebook-library.js.map +1 -1
  63. package/dist/session/browser-session.d.ts +4 -4
  64. package/dist/session/browser-session.d.ts.map +1 -1
  65. package/dist/session/browser-session.js +59 -55
  66. package/dist/session/browser-session.js.map +1 -1
  67. package/dist/session/session-manager.d.ts +3 -3
  68. package/dist/session/session-manager.d.ts.map +1 -1
  69. package/dist/session/session-manager.js +14 -14
  70. package/dist/session/session-manager.js.map +1 -1
  71. package/dist/session/shared-context-manager.d.ts +2 -2
  72. package/dist/session/shared-context-manager.d.ts.map +1 -1
  73. package/dist/session/shared-context-manager.js +60 -56
  74. package/dist/session/shared-context-manager.js.map +1 -1
  75. package/dist/tools/index.d.ts +6 -6
  76. package/dist/tools/index.d.ts.map +1 -1
  77. package/dist/tools/index.js +505 -505
  78. package/dist/tools/index.js.map +1 -1
  79. package/dist/types.d.ts +2 -2
  80. package/dist/utils/cleanup-manager.d.ts +1 -1
  81. package/dist/utils/cleanup-manager.d.ts.map +1 -1
  82. package/dist/utils/cleanup-manager.js +90 -92
  83. package/dist/utils/cleanup-manager.js.map +1 -1
  84. package/dist/utils/logger.d.ts +1 -2
  85. package/dist/utils/logger.d.ts.map +1 -1
  86. package/dist/utils/logger.js +15 -15
  87. package/dist/utils/page-utils.d.ts +1 -1
  88. package/dist/utils/page-utils.d.ts.map +1 -1
  89. package/dist/utils/page-utils.js +30 -30
  90. package/dist/utils/page-utils.js.map +1 -1
  91. package/dist/utils/stealth-utils.d.ts +2 -2
  92. package/dist/utils/stealth-utils.d.ts.map +1 -1
  93. package/dist/utils/stealth-utils.js +13 -13
  94. package/dist/utils/stealth-utils.js.map +1 -1
  95. package/docs/CHROME_PROFILE_LIMITATION.md +229 -212
  96. package/package.json +107 -79
@@ -11,112 +11,112 @@
11
11
  *
12
12
  * Based on the Python implementation from tools/*.py
13
13
  */
14
- import { CONFIG, applyBrowserOptions } from "../config.js";
15
- import { log } from "../utils/logger.js";
16
- import { RateLimitError } from "../errors.js";
17
- import { CleanupManager } from "../utils/cleanup-manager.js";
14
+ import { CONFIG, applyBrowserOptions } from '../config.js';
15
+ import { log } from '../utils/logger.js';
16
+ import { RateLimitError } from '../errors.js';
17
+ import { CleanupManager } from '../utils/cleanup-manager.js';
18
18
  /**
19
19
  * Build dynamic tool description for ask_question based on active notebook or library
20
20
  */
21
21
  function buildAskQuestionDescription(library) {
22
22
  const active = library.getActiveNotebook();
23
23
  if (active) {
24
- const topics = active.topics.join(", ");
25
- const useCases = active.use_cases.map((uc) => ` - ${uc}`).join("\n");
26
- return `# Conversational Research Partner (NotebookLM • Gemini 2.5 • Session RAG)
27
-
28
- **Active Notebook:** ${active.name}
29
- **Content:** ${active.description}
30
- **Topics:** ${topics}
31
-
32
- > Auth tip: If login is required, use the prompt 'notebooklm.auth-setup' and then verify with the 'get_health' tool. If authentication later fails (e.g., expired cookies), use the prompt 'notebooklm.auth-repair'.
33
-
34
- ## What This Tool Is
35
- - Full conversational research with Gemini (LLM) grounded on your notebook sources
36
- - Session-based: each follow-up uses prior context for deeper, more precise answers
37
- - Source-cited responses designed to minimize hallucinations
38
-
39
- ## When To Use
40
- ${useCases}
41
-
42
- ## Rules (Important)
43
- - Always prefer continuing an existing session for the same task
44
- - If you start a new thread, create a new session and keep its session_id
45
- - Ask clarifying questions before implementing; do not guess missing details
46
- - If multiple notebooks could apply, propose the top 1–2 and ask which to use
47
- - If task context changes, ask to reset the session or switch notebooks
48
- - If authentication fails, use the prompts 'notebooklm.auth-repair' (or 'notebooklm.auth-setup') and verify with 'get_health'
49
- - After every NotebookLM answer: pause, compare with the user's goal, and only respond if you are 100% sure the information is complete. Otherwise, plan the next NotebookLM question in the same session.
50
-
51
- ## Session Flow (Recommended)
52
- \`\`\`javascript
53
- // 1) Start broad (no session_id → creates one)
54
- ask_question({ question: "Give me an overview of [topic]" })
55
- // ← Save: result.session_id
56
-
57
- // 2) Go specific (same session)
58
- ask_question({ question: "Key APIs/methods?", session_id })
59
-
60
- // 3) Cover pitfalls (same session)
61
- ask_question({ question: "Common edge cases + gotchas?", session_id })
62
-
63
- // 4) Ask for production example (same session)
64
- ask_question({ question: "Show a production-ready example", session_id })
65
- \`\`\`
66
-
67
- ## Automatic Multi-Pass Strategy (Host-driven)
68
- - Simple prompts return once-and-done answers.
69
- - For complex prompts, the host should issue follow-up calls:
70
- 1. Implementation plan (APIs, dependencies, configuration, authentication).
71
- 2. Pitfalls, gaps, validation steps, missing prerequisites.
72
- - Keep the same session_id for all follow-ups, review NotebookLM's answer, and ask more questions until the problem is fully resolved.
73
- - Before replying to the user, double-check: do you truly have everything? If not, queue another ask_question immediately.
74
-
75
- ## 🔥 REAL EXAMPLE
76
-
77
- Task: "Implement error handling in n8n workflow"
78
-
79
- Bad (shallow):
80
- \`\`\`
81
- Q: "How do I handle errors in n8n?"
82
- A: [basic answer]
83
- → Implement → Probably missing edge cases!
84
- \`\`\`
85
-
86
- Good (deep):
87
- \`\`\`
88
- Q1: "What are n8n's error handling mechanisms?" (session created)
89
- A1: [Overview of error handling]
90
-
91
- Q2: "What's the recommended pattern for API errors?" (same session)
92
- A2: [Specific patterns, uses context from Q1]
93
-
94
- Q3: "How do I handle retry logic and timeouts?" (same session)
95
- A3: [Detailed approach, builds on Q1+Q2]
96
-
97
- Q4: "Show me a production example with all these patterns" (same session)
98
- A4: [Complete example with full context]
99
-
100
- → NOW implement with confidence!
101
- \`\`\`
102
-
103
- ## Notebook Selection
104
- - Default: active notebook (${active.id})
105
- - Or set notebook_id to use a library notebook
106
- - Or set notebook_url for ad-hoc notebooks (not in library)
24
+ const topics = active.topics.join(', ');
25
+ const useCases = active.use_cases.map((uc) => ` - ${uc}`).join('\n');
26
+ return `# Conversational Research Partner (NotebookLM • Gemini 2.5 • Session RAG)
27
+
28
+ **Active Notebook:** ${active.name}
29
+ **Content:** ${active.description}
30
+ **Topics:** ${topics}
31
+
32
+ > Auth tip: If login is required, use the prompt 'notebooklm.auth-setup' and then verify with the 'get_health' tool. If authentication later fails (e.g., expired cookies), use the prompt 'notebooklm.auth-repair'.
33
+
34
+ ## What This Tool Is
35
+ - Full conversational research with Gemini (LLM) grounded on your notebook sources
36
+ - Session-based: each follow-up uses prior context for deeper, more precise answers
37
+ - Source-cited responses designed to minimize hallucinations
38
+
39
+ ## When To Use
40
+ ${useCases}
41
+
42
+ ## Rules (Important)
43
+ - Always prefer continuing an existing session for the same task
44
+ - If you start a new thread, create a new session and keep its session_id
45
+ - Ask clarifying questions before implementing; do not guess missing details
46
+ - If multiple notebooks could apply, propose the top 1–2 and ask which to use
47
+ - If task context changes, ask to reset the session or switch notebooks
48
+ - If authentication fails, use the prompts 'notebooklm.auth-repair' (or 'notebooklm.auth-setup') and verify with 'get_health'
49
+ - After every NotebookLM answer: pause, compare with the user's goal, and only respond if you are 100% sure the information is complete. Otherwise, plan the next NotebookLM question in the same session.
50
+
51
+ ## Session Flow (Recommended)
52
+ \`\`\`javascript
53
+ // 1) Start broad (no session_id → creates one)
54
+ ask_question({ question: "Give me an overview of [topic]" })
55
+ // ← Save: result.session_id
56
+
57
+ // 2) Go specific (same session)
58
+ ask_question({ question: "Key APIs/methods?", session_id })
59
+
60
+ // 3) Cover pitfalls (same session)
61
+ ask_question({ question: "Common edge cases + gotchas?", session_id })
62
+
63
+ // 4) Ask for production example (same session)
64
+ ask_question({ question: "Show a production-ready example", session_id })
65
+ \`\`\`
66
+
67
+ ## Automatic Multi-Pass Strategy (Host-driven)
68
+ - Simple prompts return once-and-done answers.
69
+ - For complex prompts, the host should issue follow-up calls:
70
+ 1. Implementation plan (APIs, dependencies, configuration, authentication).
71
+ 2. Pitfalls, gaps, validation steps, missing prerequisites.
72
+ - Keep the same session_id for all follow-ups, review NotebookLM's answer, and ask more questions until the problem is fully resolved.
73
+ - Before replying to the user, double-check: do you truly have everything? If not, queue another ask_question immediately.
74
+
75
+ ## 🔥 REAL EXAMPLE
76
+
77
+ Task: "Implement error handling in n8n workflow"
78
+
79
+ Bad (shallow):
80
+ \`\`\`
81
+ Q: "How do I handle errors in n8n?"
82
+ A: [basic answer]
83
+ → Implement → Probably missing edge cases!
84
+ \`\`\`
85
+
86
+ Good (deep):
87
+ \`\`\`
88
+ Q1: "What are n8n's error handling mechanisms?" (session created)
89
+ A1: [Overview of error handling]
90
+
91
+ Q2: "What's the recommended pattern for API errors?" (same session)
92
+ A2: [Specific patterns, uses context from Q1]
93
+
94
+ Q3: "How do I handle retry logic and timeouts?" (same session)
95
+ A3: [Detailed approach, builds on Q1+Q2]
96
+
97
+ Q4: "Show me a production example with all these patterns" (same session)
98
+ A4: [Complete example with full context]
99
+
100
+ → NOW implement with confidence!
101
+ \`\`\`
102
+
103
+ ## Notebook Selection
104
+ - Default: active notebook (${active.id})
105
+ - Or set notebook_id to use a library notebook
106
+ - Or set notebook_url for ad-hoc notebooks (not in library)
107
107
  - If ambiguous which notebook fits, ASK the user which to use`;
108
108
  }
109
109
  else {
110
- return `# Conversational Research Partner (NotebookLM • Gemini 2.5 • Session RAG)
111
-
112
- ## No Active Notebook
113
- - Visit https://notebooklm.google to create a notebook and get a share link
114
- - Use **add_notebook** to add it to your library (explains how to get the link)
115
- - Use **list_notebooks** to show available sources
116
- - Use **select_notebook** to set one active
117
-
118
- > Auth tip: If login is required, use the prompt 'notebooklm.auth-setup' and then verify with the 'get_health' tool. If authentication later fails (e.g., expired cookies), use the prompt 'notebooklm.auth-repair'.
119
-
110
+ return `# Conversational Research Partner (NotebookLM • Gemini 2.5 • Session RAG)
111
+
112
+ ## No Active Notebook
113
+ - Visit https://notebooklm.google to create a notebook and get a share link
114
+ - Use **add_notebook** to add it to your library (explains how to get the link)
115
+ - Use **list_notebooks** to show available sources
116
+ - Use **select_notebook** to set one active
117
+
118
+ > Auth tip: If login is required, use the prompt 'notebooklm.auth-setup' and then verify with the 'get_health' tool. If authentication later fails (e.g., expired cookies), use the prompt 'notebooklm.auth-repair'.
119
+
120
120
  Tip: Tell the user you can manage NotebookLM library and ask which notebook to use for the current task.`;
121
121
  }
122
122
  }
@@ -126,495 +126,495 @@ Tip: Tell the user you can manage NotebookLM library and ask which notebook to u
126
126
  export function buildToolDefinitions(library) {
127
127
  return [
128
128
  {
129
- name: "ask_question",
129
+ name: 'ask_question',
130
130
  description: buildAskQuestionDescription(library),
131
131
  inputSchema: {
132
- type: "object",
132
+ type: 'object',
133
133
  properties: {
134
134
  question: {
135
- type: "string",
136
- description: "The question to ask NotebookLM",
135
+ type: 'string',
136
+ description: 'The question to ask NotebookLM',
137
137
  },
138
138
  session_id: {
139
- type: "string",
140
- description: "Optional session ID for contextual conversations. If omitted, a new session is created.",
139
+ type: 'string',
140
+ description: 'Optional session ID for contextual conversations. If omitted, a new session is created.',
141
141
  },
142
142
  notebook_id: {
143
- type: "string",
144
- description: "Optional notebook ID from your library. If omitted, uses the active notebook. " +
145
- "Use list_notebooks to see available notebooks.",
143
+ type: 'string',
144
+ description: 'Optional notebook ID from your library. If omitted, uses the active notebook. ' +
145
+ 'Use list_notebooks to see available notebooks.',
146
146
  },
147
147
  notebook_url: {
148
- type: "string",
149
- description: "Optional notebook URL (overrides notebook_id). Use this for ad-hoc queries to notebooks not in your library.",
148
+ type: 'string',
149
+ description: 'Optional notebook URL (overrides notebook_id). Use this for ad-hoc queries to notebooks not in your library.',
150
150
  },
151
151
  show_browser: {
152
- type: "boolean",
153
- description: "Show browser window for debugging (simple version). " +
154
- "For advanced control (typing speed, stealth, etc.), use browser_options instead.",
152
+ type: 'boolean',
153
+ description: 'Show browser window for debugging (simple version). ' +
154
+ 'For advanced control (typing speed, stealth, etc.), use browser_options instead.',
155
155
  },
156
156
  browser_options: {
157
- type: "object",
158
- description: "Optional browser behavior settings. Claude can control everything: " +
159
- "visibility, typing speed, stealth mode, timeouts. Useful for debugging or fine-tuning.",
157
+ type: 'object',
158
+ description: 'Optional browser behavior settings. Claude can control everything: ' +
159
+ 'visibility, typing speed, stealth mode, timeouts. Useful for debugging or fine-tuning.',
160
160
  properties: {
161
161
  show: {
162
- type: "boolean",
163
- description: "Show browser window (default: from ENV or false)",
162
+ type: 'boolean',
163
+ description: 'Show browser window (default: from ENV or false)',
164
164
  },
165
165
  headless: {
166
- type: "boolean",
167
- description: "Run browser in headless mode (default: true)",
166
+ type: 'boolean',
167
+ description: 'Run browser in headless mode (default: true)',
168
168
  },
169
169
  timeout_ms: {
170
- type: "number",
171
- description: "Browser operation timeout in milliseconds (default: 30000)",
170
+ type: 'number',
171
+ description: 'Browser operation timeout in milliseconds (default: 30000)',
172
172
  },
173
173
  stealth: {
174
- type: "object",
175
- description: "Human-like behavior settings to avoid detection",
174
+ type: 'object',
175
+ description: 'Human-like behavior settings to avoid detection',
176
176
  properties: {
177
177
  enabled: {
178
- type: "boolean",
179
- description: "Master switch for all stealth features (default: true)",
178
+ type: 'boolean',
179
+ description: 'Master switch for all stealth features (default: true)',
180
180
  },
181
181
  random_delays: {
182
- type: "boolean",
183
- description: "Random delays between actions (default: true)",
182
+ type: 'boolean',
183
+ description: 'Random delays between actions (default: true)',
184
184
  },
185
185
  human_typing: {
186
- type: "boolean",
187
- description: "Human-like typing patterns (default: true)",
186
+ type: 'boolean',
187
+ description: 'Human-like typing patterns (default: true)',
188
188
  },
189
189
  mouse_movements: {
190
- type: "boolean",
191
- description: "Realistic mouse movements (default: true)",
190
+ type: 'boolean',
191
+ description: 'Realistic mouse movements (default: true)',
192
192
  },
193
193
  typing_wpm_min: {
194
- type: "number",
195
- description: "Minimum typing speed in WPM (default: 160)",
194
+ type: 'number',
195
+ description: 'Minimum typing speed in WPM (default: 160)',
196
196
  },
197
197
  typing_wpm_max: {
198
- type: "number",
199
- description: "Maximum typing speed in WPM (default: 240)",
198
+ type: 'number',
199
+ description: 'Maximum typing speed in WPM (default: 240)',
200
200
  },
201
201
  delay_min_ms: {
202
- type: "number",
203
- description: "Minimum delay between actions in ms (default: 100)",
202
+ type: 'number',
203
+ description: 'Minimum delay between actions in ms (default: 100)',
204
204
  },
205
205
  delay_max_ms: {
206
- type: "number",
207
- description: "Maximum delay between actions in ms (default: 400)",
206
+ type: 'number',
207
+ description: 'Maximum delay between actions in ms (default: 400)',
208
208
  },
209
209
  },
210
210
  },
211
211
  viewport: {
212
- type: "object",
213
- description: "Browser viewport size",
212
+ type: 'object',
213
+ description: 'Browser viewport size',
214
214
  properties: {
215
215
  width: {
216
- type: "number",
217
- description: "Viewport width in pixels (default: 1920)",
216
+ type: 'number',
217
+ description: 'Viewport width in pixels (default: 1920)',
218
218
  },
219
219
  height: {
220
- type: "number",
221
- description: "Viewport height in pixels (default: 1080)",
220
+ type: 'number',
221
+ description: 'Viewport height in pixels (default: 1080)',
222
222
  },
223
223
  },
224
224
  },
225
225
  },
226
226
  },
227
227
  },
228
- required: ["question"],
228
+ required: ['question'],
229
229
  },
230
230
  },
231
231
  {
232
- name: "auto_discover_notebook",
233
- description: `🚀 AUTO-DISCOVERY — Automatically generate notebook metadata via NotebookLM (RECOMMENDED)
234
-
235
- ## When to Use
236
- - User provides NotebookLM URL and wants quick/automatic setup
237
- - User prefers not to manually specify metadata
238
- - Default choice for adding notebooks
239
-
240
- ## Workflow
241
- 1) User provides NotebookLM URL
242
- 2) Ask confirmation: "Add '[URL]' with auto-generated metadata?"
243
- 3) Call this tool → NotebookLM generates name, description, tags
244
- 4) Show generated metadata to user for review
245
-
246
- ## Benefits
247
- - ✅ 30 seconds vs 5 minutes manual entry
248
- - ✅ Zero-friction notebook addition
249
- - ✅ Consistent metadata quality
250
- - ✅ Discovers topics user might not think of
251
-
252
- ## Example
253
- User: "Add this NotebookLM: https://notebooklm.google.com/notebook/abc123"
254
- You: "Add this notebook with auto-generated metadata?"
255
- User: "Yes"
256
- You: Call auto_discover_notebook(url="https://...")
257
- → Returns: {name: "n8n-workflow-guide", description: "...", tags: [...]}
258
-
259
- ## Fallback
260
- If auto-discovery fails (rare), use add_notebook tool for manual entry.
261
-
262
- ## How to Get a NotebookLM Share Link
263
-
264
- Visit https://notebooklm.google/ → Login (free: 100 notebooks, 50 sources each, 500k words, 50 daily queries)
265
- 1) Click "+ New" (top right) → Upload sources (docs, knowledge)
266
- 2) Click "Share" (top right) → Select "Anyone with the link"
267
- 3) Click "Copy link" (bottom left) → Give this link to Claude
268
-
232
+ name: 'auto_discover_notebook',
233
+ description: `🚀 AUTO-DISCOVERY — Automatically generate notebook metadata via NotebookLM (RECOMMENDED)
234
+
235
+ ## When to Use
236
+ - User provides NotebookLM URL and wants quick/automatic setup
237
+ - User prefers not to manually specify metadata
238
+ - Default choice for adding notebooks
239
+
240
+ ## Workflow
241
+ 1) User provides NotebookLM URL
242
+ 2) Ask confirmation: "Add '[URL]' with auto-generated metadata?"
243
+ 3) Call this tool → NotebookLM generates name, description, tags
244
+ 4) Show generated metadata to user for review
245
+
246
+ ## Benefits
247
+ - ✅ 30 seconds vs 5 minutes manual entry
248
+ - ✅ Zero-friction notebook addition
249
+ - ✅ Consistent metadata quality
250
+ - ✅ Discovers topics user might not think of
251
+
252
+ ## Example
253
+ User: "Add this NotebookLM: https://notebooklm.google.com/notebook/abc123"
254
+ You: "Add this notebook with auto-generated metadata?"
255
+ User: "Yes"
256
+ You: Call auto_discover_notebook(url="https://...")
257
+ → Returns: {name: "n8n-workflow-guide", description: "...", tags: [...]}
258
+
259
+ ## Fallback
260
+ If auto-discovery fails (rare), use add_notebook tool for manual entry.
261
+
262
+ ## How to Get a NotebookLM Share Link
263
+
264
+ Visit https://notebooklm.google/ → Login (free: 100 notebooks, 50 sources each, 500k words, 50 daily queries)
265
+ 1) Click "+ New" (top right) → Upload sources (docs, knowledge)
266
+ 2) Click "Share" (top right) → Select "Anyone with the link"
267
+ 3) Click "Copy link" (bottom left) → Give this link to Claude
268
+
269
269
  (Upgraded: Google AI Pro/Ultra gives 5x higher limits)`,
270
270
  inputSchema: {
271
- type: "object",
271
+ type: 'object',
272
272
  properties: {
273
273
  url: {
274
- type: "string",
275
- description: "The NotebookLM notebook URL",
274
+ type: 'string',
275
+ description: 'The NotebookLM notebook URL',
276
276
  },
277
277
  },
278
- required: ["url"],
278
+ required: ['url'],
279
279
  },
280
280
  },
281
281
  {
282
- name: "add_notebook",
283
- description: `📝 MANUAL ENTRY — Add notebook with manually specified metadata (use auto_discover_notebook instead)
284
-
285
- ## When to Use
286
- - Auto-discovery failed or unavailable
287
- - User has specific metadata requirements
288
- - User prefers manual control
289
-
290
- ## Conversation Workflow (Mandatory)
291
- When the user says: "I have a NotebookLM with X"
292
-
293
- **FIRST:** Try auto_discover_notebook for faster setup
294
- **ONLY IF** user refuses auto-discovery or it fails:
295
-
296
- 1) Ask URL: "What is the NotebookLM URL?"
297
- 2) Ask content: "What knowledge is inside?" (1–2 sentences)
298
- 3) Ask topics: "Which topics does it cover?" (3–5)
299
- 4) Ask use cases: "When should we consult it?"
300
- 5) Propose metadata and confirm:
301
- - Name: [suggested]
302
- - Description: [from user]
303
- - Topics: [list]
304
- - Use cases: [list]
305
- "Add it to your library now?"
306
- 6) Only after explicit "Yes" → call this tool
307
-
308
- ## Rules
309
- - Do not add without user permission
310
- - Prefer auto_discover_notebook when possible
311
- - Do not guess metadata — ask concisely
312
- - Confirm summary before calling the tool
313
-
314
- ## Example
315
- User: "I have a notebook with n8n docs"
316
- You: "Want me to auto-generate the metadata?" (offer auto_discover_notebook first)
317
- User: "No, I'll specify it myself"
318
- You: Ask URL → content → topics → use cases; propose summary
319
- User: "Yes"
320
- You: Call add_notebook
321
-
322
- ## How to Get a NotebookLM Share Link
323
-
324
- Visit https://notebooklm.google/ → Login (free: 100 notebooks, 50 sources each, 500k words, 50 daily queries)
325
- 1) Click "+ New" (top right) → Upload sources (docs, knowledge)
326
- 2) Click "Share" (top right) → Select "Anyone with the link"
327
- 3) Click "Copy link" (bottom left) → Give this link to Claude
328
-
282
+ name: 'add_notebook',
283
+ description: `📝 MANUAL ENTRY — Add notebook with manually specified metadata (use auto_discover_notebook instead)
284
+
285
+ ## When to Use
286
+ - Auto-discovery failed or unavailable
287
+ - User has specific metadata requirements
288
+ - User prefers manual control
289
+
290
+ ## Conversation Workflow (Mandatory)
291
+ When the user says: "I have a NotebookLM with X"
292
+
293
+ **FIRST:** Try auto_discover_notebook for faster setup
294
+ **ONLY IF** user refuses auto-discovery or it fails:
295
+
296
+ 1) Ask URL: "What is the NotebookLM URL?"
297
+ 2) Ask content: "What knowledge is inside?" (1–2 sentences)
298
+ 3) Ask topics: "Which topics does it cover?" (3–5)
299
+ 4) Ask use cases: "When should we consult it?"
300
+ 5) Propose metadata and confirm:
301
+ - Name: [suggested]
302
+ - Description: [from user]
303
+ - Topics: [list]
304
+ - Use cases: [list]
305
+ "Add it to your library now?"
306
+ 6) Only after explicit "Yes" → call this tool
307
+
308
+ ## Rules
309
+ - Do not add without user permission
310
+ - Prefer auto_discover_notebook when possible
311
+ - Do not guess metadata — ask concisely
312
+ - Confirm summary before calling the tool
313
+
314
+ ## Example
315
+ User: "I have a notebook with n8n docs"
316
+ You: "Want me to auto-generate the metadata?" (offer auto_discover_notebook first)
317
+ User: "No, I'll specify it myself"
318
+ You: Ask URL → content → topics → use cases; propose summary
319
+ User: "Yes"
320
+ You: Call add_notebook
321
+
322
+ ## How to Get a NotebookLM Share Link
323
+
324
+ Visit https://notebooklm.google/ → Login (free: 100 notebooks, 50 sources each, 500k words, 50 daily queries)
325
+ 1) Click "+ New" (top right) → Upload sources (docs, knowledge)
326
+ 2) Click "Share" (top right) → Select "Anyone with the link"
327
+ 3) Click "Copy link" (bottom left) → Give this link to Claude
328
+
329
329
  (Upgraded: Google AI Pro/Ultra gives 5x higher limits)`,
330
330
  inputSchema: {
331
- type: "object",
331
+ type: 'object',
332
332
  properties: {
333
333
  url: {
334
- type: "string",
335
- description: "The NotebookLM notebook URL",
334
+ type: 'string',
335
+ description: 'The NotebookLM notebook URL',
336
336
  },
337
337
  name: {
338
- type: "string",
338
+ type: 'string',
339
339
  description: "Display name for the notebook (e.g., 'n8n Documentation')",
340
340
  },
341
341
  description: {
342
- type: "string",
343
- description: "What knowledge/content is in this notebook",
342
+ type: 'string',
343
+ description: 'What knowledge/content is in this notebook',
344
344
  },
345
345
  topics: {
346
- type: "array",
347
- items: { type: "string" },
348
- description: "Topics covered in this notebook",
346
+ type: 'array',
347
+ items: { type: 'string' },
348
+ description: 'Topics covered in this notebook',
349
349
  },
350
350
  content_types: {
351
- type: "array",
352
- items: { type: "string" },
351
+ type: 'array',
352
+ items: { type: 'string' },
353
353
  description: "Types of content (e.g., ['documentation', 'examples', 'best practices'])",
354
354
  },
355
355
  use_cases: {
356
- type: "array",
357
- items: { type: "string" },
356
+ type: 'array',
357
+ items: { type: 'string' },
358
358
  description: "When should Claude use this notebook (e.g., ['Implementing n8n workflows'])",
359
359
  },
360
360
  tags: {
361
- type: "array",
362
- items: { type: "string" },
363
- description: "Optional tags for organization",
361
+ type: 'array',
362
+ items: { type: 'string' },
363
+ description: 'Optional tags for organization',
364
364
  },
365
365
  },
366
- required: ["url", "name", "description", "topics"],
366
+ required: ['url', 'name', 'description', 'topics'],
367
367
  },
368
368
  },
369
369
  {
370
- name: "list_notebooks",
371
- description: "List all library notebooks with metadata (name, topics, use cases, URL). " +
372
- "Use this to present options, then ask which notebook to use for the task.",
370
+ name: 'list_notebooks',
371
+ description: 'List all library notebooks with metadata (name, topics, use cases, URL). ' +
372
+ 'Use this to present options, then ask which notebook to use for the task.',
373
373
  inputSchema: {
374
- type: "object",
374
+ type: 'object',
375
375
  properties: {},
376
376
  },
377
377
  },
378
378
  {
379
- name: "get_notebook",
380
- description: "Get detailed information about a specific notebook by ID",
379
+ name: 'get_notebook',
380
+ description: 'Get detailed information about a specific notebook by ID',
381
381
  inputSchema: {
382
- type: "object",
382
+ type: 'object',
383
383
  properties: {
384
384
  id: {
385
- type: "string",
386
- description: "The notebook ID",
385
+ type: 'string',
386
+ description: 'The notebook ID',
387
387
  },
388
388
  },
389
- required: ["id"],
389
+ required: ['id'],
390
390
  },
391
391
  },
392
392
  {
393
- name: "select_notebook",
394
- description: `Set a notebook as the active default (used when ask_question has no notebook_id).
395
-
396
- ## When To Use
397
- - User switches context: "Let's work on React now"
398
- - User asks explicitly to activate a notebook
399
- - Obvious task change requires another notebook
400
-
401
- ## Auto-Switching
402
- - Safe to auto-switch if the context is clear and you announce it:
403
- "Switching to React notebook for this task..."
404
- - If ambiguous, ask: "Switch to [notebook] for this task?"
405
-
406
- ## Example
407
- User: "Now let's build the React frontend"
393
+ name: 'select_notebook',
394
+ description: `Set a notebook as the active default (used when ask_question has no notebook_id).
395
+
396
+ ## When To Use
397
+ - User switches context: "Let's work on React now"
398
+ - User asks explicitly to activate a notebook
399
+ - Obvious task change requires another notebook
400
+
401
+ ## Auto-Switching
402
+ - Safe to auto-switch if the context is clear and you announce it:
403
+ "Switching to React notebook for this task..."
404
+ - If ambiguous, ask: "Switch to [notebook] for this task?"
405
+
406
+ ## Example
407
+ User: "Now let's build the React frontend"
408
408
  You: "Switching to React notebook..." (call select_notebook)`,
409
409
  inputSchema: {
410
- type: "object",
410
+ type: 'object',
411
411
  properties: {
412
412
  id: {
413
- type: "string",
414
- description: "The notebook ID to activate",
413
+ type: 'string',
414
+ description: 'The notebook ID to activate',
415
415
  },
416
416
  },
417
- required: ["id"],
417
+ required: ['id'],
418
418
  },
419
419
  },
420
420
  {
421
- name: "update_notebook",
422
- description: `Update notebook metadata based on user intent.
423
-
424
- ## Pattern
425
- 1) Identify target notebook and fields (topics, description, use_cases, tags, url)
426
- 2) Propose the exact change back to the user
427
- 3) After explicit confirmation, call this tool
428
-
429
- ## Examples
430
- - User: "React notebook also covers Next.js 14"
431
- You: "Add 'Next.js 14' to topics for React?"
432
- User: "Yes" → call update_notebook
433
-
434
- - User: "Include error handling in n8n description"
435
- You: "Update the n8n description to mention error handling?"
436
- User: "Yes" → call update_notebook
437
-
421
+ name: 'update_notebook',
422
+ description: `Update notebook metadata based on user intent.
423
+
424
+ ## Pattern
425
+ 1) Identify target notebook and fields (topics, description, use_cases, tags, url)
426
+ 2) Propose the exact change back to the user
427
+ 3) After explicit confirmation, call this tool
428
+
429
+ ## Examples
430
+ - User: "React notebook also covers Next.js 14"
431
+ You: "Add 'Next.js 14' to topics for React?"
432
+ User: "Yes" → call update_notebook
433
+
434
+ - User: "Include error handling in n8n description"
435
+ You: "Update the n8n description to mention error handling?"
436
+ User: "Yes" → call update_notebook
437
+
438
438
  Tip: You may update multiple fields at once if requested.`,
439
439
  inputSchema: {
440
- type: "object",
440
+ type: 'object',
441
441
  properties: {
442
442
  id: {
443
- type: "string",
444
- description: "The notebook ID to update",
443
+ type: 'string',
444
+ description: 'The notebook ID to update',
445
445
  },
446
446
  name: {
447
- type: "string",
448
- description: "New display name",
447
+ type: 'string',
448
+ description: 'New display name',
449
449
  },
450
450
  description: {
451
- type: "string",
452
- description: "New description",
451
+ type: 'string',
452
+ description: 'New description',
453
453
  },
454
454
  topics: {
455
- type: "array",
456
- items: { type: "string" },
457
- description: "New topics list",
455
+ type: 'array',
456
+ items: { type: 'string' },
457
+ description: 'New topics list',
458
458
  },
459
459
  content_types: {
460
- type: "array",
461
- items: { type: "string" },
462
- description: "New content types",
460
+ type: 'array',
461
+ items: { type: 'string' },
462
+ description: 'New content types',
463
463
  },
464
464
  use_cases: {
465
- type: "array",
466
- items: { type: "string" },
467
- description: "New use cases",
465
+ type: 'array',
466
+ items: { type: 'string' },
467
+ description: 'New use cases',
468
468
  },
469
469
  tags: {
470
- type: "array",
471
- items: { type: "string" },
472
- description: "New tags",
470
+ type: 'array',
471
+ items: { type: 'string' },
472
+ description: 'New tags',
473
473
  },
474
474
  url: {
475
- type: "string",
476
- description: "New notebook URL",
475
+ type: 'string',
476
+ description: 'New notebook URL',
477
477
  },
478
478
  },
479
- required: ["id"],
479
+ required: ['id'],
480
480
  },
481
481
  },
482
482
  {
483
- name: "remove_notebook",
484
- description: `Dangerous — requires explicit user confirmation.
485
-
486
- ## Confirmation Workflow
487
- 1) User requests removal ("Remove the React notebook")
488
- 2) Look up full name to confirm
489
- 3) Ask: "Remove '[notebook_name]' from your library? (Does not delete the actual NotebookLM notebook)"
490
- 4) Only on explicit "Yes" → call remove_notebook
491
-
492
- Never remove without permission or based on assumptions.
493
-
494
- Example:
495
- User: "Delete the old React notebook"
496
- You: "Remove 'React Best Practices' from your library?"
483
+ name: 'remove_notebook',
484
+ description: `Dangerous — requires explicit user confirmation.
485
+
486
+ ## Confirmation Workflow
487
+ 1) User requests removal ("Remove the React notebook")
488
+ 2) Look up full name to confirm
489
+ 3) Ask: "Remove '[notebook_name]' from your library? (Does not delete the actual NotebookLM notebook)"
490
+ 4) Only on explicit "Yes" → call remove_notebook
491
+
492
+ Never remove without permission or based on assumptions.
493
+
494
+ Example:
495
+ User: "Delete the old React notebook"
496
+ You: "Remove 'React Best Practices' from your library?"
497
497
  User: "Yes" → call remove_notebook`,
498
498
  inputSchema: {
499
- type: "object",
499
+ type: 'object',
500
500
  properties: {
501
501
  id: {
502
- type: "string",
503
- description: "The notebook ID to remove",
502
+ type: 'string',
503
+ description: 'The notebook ID to remove',
504
504
  },
505
505
  },
506
- required: ["id"],
506
+ required: ['id'],
507
507
  },
508
508
  },
509
509
  {
510
- name: "search_notebooks",
511
- description: "Search library by query (name, description, topics, tags). " +
512
- "Use to propose relevant notebooks for the task and then ask which to use.",
510
+ name: 'search_notebooks',
511
+ description: 'Search library by query (name, description, topics, tags). ' +
512
+ 'Use to propose relevant notebooks for the task and then ask which to use.',
513
513
  inputSchema: {
514
- type: "object",
514
+ type: 'object',
515
515
  properties: {
516
516
  query: {
517
- type: "string",
518
- description: "Search query",
517
+ type: 'string',
518
+ description: 'Search query',
519
519
  },
520
520
  },
521
- required: ["query"],
521
+ required: ['query'],
522
522
  },
523
523
  },
524
524
  {
525
- name: "get_library_stats",
526
- description: "Get statistics about your notebook library (total notebooks, usage, etc.)",
525
+ name: 'get_library_stats',
526
+ description: 'Get statistics about your notebook library (total notebooks, usage, etc.)',
527
527
  inputSchema: {
528
- type: "object",
528
+ type: 'object',
529
529
  properties: {},
530
530
  },
531
531
  },
532
532
  {
533
- name: "list_sessions",
534
- description: "List all active sessions with stats (age, message count, last activity). " +
535
- "Use to continue the most relevant session instead of starting from scratch.",
533
+ name: 'list_sessions',
534
+ description: 'List all active sessions with stats (age, message count, last activity). ' +
535
+ 'Use to continue the most relevant session instead of starting from scratch.',
536
536
  inputSchema: {
537
- type: "object",
537
+ type: 'object',
538
538
  properties: {},
539
539
  },
540
540
  },
541
541
  {
542
- name: "close_session",
543
- description: "Close a specific session by session ID. Ask before closing if the user might still need it.",
542
+ name: 'close_session',
543
+ description: 'Close a specific session by session ID. Ask before closing if the user might still need it.',
544
544
  inputSchema: {
545
- type: "object",
545
+ type: 'object',
546
546
  properties: {
547
547
  session_id: {
548
- type: "string",
549
- description: "The session ID to close",
548
+ type: 'string',
549
+ description: 'The session ID to close',
550
550
  },
551
551
  },
552
- required: ["session_id"],
552
+ required: ['session_id'],
553
553
  },
554
554
  },
555
555
  {
556
- name: "reset_session",
556
+ name: 'reset_session',
557
557
  description: "Reset a session's chat history (keep same session ID). " +
558
- "Use for a clean slate when the task changes; ask the user before resetting.",
558
+ 'Use for a clean slate when the task changes; ask the user before resetting.',
559
559
  inputSchema: {
560
- type: "object",
560
+ type: 'object',
561
561
  properties: {
562
562
  session_id: {
563
- type: "string",
564
- description: "The session ID to reset",
563
+ type: 'string',
564
+ description: 'The session ID to reset',
565
565
  },
566
566
  },
567
- required: ["session_id"],
567
+ required: ['session_id'],
568
568
  },
569
569
  },
570
570
  {
571
- name: "get_health",
572
- description: "Get server health status including authentication state, active sessions, and configuration. " +
573
- "Use this to verify the server is ready before starting research workflows.\n\n" +
574
- "If authenticated=false and having persistent issues:\n" +
575
- "Consider running cleanup_data(preserve_library=true) + setup_auth for fresh start with clean browser session.",
571
+ name: 'get_health',
572
+ description: 'Get server health status including authentication state, active sessions, and configuration. ' +
573
+ 'Use this to verify the server is ready before starting research workflows.\n\n' +
574
+ 'If authenticated=false and having persistent issues:\n' +
575
+ 'Consider running cleanup_data(preserve_library=true) + setup_auth for fresh start with clean browser session.',
576
576
  inputSchema: {
577
- type: "object",
577
+ type: 'object',
578
578
  properties: {},
579
579
  },
580
580
  },
581
581
  {
582
- name: "setup_auth",
583
- description: "Google authentication for NotebookLM access - opens a browser window for manual login to your Google account. " +
584
- "Returns immediately after opening the browser. You have up to 10 minutes to complete the login. " +
582
+ name: 'setup_auth',
583
+ description: 'Google authentication for NotebookLM access - opens a browser window for manual login to your Google account. ' +
584
+ 'Returns immediately after opening the browser. You have up to 10 minutes to complete the login. ' +
585
585
  "Use 'get_health' tool afterwards to verify authentication was saved successfully. " +
586
- "Use this for first-time authentication or when auto-login credentials are not available. " +
587
- "IMPORTANT: If already authenticated, this tool will skip re-authentication. " +
586
+ 'Use this for first-time authentication or when auto-login credentials are not available. ' +
587
+ 'IMPORTANT: If already authenticated, this tool will skip re-authentication. ' +
588
588
  "For switching accounts or rate-limit workarounds, use 're_auth' tool instead.\n\n" +
589
- "TROUBLESHOOTING for persistent auth issues:\n" +
590
- "If setup_auth fails or you encounter browser/session issues:\n" +
591
- "1. Ask user to close ALL Chrome/Chromium instances\n" +
592
- "2. Run cleanup_data(confirm=true, preserve_library=true) to clean old data\n" +
593
- "3. Run setup_auth again for fresh start\n" +
594
- "This helps resolve conflicts from old browser sessions and installation data.",
589
+ 'TROUBLESHOOTING for persistent auth issues:\n' +
590
+ 'If setup_auth fails or you encounter browser/session issues:\n' +
591
+ '1. Ask user to close ALL Chrome/Chromium instances\n' +
592
+ '2. Run cleanup_data(confirm=true, preserve_library=true) to clean old data\n' +
593
+ '3. Run setup_auth again for fresh start\n' +
594
+ 'This helps resolve conflicts from old browser sessions and installation data.',
595
595
  inputSchema: {
596
- type: "object",
596
+ type: 'object',
597
597
  properties: {
598
598
  show_browser: {
599
- type: "boolean",
600
- description: "Show browser window (simple version). Default: true for setup. " +
601
- "For advanced control, use browser_options instead.",
599
+ type: 'boolean',
600
+ description: 'Show browser window (simple version). Default: true for setup. ' +
601
+ 'For advanced control, use browser_options instead.',
602
602
  },
603
603
  browser_options: {
604
- type: "object",
605
- description: "Optional browser settings. Control visibility, timeouts, and stealth behavior.",
604
+ type: 'object',
605
+ description: 'Optional browser settings. Control visibility, timeouts, and stealth behavior.',
606
606
  properties: {
607
607
  show: {
608
- type: "boolean",
609
- description: "Show browser window (default: true for setup)",
608
+ type: 'boolean',
609
+ description: 'Show browser window (default: true for setup)',
610
610
  },
611
611
  headless: {
612
- type: "boolean",
613
- description: "Run browser in headless mode (default: false for setup)",
612
+ type: 'boolean',
613
+ description: 'Run browser in headless mode (default: false for setup)',
614
614
  },
615
615
  timeout_ms: {
616
- type: "number",
617
- description: "Browser operation timeout in milliseconds (default: 30000)",
616
+ type: 'number',
617
+ description: 'Browser operation timeout in milliseconds (default: 30000)',
618
618
  },
619
619
  },
620
620
  },
@@ -622,65 +622,65 @@ User: "Yes" → call remove_notebook`,
622
622
  },
623
623
  },
624
624
  {
625
- name: "de_auth",
626
- description: "De-authenticate (logout) - Clears all authentication data for security. " +
627
- "Use this when:\n" +
628
- "- User wants to log out for security reasons\n" +
629
- "- Removing credentials before shutting down\n" +
630
- "- Clearing auth without immediately re-authenticating\n\n" +
631
- "This will:\n" +
632
- "1. Close all active browser sessions\n" +
633
- "2. Delete all saved authentication data (cookies, Chrome profile)\n" +
634
- "3. Preserve notebook library and other data\n\n" +
635
- "IMPORTANT: After de_auth, the server will need re-authentication via setup_auth or re_auth before making queries.\n\n" +
625
+ name: 'de_auth',
626
+ description: 'De-authenticate (logout) - Clears all authentication data for security. ' +
627
+ 'Use this when:\n' +
628
+ '- User wants to log out for security reasons\n' +
629
+ '- Removing credentials before shutting down\n' +
630
+ '- Clearing auth without immediately re-authenticating\n\n' +
631
+ 'This will:\n' +
632
+ '1. Close all active browser sessions\n' +
633
+ '2. Delete all saved authentication data (cookies, Chrome profile)\n' +
634
+ '3. Preserve notebook library and other data\n\n' +
635
+ 'IMPORTANT: After de_auth, the server will need re-authentication via setup_auth or re_auth before making queries.\n\n' +
636
636
  "Use 'get_health' to verify de-authentication was successful (authenticated: false).",
637
637
  inputSchema: {
638
- type: "object",
638
+ type: 'object',
639
639
  properties: {},
640
640
  },
641
641
  },
642
642
  {
643
- name: "re_auth",
644
- description: "Switch to a different Google account or re-authenticate. " +
645
- "Use this when:\n" +
646
- "- NotebookLM rate limit is reached (50 queries/day for free accounts)\n" +
647
- "- You want to switch to a different Google account\n" +
648
- "- Authentication is broken and needs a fresh start\n\n" +
649
- "This will:\n" +
650
- "1. Close all active browser sessions\n" +
651
- "2. Delete all saved authentication data (cookies, Chrome profile)\n" +
652
- "3. Open browser for fresh Google login\n\n" +
643
+ name: 're_auth',
644
+ description: 'Switch to a different Google account or re-authenticate. ' +
645
+ 'Use this when:\n' +
646
+ '- NotebookLM rate limit is reached (50 queries/day for free accounts)\n' +
647
+ '- You want to switch to a different Google account\n' +
648
+ '- Authentication is broken and needs a fresh start\n\n' +
649
+ 'This will:\n' +
650
+ '1. Close all active browser sessions\n' +
651
+ '2. Delete all saved authentication data (cookies, Chrome profile)\n' +
652
+ '3. Open browser for fresh Google login\n\n' +
653
653
  "After completion, use 'get_health' to verify authentication.\n\n" +
654
- "TROUBLESHOOTING for persistent auth issues:\n" +
655
- "If re_auth fails repeatedly:\n" +
656
- "1. Ask user to close ALL Chrome/Chromium instances\n" +
657
- "2. Run cleanup_data(confirm=false, preserve_library=true) to preview old files\n" +
658
- "3. Run cleanup_data(confirm=true, preserve_library=true) to clean everything except library\n" +
659
- "4. Run re_auth again for completely fresh start\n" +
660
- "This removes old installation data and browser sessions that can cause conflicts.",
654
+ 'TROUBLESHOOTING for persistent auth issues:\n' +
655
+ 'If re_auth fails repeatedly:\n' +
656
+ '1. Ask user to close ALL Chrome/Chromium instances\n' +
657
+ '2. Run cleanup_data(confirm=false, preserve_library=true) to preview old files\n' +
658
+ '3. Run cleanup_data(confirm=true, preserve_library=true) to clean everything except library\n' +
659
+ '4. Run re_auth again for completely fresh start\n' +
660
+ 'This removes old installation data and browser sessions that can cause conflicts.',
661
661
  inputSchema: {
662
- type: "object",
662
+ type: 'object',
663
663
  properties: {
664
664
  show_browser: {
665
- type: "boolean",
666
- description: "Show browser window (simple version). Default: true for re-auth. " +
667
- "For advanced control, use browser_options instead.",
665
+ type: 'boolean',
666
+ description: 'Show browser window (simple version). Default: true for re-auth. ' +
667
+ 'For advanced control, use browser_options instead.',
668
668
  },
669
669
  browser_options: {
670
- type: "object",
671
- description: "Optional browser settings. Control visibility, timeouts, and stealth behavior.",
670
+ type: 'object',
671
+ description: 'Optional browser settings. Control visibility, timeouts, and stealth behavior.',
672
672
  properties: {
673
673
  show: {
674
- type: "boolean",
675
- description: "Show browser window (default: true for re-auth)",
674
+ type: 'boolean',
675
+ description: 'Show browser window (default: true for re-auth)',
676
676
  },
677
677
  headless: {
678
- type: "boolean",
679
- description: "Run browser in headless mode (default: false for re-auth)",
678
+ type: 'boolean',
679
+ description: 'Run browser in headless mode (default: false for re-auth)',
680
680
  },
681
681
  timeout_ms: {
682
- type: "number",
683
- description: "Browser operation timeout in milliseconds (default: 30000)",
682
+ type: 'number',
683
+ description: 'Browser operation timeout in milliseconds (default: 30000)',
684
684
  },
685
685
  },
686
686
  },
@@ -688,42 +688,42 @@ User: "Yes" → call remove_notebook`,
688
688
  },
689
689
  },
690
690
  {
691
- name: "cleanup_data",
692
- description: "ULTRATHINK Deep Cleanup - Scans entire system for ALL NotebookLM MCP data files across 8 categories. Always runs in deep mode, shows categorized preview before deletion.\n\n" +
693
- "⚠️ CRITICAL: Close ALL Chrome/Chromium instances BEFORE running this tool! Open browsers can prevent cleanup and cause issues.\n\n" +
694
- "Categories scanned:\n" +
695
- "1. Legacy Installation (notebooklm-mcp-nodejs) - Old paths with -nodejs suffix\n" +
696
- "2. Current Installation (notebooklm-mcp) - Active data, browser profiles, library\n" +
697
- "3. NPM/NPX Cache - Cached installations from npx\n" +
698
- "4. Claude CLI MCP Logs - MCP server logs from Claude CLI\n" +
699
- "5. Temporary Backups - Backup directories in system temp\n" +
700
- "6. Claude Projects Cache - Project-specific cache (optional)\n" +
701
- "7. Editor Logs (Cursor/VSCode) - MCP logs from code editors (optional)\n" +
702
- "8. Trash Files - Deleted notebooklm files in system trash (optional)\n\n" +
703
- "Works cross-platform (Linux, Windows, macOS). Safe by design: shows detailed preview before deletion, requires explicit confirmation.\n\n" +
704
- "LIBRARY PRESERVATION: Set preserve_library=true to keep your notebook library.json file while cleaning everything else.\n\n" +
705
- "RECOMMENDED WORKFLOW for fresh start:\n" +
706
- "1. Ask user to close ALL Chrome/Chromium instances\n" +
707
- "2. Run cleanup_data(confirm=false, preserve_library=true) to preview\n" +
708
- "3. Run cleanup_data(confirm=true, preserve_library=true) to execute\n" +
709
- "4. Run setup_auth or re_auth for fresh browser session\n\n" +
710
- "Use cases: Clean reinstall, troubleshooting auth issues, removing all traces before uninstall, cleaning old browser sessions and installation data.",
691
+ name: 'cleanup_data',
692
+ description: 'ULTRATHINK Deep Cleanup - Scans entire system for ALL NotebookLM MCP data files across 8 categories. Always runs in deep mode, shows categorized preview before deletion.\n\n' +
693
+ '⚠️ CRITICAL: Close ALL Chrome/Chromium instances BEFORE running this tool! Open browsers can prevent cleanup and cause issues.\n\n' +
694
+ 'Categories scanned:\n' +
695
+ '1. Legacy Installation (notebooklm-mcp-nodejs) - Old paths with -nodejs suffix\n' +
696
+ '2. Current Installation (notebooklm-mcp) - Active data, browser profiles, library\n' +
697
+ '3. NPM/NPX Cache - Cached installations from npx\n' +
698
+ '4. Claude CLI MCP Logs - MCP server logs from Claude CLI\n' +
699
+ '5. Temporary Backups - Backup directories in system temp\n' +
700
+ '6. Claude Projects Cache - Project-specific cache (optional)\n' +
701
+ '7. Editor Logs (Cursor/VSCode) - MCP logs from code editors (optional)\n' +
702
+ '8. Trash Files - Deleted notebooklm files in system trash (optional)\n\n' +
703
+ 'Works cross-platform (Linux, Windows, macOS). Safe by design: shows detailed preview before deletion, requires explicit confirmation.\n\n' +
704
+ 'LIBRARY PRESERVATION: Set preserve_library=true to keep your notebook library.json file while cleaning everything else.\n\n' +
705
+ 'RECOMMENDED WORKFLOW for fresh start:\n' +
706
+ '1. Ask user to close ALL Chrome/Chromium instances\n' +
707
+ '2. Run cleanup_data(confirm=false, preserve_library=true) to preview\n' +
708
+ '3. Run cleanup_data(confirm=true, preserve_library=true) to execute\n' +
709
+ '4. Run setup_auth or re_auth for fresh browser session\n\n' +
710
+ 'Use cases: Clean reinstall, troubleshooting auth issues, removing all traces before uninstall, cleaning old browser sessions and installation data.',
711
711
  inputSchema: {
712
- type: "object",
712
+ type: 'object',
713
713
  properties: {
714
714
  confirm: {
715
- type: "boolean",
716
- description: "Confirmation flag. Tool shows preview first, then user confirms deletion. " +
717
- "Set to true only after user has reviewed the preview and explicitly confirmed.",
715
+ type: 'boolean',
716
+ description: 'Confirmation flag. Tool shows preview first, then user confirms deletion. ' +
717
+ 'Set to true only after user has reviewed the preview and explicitly confirmed.',
718
718
  },
719
719
  preserve_library: {
720
- type: "boolean",
721
- description: "Preserve library.json file during cleanup. Default: false. " +
722
- "Set to true to keep your notebook library while deleting everything else (browser data, caches, logs).",
720
+ type: 'boolean',
721
+ description: 'Preserve library.json file during cleanup. Default: false. ' +
722
+ 'Set to true to keep your notebook library while deleting everything else (browser data, caches, logs).',
723
723
  default: false,
724
724
  },
725
725
  },
726
- required: ["confirm"],
726
+ required: ['confirm'],
727
727
  },
728
728
  },
729
729
  ];
@@ -772,7 +772,7 @@ export class ToolHandlers {
772
772
  ` { "question": "...", "notebook_url": "https://notebooklm.google.com/notebook/..." }`);
773
773
  }
774
774
  else {
775
- const availableIds = allNotebooks.map(n => n.id).join(', ');
775
+ const availableIds = allNotebooks.map((n) => n.id).join(', ');
776
776
  throw new Error(`Notebook not found: '${notebook_id}'\n\n` +
777
777
  `Available notebooks: ${availableIds}\n\n` +
778
778
  `To list all notebooks: GET /notebooks\n` +
@@ -806,7 +806,7 @@ export class ToolHandlers {
806
806
  ` GET /notebooks to list available notebooks`);
807
807
  }
808
808
  else {
809
- const availableIds = allNotebooks.map(n => `${n.id} (${n.name})`).join('\n - ');
809
+ const availableIds = allNotebooks.map((n) => `${n.id} (${n.name})`).join('\n - ');
810
810
  throw new Error(`❌ No notebook specified.\n\n` +
811
811
  `Available notebooks:\n - ${availableIds}\n\n` +
812
812
  `Please specify one of:\n` +
@@ -817,7 +817,7 @@ export class ToolHandlers {
817
817
  }
818
818
  }
819
819
  // Progress: Getting or creating session
820
- await sendProgress?.("Getting or creating browser session...", 1, 5);
820
+ await sendProgress?.('Getting or creating browser session...', 1, 5);
821
821
  // Apply browser options temporarily
822
822
  const originalConfig = { ...CONFIG };
823
823
  const effectiveConfig = applyBrowserOptions(browser_options, show_browser);
@@ -838,7 +838,7 @@ export class ToolHandlers {
838
838
  // Get or create session (with headless override to handle mode changes)
839
839
  const session = await this.sessionManager.getOrCreateSession(session_id, resolvedNotebookUrl, overrideHeadless);
840
840
  // Progress: Asking question
841
- await sendProgress?.("Asking question to NotebookLM...", 2, 5);
841
+ await sendProgress?.('Asking question to NotebookLM...', 2, 5);
842
842
  // Ask the question (pass progress callback)
843
843
  const rawAnswer = await session.ask(question, sendProgress);
844
844
  // Note: FOLLOW_UP_REMINDER removed for cleaner responses
@@ -846,7 +846,7 @@ export class ToolHandlers {
846
846
  // Get session info
847
847
  const sessionInfo = session.getInfo();
848
848
  const result = {
849
- status: "success",
849
+ status: 'success',
850
850
  question,
851
851
  answer,
852
852
  session_id: session.sessionId,
@@ -858,7 +858,7 @@ export class ToolHandlers {
858
858
  },
859
859
  };
860
860
  // Progress: Complete
861
- await sendProgress?.("Question answered successfully!", 5, 5);
861
+ await sendProgress?.('Question answered successfully!', 5, 5);
862
862
  log.success(`✅ [TOOL] ask_question completed successfully`);
863
863
  return {
864
864
  success: true,
@@ -873,15 +873,15 @@ export class ToolHandlers {
873
873
  catch (error) {
874
874
  const errorMessage = error instanceof Error ? error.message : String(error);
875
875
  // Special handling for rate limit errors
876
- if (error instanceof RateLimitError || errorMessage.toLowerCase().includes("rate limit")) {
876
+ if (error instanceof RateLimitError || errorMessage.toLowerCase().includes('rate limit')) {
877
877
  log.error(`🚫 [TOOL] Rate limit detected`);
878
878
  return {
879
879
  success: false,
880
- error: "NotebookLM rate limit reached (50 queries/day for free accounts).\n\n" +
881
- "You can:\n" +
880
+ error: 'NotebookLM rate limit reached (50 queries/day for free accounts).\n\n' +
881
+ 'You can:\n' +
882
882
  "1. Use the 're_auth' tool to login with a different Google account\n" +
883
- "2. Wait until tomorrow for the quota to reset\n" +
884
- "3. Upgrade to Google AI Pro/Ultra for 5x higher limits\n\n" +
883
+ '2. Wait until tomorrow for the quota to reset\n' +
884
+ '3. Upgrade to Google AI Pro/Ultra for 5x higher limits\n\n' +
885
885
  `Original error: ${errorMessage}`,
886
886
  };
887
887
  }
@@ -945,7 +945,7 @@ export class ToolHandlers {
945
945
  return {
946
946
  success: true,
947
947
  data: {
948
- status: "success",
948
+ status: 'success',
949
949
  message: `Session ${session_id} closed successfully`,
950
950
  session_id,
951
951
  },
@@ -989,7 +989,7 @@ export class ToolHandlers {
989
989
  return {
990
990
  success: true,
991
991
  data: {
992
- status: "success",
992
+ status: 'success',
993
993
  message: `Session ${session_id} reset successfully`,
994
994
  session_id,
995
995
  },
@@ -1016,9 +1016,9 @@ export class ToolHandlers {
1016
1016
  // Get session stats
1017
1017
  const stats = this.sessionManager.getStats();
1018
1018
  const result = {
1019
- status: "ok",
1019
+ status: 'ok',
1020
1020
  authenticated,
1021
- notebook_url: CONFIG.notebookUrl || "not configured",
1021
+ notebook_url: CONFIG.notebookUrl || 'not configured',
1022
1022
  active_sessions: stats.active_sessions,
1023
1023
  max_sessions: stats.max_sessions,
1024
1024
  session_timeout: stats.session_timeout,
@@ -1027,9 +1027,9 @@ export class ToolHandlers {
1027
1027
  auto_login_enabled: CONFIG.autoLoginEnabled,
1028
1028
  stealth_enabled: CONFIG.stealthEnabled,
1029
1029
  // Add troubleshooting tip if not authenticated
1030
- ...((!authenticated) && {
1031
- troubleshooting_tip: "For fresh start with clean browser session: Close all Chrome instances → " +
1032
- "cleanup_data(confirm=true, preserve_library=true) → setup_auth"
1030
+ ...(!authenticated && {
1031
+ troubleshooting_tip: 'For fresh start with clean browser session: Close all Chrome instances → ' +
1032
+ 'cleanup_data(confirm=true, preserve_library=true) → setup_auth',
1033
1033
  }),
1034
1034
  };
1035
1035
  log.success(`✅ [TOOL] get_health completed`);
@@ -1056,7 +1056,7 @@ export class ToolHandlers {
1056
1056
  async handleSetupAuth(args, sendProgress) {
1057
1057
  const { show_browser, browser_options } = args;
1058
1058
  // CRITICAL: Send immediate progress to reset timeout from the very start
1059
- await sendProgress?.("Initializing authentication setup...", 0, 10);
1059
+ await sendProgress?.('Initializing authentication setup...', 0, 10);
1060
1060
  log.info(`🔧 [TOOL] setup_auth called`);
1061
1061
  if (show_browser !== undefined) {
1062
1062
  log.info(` Show browser: ${show_browser}`);
@@ -1068,22 +1068,22 @@ export class ToolHandlers {
1068
1068
  Object.assign(CONFIG, effectiveConfig);
1069
1069
  try {
1070
1070
  // Progress: Starting
1071
- await sendProgress?.("Preparing authentication browser...", 1, 10);
1071
+ await sendProgress?.('Preparing authentication browser...', 1, 10);
1072
1072
  log.info(` 🌐 Opening browser for interactive login...`);
1073
1073
  // Progress: Opening browser
1074
- await sendProgress?.("Opening browser window...", 2, 10);
1074
+ await sendProgress?.('Opening browser window...', 2, 10);
1075
1075
  // Perform setup with progress updates (uses CONFIG internally)
1076
1076
  const success = await this.authManager.performSetup(sendProgress);
1077
1077
  const durationSeconds = (Date.now() - startTime) / 1000;
1078
1078
  if (success) {
1079
1079
  // Progress: Complete
1080
- await sendProgress?.("Authentication saved successfully!", 10, 10);
1080
+ await sendProgress?.('Authentication saved successfully!', 10, 10);
1081
1081
  log.success(`✅ [TOOL] setup_auth completed (${durationSeconds.toFixed(1)}s)`);
1082
1082
  return {
1083
1083
  success: true,
1084
1084
  data: {
1085
- status: "authenticated",
1086
- message: "Successfully authenticated and saved browser state",
1085
+ status: 'authenticated',
1086
+ message: 'Successfully authenticated and saved browser state',
1087
1087
  authenticated: true,
1088
1088
  duration_seconds: durationSeconds,
1089
1089
  },
@@ -1093,7 +1093,7 @@ export class ToolHandlers {
1093
1093
  log.error(`❌ [TOOL] setup_auth failed (${durationSeconds.toFixed(1)}s)`);
1094
1094
  return {
1095
1095
  success: false,
1096
- error: "Authentication failed or was cancelled",
1096
+ error: 'Authentication failed or was cancelled',
1097
1097
  };
1098
1098
  }
1099
1099
  }
@@ -1127,19 +1127,19 @@ export class ToolHandlers {
1127
1127
  log.info(`🔧 [TOOL] de_auth called`);
1128
1128
  try {
1129
1129
  // 1. Close all active sessions
1130
- log.info(" 🛑 Closing all sessions...");
1130
+ log.info(' 🛑 Closing all sessions...');
1131
1131
  await this.sessionManager.closeAllSessions();
1132
- log.success(" ✅ All sessions closed");
1132
+ log.success(' ✅ All sessions closed');
1133
1133
  // 2. Clear all auth data
1134
- log.info(" 🗑️ Clearing all authentication data...");
1134
+ log.info(' 🗑️ Clearing all authentication data...');
1135
1135
  await this.authManager.clearAllAuthData();
1136
- log.success(" ✅ Authentication data cleared");
1136
+ log.success(' ✅ Authentication data cleared');
1137
1137
  log.success(`✅ [TOOL] de_auth completed - Successfully logged out`);
1138
1138
  return {
1139
1139
  success: true,
1140
1140
  data: {
1141
- status: "de-authenticated",
1142
- message: "Successfully logged out. Use setup_auth or re_auth to authenticate again.",
1141
+ status: 'de-authenticated',
1142
+ message: 'Successfully logged out. Use setup_auth or re_auth to authenticate again.',
1143
1143
  authenticated: false,
1144
1144
  },
1145
1145
  };
@@ -1164,7 +1164,7 @@ export class ToolHandlers {
1164
1164
  */
1165
1165
  async handleReAuth(args, sendProgress) {
1166
1166
  const { show_browser, browser_options } = args;
1167
- await sendProgress?.("Preparing re-authentication...", 0, 12);
1167
+ await sendProgress?.('Preparing re-authentication...', 0, 12);
1168
1168
  log.info(`🔧 [TOOL] re_auth called`);
1169
1169
  if (show_browser !== undefined) {
1170
1170
  log.info(` Show browser: ${show_browser}`);
@@ -1176,26 +1176,26 @@ export class ToolHandlers {
1176
1176
  Object.assign(CONFIG, effectiveConfig);
1177
1177
  try {
1178
1178
  // 1. De-authenticate first (logout)
1179
- await sendProgress?.("De-authenticating...", 1, 12);
1180
- log.info(" 🔓 De-authenticating (logout)...");
1179
+ await sendProgress?.('De-authenticating...', 1, 12);
1180
+ log.info(' 🔓 De-authenticating (logout)...');
1181
1181
  const deAuthResult = await this.handleDeAuth();
1182
1182
  if (!deAuthResult.success) {
1183
1183
  throw new Error(`De-authentication failed: ${deAuthResult.error}`);
1184
1184
  }
1185
- log.success(" ✅ De-authentication complete");
1185
+ log.success(' ✅ De-authentication complete');
1186
1186
  // 2. Perform fresh setup
1187
- await sendProgress?.("Starting fresh authentication...", 3, 12);
1188
- log.info(" 🌐 Starting fresh authentication setup...");
1187
+ await sendProgress?.('Starting fresh authentication...', 3, 12);
1188
+ log.info(' 🌐 Starting fresh authentication setup...');
1189
1189
  const success = await this.authManager.performSetup(sendProgress);
1190
1190
  const durationSeconds = (Date.now() - startTime) / 1000;
1191
1191
  if (success) {
1192
- await sendProgress?.("Re-authentication complete!", 12, 12);
1192
+ await sendProgress?.('Re-authentication complete!', 12, 12);
1193
1193
  log.success(`✅ [TOOL] re_auth completed (${durationSeconds.toFixed(1)}s)`);
1194
1194
  return {
1195
1195
  success: true,
1196
1196
  data: {
1197
- status: "authenticated",
1198
- message: "Successfully re-authenticated with new account. All previous sessions have been closed.",
1197
+ status: 'authenticated',
1198
+ message: 'Successfully re-authenticated with new account. All previous sessions have been closed.',
1199
1199
  authenticated: true,
1200
1200
  duration_seconds: durationSeconds,
1201
1201
  },
@@ -1205,7 +1205,7 @@ export class ToolHandlers {
1205
1205
  log.error(`❌ [TOOL] re_auth failed (${durationSeconds.toFixed(1)}s)`);
1206
1206
  return {
1207
1207
  success: false,
1208
- error: "Re-authentication failed or was cancelled",
1208
+ error: 'Re-authentication failed or was cancelled',
1209
1209
  };
1210
1210
  }
1211
1211
  }
@@ -1244,7 +1244,7 @@ export class ToolHandlers {
1244
1244
  topics: metadata.tags, // tags → topics
1245
1245
  content_types: ['documentation'],
1246
1246
  use_cases: metadata.tags.slice(0, 3), // Use first 3 tags as use cases
1247
- auto_generated: true
1247
+ auto_generated: true,
1248
1248
  };
1249
1249
  // Add notebook to library
1250
1250
  const notebook = await this.library.addNotebook(notebookInput);
@@ -1492,7 +1492,7 @@ export class ToolHandlers {
1492
1492
  const cleanupManager = new CleanupManager();
1493
1493
  try {
1494
1494
  // Always run in deep mode
1495
- const mode = "deep";
1495
+ const mode = 'deep';
1496
1496
  if (!confirm) {
1497
1497
  // Preview mode - show what would be deleted
1498
1498
  log.info(` 📋 Generating cleanup preview (mode: ${mode})...`);
@@ -1503,7 +1503,7 @@ export class ToolHandlers {
1503
1503
  return {
1504
1504
  success: true,
1505
1505
  data: {
1506
- status: "preview",
1506
+ status: 'preview',
1507
1507
  mode,
1508
1508
  preview: {
1509
1509
  categories: preview.categories,
@@ -1526,7 +1526,7 @@ export class ToolHandlers {
1526
1526
  return {
1527
1527
  success: result.success,
1528
1528
  data: {
1529
- status: result.success ? "completed" : "partial",
1529
+ status: result.success ? 'completed' : 'partial',
1530
1530
  mode,
1531
1531
  result: {
1532
1532
  deletedPaths: result.deletedPaths,