Skip to main content
Glama
config.ts10.8 kB
/** * Configuration for NotebookLM MCP Server * * Config Priority (highest to lowest): * 1. Hardcoded Defaults (works out of the box!) * 2. Environment Variables (optional, for advanced users) * 3. Tool Parameters (passed by Claude at runtime) * * No config.json file needed - all settings via ENV or tool parameters! */ import envPaths from 'env-paths'; import fs from 'fs'; import path from 'path'; // Cross-platform data paths (unified without -nodejs suffix) // Linux: ~/.local/share/notebooklm-mcp/ // macOS: ~/Library/Application Support/notebooklm-mcp/ // Windows: %APPDATA%\notebooklm-mcp\ // IMPORTANT: Pass empty string suffix to disable envPaths' default '-nodejs' suffix! const paths = envPaths('notebooklm-mcp', { suffix: '' }); /** * Google NotebookLM Auth URL (used by setup_auth) * This is the base Google login URL that redirects to NotebookLM */ export const NOTEBOOKLM_AUTH_URL = 'https://accounts.google.com/v3/signin/identifier?continue=https%3A%2F%2Fnotebooklm.google.com%2F&flowName=GlifWebSignIn&flowEntry=ServiceLogin'; export interface Config { // NotebookLM - optional, used for legacy default notebook notebookUrl: string; // Browser Settings headless: boolean; browserTimeout: number; viewport: { width: number; height: number }; // Session Management maxSessions: number; sessionTimeout: number; // in seconds // Authentication autoLoginEnabled: boolean; loginEmail: string; loginPassword: string; autoLoginTimeoutMs: number; // Stealth Settings stealthEnabled: boolean; stealthRandomDelays: boolean; stealthHumanTyping: boolean; stealthMouseMovements: boolean; typingWpmMin: number; typingWpmMax: number; minDelayMs: number; maxDelayMs: number; // Paths configDir: string; dataDir: string; browserStateDir: string; chromeProfileDir: string; chromeInstancesDir: string; // Library Configuration (optional, for default notebook metadata) notebookDescription: string; notebookTopics: string[]; notebookContentTypes: string[]; notebookUseCases: string[]; // Multi-instance profile strategy profileStrategy: 'auto' | 'single' | 'isolated'; cloneProfileOnIsolated: boolean; cleanupInstancesOnStartup: boolean; cleanupInstancesOnShutdown: boolean; instanceProfileTtlHours: number; instanceProfileMaxCount: number; } /** * Default Configuration (works out of the box!) */ const DEFAULTS: Config = { // NotebookLM notebookUrl: '', // Browser Settings headless: true, browserTimeout: 30000, viewport: { width: 1024, height: 768 }, // Session Management maxSessions: 10, sessionTimeout: 900, // 15 minutes // Authentication autoLoginEnabled: false, loginEmail: '', loginPassword: '', autoLoginTimeoutMs: 120000, // 2 minutes // Stealth Settings stealthEnabled: true, stealthRandomDelays: true, stealthHumanTyping: true, stealthMouseMovements: true, typingWpmMin: 160, typingWpmMax: 240, minDelayMs: 100, maxDelayMs: 400, // Paths (cross-platform via env-paths) configDir: paths.config, dataDir: paths.data, browserStateDir: path.join(paths.data, 'browser_state'), chromeProfileDir: path.join(paths.data, 'chrome_profile'), chromeInstancesDir: path.join(paths.data, 'chrome_profile_instances'), // Library Configuration notebookDescription: 'General knowledge base', notebookTopics: ['General topics'], notebookContentTypes: ['documentation', 'examples'], notebookUseCases: ['General research'], // Multi-instance strategy profileStrategy: 'auto', cloneProfileOnIsolated: false, cleanupInstancesOnStartup: true, cleanupInstancesOnShutdown: true, instanceProfileTtlHours: 72, instanceProfileMaxCount: 20, }; /** * Parse boolean from string (for env vars) */ function parseBoolean(value: string | undefined, defaultValue: boolean): boolean { if (value === undefined) return defaultValue; const lower = value.toLowerCase(); if (lower === 'true' || lower === '1') return true; if (lower === 'false' || lower === '0') return false; return defaultValue; } /** * Parse integer from string (for env vars) */ function parseInteger(value: string | undefined, defaultValue: number): number { if (value === undefined) return defaultValue; const parsed = Number.parseInt(value, 10); return Number.isNaN(parsed) ? defaultValue : parsed; } /** * Parse comma-separated array (for env vars) */ function parseArray(value: string | undefined, defaultValue: string[]): string[] { if (!value) return defaultValue; return value .split(',') .map((s) => s.trim()) .filter((s) => s.length > 0); } /** * Parse profile strategy from string (for env vars) */ function parseProfileStrategy( value: string | undefined, defaultValue: Config['profileStrategy'] ): Config['profileStrategy'] { if (!value) return defaultValue; const lower = value.toLowerCase(); if (lower === 'auto' || lower === 'single' || lower === 'isolated') { return lower; } return defaultValue; } /** * Apply environment variable overrides (legacy support) */ function applyEnvOverrides(config: Config): Config { return { ...config, // Override with env vars if present notebookUrl: process.env.NOTEBOOK_URL || config.notebookUrl, headless: parseBoolean(process.env.HEADLESS, config.headless), browserTimeout: parseInteger(process.env.BROWSER_TIMEOUT, config.browserTimeout), maxSessions: parseInteger(process.env.MAX_SESSIONS, config.maxSessions), sessionTimeout: parseInteger(process.env.SESSION_TIMEOUT, config.sessionTimeout), autoLoginEnabled: parseBoolean(process.env.AUTO_LOGIN_ENABLED, config.autoLoginEnabled), loginEmail: process.env.LOGIN_EMAIL || config.loginEmail, loginPassword: process.env.LOGIN_PASSWORD || config.loginPassword, autoLoginTimeoutMs: parseInteger(process.env.AUTO_LOGIN_TIMEOUT_MS, config.autoLoginTimeoutMs), stealthEnabled: parseBoolean(process.env.STEALTH_ENABLED, config.stealthEnabled), stealthRandomDelays: parseBoolean( process.env.STEALTH_RANDOM_DELAYS, config.stealthRandomDelays ), stealthHumanTyping: parseBoolean(process.env.STEALTH_HUMAN_TYPING, config.stealthHumanTyping), stealthMouseMovements: parseBoolean( process.env.STEALTH_MOUSE_MOVEMENTS, config.stealthMouseMovements ), typingWpmMin: parseInteger(process.env.TYPING_WPM_MIN, config.typingWpmMin), typingWpmMax: parseInteger(process.env.TYPING_WPM_MAX, config.typingWpmMax), minDelayMs: parseInteger(process.env.MIN_DELAY_MS, config.minDelayMs), maxDelayMs: parseInteger(process.env.MAX_DELAY_MS, config.maxDelayMs), notebookDescription: process.env.NOTEBOOK_DESCRIPTION || config.notebookDescription, notebookTopics: parseArray(process.env.NOTEBOOK_TOPICS, config.notebookTopics), notebookContentTypes: parseArray( process.env.NOTEBOOK_CONTENT_TYPES, config.notebookContentTypes ), notebookUseCases: parseArray(process.env.NOTEBOOK_USE_CASES, config.notebookUseCases), profileStrategy: parseProfileStrategy( process.env.NOTEBOOK_PROFILE_STRATEGY, config.profileStrategy ), cloneProfileOnIsolated: parseBoolean( process.env.NOTEBOOK_CLONE_PROFILE, config.cloneProfileOnIsolated ), cleanupInstancesOnStartup: parseBoolean( process.env.NOTEBOOK_CLEANUP_ON_STARTUP, config.cleanupInstancesOnStartup ), cleanupInstancesOnShutdown: parseBoolean( process.env.NOTEBOOK_CLEANUP_ON_SHUTDOWN, config.cleanupInstancesOnShutdown ), instanceProfileTtlHours: parseInteger( process.env.NOTEBOOK_INSTANCE_TTL_HOURS, config.instanceProfileTtlHours ), instanceProfileMaxCount: parseInteger( process.env.NOTEBOOK_INSTANCE_MAX_COUNT, config.instanceProfileMaxCount ), }; } /** * Build final configuration * Priority: Defaults → Environment Variables → Tool Parameters (at runtime) * No config.json files - everything via ENV or tool parameters! */ function buildConfig(): Config { return applyEnvOverrides(DEFAULTS); } /** * Global configuration instance */ export const CONFIG: Config = buildConfig(); /** * Ensure all required directories exist * NOTE: We do NOT create configDir - it's not needed! */ export function ensureDirectories(): void { const dirs = [ CONFIG.dataDir, CONFIG.browserStateDir, CONFIG.chromeProfileDir, CONFIG.chromeInstancesDir, ]; for (const dir of dirs) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } } } /** * Browser options that can be passed via tool parameters */ export interface BrowserOptions { show?: boolean; headless?: boolean; timeout_ms?: number; stealth?: { enabled?: boolean; random_delays?: boolean; human_typing?: boolean; mouse_movements?: boolean; typing_wpm_min?: number; typing_wpm_max?: number; delay_min_ms?: number; delay_max_ms?: number; }; viewport?: { width?: number; height?: number; }; } /** * Apply browser options to CONFIG (returns modified copy, doesn't mutate global CONFIG) */ export function applyBrowserOptions(options?: BrowserOptions, legacyShowBrowser?: boolean): Config { const config = { ...CONFIG }; // Handle legacy show_browser parameter if (legacyShowBrowser !== undefined) { config.headless = !legacyShowBrowser; } // Apply browser_options (takes precedence over legacy parameter) if (options) { if (options.show !== undefined) { config.headless = !options.show; } if (options.headless !== undefined) { config.headless = options.headless; } if (options.timeout_ms !== undefined) { config.browserTimeout = options.timeout_ms; } if (options.stealth) { const s = options.stealth; if (s.enabled !== undefined) config.stealthEnabled = s.enabled; if (s.random_delays !== undefined) config.stealthRandomDelays = s.random_delays; if (s.human_typing !== undefined) config.stealthHumanTyping = s.human_typing; if (s.mouse_movements !== undefined) config.stealthMouseMovements = s.mouse_movements; if (s.typing_wpm_min !== undefined) config.typingWpmMin = s.typing_wpm_min; if (s.typing_wpm_max !== undefined) config.typingWpmMax = s.typing_wpm_max; if (s.delay_min_ms !== undefined) config.minDelayMs = s.delay_min_ms; if (s.delay_max_ms !== undefined) config.maxDelayMs = s.delay_max_ms; } if (options.viewport) { config.viewport = { width: options.viewport.width ?? config.viewport.width, height: options.viewport.height ?? config.viewport.height, }; } } return config; } // Create directories on import ensureDirectories();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/roomi-fields/notebooklm-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server