Skip to main content
Glama
DevstackK

Notebooklm-Unofficial-API-MCP

by DevstackK

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": true
}
logging
{}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
extensions
{
  "io.modelcontextprotocol/ui": {}
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
notebook_listA

List all notebooks (id + title + metadata).

Returns a bounded page: limit (default 50) items from offset (default 0), plus total / offset / has_more. Page forward by re-calling with offset += limit while has_more is true.

notebook_createB

Create a new notebook with the given title.

notebook_describeA

Fetch a notebook's AI-generated description. Accepts a notebook name or ID.

Returns the resolved notebook_id plus the AI description. Pass include_metadata=True to additionally fetch the notebook's metadata (details + source list) and surface it under a metadata key; the default output (include_metadata omitted) is unchanged.

notebook_renameB

Rename a notebook. Accepts a notebook name or ID.

notebook_deleteA

Delete a notebook (irreversible). Accepts a notebook name or ID.

Two-step confirmation: called with confirm=False (the default) it does NOT delete — it returns a needs_confirmation preview of the resolved notebook. Re-submit its canonical notebook_id with confirm=True.

source_listA

List a notebook's sources. Accepts a notebook name or ID.

detail (default full): full gives each source's metadata plus string kind / status_label / drive_status_label labels; compact returns only id / title / kind / status_label / drive_status_label / created_at — a low-token roster.

drive_status_label is a separate axis, null on non-Drive sources: a Drive file that was deleted or unshared keeps reading status_label="ready" (ingestion did finish) while this turns deleted / inaccessible / syncing, so answers may be stale.

Pass status to list only sources whose status_label matches (error = a broken import's ghost row). Pass label (name or ID) to restrict to that label's members; composes with status.

source_readA

Read a source at one of two detail levels. Accepts a notebook/source name or ID.

detail selects what you get back (two distinct shapes):

  • summary — a tiny AI digest for low-token triage: {notebook_id, source_id, summary, keywords}. Cheap to fan out across many sources before deciding which to pull in full.

  • full (DEFAULT) — the source metadata (incl. kind/status_label/ drive_status_label) plus the extracted content, the full char_count, and a truncated flag. content is ALWAYS bounded: omitting max_chars caps it at the first 10,000 chars; raise max_chars and/or page with offset (slice [offset : offset+max_chars]). char_count stays the FULL length. content is null (char_count 0) when the source isn't ready yet or has no extractable text.

output_format (text default / markdown, needs the server's markdownify extra) and max_chars / offset apply only to detail="full" (ignored for summary). Prefer chat_ask for querying large sources rather than pulling the whole body.

source_renameC

Rename a source. Accepts a notebook/source name or ID.

source_deleteA

Delete sources. source XOR sources. Preview: omit both. Confirm with the preview's canonical notebook_id and source id(s).

source_waitA

Wait for sources to finish processing. Accepts a notebook name or ID.

Waits for a subset when sources (list or comma/JSON string) is given, a single source when source (name or ID) is given, else every source. All three modes return the SAME structured aggregate, so an agent never has to branch on the shape:

{"notebook_id", "ok", "ready", "timed_out", "failed", "not_found"}

plus per-bucket *_count + total_count. ready holds sources that reached READY (with kind / status_label labels); timed_out / failed / not_found hold {"source_id", "error"} entries. ok is true iff all error buckets empty. Subset and all-sources modes report partial progress (a slow or failed source no longer discards the ones that did become ready).

A READY web-page entry may carry a non-blocking warning when its indexed text is thin (likely dead link / soft-404 / paywall); advisory only (still READY, still ok — verify with source_read (detail="full")).

An unresolved ref in sources / source raises NOT_FOUND before the wait — an input error, distinct from a resolved source the backend reports missing / failed / slow (which lands in a bucket).

source_addA

Add sources by notebook name or ID: single, batch, or in-channel bytes.

Choose ONE mode:

Single mode — pass source_type; it selects the required input:

  • url / youtube — require url (youtube → a YouTube link).

  • text — requires text; title optional.

  • file — over stdio, requires path (a local path on the server host inside NOTEBOOKLM_MCP_ALLOWED_ROOTS; host-path file-add is off until that env is set — not $HOME and not ~/.notebooklm). Over the remote (http) connector the host filesystem is unreachable, so it returns upload_required with two actor paths: human_upload (open the signed URL in a browser) and agent_upload (an agent POSTs the bytes as the raw body); agent_instructions gives the rule (try agent_upload, else human_upload.url). Alternatively pass bytes_base64 to add a SMALL file in-channel (any transport, no signed URL): standard base64 (not URL-safe) ≤ 10,000 chars (≈ 7 KB); filename seeds the title/extension. A bigger file must take the signed URL (≤ 200 MiB).

  • drive — requires document_id + mime_type (one of google-doc|google-slides|google-sheets|pdf; required, no default — a wrong default fails non-Doc imports, #1827).

The single-mode content inputs are mutually exclusive — supply only the one your source_type requires (bytes_base64 is the file alternative to path). An explicit title for url/youtube/drive is honored via a post-add rename; a miss returns title_override_applied: false (#1960).

Pass wait=true to block until the ONE added source finishes processing and return the source_wait aggregate (buckets + per-bucket *_count + total_count) with a top-level source_id (present even on timeout/failure); timeout/interval tune the poll. wait is single-mode only and NOT for a remote file signed-URL upload (add it, then source_wait). Without wait the added source is echoed under source with string kind / status_label labels; imports are ASYNCHRONOUS so the echo is usually still processing/preparing — confirm with source_wait or source_list(status="error"). A failed import is flagged inline (status_label="error" + a warning); source_wait also flags a READY web page with suspiciously thin text (dead link/soft-404/paywall).

Single web URLs support fallback_fetch=True: eligible connection failures become static text with fallback provenance (impersonate + markdown extras). cleanup_on_failure=True additionally deletes the attributable failed stub once its replacement is ready. Private fetch destinations are blocked.

Batch mode — pass urls (http/https, including YouTube). Returns notebook_id, added/failed counts and per-input results with input, status (added/error), source fields or an error object.

results[i] corresponds to urls[i] and includes commit_state (confirmed/rejected/unknown/not_sent). Batch is URL-only; invalid entries are not_sent errors. Escaping failures retain settled members as batch_outcome. Single-mode inputs and wait are invalid with urls; allow_internal applies to every entry.

source_add_drive_fileA

Add a Google Drive file by downloading it server-side and uploading it (supports epub/docx/pptx/txt/md/rtf/odt/csv/tsv/pdf).

Probes the Drive file and routes: a downloadable type is fetched server-side and uploaded; a Google-native Doc/Slides/Sheet isn't downloadable and returns a pointer error → use source_add(source_type='drive', mime_type=…) for those (or for a Drive PDF you'd rather add by reference). Use source_add(source_type='file') when you hold the bytes locally.

Accepts a notebook name or ID and a Drive file id or share URL (/d/<id>, /file/d/<id>/…, ?id=<id>). The fetch runs server-side with the profile's session, so it works on the remote (http) connector too — no upload_required step. Processed ASYNCHRONOUSLY; pass wait=true to block until READY (else confirm via source_wait / source_list(status="error")).

source_list_play_booksA

List Google Play Books eligible to be added as sources (Expert Intelligence).

Returns the account's purchased ebooks NotebookLM can ingest (US only, 18+). Each row has content_id (pass it to source_add_play_book), title, authors, export_disabled and reason (a non-exportable title cannot be added), plus cover_url / store_url. Empty for an account with no Play Books library. Web backend only.

source_add_play_bookA

Add a Google Play Book as a source by its content id (Expert Intelligence).

content_id is a volume id from source_list_play_books. The title must be exportable — a publisher-blocked one is refused up front. Accepts a notebook name or ID. Reads back as an expert_intelligence source. Processed asynchronously; pass wait=true to block until READY. Web backend only.

chat_askA

Ask a notebook's sources a question, and/or recall prior turns. Accepts a notebook name or ID.

Pass conversation_id to continue a specific conversation; omit it to continue the notebook's most-recent conversation (or start a new one).

source_ids (optional) scopes the question to specific sources by id/prefix/title; omit it to query every source. It accepts a real list, a JSON-array string, or a comma-separated string (the comma form cannot carry a source title that itself contains a comma — use a JSON array or a real list for those).

history (optional, default 0): the max number of prior Q&A pairs (each a {question, answer}) to also return (oldest-first), from the conversation as it stood before this question. There is no unbounded "all" value — pass a generously large number (e.g. 100) for the whole conversation. Omit question (leave it empty) with history > 0 to recall prior pairs without asking anything new; a recall-only call also echoes the conversation_id it read. Pass neither and the call is rejected.

Returns the answer, turn_number, and citation references (when a question is asked). references controls citation detail: lite (default) returns source_id / citation_number / cited_text; full adds chunk-level char offsets and scores.

suggest_followups (optional, default False): when True the result also carries a suggested_prompts list of AI-suggested follow-up questions (each a {title, prompt}), scoped to the same source_ids and steered by question when one is given. It works on its own too — pass it with no question (and history 0) to get suggested questions without asking anything. When omitted/False the result never contains a suggested_prompts key.

chat_startA

Start a chat ask as a detached background task and return immediately.

The watchdog-safe way to ask when generation may run long (large/shared notebooks routinely take 1–3 minutes, and remote MCP transports cut a call at ~60s of silence — which kills a blocking chat_ask mid-generation). The ask runs server-side regardless of what happens to this call; chat_status collects the answer. For questions expected to answer quickly, chat_ask stays the one-call path.

Parameters mirror chat_ask's ask path (history / suggest_followups are chat_ask-only). Returns one of:

  • {"status": "started", "task_id": ...} — call chat_status with this task_id in ~20–30s; re-invoke while pending.

  • {"status": "already_running", "task_id": ...} — an identical ask is still in flight; poll that task_id (no double generation).

A finished ask is never replayed: starting the same question again re-asks and appends a new conversation turn, exactly like chat_ask (so an answer can't go stale after source_add / chat_configure). A finished payload stays pollable by its task_id for ~30 minutes, then chat_status reports unknown.

chat_statusA

Poll detached asks, or inspect a notebook's live generation session.

Detached-task mode: task_id accepts one id, a list, or a comma-separated string — poll a whole batch in ONE call and re-invoke every ~20–30s while anything is pending. Per-task statuses:

  • pending (state: queued waiting for a generation slot, or generating) — re-invoke; polling never restarts a generation.

  • completed — the full chat_ask-shaped payload, plus queued_s / generation_s timings.

  • failed — error: {code, message, retriable, ...}; a retriable code is worth one chat_start retry (which re-runs the generation).

  • unknown — no such task, or its result expired; chat_start again.

A single string id returns that task's status dict; a list (even of one) returns {"tasks": [...]} in input order. At most 64 ids per call.

Live-session mode: pass notebook instead of task_id to inspect Google's current chat session. conversation_id is optional and defaults to that notebook's most-recent conversation. The result is idle or generating and includes the generation token when Google supplies one. The two modes are mutually exclusive.

chat_cancelA

Stop a notebook's active chat generation.

conversation_id defaults to the notebook's most-recent session. Pass the task_id returned by chat_start when cancelling a detached ask: the MCP server abandons its local response stream first, then cancels the associated Google session when one exists. An unknown task ID or one belonging to another notebook is rejected.

chat_configureA

Configure a notebook's chat behavior. Accepts a notebook name or ID.

Two mutually-exclusive ways to configure:

  • chat_mode is a preset — one of default / learning-guide / concise / detailed. It replaces the whole block, so it can't be combined with goal / response_length (that's rejected).

  • goal (custom persona; selects the CUSTOM goal) and response_length (default / longer / shorter) set a custom config. A partial call (just one) merges with the current settings — the omitted field is preserved. Only a bare call (no preset, neither field) is rejected, as it would reset every setting to its default.

suggest_promptsA

Get AI-suggested, ready-to-send prompts for a studio surface. Accepts a notebook name or ID.

surface selects what the prompts are written for (default ask):

  • ask — chat questions to ask the notebook's content.

  • audio-deep-dive / audio-brief / audio-critique / audio-debate — prompts to steer an Audio Overview in that format.

  • video-explainer / video-short — prompts to steer a Video Overview.

  • quiz / flashcards — prompts to steer quiz / flashcard generation.

Each result is a ready-to-send instruction you can pass to the matching generator (chat_ask for ask; studio_generate's instructions for the studio formats). source_ids (optional) scopes the suggestions to specific sources; omit for all. query optionally steers the suggestions.

Related: chat_ask(suggest_followups=true) returns ask-surface suggestions inline with a question (ask + follow-ups in one call); this tool is the standalone selector across every surface.

note_saveA

Create a note, or update an existing one (upsert). Accepts a notebook name or ID.

Mode is chosen SOLELY by note:

  • note omitted → create a new note; title AND content are both required. Returns status="created".

  • note given (a note name or id) → update that note; supply title and/or content (at least one — title-only renames, content-only replaces the body). A ref that doesn't resolve is a not-found error, NEVER a stray create. Returns status="updated".

studio_listA

List a notebook's Studio panel — text notes AND generated artifacts.

Accepts a notebook name or ID. Returns a merged items list; each item has id / title / type (note or a hyphenated artifact kind); artifacts also carry status_label / url. Bounded page of limit (default 50) from offset, with total / offset / has_more.

  • detail ladder (NOTE bodies only; read a report/data-table body via studio_download): summary (default) gives each note a bounded content_preview + char_count. Artifacts add created_at, generation_prompt, duration_seconds, slide_count, source_count (unknown metadata/counts are null). full = whole content; compact = a id/title/type/status_label/created_at roster.

  • kind filters to one type.

  • item (name or id) fetches just that item as a 1-element list with the note's FULL content or the artifact's summary metadata; no match is NOT_FOUND. limit / offset / detail are ignored with item; kind scopes resolution.

studio_generateA

Start generating a studio artifact. Accepts a notebook name or ID.

Non-blocking: returns immediately with a task_id; poll studio_status(notebook, task_id) until is_complete is true. Exception: mind-map renders synchronously (no task_id) — its node tree is under mind_map (or null), the map's id under mind_map_id.

artifact_type selects the artifact kind (each routes to its own generator):

  • audio — podcast-style overview (audio_format: deep-dive|brief|critique|debate, audio_length: short|default|long).

  • video — video overview (video_format: explainer|brief|cinematic|short, style: auto|custom|classic|whiteboard| kawaii|anime|watercolor|retro-print|heritage|paper-craft, style_prompt: free-text custom-style prompt — requires style=custom).

  • cinematic-video — AI-generated documentary video.

  • slide-deck — slide deck (deck_format: detailed|presenter, deck_length: default|short).

  • quiz / flashcards — study aids (quantity: fewer|standard|more, difficulty: easy|medium|hard).

  • infographic — single-image infographic (orientation: landscape|portrait|square, detail: concise|standard|detailed, style: auto|sketch-note|professional|bento-grid|editorial| instructional|bricks|clay|anime|kawaii|scientific).

  • data-table — extracted data table.

  • mind-map — mind map (map_kind: interactive|note-backed).

  • report — text report (report_format: briefing-doc|study-guide|blog-post|custom).

Each per-kind option is valid ONLY for the kind(s) listed above; passing one to a different artifact_type is a validation error, not a silent no-op. Options default to the standard choice when omitted.

source_ids (optional) scopes generation to specific sources; omit it to use every source. It accepts a real list, a JSON-array string, or a comma-separated string (a source title containing a comma needs the JSON-array or list form). instructions is free-text guidance for kinds that accept it (including mind-map). language (optional) is a language code, e.g. en/ja/zh_Hans.

studio_statusA

Poll a generation task's status. Accepts a notebook name or ID.

Stateless: pass the task_id from studio_generate. Returns status / url / error / is_complete / media_ready; poll until done. A pending url is provisional — trust it only if media_ready.

studio_downloadA

Download a generated artifact. Accepts a notebook name or ID.

Target the artifact in ONE of two ways (exactly one):

  • artifact — a name-or-id ref (title / id / unique-id-prefix), the form the other artifact_* tools take; resolves to its type + id.

  • artifact_type — a registry-advertised type, optionally with artifact_id (full or unique-prefix) for a specific one; omit artifact_id to get the latest artifact of that type.

output_format overrides the default where the selected artifact type supports it. The tool schema advertises the current type and format enums.

Over stdio the artifact is written to path (required). Over the remote (http) connector the server filesystem is unreachable, so the tool returns a clickable resource_link plus {"status": "download_ready", "url": …} — a short-lived signed URL; path is ignored. A text kind (report/data-table) also returns the body inline (bounded content + char_count + truncated) for link-incapable hosts. On the remote connector an explicit artifact_id (and output_format) is validated up front — an unknown/ambiguous id fails immediately, not as a 400 when opened.

studio_renameA

Rename a Studio item (title only) — a text note OR an artifact.

Accepts a notebook name or ID plus an item name-or-id ref resolved over the merged notes+artifacts list (mirroring studio_delete). Routing is by resolved type: a note is renamed through the note system, preserving its content via a get-then-update; every artifact type — audio, video, slide-deck, quiz, flashcards, infographic, data-table, report, and BOTH mind-map kinds — through the artifact rename RPC (note-backed mind maps route back through the note system inside the shared core). Callers need not know which backing an item has.

Returns item_id / type plus the applied new_title and is_mind_map.

studio_retryA

Retry a failed Studio artifact in place (the UI "Retry" action).

Accepts a notebook/artifact name or ID. Non-blocking: on acceptance it returns the kicked-off task_id (equal to the artifact id) and the new status; poll studio_status(notebook, task_id) until complete. A synchronous refusal (rate limit / quota / not-retryable) surfaces as an error.

studio_deleteA

Delete notes/artifacts irreversibly: item XOR items (1–100 refs).

Names/IDs resolve over Studio. Preview first; confirm with canonical notebook_id and item ID(s). Batch confirmation requires full IDs. Text notes batch into one write; artifacts (including mind maps) route individually by kind. Batch returns deleted / not_found arrays. An absent single full ID is an idempotent success.

research_startA

Start a research session in a notebook. Accepts a notebook name or ID.

Non-blocking. Carry the returned poll_task_id into research_status / research_import / research_cancel — the single id that drives polling (it resolves deep vs fast for you). Poll research_status until completed, then research_import to add the sources.

source is web (default) or drive. mode is fast (default) or deep (deep is web-only).

research_statusA

Check a notebook's research status. Accepts a notebook name or ID.

Returns status (no_research|in_progress|completed|failed|not_found), poll_task_id, sources, and report metadata. Poll until completed, then pass poll_task_id to research_import.

termination_reason splits failed into no_results|cancelled| unknown (else completed|in_progress); see hint.

report and per-source report_markdown are omitted unless include_report=True (truncated to report_max_chars). report_char_count is the full size; report_truncated flags an omitted/truncated one. source_limit/source_offset page sources.

poll_task_id (optional) pins one of several in-flight tasks; omit for a single task (ambiguous with two+). An unmatched pin reports not_found. task_id is a deprecated alias (removed in v0.9.0).

research_cancelA

Cancel an in-flight research run in a notebook.

Accepts a notebook name or ID and the poll_task_id to cancel — the value from research_start / research_status. run_id is a deprecated alias (removed in v0.9.0).

Sends the cancel unless the run is already TERMINAL (completed / failed), which returns cancel_requested: false with the observed status and no RPC. Otherwise returns cancel_requested: true with run_status_before; a just-started run reading not_found / no_research (replication lag) is cancelled too. Fire-and-forget; poll research_status afterward to confirm.

research_importA

Import a completed research task's sources into the notebook.

Accepts a notebook name or ID and the poll_task_id to import — the value from research_start / research_status. task_id is a deprecated alias (removed in v0.9.0).

The id pins the task. Timeout-tolerant: a timed-out import reconciles what committed. Idempotent: sources already present (by URL) are skipped as already_present; allow_duplicate re-adds them.

cited_only imports only report-cited sources (all, if none resolve). max_sources caps the count.

share_statusA

Get a notebook's sharing status. Accepts a notebook name or ID.

Returns is_public, access (restricted | anyone_with_link), the share_url, and the list of shared_users ({email, permission, display_name, avatar_url}). permission / access are string labels.

NOTE: view_level is intentionally NOT returned here — the read API does not report it (it would always read "full"). Set it via share_set_access, which echoes the value it just set.

share_set_accessA

Set a notebook's link-access settings. Accepts a notebook name or ID.

Provide public (True = anyone-with-link, False = restricted) and/or view_level (full or chat). Public widening (public=True on a restricted notebook) returns a needs_confirmation preview; confirm with its canonical notebook_id. Restricting and view_level changes are not gated.

Returns the updated status (or preview). view_level is echoed only when set here (the read API can't report it); with both fields it is applied before public so a partial failure fails closed.

share_set_userA

Upsert access: email + permission OR 1–100 grants [{email, permission}].

Preview lists every grant; confirm with its canonical notebook_id. Batches allow mixed editor/viewer permissions in one write. notify and message apply to all grantees. Returns updated sharing status.

share_remove_userA

Remove a user's access to a notebook. Accepts a notebook name or ID.

Confirm-gated: called with confirm=False (default) it does NOT mutate — it returns a preview. Re-submit its canonical notebook_id and verbatim email with confirm=True.

server_infoA

Report the server version and local authentication health.

Returns the package version, live chat_tasks gauges ({generating, queued, concurrency, cached_results} — check load before submitting a chat_start batch), and an auth block (authenticated / storage_exists / json_valid / cookies_present / sid_cookie / profile). Use it to confirm the server is logged in before driving notebook tools; if authenticated is false, run notebooklm login on the server host.

Set include_account=True to also fetch an account block: the signed-in identity {email, authuser} (persisted first, then a live WIZ_global_data probe when authenticated; None only when it can't be discovered) plus quota fields {available, notebook_limit, source_limit, tier, output_language, output_language_is_default} (output_language is the global account code, e.g. "en", or None with output_language_is_default: true when using NotebookLM's default). The quota fields need a live session, so the block is off by default — the default call is a fast, network-free probe. When the session is missing or stale the fields degrade to {available: False, reason: ...} (identity still included) rather than failing the whole call.

profile names the resolved storage profile the probe ran against (e.g. "default"); the booleans are the actual health signals.

The absolute on-disk storage path is deliberately not returned: it leaks the server-host OS username / filesystem layout to any (possibly remote) caller, while telling the agent nothing it can act on.

await_uploadA

Wait for a file uploaded via a source_add(source_type="file") link to land.

Pass the human_upload.url (or the bare token) that source_add returned. Polls the server in-process until the browser/agent upload commits the source:

  • {"status":"received","source_id",...,"file":{...}} — the upload landed.

  • {"status":"pending",...} — nothing yet after ~timeout s; re-invoke with the same link (the wait resumes; a transport reset does not lose it).

  • {"status":"unconfirmed",...} — the upload may have landed, but its source could not be confirmed. Check source_list before requesting a new link to avoid adding the same file twice.

  • {"status":"expired_or_invalid",...} — the link failed; mint a fresh one via source_add(source_type="file").

pingA

Health check for this wrapper server.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A3.6/5.0

Scored across 39 tools

Disambiguation3/5

Several tools overlap in purpose: source_add, source_add_drive_file, and source_add_play_book all add sources, while source_wait and await_upload both wait for source readiness, and chat_ask/chat_start/chat_status form a related set. The detailed descriptions clarify routing, but an agent can still face misselection across these families.

Naming Consistency4/5

Most names follow a clear domain_action convention (notebook_*, source_*, chat_*, share_*, studio_*, research_*). A few outliers like ping, await_upload, server_info, and suggest_prompts break the pattern, but the set is largely predictable.

Tool Count2/5

At 39 tools, the surface is well above the typical 15-tool sweet spot and includes several specialized variants that could be consolidated. While NotebookLM is a broad domain, the count feels over-granular rather than tightly scoped.

Completeness4/5

The set covers notebook, source, chat, sharing, studio, research, and note lifecycles, including add/read/rename/delete and async polling. Minor gaps remain, such as direct notebook metadata updates or source reprocessing/resync, but most core workflows are supported.

Maintenance

ActivityStale
ResponsivenessNo issues