@roomi-fields/notebooklm-mcp 2.0.4 → 2.1.0

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.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **Automate Google NotebookLM at scale. 33-endpoint HTTP REST API for n8n / Zapier / Make / curl, plus an MCP server for Claude Code / Cursor / Codex. Citation-backed Q&A, full Studio generation (audio · video · infographic · report · presentation · data table), multi-account rotation with auto-reauth.**
6
6
 
7
- > v2.0.4 — production-grade, batch-tested on overnight runs of 1 000+ questions. Tools are now a namespaced tree (`notebook_ask`, `source_add`, `session_list`…) with MCP `annotations` + `outputSchema` on every tool — **the old flat names still work as aliases, nothing breaks**. See the [changelog](./CHANGELOG.md) for the full mapping. [Compare with `PleasePrompto/notebooklm-mcp` v2.0.0](https://roomi-fields.github.io/notebooklm-mcp/compare) to see when this project is the right pick (REST API, full Studio, auto-reauth) and when the MCP-only upstream is.
7
+ > v2.1.0 — production-grade, batch-tested on overnight runs of 1 000+ questions. Tools are now a namespaced tree (`notebook_ask`, `source_add`, `session_list`…) with MCP `annotations` + `outputSchema` on every tool — **the old flat names still work as aliases, nothing breaks**. See the [changelog](./CHANGELOG.md) for the full mapping. [Compare with `PleasePrompto/notebooklm-mcp` v2.0.0](https://roomi-fields.github.io/notebooklm-mcp/compare) to see when this project is the right pick (REST API, full Studio, auto-reauth) and when the MCP-only upstream is.
8
8
 
9
9
  <!-- Badges -->
10
10
 
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import type { Page } from 'patchright';
12
12
  import type { SourceUploadInput, SourceUploadResult, SourceDeleteInput, SourceDeleteResult, ContentType, ContentGenerationInput, ContentGenerationResult, NotebookSource, GeneratedContent, NotebookContentOverview, ContentDownloadResult, NoteInput, NoteResult, SaveChatToNoteInput, SaveChatToNoteResult, NoteToSourceInput, NoteToSourceResult } from './types.js';
13
+ import type { NoteListResult, NoteGetInput, NoteGetResult } from '../types.js';
13
14
  export declare class ContentManager {
14
15
  private page;
15
16
  constructor(page: Page);
@@ -368,5 +369,14 @@ export declare class ContentManager {
368
369
  * Extract the content of a note for fallback conversion
369
370
  */
370
371
  private extractNoteContent;
372
+ /**
373
+ * List all notes in the NotebookLM Studio panel.
374
+ * Handles scrolling inside the Studio panel to ensure all notes are loaded.
375
+ */
376
+ listNotes(): Promise<NoteListResult>;
377
+ /**
378
+ * Get the title and text content of a specific note by title or ID.
379
+ */
380
+ getNoteContent(input: NoteGetInput): Promise<NoteGetResult>;
371
381
  }
372
382
  //# sourceMappingURL=content-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"content-manager.d.ts","sourceRoot":"","sources":["../../src/content/content-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content-manager.d.ts","sourceRoot":"","sources":["../../src/content/content-manager.ts"],"names":[],"mappings":"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"}
@@ -4009,6 +4009,8 @@ export class ContentManager {
4009
4009
  ) {
4010
4010
  // Selectors for note items in the Studio panel
4011
4011
  const noteItemSelectors = [
4012
+ 'artifact-library-note',
4013
+ '[class*="artifact-item-button"]',
4012
4014
  '.note-item',
4013
4015
  '[data-item="note"]',
4014
4016
  '.notes-list-item',
@@ -4046,7 +4048,7 @@ export class ContentManager {
4046
4048
  }
4047
4049
  // Also check specific title elements
4048
4050
  try {
4049
- const titleText = await el.$eval('.note-title, .title, [class*="title"], [class*="name"], h3, h4', (e) => e.textContent?.trim() || '');
4051
+ const titleText = await el.$eval('.artifact-title, [class*="artifact-title"], .note-title, .title, [class*="title"], [class*="name"], h3, h4', (e) => e.textContent?.trim() || '');
4050
4052
  if (titleText.toLowerCase().includes(noteTitle.toLowerCase())) {
4051
4053
  return el;
4052
4054
  }
@@ -4248,7 +4250,209 @@ export class ContentManager {
4248
4250
  // Method 1: Click the note to open it and extract content
4249
4251
  await noteElement.click();
4250
4252
  await randomDelay(500, 1000);
4251
- // Look for expanded note content
4253
+ /* @SDD:T1.1.1:START Implement robust table and note content DOM walker serialization */
4254
+ // Wait for the note viewer to be visible
4255
+ try {
4256
+ await this.page.waitForSelector('labs-tailwind-doc-viewer:visible, note-editor:visible, .ProseMirror:visible', { timeout: 5000 });
4257
+ }
4258
+ catch {
4259
+ log.warning(' ⚠️ Timeout waiting for note viewer to be visible, attempting fallback...');
4260
+ }
4261
+ // Try serialization on the active viewer element
4262
+ const activeViewerSelector = 'labs-tailwind-doc-viewer:visible, note-editor:visible, .ProseMirror:visible';
4263
+ const activeViewer = await this.page.$(activeViewerSelector);
4264
+ if (activeViewer) {
4265
+ const serialized = await activeViewer.evaluate((viewerEl) => {
4266
+ // Inline serialization helper for text nodes and elements
4267
+ function serializeInline(node) {
4268
+ let text = '';
4269
+ const childNodes = Array.from(node.childNodes);
4270
+ if (childNodes.length === 0) {
4271
+ return node.textContent || '';
4272
+ }
4273
+ for (const child of childNodes) {
4274
+ if (child.nodeType === 3) {
4275
+ // Node.TEXT_NODE
4276
+ text += child.textContent;
4277
+ }
4278
+ else if (child.nodeType === 1) {
4279
+ // Node.ELEMENT_NODE
4280
+ const elNode = child;
4281
+ const tagName = elNode.tagName.toLowerCase();
4282
+ const childText = serializeInline(elNode);
4283
+ if (tagName === 'b' || tagName === 'strong') {
4284
+ text += `**${childText}**`;
4285
+ }
4286
+ else if (tagName === 'i' || tagName === 'em') {
4287
+ text += `*${childText}*`;
4288
+ }
4289
+ else if (tagName === 'code' || elNode.classList.contains('code')) {
4290
+ text += `\`${childText}\``;
4291
+ }
4292
+ else if (tagName === 'a') {
4293
+ const href = elNode.getAttribute('href') || '';
4294
+ text += `[${childText}](${href})`;
4295
+ }
4296
+ else {
4297
+ text += childText;
4298
+ }
4299
+ }
4300
+ }
4301
+ return text;
4302
+ }
4303
+ const elementListRenderer = viewerEl.querySelector('element-list-renderer');
4304
+ if (!elementListRenderer) {
4305
+ // ProseMirror / semantic HTML fallback
4306
+ const editor = viewerEl.querySelector('.ProseMirror') || viewerEl;
4307
+ function serializeNode(node) {
4308
+ if (node.nodeType === 3) {
4309
+ // Node.TEXT_NODE
4310
+ return node.textContent || '';
4311
+ }
4312
+ if (node.nodeType !== 1) {
4313
+ // Node.ELEMENT_NODE
4314
+ return '';
4315
+ }
4316
+ const elNode = node;
4317
+ const tagName = elNode.tagName.toLowerCase();
4318
+ if (tagName === 'b' || tagName === 'strong') {
4319
+ return `**${Array.from(elNode.childNodes).map(serializeNode).join('')}**`;
4320
+ }
4321
+ if (tagName === 'i' || tagName === 'em') {
4322
+ return `*${Array.from(elNode.childNodes).map(serializeNode).join('')}*`;
4323
+ }
4324
+ if (tagName === 'code') {
4325
+ return `\`${elNode.textContent || ''}\``;
4326
+ }
4327
+ if (tagName === 'p') {
4328
+ return `${Array.from(elNode.childNodes).map(serializeNode).join('')}\n\n`;
4329
+ }
4330
+ if (tagName === 'h1') {
4331
+ return `# ${Array.from(elNode.childNodes).map(serializeNode).join('')}\n\n`;
4332
+ }
4333
+ if (tagName === 'h2') {
4334
+ return `## ${Array.from(elNode.childNodes).map(serializeNode).join('')}\n\n`;
4335
+ }
4336
+ if (tagName === 'h3') {
4337
+ return `### ${Array.from(elNode.childNodes).map(serializeNode).join('')}\n\n`;
4338
+ }
4339
+ if (tagName === 'ul') {
4340
+ return `${Array.from(elNode.childNodes).map(serializeNode).join('')}\n`;
4341
+ }
4342
+ if (tagName === 'li') {
4343
+ return `* ${Array.from(elNode.childNodes).map(serializeNode).join('')}\n`;
4344
+ }
4345
+ if (tagName === 'table') {
4346
+ let tableMd = '\n';
4347
+ const rows = Array.from(elNode.querySelectorAll('tr'));
4348
+ rows.forEach((row, rowIndex) => {
4349
+ const cells = Array.from(row.querySelectorAll('th, td'));
4350
+ const cellTexts = cells.map((cell) => Array.from(cell.childNodes).map(serializeNode).join('').trim());
4351
+ tableMd += `| ${cellTexts.join(' | ')} |\n`;
4352
+ if (rowIndex === 0) {
4353
+ const separator = cellTexts.map(() => '---');
4354
+ tableMd += `| ${separator.join(' | ')} |\n`;
4355
+ }
4356
+ });
4357
+ return tableMd + '\n';
4358
+ }
4359
+ if (tagName === 'pre') {
4360
+ return `\n\`\`\`\n${elNode.textContent?.trim() || ''}\n\`\`\`\n\n`;
4361
+ }
4362
+ return Array.from(elNode.childNodes).map(serializeNode).join('');
4363
+ }
4364
+ return Array.from(editor.childNodes)
4365
+ .map(serializeNode)
4366
+ .join('')
4367
+ .replace(/\n{3,}/g, '\n\n')
4368
+ .trim();
4369
+ }
4370
+ let markdown = '';
4371
+ const structuralElements = Array.from(elementListRenderer.children);
4372
+ function processStructuralElement(el) {
4373
+ const tableView = el.querySelector('table-element-view');
4374
+ if (tableView) {
4375
+ const table = tableView.querySelector('table');
4376
+ if (table) {
4377
+ const rows = Array.from(table.querySelectorAll('tr'));
4378
+ if (rows.length === 0)
4379
+ return;
4380
+ let tableMarkdown = '\n';
4381
+ rows.forEach((row, rowIndex) => {
4382
+ const cells = Array.from(row.querySelectorAll('th, td'));
4383
+ const cellTexts = cells.map((cell) => {
4384
+ const paragraph = cell.querySelector('.paragraph');
4385
+ if (paragraph) {
4386
+ return serializeInline(paragraph).trim();
4387
+ }
4388
+ return serializeInline(cell).trim();
4389
+ });
4390
+ tableMarkdown += `| ${cellTexts.join(' | ')} |\n`;
4391
+ if (rowIndex === 0) {
4392
+ const separator = cellTexts.map(() => '---');
4393
+ tableMarkdown += `| ${separator.join(' | ')} |\n`;
4394
+ }
4395
+ });
4396
+ markdown += tableMarkdown + '\n';
4397
+ }
4398
+ return;
4399
+ }
4400
+ const paragraphView = el.querySelector('paragraph-element-view');
4401
+ if (paragraphView) {
4402
+ const textContainer = paragraphView.querySelector('.paragraph');
4403
+ if (textContainer) {
4404
+ const text = serializeInline(textContainer).trim();
4405
+ if (!text)
4406
+ return;
4407
+ if (textContainer.classList.contains('list-item') ||
4408
+ textContainer.tagName.toLowerCase() === 'li') {
4409
+ markdown += `* ${text}\n`;
4410
+ }
4411
+ else if (textContainer.classList.contains('heading1')) {
4412
+ markdown += `\n# ${text}\n\n`;
4413
+ }
4414
+ else if (textContainer.classList.contains('heading2')) {
4415
+ markdown += `\n## ${text}\n\n`;
4416
+ }
4417
+ else if (textContainer.classList.contains('heading3')) {
4418
+ markdown += `\n### ${text}\n\n`;
4419
+ }
4420
+ else {
4421
+ if (!textContainer.classList.contains('table-paragraph')) {
4422
+ markdown += `${text}\n\n`;
4423
+ }
4424
+ }
4425
+ }
4426
+ return;
4427
+ }
4428
+ const preEl = el.querySelector('pre, code');
4429
+ if (preEl) {
4430
+ markdown += `\n\`\`\`\n${preEl.textContent?.trim() || ''}\n\`\`\`\n\n`;
4431
+ return;
4432
+ }
4433
+ }
4434
+ for (const structuralEl of structuralElements) {
4435
+ const tagName = structuralEl.tagName.toLowerCase();
4436
+ if (tagName !== 'labs-tailwind-structural-element-view-v2') {
4437
+ if (tagName === 'ul' || tagName === 'ol') {
4438
+ const nestedElements = Array.from(structuralEl.querySelectorAll('labs-tailwind-structural-element-view-v2'));
4439
+ for (const nestedEl of nestedElements) {
4440
+ processStructuralElement(nestedEl);
4441
+ }
4442
+ }
4443
+ continue;
4444
+ }
4445
+ processStructuralElement(structuralEl);
4446
+ }
4447
+ return markdown.replace(/\n{3,}/g, '\n\n').trim();
4448
+ });
4449
+ if (serialized && serialized.length > 10) {
4450
+ log.info(` ✅ Serialized note content via DOM walker (${serialized.length} chars)`);
4451
+ return noteTitle ? `# ${noteTitle}\n\n${serialized}` : serialized;
4452
+ }
4453
+ }
4454
+ /* @SDD:T1.1.1:END */
4455
+ // Fallback method to existing selectors
4252
4456
  const contentSelectors = [
4253
4457
  // Note body/content areas
4254
4458
  '.note-content',
@@ -4318,5 +4522,132 @@ export class ContentManager {
4318
4522
  return null;
4319
4523
  }
4320
4524
  }
4525
+ /**
4526
+ * List all notes in the NotebookLM Studio panel.
4527
+ * Handles scrolling inside the Studio panel to ensure all notes are loaded.
4528
+ */
4529
+ async listNotes() {
4530
+ log.info(`📚 Listing notes in Studio panel...`);
4531
+ try {
4532
+ // Step 1: Ensure we are in the Studio panel
4533
+ await this.navigateToStudio();
4534
+ await randomDelay(1000, 2000);
4535
+ // Step 2: Locate the scrollable container in the Studio panel
4536
+ const scrollContainerSelector = '.studio-panel, [class*="studio-panel"]';
4537
+ const containerExists = (await this.page.locator(scrollContainerSelector).count()) > 0;
4538
+ if (containerExists) {
4539
+ // Scroll the panel down a few times to trigger lazy load of old notes
4540
+ log.info(' ⏳ Scrolling Studio panel to load all notes...');
4541
+ for (let i = 0; i < 5; i++) {
4542
+ await this.page.evaluate((selector) => {
4543
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4544
+ const panel = globalThis.document.querySelector(selector);
4545
+ if (panel) {
4546
+ panel.scrollTop = panel.scrollHeight;
4547
+ }
4548
+ }, scrollContainerSelector);
4549
+ await randomDelay(300, 500);
4550
+ }
4551
+ }
4552
+ // Step 3: Scrape note elements from the DOM
4553
+ const notes = [];
4554
+ const seenTitles = new Set();
4555
+ // Selectors based on verified DOM structure
4556
+ const noteItemSelector = 'artifact-library-note, [class*="artifact-item-button"]';
4557
+ const noteElements = await this.page.locator(noteItemSelector).all();
4558
+ log.info(` 🔍 Found ${noteElements.length} candidate note elements`);
4559
+ for (const el of noteElements) {
4560
+ try {
4561
+ const titleText = (await el
4562
+ .locator('.artifact-title, [class*="artifact-title"]')
4563
+ .first()
4564
+ .textContent()) || '';
4565
+ const detailsText = (await el
4566
+ .locator('.artifact-details, [class*="artifact-details"]')
4567
+ .first()
4568
+ .textContent()) || '';
4569
+ const noteId = (await el.getAttribute('data-note-id')) ||
4570
+ (await el.getAttribute('data-id')) ||
4571
+ undefined;
4572
+ const title = titleText.trim();
4573
+ if (title && !seenTitles.has(title)) {
4574
+ seenTitles.add(title);
4575
+ notes.push({
4576
+ id: noteId,
4577
+ title,
4578
+ details: detailsText.trim() || undefined,
4579
+ });
4580
+ }
4581
+ }
4582
+ catch {
4583
+ continue;
4584
+ }
4585
+ }
4586
+ log.success(` ✅ Successfully listed ${notes.length} notes`);
4587
+ return { success: true, notes };
4588
+ }
4589
+ catch (error) {
4590
+ const errorMsg = error instanceof Error ? error.message : String(error);
4591
+ log.error(` ❌ Failed to list notes: ${errorMsg}`);
4592
+ return { success: false, notes: [], error: errorMsg };
4593
+ }
4594
+ }
4595
+ /**
4596
+ * Get the title and text content of a specific note by title or ID.
4597
+ */
4598
+ async getNoteContent(input) {
4599
+ const { noteTitle, noteId } = input;
4600
+ log.info(`📄 Fetching content of note: "${noteTitle || noteId}"`);
4601
+ try {
4602
+ await this.navigateToStudio();
4603
+ await randomDelay(500, 1000);
4604
+ // Reuse existing helper to find the note element
4605
+ const noteElement = await this.findNoteElement(noteTitle, noteId);
4606
+ if (!noteElement) {
4607
+ return {
4608
+ success: false,
4609
+ title: noteTitle || '',
4610
+ content: '',
4611
+ error: `Note not found: "${noteTitle || noteId}"`,
4612
+ };
4613
+ }
4614
+ // Reuse existing helper to click note, wait, and extract content text
4615
+ const content = await this.extractNoteContent(noteElement, noteTitle);
4616
+ // Close the note popover/editor if open to return to clean state
4617
+ try {
4618
+ await this.page.keyboard.press('Escape');
4619
+ await randomDelay(200, 400);
4620
+ }
4621
+ catch {
4622
+ // Ignore escape errors
4623
+ }
4624
+ if (content) {
4625
+ log.success(` ✅ Retrieved note content (${content.length} characters)`);
4626
+ return {
4627
+ success: true,
4628
+ title: noteTitle || '',
4629
+ content,
4630
+ };
4631
+ }
4632
+ else {
4633
+ return {
4634
+ success: false,
4635
+ title: noteTitle || '',
4636
+ content: '',
4637
+ error: 'Could not extract content from note editor.',
4638
+ };
4639
+ }
4640
+ }
4641
+ catch (error) {
4642
+ const errorMsg = error instanceof Error ? error.message : String(error);
4643
+ log.error(` ❌ Failed to get note content: ${errorMsg}`);
4644
+ return {
4645
+ success: false,
4646
+ title: noteTitle || '',
4647
+ content: '',
4648
+ error: errorMsg,
4649
+ };
4650
+ }
4651
+ }
4321
4652
  }
4322
4653
  //# sourceMappingURL=content-manager.js.map