@pan-sec/notebooklm-mcp 2026.2.11 → 2026.3.1
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 +62 -19
- package/SECURITY.md +31 -61
- package/dist/auth/auth-manager.d.ts +2 -1
- package/dist/auth/auth-manager.d.ts.map +1 -1
- package/dist/auth/auth-manager.js +117 -44
- package/dist/auth/auth-manager.js.map +1 -1
- package/dist/auth/mcp-auth.d.ts +24 -4
- package/dist/auth/mcp-auth.d.ts.map +1 -1
- package/dist/auth/mcp-auth.js +149 -19
- package/dist/auth/mcp-auth.js.map +1 -1
- package/dist/compliance/alert-manager.d.ts.map +1 -1
- package/dist/compliance/alert-manager.js +7 -4
- package/dist/compliance/alert-manager.js.map +1 -1
- package/dist/compliance/breach-detection.d.ts.map +1 -1
- package/dist/compliance/breach-detection.js +14 -7
- package/dist/compliance/breach-detection.js.map +1 -1
- package/dist/compliance/change-log.d.ts.map +1 -1
- package/dist/compliance/change-log.js +7 -4
- package/dist/compliance/change-log.js.map +1 -1
- package/dist/compliance/compliance-logger.d.ts.map +1 -1
- package/dist/compliance/compliance-logger.js +11 -6
- package/dist/compliance/compliance-logger.js.map +1 -1
- package/dist/compliance/consent-manager.d.ts.map +1 -1
- package/dist/compliance/consent-manager.js +5 -3
- package/dist/compliance/consent-manager.js.map +1 -1
- package/dist/compliance/data-erasure.d.ts +1 -1
- package/dist/compliance/data-erasure.d.ts.map +1 -1
- package/dist/compliance/data-erasure.js +142 -83
- package/dist/compliance/data-erasure.js.map +1 -1
- package/dist/compliance/data-export.d.ts.map +1 -1
- package/dist/compliance/data-export.js +23 -12
- package/dist/compliance/data-export.js.map +1 -1
- package/dist/compliance/data-inventory.d.ts.map +1 -1
- package/dist/compliance/data-inventory.js +7 -6
- package/dist/compliance/data-inventory.js.map +1 -1
- package/dist/compliance/dsar-handler.d.ts +7 -1
- package/dist/compliance/dsar-handler.d.ts.map +1 -1
- package/dist/compliance/dsar-handler.js +74 -61
- package/dist/compliance/dsar-handler.js.map +1 -1
- package/dist/compliance/evidence-collector.d.ts.map +1 -1
- package/dist/compliance/evidence-collector.js +10 -6
- package/dist/compliance/evidence-collector.js.map +1 -1
- package/dist/compliance/health-monitor.d.ts.map +1 -1
- package/dist/compliance/health-monitor.js +15 -9
- package/dist/compliance/health-monitor.js.map +1 -1
- package/dist/compliance/incident-manager.d.ts.map +1 -1
- package/dist/compliance/incident-manager.js +5 -3
- package/dist/compliance/incident-manager.js.map +1 -1
- package/dist/compliance/policy-docs.d.ts.map +1 -1
- package/dist/compliance/policy-docs.js +14 -11
- package/dist/compliance/policy-docs.js.map +1 -1
- package/dist/compliance/privacy-notice-text.d.ts.map +1 -1
- package/dist/compliance/privacy-notice-text.js +3 -4
- package/dist/compliance/privacy-notice-text.js.map +1 -1
- package/dist/compliance/privacy-notice.d.ts.map +1 -1
- package/dist/compliance/privacy-notice.js +5 -3
- package/dist/compliance/privacy-notice.js.map +1 -1
- package/dist/compliance/report-generator.d.ts.map +1 -1
- package/dist/compliance/report-generator.js +5 -3
- package/dist/compliance/report-generator.js.map +1 -1
- package/dist/compliance/retention-engine.d.ts.map +1 -1
- package/dist/compliance/retention-engine.js +24 -10
- package/dist/compliance/retention-engine.js.map +1 -1
- package/dist/compliance/siem-exporter.d.ts.map +1 -1
- package/dist/compliance/siem-exporter.js +40 -16
- package/dist/compliance/siem-exporter.js.map +1 -1
- package/dist/config.d.ts +8 -31
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +26 -64
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +22 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +55 -4
- package/dist/errors.js.map +1 -1
- package/dist/gemini/gemini-client.d.ts +1 -0
- package/dist/gemini/gemini-client.d.ts.map +1 -1
- package/dist/gemini/gemini-client.js +50 -49
- package/dist/gemini/gemini-client.js.map +1 -1
- package/dist/gemini/types.d.ts +3 -1
- package/dist/gemini/types.d.ts.map +1 -1
- package/dist/gemini/types.js.map +1 -1
- package/dist/index.d.ts +52 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +412 -89
- package/dist/index.js.map +1 -1
- package/dist/library/notebook-library.d.ts.map +1 -1
- package/dist/library/notebook-library.js +2 -1
- package/dist/library/notebook-library.js.map +1 -1
- package/dist/logging/query-logger.d.ts +13 -1
- package/dist/logging/query-logger.d.ts.map +1 -1
- package/dist/logging/query-logger.js +62 -10
- package/dist/logging/query-logger.js.map +1 -1
- package/dist/notebook-creation/audio-manager.d.ts.map +1 -1
- package/dist/notebook-creation/audio-manager.js +19 -24
- package/dist/notebook-creation/audio-manager.js.map +1 -1
- package/dist/notebook-creation/browser-options.d.ts +28 -0
- package/dist/notebook-creation/browser-options.d.ts.map +1 -0
- package/dist/notebook-creation/browser-options.js +75 -0
- package/dist/notebook-creation/browser-options.js.map +1 -0
- package/dist/notebook-creation/data-table-manager.d.ts.map +1 -1
- package/dist/notebook-creation/data-table-manager.js +20 -21
- package/dist/notebook-creation/data-table-manager.js.map +1 -1
- package/dist/notebook-creation/discover-creation-flow.d.ts +0 -6
- package/dist/notebook-creation/discover-creation-flow.d.ts.map +1 -1
- package/dist/notebook-creation/discover-creation-flow.js +10 -10
- package/dist/notebook-creation/discover-creation-flow.js.map +1 -1
- package/dist/notebook-creation/discover-quota.d.ts +0 -6
- package/dist/notebook-creation/discover-quota.d.ts.map +1 -1
- package/dist/notebook-creation/discover-quota.js +12 -13
- package/dist/notebook-creation/discover-quota.js.map +1 -1
- package/dist/notebook-creation/discover-sources.js +15 -16
- package/dist/notebook-creation/discover-sources.js.map +1 -1
- package/dist/notebook-creation/dom-scripts.d.ts +10 -0
- package/dist/notebook-creation/dom-scripts.d.ts.map +1 -0
- package/dist/notebook-creation/dom-scripts.js +58 -0
- package/dist/notebook-creation/dom-scripts.js.map +1 -0
- package/dist/notebook-creation/errors.d.ts +18 -0
- package/dist/notebook-creation/errors.d.ts.map +1 -0
- package/dist/notebook-creation/errors.js +20 -0
- package/dist/notebook-creation/errors.js.map +1 -0
- package/dist/notebook-creation/index.d.ts +2 -1
- package/dist/notebook-creation/index.d.ts.map +1 -1
- package/dist/notebook-creation/index.js +2 -1
- package/dist/notebook-creation/index.js.map +1 -1
- package/dist/notebook-creation/notebook-creator.d.ts +6 -82
- package/dist/notebook-creation/notebook-creator.d.ts.map +1 -1
- package/dist/notebook-creation/notebook-creator.js +49 -835
- package/dist/notebook-creation/notebook-creator.js.map +1 -1
- package/dist/notebook-creation/notebook-nav.d.ts +19 -0
- package/dist/notebook-creation/notebook-nav.d.ts.map +1 -0
- package/dist/notebook-creation/notebook-nav.js +240 -0
- package/dist/notebook-creation/notebook-nav.js.map +1 -0
- package/dist/notebook-creation/notebook-sync.d.ts.map +1 -1
- package/dist/notebook-creation/notebook-sync.js +36 -38
- package/dist/notebook-creation/notebook-sync.js.map +1 -1
- package/dist/notebook-creation/selector-discovery.d.ts.map +1 -1
- package/dist/notebook-creation/selector-discovery.js +17 -24
- package/dist/notebook-creation/selector-discovery.js.map +1 -1
- package/dist/notebook-creation/selectors.d.ts +23 -37
- package/dist/notebook-creation/selectors.d.ts.map +1 -1
- package/dist/notebook-creation/selectors.js +56 -60
- package/dist/notebook-creation/selectors.js.map +1 -1
- package/dist/notebook-creation/source-manager.d.ts +25 -0
- package/dist/notebook-creation/source-manager.d.ts.map +1 -1
- package/dist/notebook-creation/source-manager.js +689 -50
- package/dist/notebook-creation/source-manager.js.map +1 -1
- package/dist/notebook-creation/types.d.ts +4 -0
- package/dist/notebook-creation/types.d.ts.map +1 -1
- package/dist/notebook-creation/video-manager.d.ts.map +1 -1
- package/dist/notebook-creation/video-manager.js +33 -35
- package/dist/notebook-creation/video-manager.js.map +1 -1
- package/dist/observability/metrics.d.ts +19 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +35 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/quota/quota-manager.d.ts +11 -3
- package/dist/quota/quota-manager.d.ts.map +1 -1
- package/dist/quota/quota-manager.js +139 -47
- package/dist/quota/quota-manager.js.map +1 -1
- package/dist/resources/resource-handlers.d.ts.map +1 -1
- package/dist/resources/resource-handlers.js +39 -17
- package/dist/resources/resource-handlers.js.map +1 -1
- package/dist/session/browser-session.d.ts.map +1 -1
- package/dist/session/browser-session.js +22 -22
- package/dist/session/browser-session.js.map +1 -1
- package/dist/session/session-timeout.d.ts.map +1 -1
- package/dist/session/session-timeout.js +4 -2
- package/dist/session/session-timeout.js.map +1 -1
- package/dist/session/shared-context-manager.d.ts.map +1 -1
- package/dist/session/shared-context-manager.js +31 -30
- package/dist/session/shared-context-manager.js.map +1 -1
- package/dist/tools/annotations.d.ts.map +1 -1
- package/dist/tools/annotations.js +9 -56
- package/dist/tools/annotations.js.map +1 -1
- package/dist/tools/definitions/ask-question.d.ts.map +1 -1
- package/dist/tools/definitions/ask-question.js +35 -100
- package/dist/tools/definitions/ask-question.js.map +1 -1
- package/dist/tools/definitions/chat-history.d.ts +47 -1
- package/dist/tools/definitions/chat-history.d.ts.map +1 -1
- package/dist/tools/definitions/chat-history.js +10 -1
- package/dist/tools/definitions/chat-history.js.map +1 -1
- package/dist/tools/definitions/data-tables.d.ts.map +1 -1
- package/dist/tools/definitions/data-tables.js +2 -0
- package/dist/tools/definitions/data-tables.js.map +1 -1
- package/dist/tools/definitions/gemini.d.ts.map +1 -1
- package/dist/tools/definitions/gemini.js +54 -11
- package/dist/tools/definitions/gemini.js.map +1 -1
- package/dist/tools/definitions/notebook-management.d.ts.map +1 -1
- package/dist/tools/definitions/notebook-management.js +100 -70
- package/dist/tools/definitions/notebook-management.js.map +1 -1
- package/dist/tools/definitions/query-history.d.ts +47 -1
- package/dist/tools/definitions/query-history.d.ts.map +1 -1
- package/dist/tools/definitions/query-history.js +7 -0
- package/dist/tools/definitions/query-history.js.map +1 -1
- package/dist/tools/definitions/session-management.d.ts.map +1 -1
- package/dist/tools/definitions/session-management.js +5 -0
- package/dist/tools/definitions/session-management.js.map +1 -1
- package/dist/tools/definitions/system.d.ts.map +1 -1
- package/dist/tools/definitions/system.js +71 -100
- package/dist/tools/definitions/system.js.map +1 -1
- package/dist/tools/definitions/video.d.ts.map +1 -1
- package/dist/tools/definitions/video.js +4 -1
- package/dist/tools/definitions/video.js.map +1 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +4 -0
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/handlers/ask-question.d.ts +1 -1
- package/dist/tools/handlers/ask-question.d.ts.map +1 -1
- package/dist/tools/handlers/ask-question.js +57 -13
- package/dist/tools/handlers/ask-question.js.map +1 -1
- package/dist/tools/handlers/audio-video.d.ts.map +1 -1
- package/dist/tools/handlers/audio-video.js +22 -161
- package/dist/tools/handlers/audio-video.js.map +1 -1
- package/dist/tools/handlers/auth.d.ts +14 -19
- package/dist/tools/handlers/auth.d.ts.map +1 -1
- package/dist/tools/handlers/auth.js +77 -121
- package/dist/tools/handlers/auth.js.map +1 -1
- package/dist/tools/handlers/error-utils.d.ts +16 -0
- package/dist/tools/handlers/error-utils.d.ts.map +1 -0
- package/dist/tools/handlers/error-utils.js +39 -0
- package/dist/tools/handlers/error-utils.js.map +1 -0
- package/dist/tools/handlers/gemini.d.ts +2 -0
- package/dist/tools/handlers/gemini.d.ts.map +1 -1
- package/dist/tools/handlers/gemini.js +88 -51
- package/dist/tools/handlers/gemini.js.map +1 -1
- package/dist/tools/handlers/index.d.ts +39 -47
- package/dist/tools/handlers/index.d.ts.map +1 -1
- package/dist/tools/handlers/index.js +15 -4
- package/dist/tools/handlers/index.js.map +1 -1
- package/dist/tools/handlers/notebook-creation.d.ts.map +1 -1
- package/dist/tools/handlers/notebook-creation.js +102 -86
- package/dist/tools/handlers/notebook-creation.js.map +1 -1
- package/dist/tools/handlers/notebook-management.d.ts +8 -8
- package/dist/tools/handlers/notebook-management.d.ts.map +1 -1
- package/dist/tools/handlers/notebook-management.js +34 -80
- package/dist/tools/handlers/notebook-management.js.map +1 -1
- package/dist/tools/handlers/session-management.d.ts +8 -10
- package/dist/tools/handlers/session-management.d.ts.map +1 -1
- package/dist/tools/handlers/session-management.js +34 -63
- package/dist/tools/handlers/session-management.js.map +1 -1
- package/dist/tools/handlers/system.d.ts.map +1 -1
- package/dist/tools/handlers/system.js +45 -10
- package/dist/tools/handlers/system.js.map +1 -1
- package/dist/tools/handlers/types.d.ts +1 -1
- package/dist/tools/handlers/types.d.ts.map +1 -1
- package/dist/tools/handlers/webhooks.d.ts.map +1 -1
- package/dist/tools/handlers/webhooks.js +15 -13
- package/dist/tools/handlers/webhooks.js.map +1 -1
- package/dist/types.d.ts +7 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/audit-logger.d.ts +19 -1
- package/dist/utils/audit-logger.d.ts.map +1 -1
- package/dist/utils/audit-logger.js +198 -30
- package/dist/utils/audit-logger.js.map +1 -1
- package/dist/utils/cleanup-manager.d.ts.map +1 -1
- package/dist/utils/cleanup-manager.js +6 -3
- package/dist/utils/cleanup-manager.js.map +1 -1
- package/dist/utils/crypto.d.ts +4 -1
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js +32 -21
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/file-lock.d.ts.map +1 -1
- package/dist/utils/file-lock.js +87 -16
- package/dist/utils/file-lock.js.map +1 -1
- package/dist/utils/file-permissions.d.ts +2 -0
- package/dist/utils/file-permissions.d.ts.map +1 -1
- package/dist/utils/file-permissions.js +2 -1
- package/dist/utils/file-permissions.js.map +1 -1
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +16 -0
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/page-utils.d.ts +13 -0
- package/dist/utils/page-utils.d.ts.map +1 -1
- package/dist/utils/page-utils.js +61 -39
- package/dist/utils/page-utils.js.map +1 -1
- package/dist/utils/response-validator.d.ts.map +1 -1
- package/dist/utils/response-validator.js +27 -22
- package/dist/utils/response-validator.js.map +1 -1
- package/dist/utils/secrets-scanner.d.ts +11 -0
- package/dist/utils/secrets-scanner.d.ts.map +1 -1
- package/dist/utils/secrets-scanner.js +65 -17
- package/dist/utils/secrets-scanner.js.map +1 -1
- package/dist/utils/secure-memory.d.ts +9 -31
- package/dist/utils/secure-memory.d.ts.map +1 -1
- package/dist/utils/secure-memory.js +17 -102
- package/dist/utils/secure-memory.js.map +1 -1
- package/dist/utils/security.d.ts +4 -3
- package/dist/utils/security.d.ts.map +1 -1
- package/dist/utils/security.js +43 -13
- package/dist/utils/security.js.map +1 -1
- package/dist/utils/stealth-utils.d.ts.map +1 -1
- package/dist/utils/stealth-utils.js +4 -4
- package/dist/utils/stealth-utils.js.map +1 -1
- package/dist/webhooks/types.d.ts +4 -0
- package/dist/webhooks/types.d.ts.map +1 -1
- package/dist/webhooks/webhook-dispatcher.d.ts +80 -12
- package/dist/webhooks/webhook-dispatcher.d.ts.map +1 -1
- package/dist/webhooks/webhook-dispatcher.js +497 -74
- package/dist/webhooks/webhook-dispatcher.js.map +1 -1
- package/docs/archive/ISSUES-legacy-2026-04-24.md +644 -0
- package/docs/dependency-risk.md +25 -0
- package/docs/testing-runbook.md +166 -0
- package/docs/usage-guide.md +2 -1
- package/package.json +34 -16
package/dist/index.js
CHANGED
|
@@ -32,6 +32,8 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
32
32
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
33
33
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
34
34
|
import { createRequire } from "module";
|
|
35
|
+
import crypto from "node:crypto";
|
|
36
|
+
import { pathToFileURL } from "node:url";
|
|
35
37
|
import { AuthManager } from "./auth/auth-manager.js";
|
|
36
38
|
import { SessionManager } from "./session/session-manager.js";
|
|
37
39
|
// Read version from package.json
|
|
@@ -43,15 +45,126 @@ import { ToolHandlers, buildToolDefinitions } from "./tools/index.js";
|
|
|
43
45
|
import { ResourceHandlers } from "./resources/resource-handlers.js";
|
|
44
46
|
import { SettingsManager } from "./utils/settings-manager.js";
|
|
45
47
|
import { CliHandler } from "./utils/cli-handler.js";
|
|
46
|
-
import { CONFIG } from "./config.js";
|
|
48
|
+
import { CONFIG, ensureDirectories } from "./config.js";
|
|
47
49
|
import { log } from "./utils/logger.js";
|
|
48
50
|
import { audit, getAuditLogger } from "./utils/audit-logger.js";
|
|
49
51
|
import { checkSecurityContext } from "./utils/security.js";
|
|
50
52
|
import { getMCPAuthenticator, authenticateMCPRequest } from "./auth/mcp-auth.js";
|
|
53
|
+
import { getComplianceTools, handleComplianceToolCall, } from "./compliance/compliance-tools.js";
|
|
54
|
+
import { getPrivacyNoticeManager, getPrivacyNoticeCLIText } from "./compliance/privacy-notice.js";
|
|
55
|
+
import { runRetentionPolicies } from "./compliance/retention-engine.js";
|
|
56
|
+
import { getBreachDetector } from "./compliance/breach-detection.js";
|
|
57
|
+
import { exportToSIEM } from "./compliance/siem-exporter.js";
|
|
58
|
+
const LIST_TOOLS_PAGE_SIZE = 25;
|
|
59
|
+
const TOOL_NAMES = [
|
|
60
|
+
"ask_question", "add_notebook", "list_notebooks", "get_notebook", "select_notebook",
|
|
61
|
+
"update_notebook", "remove_notebook", "search_notebooks", "get_library_stats",
|
|
62
|
+
"export_library", "get_quota", "set_quota_tier", "get_project_info", "create_notebook",
|
|
63
|
+
"batch_create_notebooks", "sync_library", "list_sessions", "close_session", "reset_session",
|
|
64
|
+
"get_health", "setup_auth", "re_auth", "cleanup_data", "list_sources", "add_source",
|
|
65
|
+
"add_folder", "remove_source", "generate_audio_overview", "get_audio_status", "download_audio",
|
|
66
|
+
"generate_video_overview", "get_video_status", "generate_data_table", "get_data_table",
|
|
67
|
+
"configure_webhook", "list_webhooks", "test_webhook", "remove_webhook", "deep_research",
|
|
68
|
+
"gemini_query", "get_research_status", "upload_document", "query_document", "list_documents",
|
|
69
|
+
"delete_document", "query_chunked_document", "get_query_history", "get_notebook_chat_history",
|
|
70
|
+
"submit_dsar", "export_user_data", "request_data_erasure",
|
|
71
|
+
"grant_consent", "revoke_consent", "report_security_incident", "collect_audit_evidence",
|
|
72
|
+
"generate_compliance_report",
|
|
73
|
+
];
|
|
74
|
+
function isToolName(name) {
|
|
75
|
+
return TOOL_NAMES.includes(name);
|
|
76
|
+
}
|
|
77
|
+
function toToolArgs(value) {
|
|
78
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
79
|
+
return Object.fromEntries(Object.entries(value));
|
|
80
|
+
}
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
function asToolInput(args) {
|
|
84
|
+
return args;
|
|
85
|
+
}
|
|
86
|
+
function parseListToolsCursor(cursor, totalTools) {
|
|
87
|
+
if (!cursor)
|
|
88
|
+
return 0;
|
|
89
|
+
const offset = Number.parseInt(cursor, 10);
|
|
90
|
+
if (!Number.isInteger(offset) || offset < 0 || offset >= totalTools)
|
|
91
|
+
return 0;
|
|
92
|
+
return offset;
|
|
93
|
+
}
|
|
94
|
+
function classifyToolError(error) {
|
|
95
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
96
|
+
return /\b(transport|protocol|json-?rpc|connection|stdio|notification)\b/i.test(message)
|
|
97
|
+
? "transport"
|
|
98
|
+
: "domain";
|
|
99
|
+
}
|
|
100
|
+
const TOOLS_EXEMPT_FROM_AUTH = new Set([
|
|
101
|
+
"ask_question", "add_notebook", "list_notebooks", "get_notebook", "select_notebook",
|
|
102
|
+
"update_notebook", "remove_notebook", "search_notebooks", "get_library_stats",
|
|
103
|
+
"get_quota", "set_quota_tier", "get_project_info", "create_notebook",
|
|
104
|
+
"batch_create_notebooks", "sync_library", "list_sessions", "close_session", "reset_session",
|
|
105
|
+
"get_health", "list_sources", "add_source", "remove_source",
|
|
106
|
+
"generate_audio_overview", "get_audio_status", "generate_video_overview", "get_video_status",
|
|
107
|
+
"generate_data_table", "get_data_table", "list_webhooks",
|
|
108
|
+
"deep_research", "gemini_query", "get_research_status",
|
|
109
|
+
"query_document", "list_documents", "query_chunked_document",
|
|
110
|
+
"get_query_history", "get_notebook_chat_history",
|
|
111
|
+
]);
|
|
112
|
+
const TOOLS_REQUIRING_AUTH = new Set([
|
|
113
|
+
"add_folder",
|
|
114
|
+
"cleanup_data",
|
|
115
|
+
"export_library",
|
|
116
|
+
"setup_auth",
|
|
117
|
+
"re_auth",
|
|
118
|
+
"configure_webhook",
|
|
119
|
+
"remove_webhook",
|
|
120
|
+
"test_webhook",
|
|
121
|
+
"delete_document",
|
|
122
|
+
"upload_document",
|
|
123
|
+
"download_audio",
|
|
124
|
+
// Compliance — destructive or privileged operations.
|
|
125
|
+
"submit_dsar",
|
|
126
|
+
"export_user_data",
|
|
127
|
+
"request_data_erasure",
|
|
128
|
+
"grant_consent",
|
|
129
|
+
"revoke_consent",
|
|
130
|
+
"report_security_incident",
|
|
131
|
+
"collect_audit_evidence",
|
|
132
|
+
"generate_compliance_report",
|
|
133
|
+
]);
|
|
134
|
+
const ADVANCED_TOOLS = new Set([
|
|
135
|
+
"export_library",
|
|
136
|
+
"list_sessions",
|
|
137
|
+
"close_session",
|
|
138
|
+
"reset_session",
|
|
139
|
+
"cleanup_data",
|
|
140
|
+
"configure_webhook",
|
|
141
|
+
"list_webhooks",
|
|
142
|
+
"test_webhook",
|
|
143
|
+
"remove_webhook",
|
|
144
|
+
"deep_research",
|
|
145
|
+
"gemini_query",
|
|
146
|
+
"get_research_status",
|
|
147
|
+
"upload_document",
|
|
148
|
+
"query_document",
|
|
149
|
+
"list_documents",
|
|
150
|
+
"delete_document",
|
|
151
|
+
"query_chunked_document",
|
|
152
|
+
"get_query_history",
|
|
153
|
+
"get_notebook_chat_history",
|
|
154
|
+
"submit_dsar",
|
|
155
|
+
"export_user_data",
|
|
156
|
+
"request_data_erasure",
|
|
157
|
+
"grant_consent",
|
|
158
|
+
"revoke_consent",
|
|
159
|
+
"report_security_incident",
|
|
160
|
+
"collect_audit_evidence",
|
|
161
|
+
"generate_compliance_report",
|
|
162
|
+
]);
|
|
51
163
|
/**
|
|
52
164
|
* Main MCP Server Class
|
|
53
165
|
*/
|
|
54
|
-
class NotebookLMMCPServer {
|
|
166
|
+
export class NotebookLMMCPServer {
|
|
167
|
+
options;
|
|
55
168
|
server;
|
|
56
169
|
authManager;
|
|
57
170
|
sessionManager;
|
|
@@ -61,8 +174,12 @@ class NotebookLMMCPServer {
|
|
|
61
174
|
settingsManager;
|
|
62
175
|
toolDefinitions;
|
|
63
176
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
-
toolRegistry;
|
|
65
|
-
|
|
177
|
+
toolRegistry = new Map();
|
|
178
|
+
complianceToolNames;
|
|
179
|
+
retentionTimer;
|
|
180
|
+
advancedToolsEnabled;
|
|
181
|
+
constructor(options = {}) {
|
|
182
|
+
this.options = options;
|
|
66
183
|
// Initialize MCP Server
|
|
67
184
|
this.server = new Server({
|
|
68
185
|
name: "notebooklm-mcp",
|
|
@@ -72,8 +189,7 @@ class NotebookLMMCPServer {
|
|
|
72
189
|
tools: {},
|
|
73
190
|
resources: {},
|
|
74
191
|
prompts: {},
|
|
75
|
-
completions: {},
|
|
76
|
-
logging: {},
|
|
192
|
+
completions: {},
|
|
77
193
|
},
|
|
78
194
|
});
|
|
79
195
|
// Initialize managers
|
|
@@ -81,21 +197,37 @@ class NotebookLMMCPServer {
|
|
|
81
197
|
this.sessionManager = new SessionManager(this.authManager);
|
|
82
198
|
this.library = new NotebookLibrary();
|
|
83
199
|
this.settingsManager = new SettingsManager();
|
|
200
|
+
this.advancedToolsEnabled = process.env.NLMCP_ADVANCED_TOOLS === "1";
|
|
84
201
|
// Initialize handlers
|
|
85
202
|
this.toolHandlers = new ToolHandlers(this.sessionManager, this.authManager, this.library);
|
|
86
203
|
this.resourceHandlers = new ResourceHandlers(this.library);
|
|
87
204
|
// Build and Filter tool definitions
|
|
88
205
|
const allTools = buildToolDefinitions(this.library);
|
|
89
|
-
this.toolDefinitions = this.settingsManager.filterTools(allTools);
|
|
206
|
+
this.toolDefinitions = this.filterAdvancedTools(this.settingsManager.filterTools(allTools));
|
|
207
|
+
// Track compliance tool names for the short-circuit dispatch path.
|
|
208
|
+
this.complianceToolNames = new Set(this.filterAdvancedTools(getComplianceTools()).map((t) => t.name));
|
|
90
209
|
// Setup handlers
|
|
91
210
|
this.setupHandlers();
|
|
92
|
-
this.
|
|
211
|
+
if (this.options.registerShutdownHandlers !== false) {
|
|
212
|
+
this.setupShutdownHandlers();
|
|
213
|
+
}
|
|
93
214
|
const activeSettings = this.settingsManager.getEffectiveSettings();
|
|
94
215
|
log.info("🚀 NotebookLM MCP Server initialized");
|
|
95
216
|
log.info(` Version: ${VERSION}`);
|
|
96
217
|
log.info(` Node: ${process.version}`);
|
|
97
218
|
log.info(` Platform: ${process.platform}`);
|
|
98
219
|
log.info(` Profile: ${activeSettings.profile} (${this.toolDefinitions.length} tools active)`);
|
|
220
|
+
log.info(` Advanced tools: ${this.advancedToolsEnabled ? "enabled" : "disabled"}`);
|
|
221
|
+
}
|
|
222
|
+
filterAdvancedTools(tools) {
|
|
223
|
+
if (this.advancedToolsEnabled)
|
|
224
|
+
return tools;
|
|
225
|
+
return tools.filter((tool) => !isToolName(tool.name) || !ADVANCED_TOOLS.has(tool.name));
|
|
226
|
+
}
|
|
227
|
+
filterAdvancedToolRegistry(registry) {
|
|
228
|
+
if (this.advancedToolsEnabled)
|
|
229
|
+
return registry;
|
|
230
|
+
return new Map(Array.from(registry.entries()).filter(([name]) => !isToolName(name) || !ADVANCED_TOOLS.has(name)));
|
|
99
231
|
}
|
|
100
232
|
/**
|
|
101
233
|
* Setup MCP request handlers
|
|
@@ -104,99 +236,124 @@ class NotebookLMMCPServer {
|
|
|
104
236
|
// Register Resource Handlers (Resources, Templates, Completions)
|
|
105
237
|
this.resourceHandlers.registerHandlers(this.server);
|
|
106
238
|
// Build tool registry once (not per-request)
|
|
107
|
-
|
|
108
|
-
this.toolRegistry = new Map([
|
|
239
|
+
this.toolRegistry = this.filterAdvancedToolRegistry(new Map([
|
|
109
240
|
// Ask Question
|
|
110
|
-
["ask_question", (
|
|
241
|
+
["ask_question", (args, progress) => this.toolHandlers.handleAskQuestion(asToolInput(args), progress)],
|
|
111
242
|
// Notebook Management
|
|
112
|
-
["add_notebook", (
|
|
243
|
+
["add_notebook", (args) => this.toolHandlers.handleAddNotebook(asToolInput(args))],
|
|
113
244
|
["list_notebooks", () => this.toolHandlers.handleListNotebooks()],
|
|
114
|
-
["get_notebook", (
|
|
115
|
-
["select_notebook", (
|
|
116
|
-
["update_notebook", (
|
|
117
|
-
["remove_notebook", (
|
|
118
|
-
["search_notebooks", (
|
|
245
|
+
["get_notebook", (args) => this.toolHandlers.handleGetNotebook(asToolInput(args))],
|
|
246
|
+
["select_notebook", (args) => this.toolHandlers.handleSelectNotebook(asToolInput(args))],
|
|
247
|
+
["update_notebook", (args) => this.toolHandlers.handleUpdateNotebook(asToolInput(args))],
|
|
248
|
+
["remove_notebook", (args) => this.toolHandlers.handleRemoveNotebook(asToolInput(args))],
|
|
249
|
+
["search_notebooks", (args) => this.toolHandlers.handleSearchNotebooks(asToolInput(args))],
|
|
119
250
|
["get_library_stats", () => this.toolHandlers.handleGetLibraryStats()],
|
|
120
|
-
["export_library", (
|
|
251
|
+
["export_library", (args) => this.toolHandlers.handleExportLibrary(asToolInput(args))],
|
|
121
252
|
// Quota & System
|
|
122
|
-
["get_quota", (
|
|
123
|
-
["set_quota_tier", (
|
|
253
|
+
["get_quota", (args) => this.toolHandlers.handleGetQuota(asToolInput(args))],
|
|
254
|
+
["set_quota_tier", (args) => this.toolHandlers.handleSetQuotaTier(asToolInput(args))],
|
|
124
255
|
["get_project_info", () => this.toolHandlers.handleGetProjectInfo()],
|
|
125
256
|
// Notebook Creation
|
|
126
|
-
["create_notebook", (
|
|
127
|
-
["batch_create_notebooks", (
|
|
128
|
-
["sync_library", (
|
|
257
|
+
["create_notebook", (args, progress) => this.toolHandlers.handleCreateNotebook(asToolInput(args), progress)],
|
|
258
|
+
["batch_create_notebooks", (args, progress) => this.toolHandlers.handleBatchCreateNotebooks(asToolInput(args), progress)],
|
|
259
|
+
["sync_library", (args) => this.toolHandlers.handleSyncLibrary(asToolInput(args))],
|
|
129
260
|
// Session Management
|
|
130
261
|
["list_sessions", () => this.toolHandlers.handleListSessions()],
|
|
131
|
-
["close_session", (
|
|
132
|
-
["reset_session", (
|
|
133
|
-
["get_health", (
|
|
262
|
+
["close_session", (args) => this.toolHandlers.handleCloseSession(asToolInput(args))],
|
|
263
|
+
["reset_session", (args) => this.toolHandlers.handleResetSession(asToolInput(args))],
|
|
264
|
+
["get_health", (args) => this.toolHandlers.handleGetHealth(asToolInput(args))],
|
|
134
265
|
// Auth
|
|
135
|
-
["setup_auth", (
|
|
136
|
-
["re_auth", (
|
|
137
|
-
["cleanup_data", (
|
|
266
|
+
["setup_auth", (args, progress) => this.toolHandlers.handleSetupAuth(asToolInput(args), progress)],
|
|
267
|
+
["re_auth", (args, progress) => this.toolHandlers.handleReAuth(asToolInput(args), progress)],
|
|
268
|
+
["cleanup_data", (args) => this.toolHandlers.handleCleanupData(asToolInput(args))],
|
|
138
269
|
// Sources
|
|
139
|
-
["list_sources", (
|
|
140
|
-
["add_source", (
|
|
141
|
-
["add_folder", (
|
|
142
|
-
["remove_source", (
|
|
270
|
+
["list_sources", (args) => this.toolHandlers.handleListSources(asToolInput(args))],
|
|
271
|
+
["add_source", (args) => this.toolHandlers.handleAddSource(asToolInput(args))],
|
|
272
|
+
["add_folder", (args, progress) => this.toolHandlers.handleAddFolder(asToolInput(args), progress)],
|
|
273
|
+
["remove_source", (args) => this.toolHandlers.handleRemoveSource(asToolInput(args))],
|
|
143
274
|
// Audio / Video / Data Table
|
|
144
|
-
["generate_audio_overview", (
|
|
145
|
-
["get_audio_status", (
|
|
146
|
-
["download_audio", (
|
|
147
|
-
["generate_video_overview", (
|
|
148
|
-
["get_video_status", (
|
|
149
|
-
["generate_data_table", (
|
|
150
|
-
["get_data_table", (
|
|
275
|
+
["generate_audio_overview", (args) => this.toolHandlers.handleGenerateAudioOverview(asToolInput(args))],
|
|
276
|
+
["get_audio_status", (args) => this.toolHandlers.handleGetAudioStatus(asToolInput(args))],
|
|
277
|
+
["download_audio", (args) => this.toolHandlers.handleDownloadAudio(asToolInput(args))],
|
|
278
|
+
["generate_video_overview", (args) => this.toolHandlers.handleGenerateVideoOverview(asToolInput(args))],
|
|
279
|
+
["get_video_status", (args) => this.toolHandlers.handleGetVideoStatus(asToolInput(args))],
|
|
280
|
+
["generate_data_table", (args) => this.toolHandlers.handleGenerateDataTable(asToolInput(args))],
|
|
281
|
+
["get_data_table", (args) => this.toolHandlers.handleGetDataTable(asToolInput(args))],
|
|
151
282
|
// Webhooks
|
|
152
|
-
["configure_webhook", (
|
|
283
|
+
["configure_webhook", (args) => this.toolHandlers.handleConfigureWebhook(asToolInput(args))],
|
|
153
284
|
["list_webhooks", () => this.toolHandlers.handleListWebhooks()],
|
|
154
|
-
["test_webhook", (
|
|
155
|
-
["remove_webhook", (
|
|
285
|
+
["test_webhook", (args) => this.toolHandlers.handleTestWebhook(asToolInput(args))],
|
|
286
|
+
["remove_webhook", (args) => this.toolHandlers.handleRemoveWebhook(asToolInput(args))],
|
|
156
287
|
// Gemini API
|
|
157
|
-
["deep_research", (
|
|
158
|
-
["gemini_query", (
|
|
159
|
-
["get_research_status", (
|
|
160
|
-
["upload_document", (
|
|
161
|
-
["query_document", (
|
|
162
|
-
["list_documents", (
|
|
163
|
-
["delete_document", (
|
|
164
|
-
["query_chunked_document", (
|
|
165
|
-
["get_query_history", (
|
|
166
|
-
["get_notebook_chat_history", (
|
|
167
|
-
]);
|
|
168
|
-
//
|
|
169
|
-
this.
|
|
288
|
+
["deep_research", (args, progress) => this.toolHandlers.handleDeepResearch(asToolInput(args), progress)],
|
|
289
|
+
["gemini_query", (args) => this.toolHandlers.handleGeminiQuery(asToolInput(args))],
|
|
290
|
+
["get_research_status", (args) => this.toolHandlers.handleGetResearchStatus(asToolInput(args))],
|
|
291
|
+
["upload_document", (args) => this.toolHandlers.handleUploadDocument(asToolInput(args))],
|
|
292
|
+
["query_document", (args) => this.toolHandlers.handleQueryDocument(asToolInput(args))],
|
|
293
|
+
["list_documents", (args) => this.toolHandlers.handleListDocuments(asToolInput(args))],
|
|
294
|
+
["delete_document", (args) => this.toolHandlers.handleDeleteDocument(asToolInput(args))],
|
|
295
|
+
["query_chunked_document", (args) => this.toolHandlers.handleQueryChunkedDocument(asToolInput(args))],
|
|
296
|
+
["get_query_history", (args) => this.toolHandlers.handleGetQueryHistory(asToolInput(args))],
|
|
297
|
+
["get_notebook_chat_history", (args) => this.toolHandlers.handleGetNotebookChatHistory(asToolInput(args))],
|
|
298
|
+
]));
|
|
299
|
+
// Startup assertion: every dispatched tool must be explicitly auth-classified (I313)
|
|
300
|
+
for (const toolName of this.toolRegistry.keys()) {
|
|
301
|
+
if (isToolName(toolName) && !TOOLS_REQUIRING_AUTH.has(toolName) && !TOOLS_EXEMPT_FROM_AUTH.has(toolName)) {
|
|
302
|
+
log.warning(`⚠️ Tool '${toolName}' not in auth lists — defaulting to unauthenticated`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
for (const toolName of this.complianceToolNames) {
|
|
306
|
+
if (!isToolName(toolName)) {
|
|
307
|
+
// Compliance tools not in TOOL_NAMES use read-auth by default; log for audit visibility
|
|
308
|
+
log.info(` [auth] compliance:'${toolName}' → read-auth (not in TOOLS_REQUIRING_AUTH)`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// List available tools — rebuild each call so ask_question description reflects current notebook (I022)
|
|
312
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
170
313
|
log.info("📋 [MCP] list_tools request received");
|
|
314
|
+
const allTools = buildToolDefinitions(this.library);
|
|
315
|
+
const tools = this.filterAdvancedTools(this.settingsManager.filterTools(allTools));
|
|
316
|
+
const offset = parseListToolsCursor(request.params?.cursor, tools.length);
|
|
317
|
+
const page = tools.slice(offset, offset + LIST_TOOLS_PAGE_SIZE);
|
|
318
|
+
const nextOffset = offset + page.length;
|
|
171
319
|
return {
|
|
172
|
-
tools:
|
|
320
|
+
tools: page,
|
|
321
|
+
...(nextOffset < tools.length && { nextCursor: String(nextOffset) }),
|
|
173
322
|
};
|
|
174
323
|
});
|
|
175
324
|
// Handle tool calls
|
|
176
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
325
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => log.withContext({
|
|
326
|
+
correlation_id: crypto.randomUUID(),
|
|
327
|
+
tool: request.params.name,
|
|
328
|
+
}, async () => {
|
|
177
329
|
const { name, arguments: args } = request.params;
|
|
178
|
-
|
|
179
|
-
const
|
|
330
|
+
// Per MCP spec, _meta lives on request.params, not inside arguments
|
|
331
|
+
const meta = request.params._meta;
|
|
332
|
+
const progressToken = meta?.progressToken;
|
|
333
|
+
const authToken = meta?.authToken || process.env.NLMCP_AUTH_TOKEN;
|
|
180
334
|
log.info(`🔧 [MCP] Tool call: ${name}`);
|
|
181
335
|
if (progressToken) {
|
|
182
336
|
log.info(` 📊 Progress token: ${progressToken}`);
|
|
183
337
|
}
|
|
184
338
|
// === SECURITY: MCP Authentication ===
|
|
185
|
-
// Tools that
|
|
186
|
-
|
|
187
|
-
|
|
339
|
+
// Tools that touch the filesystem, wipe credentials, dispatch outbound
|
|
340
|
+
// HTTP, delete remote resources, or exercise GDPR data-subject rights
|
|
341
|
+
// always require auth, even if globally disabled via NLMCP_AUTH_DISABLED.
|
|
342
|
+
const requiresAuth = isToolName(name) && TOOLS_REQUIRING_AUTH.has(name);
|
|
188
343
|
const authResult = requiresAuth
|
|
189
|
-
? await authenticateMCPRequest(authToken, name, true)
|
|
190
|
-
: await authenticateMCPRequest(authToken, name);
|
|
344
|
+
? await authenticateMCPRequest(authToken, name, true, "admin")
|
|
345
|
+
: await authenticateMCPRequest(authToken, name, false, "read");
|
|
191
346
|
if (!authResult.authenticated) {
|
|
192
347
|
log.warning(`🔒 [MCP] Authentication failed for tool: ${name}`);
|
|
193
348
|
return {
|
|
349
|
+
isError: true,
|
|
194
350
|
content: [
|
|
195
351
|
{
|
|
196
352
|
type: "text",
|
|
197
353
|
text: JSON.stringify({
|
|
198
354
|
success: false,
|
|
199
355
|
error: authResult.error || "Authentication required",
|
|
356
|
+
_errorType: "domain",
|
|
200
357
|
}),
|
|
201
358
|
},
|
|
202
359
|
],
|
|
@@ -218,19 +375,33 @@ class NotebookLMMCPServer {
|
|
|
218
375
|
}
|
|
219
376
|
};
|
|
220
377
|
try {
|
|
378
|
+
// Compliance tools have their own dispatcher that returns MCP-shaped
|
|
379
|
+
// TextContent[] directly. Short-circuit before the generic wrapper
|
|
380
|
+
// so dashboard/report text isn't double-encoded as JSON.
|
|
381
|
+
if (this.complianceToolNames.has(name)) {
|
|
382
|
+
const content = await handleComplianceToolCall(name, toToolArgs(args));
|
|
383
|
+
return { content };
|
|
384
|
+
}
|
|
221
385
|
const handler = this.toolRegistry.get(name);
|
|
222
386
|
if (!handler) {
|
|
223
387
|
log.error(`❌ [MCP] Unknown tool: ${name}`);
|
|
388
|
+
const errorBody = {
|
|
389
|
+
success: false,
|
|
390
|
+
error: `Unknown tool: ${name}`,
|
|
391
|
+
_errorType: "domain",
|
|
392
|
+
};
|
|
224
393
|
return {
|
|
394
|
+
isError: true,
|
|
225
395
|
content: [
|
|
226
396
|
{
|
|
227
397
|
type: "text",
|
|
228
|
-
text: JSON.stringify(
|
|
398
|
+
text: JSON.stringify(errorBody, null, 2),
|
|
229
399
|
},
|
|
230
400
|
],
|
|
401
|
+
structuredContent: errorBody,
|
|
231
402
|
};
|
|
232
403
|
}
|
|
233
|
-
const result = await handler(args, sendProgress);
|
|
404
|
+
const result = await handler(toToolArgs(args), sendProgress);
|
|
234
405
|
// Return result
|
|
235
406
|
return {
|
|
236
407
|
content: [
|
|
@@ -239,37 +410,77 @@ class NotebookLMMCPServer {
|
|
|
239
410
|
text: JSON.stringify(result, null, 2),
|
|
240
411
|
},
|
|
241
412
|
],
|
|
413
|
+
structuredContent: result,
|
|
242
414
|
};
|
|
243
415
|
}
|
|
244
416
|
catch (error) {
|
|
245
|
-
const
|
|
246
|
-
|
|
417
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
418
|
+
const errorType = classifyToolError(error);
|
|
419
|
+
log.error(`❌ [MCP] Tool execution error for '${name}': ${rawMessage}`);
|
|
420
|
+
// Sanitize before returning to client: strip absolute paths and stack fragments (I328)
|
|
421
|
+
const sanitized = rawMessage
|
|
422
|
+
.replace(/(?:\/[^\s/:,'"]+)+/g, "[path]")
|
|
423
|
+
.replace(/\bat\s+\S+\s+\(\S+:\d+:\d+\)/g, "")
|
|
424
|
+
.trim();
|
|
425
|
+
const errorBody = {
|
|
426
|
+
success: false,
|
|
427
|
+
data: null,
|
|
428
|
+
error: sanitized,
|
|
429
|
+
_errorType: errorType,
|
|
430
|
+
};
|
|
247
431
|
return {
|
|
432
|
+
isError: true,
|
|
248
433
|
content: [
|
|
249
434
|
{
|
|
250
435
|
type: "text",
|
|
251
|
-
text: JSON.stringify(
|
|
252
|
-
success: false,
|
|
253
|
-
error: errorMessage,
|
|
254
|
-
}, null, 2),
|
|
436
|
+
text: JSON.stringify(errorBody, null, 2),
|
|
255
437
|
},
|
|
256
438
|
],
|
|
439
|
+
structuredContent: errorBody,
|
|
257
440
|
};
|
|
258
441
|
}
|
|
259
|
-
});
|
|
442
|
+
}));
|
|
260
443
|
}
|
|
261
444
|
/**
|
|
262
445
|
* Setup graceful shutdown handlers
|
|
263
446
|
*/
|
|
264
447
|
setupShutdownHandlers() {
|
|
265
448
|
let shuttingDown = false;
|
|
266
|
-
const
|
|
449
|
+
const flushFatalError = async (signal, error) => {
|
|
450
|
+
if (signal !== "uncaughtException" && signal !== "unhandledRejection")
|
|
451
|
+
return;
|
|
452
|
+
const message = error instanceof Error ? error.message : String(error ?? signal);
|
|
453
|
+
try {
|
|
454
|
+
await this.server.notification({
|
|
455
|
+
method: "notifications/message",
|
|
456
|
+
params: {
|
|
457
|
+
level: "error",
|
|
458
|
+
logger: "notebooklm-mcp",
|
|
459
|
+
data: {
|
|
460
|
+
success: false,
|
|
461
|
+
error: message,
|
|
462
|
+
_errorType: "transport",
|
|
463
|
+
signal,
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
catch (notifyError) {
|
|
469
|
+
log.warning(`⚠️ Failed to flush fatal MCP error notification: ${notifyError instanceof Error ? notifyError.message : String(notifyError)}`);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
const shutdown = async (signal, error) => {
|
|
267
473
|
if (shuttingDown) {
|
|
268
474
|
return;
|
|
269
475
|
}
|
|
270
476
|
shuttingDown = true;
|
|
271
477
|
log.info(`\n🛑 Received ${signal}, shutting down gracefully...`);
|
|
272
478
|
try {
|
|
479
|
+
await flushFatalError(signal, error);
|
|
480
|
+
if (this.retentionTimer) {
|
|
481
|
+
clearInterval(this.retentionTimer);
|
|
482
|
+
this.retentionTimer = undefined;
|
|
483
|
+
}
|
|
273
484
|
// Cleanup tool handlers (closes all sessions)
|
|
274
485
|
await this.toolHandlers.cleanup();
|
|
275
486
|
// Close server
|
|
@@ -282,28 +493,108 @@ class NotebookLMMCPServer {
|
|
|
282
493
|
process.exit(1);
|
|
283
494
|
}
|
|
284
495
|
};
|
|
285
|
-
const requestShutdown = (signal) => {
|
|
286
|
-
void shutdown(signal);
|
|
496
|
+
const requestShutdown = (signal, error) => {
|
|
497
|
+
void shutdown(signal, error);
|
|
287
498
|
};
|
|
288
499
|
process.on("SIGINT", () => requestShutdown("SIGINT"));
|
|
289
500
|
process.on("SIGTERM", () => requestShutdown("SIGTERM"));
|
|
290
501
|
process.on("uncaughtException", (error) => {
|
|
291
502
|
log.error(`💥 Uncaught exception: ${error}`);
|
|
292
503
|
log.error(error.stack || "");
|
|
293
|
-
requestShutdown("uncaughtException");
|
|
504
|
+
requestShutdown("uncaughtException", error);
|
|
294
505
|
});
|
|
295
506
|
process.on("unhandledRejection", (reason, promise) => {
|
|
296
507
|
log.error(`💥 Unhandled rejection at: ${promise}`);
|
|
297
508
|
log.error(`Reason: ${reason}`);
|
|
298
|
-
requestShutdown("unhandledRejection");
|
|
509
|
+
requestShutdown("unhandledRejection", reason);
|
|
299
510
|
});
|
|
300
511
|
}
|
|
512
|
+
/**
|
|
513
|
+
* Bootstrap the compliance module at startup:
|
|
514
|
+
* 1. Display the privacy notice to stderr on first run and auto-record
|
|
515
|
+
* acknowledgment (stdio MCP cannot prompt interactively, so the
|
|
516
|
+
* notice is informational — operators wishing for explicit consent
|
|
517
|
+
* should call the `grant_consent` MCP tool).
|
|
518
|
+
* 2. Run the retention engine immediately and schedule it every 6 hours
|
|
519
|
+
* so archive/delete policies actually fire. Timer is unref()'d so it
|
|
520
|
+
* does not keep the process alive on shutdown.
|
|
521
|
+
*/
|
|
522
|
+
async bootstrapCompliance() {
|
|
523
|
+
try {
|
|
524
|
+
const privacy = getPrivacyNoticeManager();
|
|
525
|
+
if (await privacy.needsDisplay()) {
|
|
526
|
+
log.info("");
|
|
527
|
+
log.info("━━━ Privacy notice (first run) ━━━");
|
|
528
|
+
// Multi-line notice — write via stderr directly so formatting survives.
|
|
529
|
+
process.stderr.write(getPrivacyNoticeCLIText() + "\n");
|
|
530
|
+
log.info("━━━ End privacy notice ━━━");
|
|
531
|
+
log.info("");
|
|
532
|
+
await privacy.acknowledge("auto");
|
|
533
|
+
log.info("📜 Privacy notice recorded (method=auto). Use grant_consent to record explicit consent.");
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
catch (err) {
|
|
537
|
+
log.warning(`⚠️ Privacy notice bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
538
|
+
}
|
|
539
|
+
try {
|
|
540
|
+
const runOnce = async () => {
|
|
541
|
+
const results = await runRetentionPolicies();
|
|
542
|
+
if (results.length > 0) {
|
|
543
|
+
log.info(`🗂️ Retention engine ran ${results.length} policy(ies)`);
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
await runOnce();
|
|
547
|
+
const SIX_HOURS_MS = 6 * 60 * 60 * 1000;
|
|
548
|
+
this.retentionTimer = setInterval(() => {
|
|
549
|
+
void runOnce().catch((err) => log.warning(`⚠️ retention policy run failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
550
|
+
}, SIX_HOURS_MS);
|
|
551
|
+
this.retentionTimer.unref();
|
|
552
|
+
}
|
|
553
|
+
catch (err) {
|
|
554
|
+
log.warning(`⚠️ Retention engine bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
555
|
+
}
|
|
556
|
+
// Subscribe breach detector to audit event stream (I244)
|
|
557
|
+
try {
|
|
558
|
+
const breachDetector = getBreachDetector();
|
|
559
|
+
getAuditLogger().onEvent(async (event) => {
|
|
560
|
+
if (event.eventType !== "auth" && event.eventType !== "security")
|
|
561
|
+
return;
|
|
562
|
+
try {
|
|
563
|
+
await breachDetector.checkEvent(event.eventName, event.details);
|
|
564
|
+
}
|
|
565
|
+
catch (err) {
|
|
566
|
+
log.warning(`breach detector checkEvent failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
log.info("🛡️ Breach detector subscribed to audit events");
|
|
570
|
+
}
|
|
571
|
+
catch (err) {
|
|
572
|
+
log.warning(`⚠️ Breach detector bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
573
|
+
}
|
|
574
|
+
// Pipe audit events to SIEM when enabled (I247). exportToSIEM is a no-op
|
|
575
|
+
// when NLMCP_SIEM_ENABLED is not true.
|
|
576
|
+
try {
|
|
577
|
+
getAuditLogger().onEvent(async (event) => {
|
|
578
|
+
const severity = event.eventType === "security" && typeof event.details.severity === "string"
|
|
579
|
+
? event.details.severity
|
|
580
|
+
: event.success ? "info" : "error";
|
|
581
|
+
await exportToSIEM(event.eventType, event.eventName, severity, `${event.eventType}:${event.eventName}`, "audit-logger", event.details);
|
|
582
|
+
});
|
|
583
|
+
log.info("📡 SIEM audit export bridge registered");
|
|
584
|
+
}
|
|
585
|
+
catch (err) {
|
|
586
|
+
log.warning(`⚠️ SIEM bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
301
589
|
/**
|
|
302
590
|
* Start the MCP server
|
|
303
591
|
*/
|
|
304
|
-
async start() {
|
|
592
|
+
async start(transport = new StdioServerTransport()) {
|
|
305
593
|
log.info("🎯 Starting NotebookLM MCP Server (Security Hardened)...");
|
|
306
594
|
log.info("");
|
|
595
|
+
if (process.env.NODE_ENV !== "test") {
|
|
596
|
+
ensureDirectories();
|
|
597
|
+
}
|
|
307
598
|
// Security: Check security context and warn about issues
|
|
308
599
|
const securityCheck = checkSecurityContext();
|
|
309
600
|
if (!securityCheck.secure) {
|
|
@@ -317,6 +608,21 @@ class NotebookLMMCPServer {
|
|
|
317
608
|
const mcpAuth = getMCPAuthenticator();
|
|
318
609
|
await mcpAuth.initialize();
|
|
319
610
|
const authStatus = mcpAuth.getStatus();
|
|
611
|
+
// Audit: verify hash-chain integrity from previous run (I218)
|
|
612
|
+
try {
|
|
613
|
+
const integrity = await getAuditLogger().verifyIntegrity();
|
|
614
|
+
if (!integrity.valid) {
|
|
615
|
+
log.warning(`⚠️ Audit log integrity check failed: ${integrity.errors.join(", ")}`);
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
log.info("🔒 Audit log integrity verified");
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
catch (err) {
|
|
622
|
+
log.warning(`⚠️ Audit integrity check error: ${err instanceof Error ? err.message : String(err)}`);
|
|
623
|
+
}
|
|
624
|
+
// Compliance: surface privacy notice on first run and schedule retention.
|
|
625
|
+
await this.bootstrapCompliance();
|
|
320
626
|
// Audit: Log server startup
|
|
321
627
|
await audit.system("server_start", {
|
|
322
628
|
version: VERSION,
|
|
@@ -337,10 +643,15 @@ class NotebookLMMCPServer {
|
|
|
337
643
|
log.info(` Session Timeout: ${CONFIG.sessionTimeout}s`);
|
|
338
644
|
log.info(` Stealth: ${CONFIG.stealthEnabled}`);
|
|
339
645
|
log.info(` Audit Logging: ${getAuditLogger().getStats().totalEvents >= 0 ? 'enabled' : 'disabled'}`);
|
|
340
|
-
|
|
646
|
+
// I314: verbose effective auth state
|
|
647
|
+
log.info(` MCP Authentication: ${authStatus.enabled ? 'ENABLED' : 'DISABLED'}`);
|
|
648
|
+
if (authStatus.enabled) {
|
|
649
|
+
const tokenSrc = process.env.NLMCP_AUTH_TOKEN ? "env var" : "hash file";
|
|
650
|
+
log.info(` Admin token: ${authStatus.hasToken ? `configured (${tokenSrc})` : 'NOT SET — all admin tools will reject'}`);
|
|
651
|
+
log.info(` Read-only token: ${authStatus.hasReadOnlyToken ? 'configured' : 'not set (admin token used for read)'}`);
|
|
652
|
+
log.info(` Admin tools (${TOOLS_REQUIRING_AUTH.size}): require auth even if globally disabled`);
|
|
653
|
+
}
|
|
341
654
|
log.info("");
|
|
342
|
-
// Create stdio transport
|
|
343
|
-
const transport = new StdioServerTransport();
|
|
344
655
|
// Connect server to transport
|
|
345
656
|
await this.server.connect(transport);
|
|
346
657
|
log.success("✅ MCP Server connected via stdio");
|
|
@@ -348,19 +659,27 @@ class NotebookLMMCPServer {
|
|
|
348
659
|
log.info("");
|
|
349
660
|
log.info("💡 Available tools:");
|
|
350
661
|
for (const tool of this.toolDefinitions) {
|
|
351
|
-
const
|
|
352
|
-
|
|
662
|
+
const raw = tool.description ? tool.description.split('\n')[0] : 'No description';
|
|
663
|
+
const desc = raw.replace(/\p{Extended_Pictographic}/gu, '').trim();
|
|
664
|
+
log.info(` - ${tool.name}: ${desc.substring(0, 80)}${desc.length > 80 ? '...' : ''}`);
|
|
353
665
|
}
|
|
354
666
|
log.info("");
|
|
355
667
|
log.info("📖 For documentation, see: README.md");
|
|
356
|
-
log.info("📖 For MCP details, see: MCP_INFOS.md");
|
|
357
668
|
log.info("");
|
|
358
669
|
}
|
|
670
|
+
async stop() {
|
|
671
|
+
if (this.retentionTimer) {
|
|
672
|
+
clearInterval(this.retentionTimer);
|
|
673
|
+
this.retentionTimer = undefined;
|
|
674
|
+
}
|
|
675
|
+
await this.toolHandlers.cleanup();
|
|
676
|
+
await this.server.close();
|
|
677
|
+
}
|
|
359
678
|
}
|
|
360
679
|
/**
|
|
361
680
|
* Main entry point
|
|
362
681
|
*/
|
|
363
|
-
async function main() {
|
|
682
|
+
export async function main() {
|
|
364
683
|
// Handle CLI commands
|
|
365
684
|
const args = process.argv.slice(2);
|
|
366
685
|
if (args.length > 0 && args[0] === "config") {
|
|
@@ -394,6 +713,10 @@ async function main() {
|
|
|
394
713
|
process.exit(1);
|
|
395
714
|
}
|
|
396
715
|
}
|
|
397
|
-
|
|
398
|
-
|
|
716
|
+
const isDirectRun = process.argv[1]
|
|
717
|
+
? import.meta.url === pathToFileURL(process.argv[1]).href
|
|
718
|
+
: false;
|
|
719
|
+
if (isDirectRun) {
|
|
720
|
+
void main();
|
|
721
|
+
}
|
|
399
722
|
//# sourceMappingURL=index.js.map
|