@pan-sec/notebooklm-mcp 2026.2.10 → 2026.3.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 +71 -27
- 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 +97 -42
- package/dist/auth/auth-manager.js.map +1 -1
- package/dist/auth/mcp-auth.d.ts +22 -4
- package/dist/auth/mcp-auth.d.ts.map +1 -1
- package/dist/auth/mcp-auth.js +120 -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 +18 -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 +4 -31
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -63
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +21 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +54 -1
- 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 +399 -85
- 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 +21 -22
- 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 -0
- package/dist/notebook-creation/index.d.ts.map +1 -1
- package/dist/notebook-creation/index.js +2 -0
- 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 +239 -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 +26 -21
- package/dist/notebook-creation/selectors.d.ts.map +1 -1
- package/dist/notebook-creation/selectors.js +79 -36
- package/dist/notebook-creation/selectors.js.map +1 -1
- package/dist/notebook-creation/source-manager.d.ts +22 -0
- package/dist/notebook-creation/source-manager.d.ts.map +1 -1
- package/dist/notebook-creation/source-manager.js +716 -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 +45 -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 +29 -12
- 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.js +9 -9
- 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 +40 -10
- 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 +3 -0
- 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 +56 -12
- 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 +15 -7
- 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 +7 -0
- package/dist/tools/handlers/error-utils.d.ts.map +1 -0
- package/dist/tools/handlers/error-utils.js +17 -0
- package/dist/tools/handlers/error-utils.js.map +1 -0
- package/dist/tools/handlers/gemini.d.ts +1 -0
- package/dist/tools/handlers/gemini.d.ts.map +1 -1
- package/dist/tools/handlers/gemini.js +81 -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 +13 -2
- 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 +99 -20
- 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.map +1 -1
- package/dist/tools/handlers/session-management.js +12 -5
- 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 +193 -27
- 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 +80 -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.map +1 -1
- package/dist/utils/page-utils.js +22 -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 +63 -15
- 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 +41 -11
- 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 +2 -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 +472 -72
- 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 +33 -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,132 @@ 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", "get_data_inventory",
|
|
71
|
+
"get_privacy_notice", "get_compliance_report", "check_breach_risk", "manage_consent",
|
|
72
|
+
"grant_consent", "revoke_consent", "report_security_incident", "collect_audit_evidence",
|
|
73
|
+
"generate_compliance_report",
|
|
74
|
+
];
|
|
75
|
+
function isToolName(name) {
|
|
76
|
+
return TOOL_NAMES.includes(name);
|
|
77
|
+
}
|
|
78
|
+
function toToolArgs(value) {
|
|
79
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
80
|
+
return Object.fromEntries(Object.entries(value));
|
|
81
|
+
}
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
function asToolInput(args) {
|
|
85
|
+
return args;
|
|
86
|
+
}
|
|
87
|
+
function parseListToolsCursor(cursor, totalTools) {
|
|
88
|
+
if (!cursor)
|
|
89
|
+
return 0;
|
|
90
|
+
const offset = Number.parseInt(cursor, 10);
|
|
91
|
+
if (!Number.isInteger(offset) || offset < 0 || offset >= totalTools)
|
|
92
|
+
return 0;
|
|
93
|
+
return offset;
|
|
94
|
+
}
|
|
95
|
+
function classifyToolError(error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
return /\b(transport|protocol|json-?rpc|connection|stdio|notification)\b/i.test(message)
|
|
98
|
+
? "transport"
|
|
99
|
+
: "domain";
|
|
100
|
+
}
|
|
101
|
+
const TOOLS_EXEMPT_FROM_AUTH = new Set([
|
|
102
|
+
"ask_question", "add_notebook", "list_notebooks", "get_notebook", "select_notebook",
|
|
103
|
+
"update_notebook", "remove_notebook", "search_notebooks", "get_library_stats",
|
|
104
|
+
"get_quota", "set_quota_tier", "get_project_info", "create_notebook",
|
|
105
|
+
"batch_create_notebooks", "sync_library", "list_sessions", "close_session", "reset_session",
|
|
106
|
+
"get_health", "list_sources", "add_source", "remove_source",
|
|
107
|
+
"generate_audio_overview", "get_audio_status", "generate_video_overview", "get_video_status",
|
|
108
|
+
"generate_data_table", "get_data_table", "list_webhooks",
|
|
109
|
+
"deep_research", "gemini_query", "get_research_status",
|
|
110
|
+
"query_document", "list_documents", "query_chunked_document",
|
|
111
|
+
"get_query_history", "get_notebook_chat_history",
|
|
112
|
+
]);
|
|
113
|
+
const TOOLS_REQUIRING_AUTH = new Set([
|
|
114
|
+
"add_folder",
|
|
115
|
+
"cleanup_data",
|
|
116
|
+
"export_library",
|
|
117
|
+
"setup_auth",
|
|
118
|
+
"re_auth",
|
|
119
|
+
"configure_webhook",
|
|
120
|
+
"remove_webhook",
|
|
121
|
+
"test_webhook",
|
|
122
|
+
"delete_document",
|
|
123
|
+
"upload_document",
|
|
124
|
+
"download_audio",
|
|
125
|
+
// Compliance — destructive or privileged operations.
|
|
126
|
+
"submit_dsar",
|
|
127
|
+
"export_user_data",
|
|
128
|
+
"request_data_erasure",
|
|
129
|
+
"grant_consent",
|
|
130
|
+
"revoke_consent",
|
|
131
|
+
"report_security_incident",
|
|
132
|
+
"collect_audit_evidence",
|
|
133
|
+
"generate_compliance_report",
|
|
134
|
+
]);
|
|
135
|
+
const ADVANCED_TOOLS = new Set([
|
|
136
|
+
"export_library",
|
|
137
|
+
"list_sessions",
|
|
138
|
+
"close_session",
|
|
139
|
+
"reset_session",
|
|
140
|
+
"cleanup_data",
|
|
141
|
+
"configure_webhook",
|
|
142
|
+
"list_webhooks",
|
|
143
|
+
"test_webhook",
|
|
144
|
+
"remove_webhook",
|
|
145
|
+
"deep_research",
|
|
146
|
+
"gemini_query",
|
|
147
|
+
"get_research_status",
|
|
148
|
+
"upload_document",
|
|
149
|
+
"query_document",
|
|
150
|
+
"list_documents",
|
|
151
|
+
"delete_document",
|
|
152
|
+
"query_chunked_document",
|
|
153
|
+
"get_query_history",
|
|
154
|
+
"get_notebook_chat_history",
|
|
155
|
+
"submit_dsar",
|
|
156
|
+
"export_user_data",
|
|
157
|
+
"request_data_erasure",
|
|
158
|
+
"get_data_inventory",
|
|
159
|
+
"get_privacy_notice",
|
|
160
|
+
"get_compliance_report",
|
|
161
|
+
"check_breach_risk",
|
|
162
|
+
"manage_consent",
|
|
163
|
+
"grant_consent",
|
|
164
|
+
"revoke_consent",
|
|
165
|
+
"report_security_incident",
|
|
166
|
+
"collect_audit_evidence",
|
|
167
|
+
"generate_compliance_report",
|
|
168
|
+
]);
|
|
51
169
|
/**
|
|
52
170
|
* Main MCP Server Class
|
|
53
171
|
*/
|
|
54
|
-
class NotebookLMMCPServer {
|
|
172
|
+
export class NotebookLMMCPServer {
|
|
173
|
+
options;
|
|
55
174
|
server;
|
|
56
175
|
authManager;
|
|
57
176
|
sessionManager;
|
|
@@ -62,7 +181,11 @@ class NotebookLMMCPServer {
|
|
|
62
181
|
toolDefinitions;
|
|
63
182
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
183
|
toolRegistry;
|
|
65
|
-
|
|
184
|
+
complianceToolNames;
|
|
185
|
+
retentionTimer;
|
|
186
|
+
advancedToolsEnabled;
|
|
187
|
+
constructor(options = {}) {
|
|
188
|
+
this.options = options;
|
|
66
189
|
// Initialize MCP Server
|
|
67
190
|
this.server = new Server({
|
|
68
191
|
name: "notebooklm-mcp",
|
|
@@ -72,8 +195,7 @@ class NotebookLMMCPServer {
|
|
|
72
195
|
tools: {},
|
|
73
196
|
resources: {},
|
|
74
197
|
prompts: {},
|
|
75
|
-
completions: {},
|
|
76
|
-
logging: {},
|
|
198
|
+
completions: {},
|
|
77
199
|
},
|
|
78
200
|
});
|
|
79
201
|
// Initialize managers
|
|
@@ -81,21 +203,37 @@ class NotebookLMMCPServer {
|
|
|
81
203
|
this.sessionManager = new SessionManager(this.authManager);
|
|
82
204
|
this.library = new NotebookLibrary();
|
|
83
205
|
this.settingsManager = new SettingsManager();
|
|
206
|
+
this.advancedToolsEnabled = process.env.NLMCP_ADVANCED_TOOLS === "1";
|
|
84
207
|
// Initialize handlers
|
|
85
208
|
this.toolHandlers = new ToolHandlers(this.sessionManager, this.authManager, this.library);
|
|
86
209
|
this.resourceHandlers = new ResourceHandlers(this.library);
|
|
87
210
|
// Build and Filter tool definitions
|
|
88
211
|
const allTools = buildToolDefinitions(this.library);
|
|
89
|
-
this.toolDefinitions = this.settingsManager.filterTools(allTools);
|
|
212
|
+
this.toolDefinitions = this.filterAdvancedTools(this.settingsManager.filterTools(allTools));
|
|
213
|
+
// Track compliance tool names for the short-circuit dispatch path.
|
|
214
|
+
this.complianceToolNames = new Set(this.filterAdvancedTools(getComplianceTools()).map((t) => t.name));
|
|
90
215
|
// Setup handlers
|
|
91
216
|
this.setupHandlers();
|
|
92
|
-
this.
|
|
217
|
+
if (this.options.registerShutdownHandlers !== false) {
|
|
218
|
+
this.setupShutdownHandlers();
|
|
219
|
+
}
|
|
93
220
|
const activeSettings = this.settingsManager.getEffectiveSettings();
|
|
94
221
|
log.info("🚀 NotebookLM MCP Server initialized");
|
|
95
222
|
log.info(` Version: ${VERSION}`);
|
|
96
223
|
log.info(` Node: ${process.version}`);
|
|
97
224
|
log.info(` Platform: ${process.platform}`);
|
|
98
225
|
log.info(` Profile: ${activeSettings.profile} (${this.toolDefinitions.length} tools active)`);
|
|
226
|
+
log.info(` Advanced tools: ${this.advancedToolsEnabled ? "enabled" : "disabled"}`);
|
|
227
|
+
}
|
|
228
|
+
filterAdvancedTools(tools) {
|
|
229
|
+
if (this.advancedToolsEnabled)
|
|
230
|
+
return tools;
|
|
231
|
+
return tools.filter((tool) => !isToolName(tool.name) || !ADVANCED_TOOLS.has(tool.name));
|
|
232
|
+
}
|
|
233
|
+
filterAdvancedToolRegistry(registry) {
|
|
234
|
+
if (this.advancedToolsEnabled)
|
|
235
|
+
return registry;
|
|
236
|
+
return new Map(Array.from(registry.entries()).filter(([name]) => !isToolName(name) || !ADVANCED_TOOLS.has(name)));
|
|
99
237
|
}
|
|
100
238
|
/**
|
|
101
239
|
* Setup MCP request handlers
|
|
@@ -104,99 +242,118 @@ class NotebookLMMCPServer {
|
|
|
104
242
|
// Register Resource Handlers (Resources, Templates, Completions)
|
|
105
243
|
this.resourceHandlers.registerHandlers(this.server);
|
|
106
244
|
// Build tool registry once (not per-request)
|
|
107
|
-
|
|
108
|
-
this.toolRegistry = new Map([
|
|
245
|
+
this.toolRegistry = this.filterAdvancedToolRegistry(new Map([
|
|
109
246
|
// Ask Question
|
|
110
|
-
["ask_question", (
|
|
247
|
+
["ask_question", (args, progress) => this.toolHandlers.handleAskQuestion(asToolInput(args), progress)],
|
|
111
248
|
// Notebook Management
|
|
112
|
-
["add_notebook", (
|
|
249
|
+
["add_notebook", (args) => this.toolHandlers.handleAddNotebook(asToolInput(args))],
|
|
113
250
|
["list_notebooks", () => this.toolHandlers.handleListNotebooks()],
|
|
114
|
-
["get_notebook", (
|
|
115
|
-
["select_notebook", (
|
|
116
|
-
["update_notebook", (
|
|
117
|
-
["remove_notebook", (
|
|
118
|
-
["search_notebooks", (
|
|
251
|
+
["get_notebook", (args) => this.toolHandlers.handleGetNotebook(asToolInput(args))],
|
|
252
|
+
["select_notebook", (args) => this.toolHandlers.handleSelectNotebook(asToolInput(args))],
|
|
253
|
+
["update_notebook", (args) => this.toolHandlers.handleUpdateNotebook(asToolInput(args))],
|
|
254
|
+
["remove_notebook", (args) => this.toolHandlers.handleRemoveNotebook(asToolInput(args))],
|
|
255
|
+
["search_notebooks", (args) => this.toolHandlers.handleSearchNotebooks(asToolInput(args))],
|
|
119
256
|
["get_library_stats", () => this.toolHandlers.handleGetLibraryStats()],
|
|
120
|
-
["export_library", (
|
|
257
|
+
["export_library", (args) => this.toolHandlers.handleExportLibrary(asToolInput(args))],
|
|
121
258
|
// Quota & System
|
|
122
|
-
["get_quota", (
|
|
123
|
-
["set_quota_tier", (
|
|
259
|
+
["get_quota", (args) => this.toolHandlers.handleGetQuota(asToolInput(args))],
|
|
260
|
+
["set_quota_tier", (args) => this.toolHandlers.handleSetQuotaTier(asToolInput(args))],
|
|
124
261
|
["get_project_info", () => this.toolHandlers.handleGetProjectInfo()],
|
|
125
262
|
// Notebook Creation
|
|
126
|
-
["create_notebook", (
|
|
127
|
-
["batch_create_notebooks", (
|
|
128
|
-
["sync_library", (
|
|
263
|
+
["create_notebook", (args, progress) => this.toolHandlers.handleCreateNotebook(asToolInput(args), progress)],
|
|
264
|
+
["batch_create_notebooks", (args, progress) => this.toolHandlers.handleBatchCreateNotebooks(asToolInput(args), progress)],
|
|
265
|
+
["sync_library", (args) => this.toolHandlers.handleSyncLibrary(asToolInput(args))],
|
|
129
266
|
// Session Management
|
|
130
267
|
["list_sessions", () => this.toolHandlers.handleListSessions()],
|
|
131
|
-
["close_session", (
|
|
132
|
-
["reset_session", (
|
|
133
|
-
["get_health", (
|
|
268
|
+
["close_session", (args) => this.toolHandlers.handleCloseSession(asToolInput(args))],
|
|
269
|
+
["reset_session", (args) => this.toolHandlers.handleResetSession(asToolInput(args))],
|
|
270
|
+
["get_health", (args) => this.toolHandlers.handleGetHealth(asToolInput(args))],
|
|
134
271
|
// Auth
|
|
135
|
-
["setup_auth", (
|
|
136
|
-
["re_auth", (
|
|
137
|
-
["cleanup_data", (
|
|
272
|
+
["setup_auth", (args, progress) => this.toolHandlers.handleSetupAuth(asToolInput(args), progress)],
|
|
273
|
+
["re_auth", (args, progress) => this.toolHandlers.handleReAuth(asToolInput(args), progress)],
|
|
274
|
+
["cleanup_data", (args) => this.toolHandlers.handleCleanupData(asToolInput(args))],
|
|
138
275
|
// Sources
|
|
139
|
-
["list_sources", (
|
|
140
|
-
["add_source", (
|
|
141
|
-
["add_folder", (
|
|
142
|
-
["remove_source", (
|
|
276
|
+
["list_sources", (args) => this.toolHandlers.handleListSources(asToolInput(args))],
|
|
277
|
+
["add_source", (args) => this.toolHandlers.handleAddSource(asToolInput(args))],
|
|
278
|
+
["add_folder", (args, progress) => this.toolHandlers.handleAddFolder(asToolInput(args), progress)],
|
|
279
|
+
["remove_source", (args) => this.toolHandlers.handleRemoveSource(asToolInput(args))],
|
|
143
280
|
// 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", (
|
|
281
|
+
["generate_audio_overview", (args) => this.toolHandlers.handleGenerateAudioOverview(asToolInput(args))],
|
|
282
|
+
["get_audio_status", (args) => this.toolHandlers.handleGetAudioStatus(asToolInput(args))],
|
|
283
|
+
["download_audio", (args) => this.toolHandlers.handleDownloadAudio(asToolInput(args))],
|
|
284
|
+
["generate_video_overview", (args) => this.toolHandlers.handleGenerateVideoOverview(asToolInput(args))],
|
|
285
|
+
["get_video_status", (args) => this.toolHandlers.handleGetVideoStatus(asToolInput(args))],
|
|
286
|
+
["generate_data_table", (args) => this.toolHandlers.handleGenerateDataTable(asToolInput(args))],
|
|
287
|
+
["get_data_table", (args) => this.toolHandlers.handleGetDataTable(asToolInput(args))],
|
|
151
288
|
// Webhooks
|
|
152
|
-
["configure_webhook", (
|
|
289
|
+
["configure_webhook", (args) => this.toolHandlers.handleConfigureWebhook(asToolInput(args))],
|
|
153
290
|
["list_webhooks", () => this.toolHandlers.handleListWebhooks()],
|
|
154
|
-
["test_webhook", (
|
|
155
|
-
["remove_webhook", (
|
|
291
|
+
["test_webhook", (args) => this.toolHandlers.handleTestWebhook(asToolInput(args))],
|
|
292
|
+
["remove_webhook", (args) => this.toolHandlers.handleRemoveWebhook(asToolInput(args))],
|
|
156
293
|
// 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.
|
|
294
|
+
["deep_research", (args, progress) => this.toolHandlers.handleDeepResearch(asToolInput(args), progress)],
|
|
295
|
+
["gemini_query", (args) => this.toolHandlers.handleGeminiQuery(asToolInput(args))],
|
|
296
|
+
["get_research_status", (args) => this.toolHandlers.handleGetResearchStatus(asToolInput(args))],
|
|
297
|
+
["upload_document", (args) => this.toolHandlers.handleUploadDocument(asToolInput(args))],
|
|
298
|
+
["query_document", (args) => this.toolHandlers.handleQueryDocument(asToolInput(args))],
|
|
299
|
+
["list_documents", (args) => this.toolHandlers.handleListDocuments(asToolInput(args))],
|
|
300
|
+
["delete_document", (args) => this.toolHandlers.handleDeleteDocument(asToolInput(args))],
|
|
301
|
+
["query_chunked_document", (args) => this.toolHandlers.handleQueryChunkedDocument(asToolInput(args))],
|
|
302
|
+
["get_query_history", (args) => this.toolHandlers.handleGetQueryHistory(asToolInput(args))],
|
|
303
|
+
["get_notebook_chat_history", (args) => this.toolHandlers.handleGetNotebookChatHistory(asToolInput(args))],
|
|
304
|
+
]));
|
|
305
|
+
// Startup assertion: every registered tool must be explicitly auth-classified (I313)
|
|
306
|
+
for (const toolName of this.toolRegistry.keys()) {
|
|
307
|
+
if (isToolName(toolName) && !TOOLS_REQUIRING_AUTH.has(toolName) && !TOOLS_EXEMPT_FROM_AUTH.has(toolName)) {
|
|
308
|
+
log.warning(`⚠️ Tool '${toolName}' not in auth lists — defaulting to unauthenticated`);
|
|
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,76 @@ 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
|
+
error: sanitized,
|
|
428
|
+
_errorType: errorType,
|
|
429
|
+
};
|
|
247
430
|
return {
|
|
431
|
+
isError: true,
|
|
248
432
|
content: [
|
|
249
433
|
{
|
|
250
434
|
type: "text",
|
|
251
|
-
text: JSON.stringify(
|
|
252
|
-
success: false,
|
|
253
|
-
error: errorMessage,
|
|
254
|
-
}, null, 2),
|
|
435
|
+
text: JSON.stringify(errorBody, null, 2),
|
|
255
436
|
},
|
|
256
437
|
],
|
|
438
|
+
structuredContent: errorBody,
|
|
257
439
|
};
|
|
258
440
|
}
|
|
259
|
-
});
|
|
441
|
+
}));
|
|
260
442
|
}
|
|
261
443
|
/**
|
|
262
444
|
* Setup graceful shutdown handlers
|
|
263
445
|
*/
|
|
264
446
|
setupShutdownHandlers() {
|
|
265
447
|
let shuttingDown = false;
|
|
266
|
-
const
|
|
448
|
+
const flushFatalError = async (signal, error) => {
|
|
449
|
+
if (signal !== "uncaughtException" && signal !== "unhandledRejection")
|
|
450
|
+
return;
|
|
451
|
+
const message = error instanceof Error ? error.message : String(error ?? signal);
|
|
452
|
+
try {
|
|
453
|
+
await this.server.notification({
|
|
454
|
+
method: "notifications/message",
|
|
455
|
+
params: {
|
|
456
|
+
level: "error",
|
|
457
|
+
logger: "notebooklm-mcp",
|
|
458
|
+
data: {
|
|
459
|
+
success: false,
|
|
460
|
+
error: message,
|
|
461
|
+
_errorType: "transport",
|
|
462
|
+
signal,
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
catch (notifyError) {
|
|
468
|
+
log.warning(`⚠️ Failed to flush fatal MCP error notification: ${notifyError instanceof Error ? notifyError.message : String(notifyError)}`);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
const shutdown = async (signal, error) => {
|
|
267
472
|
if (shuttingDown) {
|
|
268
473
|
return;
|
|
269
474
|
}
|
|
270
475
|
shuttingDown = true;
|
|
271
476
|
log.info(`\n🛑 Received ${signal}, shutting down gracefully...`);
|
|
272
477
|
try {
|
|
478
|
+
await flushFatalError(signal, error);
|
|
479
|
+
if (this.retentionTimer) {
|
|
480
|
+
clearInterval(this.retentionTimer);
|
|
481
|
+
this.retentionTimer = undefined;
|
|
482
|
+
}
|
|
273
483
|
// Cleanup tool handlers (closes all sessions)
|
|
274
484
|
await this.toolHandlers.cleanup();
|
|
275
485
|
// Close server
|
|
@@ -282,28 +492,108 @@ class NotebookLMMCPServer {
|
|
|
282
492
|
process.exit(1);
|
|
283
493
|
}
|
|
284
494
|
};
|
|
285
|
-
const requestShutdown = (signal) => {
|
|
286
|
-
void shutdown(signal);
|
|
495
|
+
const requestShutdown = (signal, error) => {
|
|
496
|
+
void shutdown(signal, error);
|
|
287
497
|
};
|
|
288
498
|
process.on("SIGINT", () => requestShutdown("SIGINT"));
|
|
289
499
|
process.on("SIGTERM", () => requestShutdown("SIGTERM"));
|
|
290
500
|
process.on("uncaughtException", (error) => {
|
|
291
501
|
log.error(`💥 Uncaught exception: ${error}`);
|
|
292
502
|
log.error(error.stack || "");
|
|
293
|
-
requestShutdown("uncaughtException");
|
|
503
|
+
requestShutdown("uncaughtException", error);
|
|
294
504
|
});
|
|
295
505
|
process.on("unhandledRejection", (reason, promise) => {
|
|
296
506
|
log.error(`💥 Unhandled rejection at: ${promise}`);
|
|
297
507
|
log.error(`Reason: ${reason}`);
|
|
298
|
-
requestShutdown("unhandledRejection");
|
|
508
|
+
requestShutdown("unhandledRejection", reason);
|
|
299
509
|
});
|
|
300
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* Bootstrap the compliance module at startup:
|
|
513
|
+
* 1. Display the privacy notice to stderr on first run and auto-record
|
|
514
|
+
* acknowledgment (stdio MCP cannot prompt interactively, so the
|
|
515
|
+
* notice is informational — operators wishing for explicit consent
|
|
516
|
+
* should call the `grant_consent` MCP tool).
|
|
517
|
+
* 2. Run the retention engine immediately and schedule it every 6 hours
|
|
518
|
+
* so archive/delete policies actually fire. Timer is unref()'d so it
|
|
519
|
+
* does not keep the process alive on shutdown.
|
|
520
|
+
*/
|
|
521
|
+
async bootstrapCompliance() {
|
|
522
|
+
try {
|
|
523
|
+
const privacy = getPrivacyNoticeManager();
|
|
524
|
+
if (await privacy.needsDisplay()) {
|
|
525
|
+
log.info("");
|
|
526
|
+
log.info("━━━ Privacy notice (first run) ━━━");
|
|
527
|
+
// Multi-line notice — write via stderr directly so formatting survives.
|
|
528
|
+
process.stderr.write(getPrivacyNoticeCLIText() + "\n");
|
|
529
|
+
log.info("━━━ End privacy notice ━━━");
|
|
530
|
+
log.info("");
|
|
531
|
+
await privacy.acknowledge("auto");
|
|
532
|
+
log.info("📜 Privacy notice recorded (method=auto). Use grant_consent to record explicit consent.");
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
log.warning(`⚠️ Privacy notice bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
const runOnce = async () => {
|
|
540
|
+
const results = await runRetentionPolicies();
|
|
541
|
+
if (results.length > 0) {
|
|
542
|
+
log.info(`🗂️ Retention engine ran ${results.length} policy(ies)`);
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
await runOnce();
|
|
546
|
+
const SIX_HOURS_MS = 6 * 60 * 60 * 1000;
|
|
547
|
+
this.retentionTimer = setInterval(() => {
|
|
548
|
+
void runOnce().catch((err) => log.warning(`⚠️ retention policy run failed: ${err}`));
|
|
549
|
+
}, SIX_HOURS_MS);
|
|
550
|
+
this.retentionTimer.unref();
|
|
551
|
+
}
|
|
552
|
+
catch (err) {
|
|
553
|
+
log.warning(`⚠️ Retention engine bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
554
|
+
}
|
|
555
|
+
// Subscribe breach detector to audit event stream (I244)
|
|
556
|
+
try {
|
|
557
|
+
const breachDetector = getBreachDetector();
|
|
558
|
+
getAuditLogger().onEvent(async (event) => {
|
|
559
|
+
if (event.eventType !== "auth" && event.eventType !== "security")
|
|
560
|
+
return;
|
|
561
|
+
try {
|
|
562
|
+
await breachDetector.checkEvent(event.eventName, event.details);
|
|
563
|
+
}
|
|
564
|
+
catch (err) {
|
|
565
|
+
log.warning(`breach detector checkEvent failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
log.info("🛡️ Breach detector subscribed to audit events");
|
|
569
|
+
}
|
|
570
|
+
catch (err) {
|
|
571
|
+
log.warning(`⚠️ Breach detector bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
572
|
+
}
|
|
573
|
+
// Pipe audit events to SIEM when enabled (I247). exportToSIEM is a no-op
|
|
574
|
+
// when NLMCP_SIEM_ENABLED is not true.
|
|
575
|
+
try {
|
|
576
|
+
getAuditLogger().onEvent(async (event) => {
|
|
577
|
+
const severity = event.eventType === "security" && typeof event.details.severity === "string"
|
|
578
|
+
? event.details.severity
|
|
579
|
+
: event.success ? "info" : "error";
|
|
580
|
+
await exportToSIEM(event.eventType, event.eventName, severity, `${event.eventType}:${event.eventName}`, "audit-logger", event.details);
|
|
581
|
+
});
|
|
582
|
+
log.info("📡 SIEM audit export bridge registered");
|
|
583
|
+
}
|
|
584
|
+
catch (err) {
|
|
585
|
+
log.warning(`⚠️ SIEM bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
301
588
|
/**
|
|
302
589
|
* Start the MCP server
|
|
303
590
|
*/
|
|
304
|
-
async start() {
|
|
591
|
+
async start(transport = new StdioServerTransport()) {
|
|
305
592
|
log.info("🎯 Starting NotebookLM MCP Server (Security Hardened)...");
|
|
306
593
|
log.info("");
|
|
594
|
+
if (process.env.NODE_ENV !== "test") {
|
|
595
|
+
ensureDirectories();
|
|
596
|
+
}
|
|
307
597
|
// Security: Check security context and warn about issues
|
|
308
598
|
const securityCheck = checkSecurityContext();
|
|
309
599
|
if (!securityCheck.secure) {
|
|
@@ -317,6 +607,21 @@ class NotebookLMMCPServer {
|
|
|
317
607
|
const mcpAuth = getMCPAuthenticator();
|
|
318
608
|
await mcpAuth.initialize();
|
|
319
609
|
const authStatus = mcpAuth.getStatus();
|
|
610
|
+
// Audit: verify hash-chain integrity from previous run (I218)
|
|
611
|
+
try {
|
|
612
|
+
const integrity = await getAuditLogger().verifyIntegrity();
|
|
613
|
+
if (!integrity.valid) {
|
|
614
|
+
log.warning(`⚠️ Audit log integrity check failed: ${integrity.errors.join(", ")}`);
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
log.info("🔒 Audit log integrity verified");
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
catch (err) {
|
|
621
|
+
log.warning(`⚠️ Audit integrity check error: ${err instanceof Error ? err.message : String(err)}`);
|
|
622
|
+
}
|
|
623
|
+
// Compliance: surface privacy notice on first run and schedule retention.
|
|
624
|
+
await this.bootstrapCompliance();
|
|
320
625
|
// Audit: Log server startup
|
|
321
626
|
await audit.system("server_start", {
|
|
322
627
|
version: VERSION,
|
|
@@ -339,8 +644,6 @@ class NotebookLMMCPServer {
|
|
|
339
644
|
log.info(` Audit Logging: ${getAuditLogger().getStats().totalEvents >= 0 ? 'enabled' : 'disabled'}`);
|
|
340
645
|
log.info(` MCP Authentication: ${authStatus.enabled ? 'enabled' : 'disabled'}`);
|
|
341
646
|
log.info("");
|
|
342
|
-
// Create stdio transport
|
|
343
|
-
const transport = new StdioServerTransport();
|
|
344
647
|
// Connect server to transport
|
|
345
648
|
await this.server.connect(transport);
|
|
346
649
|
log.success("✅ MCP Server connected via stdio");
|
|
@@ -353,14 +656,21 @@ class NotebookLMMCPServer {
|
|
|
353
656
|
}
|
|
354
657
|
log.info("");
|
|
355
658
|
log.info("📖 For documentation, see: README.md");
|
|
356
|
-
log.info("📖 For MCP details, see: MCP_INFOS.md");
|
|
357
659
|
log.info("");
|
|
358
660
|
}
|
|
661
|
+
async stop() {
|
|
662
|
+
if (this.retentionTimer) {
|
|
663
|
+
clearInterval(this.retentionTimer);
|
|
664
|
+
this.retentionTimer = undefined;
|
|
665
|
+
}
|
|
666
|
+
await this.toolHandlers.cleanup();
|
|
667
|
+
await this.server.close();
|
|
668
|
+
}
|
|
359
669
|
}
|
|
360
670
|
/**
|
|
361
671
|
* Main entry point
|
|
362
672
|
*/
|
|
363
|
-
async function main() {
|
|
673
|
+
export async function main() {
|
|
364
674
|
// Handle CLI commands
|
|
365
675
|
const args = process.argv.slice(2);
|
|
366
676
|
if (args.length > 0 && args[0] === "config") {
|
|
@@ -394,6 +704,10 @@ async function main() {
|
|
|
394
704
|
process.exit(1);
|
|
395
705
|
}
|
|
396
706
|
}
|
|
397
|
-
|
|
398
|
-
|
|
707
|
+
const isDirectRun = process.argv[1]
|
|
708
|
+
? import.meta.url === pathToFileURL(process.argv[1]).href
|
|
709
|
+
: false;
|
|
710
|
+
if (isDirectRun) {
|
|
711
|
+
void main();
|
|
712
|
+
}
|
|
399
713
|
//# sourceMappingURL=index.js.map
|