@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.
Files changed (302) hide show
  1. package/README.md +71 -27
  2. package/SECURITY.md +31 -61
  3. package/dist/auth/auth-manager.d.ts +2 -1
  4. package/dist/auth/auth-manager.d.ts.map +1 -1
  5. package/dist/auth/auth-manager.js +97 -42
  6. package/dist/auth/auth-manager.js.map +1 -1
  7. package/dist/auth/mcp-auth.d.ts +22 -4
  8. package/dist/auth/mcp-auth.d.ts.map +1 -1
  9. package/dist/auth/mcp-auth.js +120 -19
  10. package/dist/auth/mcp-auth.js.map +1 -1
  11. package/dist/compliance/alert-manager.d.ts.map +1 -1
  12. package/dist/compliance/alert-manager.js +7 -4
  13. package/dist/compliance/alert-manager.js.map +1 -1
  14. package/dist/compliance/breach-detection.d.ts.map +1 -1
  15. package/dist/compliance/breach-detection.js +14 -7
  16. package/dist/compliance/breach-detection.js.map +1 -1
  17. package/dist/compliance/change-log.d.ts.map +1 -1
  18. package/dist/compliance/change-log.js +7 -4
  19. package/dist/compliance/change-log.js.map +1 -1
  20. package/dist/compliance/compliance-logger.d.ts.map +1 -1
  21. package/dist/compliance/compliance-logger.js +11 -6
  22. package/dist/compliance/compliance-logger.js.map +1 -1
  23. package/dist/compliance/consent-manager.d.ts.map +1 -1
  24. package/dist/compliance/consent-manager.js +5 -3
  25. package/dist/compliance/consent-manager.js.map +1 -1
  26. package/dist/compliance/data-erasure.d.ts +1 -1
  27. package/dist/compliance/data-erasure.d.ts.map +1 -1
  28. package/dist/compliance/data-erasure.js +142 -83
  29. package/dist/compliance/data-erasure.js.map +1 -1
  30. package/dist/compliance/data-export.d.ts.map +1 -1
  31. package/dist/compliance/data-export.js +23 -12
  32. package/dist/compliance/data-export.js.map +1 -1
  33. package/dist/compliance/data-inventory.d.ts.map +1 -1
  34. package/dist/compliance/data-inventory.js +7 -6
  35. package/dist/compliance/data-inventory.js.map +1 -1
  36. package/dist/compliance/dsar-handler.d.ts +7 -1
  37. package/dist/compliance/dsar-handler.d.ts.map +1 -1
  38. package/dist/compliance/dsar-handler.js +74 -61
  39. package/dist/compliance/dsar-handler.js.map +1 -1
  40. package/dist/compliance/evidence-collector.d.ts.map +1 -1
  41. package/dist/compliance/evidence-collector.js +10 -6
  42. package/dist/compliance/evidence-collector.js.map +1 -1
  43. package/dist/compliance/health-monitor.d.ts.map +1 -1
  44. package/dist/compliance/health-monitor.js +15 -9
  45. package/dist/compliance/health-monitor.js.map +1 -1
  46. package/dist/compliance/incident-manager.d.ts.map +1 -1
  47. package/dist/compliance/incident-manager.js +5 -3
  48. package/dist/compliance/incident-manager.js.map +1 -1
  49. package/dist/compliance/policy-docs.d.ts.map +1 -1
  50. package/dist/compliance/policy-docs.js +14 -11
  51. package/dist/compliance/policy-docs.js.map +1 -1
  52. package/dist/compliance/privacy-notice-text.d.ts.map +1 -1
  53. package/dist/compliance/privacy-notice-text.js +3 -4
  54. package/dist/compliance/privacy-notice-text.js.map +1 -1
  55. package/dist/compliance/privacy-notice.d.ts.map +1 -1
  56. package/dist/compliance/privacy-notice.js +5 -3
  57. package/dist/compliance/privacy-notice.js.map +1 -1
  58. package/dist/compliance/report-generator.d.ts.map +1 -1
  59. package/dist/compliance/report-generator.js +5 -3
  60. package/dist/compliance/report-generator.js.map +1 -1
  61. package/dist/compliance/retention-engine.d.ts.map +1 -1
  62. package/dist/compliance/retention-engine.js +18 -10
  63. package/dist/compliance/retention-engine.js.map +1 -1
  64. package/dist/compliance/siem-exporter.d.ts.map +1 -1
  65. package/dist/compliance/siem-exporter.js +40 -16
  66. package/dist/compliance/siem-exporter.js.map +1 -1
  67. package/dist/config.d.ts +4 -31
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/config.js +25 -63
  70. package/dist/config.js.map +1 -1
  71. package/dist/errors.d.ts +21 -0
  72. package/dist/errors.d.ts.map +1 -1
  73. package/dist/errors.js +54 -1
  74. package/dist/errors.js.map +1 -1
  75. package/dist/gemini/gemini-client.d.ts +1 -0
  76. package/dist/gemini/gemini-client.d.ts.map +1 -1
  77. package/dist/gemini/gemini-client.js +50 -49
  78. package/dist/gemini/gemini-client.js.map +1 -1
  79. package/dist/gemini/types.d.ts +3 -1
  80. package/dist/gemini/types.d.ts.map +1 -1
  81. package/dist/gemini/types.js.map +1 -1
  82. package/dist/index.d.ts +52 -1
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +399 -85
  85. package/dist/index.js.map +1 -1
  86. package/dist/library/notebook-library.d.ts.map +1 -1
  87. package/dist/library/notebook-library.js +2 -1
  88. package/dist/library/notebook-library.js.map +1 -1
  89. package/dist/logging/query-logger.d.ts +13 -1
  90. package/dist/logging/query-logger.d.ts.map +1 -1
  91. package/dist/logging/query-logger.js +62 -10
  92. package/dist/logging/query-logger.js.map +1 -1
  93. package/dist/notebook-creation/audio-manager.d.ts.map +1 -1
  94. package/dist/notebook-creation/audio-manager.js +19 -24
  95. package/dist/notebook-creation/audio-manager.js.map +1 -1
  96. package/dist/notebook-creation/browser-options.d.ts +28 -0
  97. package/dist/notebook-creation/browser-options.d.ts.map +1 -0
  98. package/dist/notebook-creation/browser-options.js +75 -0
  99. package/dist/notebook-creation/browser-options.js.map +1 -0
  100. package/dist/notebook-creation/data-table-manager.d.ts.map +1 -1
  101. package/dist/notebook-creation/data-table-manager.js +21 -22
  102. package/dist/notebook-creation/data-table-manager.js.map +1 -1
  103. package/dist/notebook-creation/discover-creation-flow.d.ts +0 -6
  104. package/dist/notebook-creation/discover-creation-flow.d.ts.map +1 -1
  105. package/dist/notebook-creation/discover-creation-flow.js +10 -10
  106. package/dist/notebook-creation/discover-creation-flow.js.map +1 -1
  107. package/dist/notebook-creation/discover-quota.d.ts +0 -6
  108. package/dist/notebook-creation/discover-quota.d.ts.map +1 -1
  109. package/dist/notebook-creation/discover-quota.js +12 -13
  110. package/dist/notebook-creation/discover-quota.js.map +1 -1
  111. package/dist/notebook-creation/discover-sources.js +15 -16
  112. package/dist/notebook-creation/discover-sources.js.map +1 -1
  113. package/dist/notebook-creation/dom-scripts.d.ts +10 -0
  114. package/dist/notebook-creation/dom-scripts.d.ts.map +1 -0
  115. package/dist/notebook-creation/dom-scripts.js +58 -0
  116. package/dist/notebook-creation/dom-scripts.js.map +1 -0
  117. package/dist/notebook-creation/errors.d.ts +18 -0
  118. package/dist/notebook-creation/errors.d.ts.map +1 -0
  119. package/dist/notebook-creation/errors.js +20 -0
  120. package/dist/notebook-creation/errors.js.map +1 -0
  121. package/dist/notebook-creation/index.d.ts +2 -0
  122. package/dist/notebook-creation/index.d.ts.map +1 -1
  123. package/dist/notebook-creation/index.js +2 -0
  124. package/dist/notebook-creation/index.js.map +1 -1
  125. package/dist/notebook-creation/notebook-creator.d.ts +6 -82
  126. package/dist/notebook-creation/notebook-creator.d.ts.map +1 -1
  127. package/dist/notebook-creation/notebook-creator.js +49 -835
  128. package/dist/notebook-creation/notebook-creator.js.map +1 -1
  129. package/dist/notebook-creation/notebook-nav.d.ts +19 -0
  130. package/dist/notebook-creation/notebook-nav.d.ts.map +1 -0
  131. package/dist/notebook-creation/notebook-nav.js +239 -0
  132. package/dist/notebook-creation/notebook-nav.js.map +1 -0
  133. package/dist/notebook-creation/notebook-sync.d.ts.map +1 -1
  134. package/dist/notebook-creation/notebook-sync.js +36 -38
  135. package/dist/notebook-creation/notebook-sync.js.map +1 -1
  136. package/dist/notebook-creation/selector-discovery.d.ts.map +1 -1
  137. package/dist/notebook-creation/selector-discovery.js +17 -24
  138. package/dist/notebook-creation/selector-discovery.js.map +1 -1
  139. package/dist/notebook-creation/selectors.d.ts +26 -21
  140. package/dist/notebook-creation/selectors.d.ts.map +1 -1
  141. package/dist/notebook-creation/selectors.js +79 -36
  142. package/dist/notebook-creation/selectors.js.map +1 -1
  143. package/dist/notebook-creation/source-manager.d.ts +22 -0
  144. package/dist/notebook-creation/source-manager.d.ts.map +1 -1
  145. package/dist/notebook-creation/source-manager.js +716 -50
  146. package/dist/notebook-creation/source-manager.js.map +1 -1
  147. package/dist/notebook-creation/types.d.ts +4 -0
  148. package/dist/notebook-creation/types.d.ts.map +1 -1
  149. package/dist/notebook-creation/video-manager.d.ts.map +1 -1
  150. package/dist/notebook-creation/video-manager.js +45 -35
  151. package/dist/notebook-creation/video-manager.js.map +1 -1
  152. package/dist/observability/metrics.d.ts +19 -0
  153. package/dist/observability/metrics.d.ts.map +1 -0
  154. package/dist/observability/metrics.js +35 -0
  155. package/dist/observability/metrics.js.map +1 -0
  156. package/dist/quota/quota-manager.d.ts +11 -3
  157. package/dist/quota/quota-manager.d.ts.map +1 -1
  158. package/dist/quota/quota-manager.js +139 -47
  159. package/dist/quota/quota-manager.js.map +1 -1
  160. package/dist/resources/resource-handlers.d.ts.map +1 -1
  161. package/dist/resources/resource-handlers.js +29 -12
  162. package/dist/resources/resource-handlers.js.map +1 -1
  163. package/dist/session/browser-session.d.ts.map +1 -1
  164. package/dist/session/browser-session.js +22 -22
  165. package/dist/session/browser-session.js.map +1 -1
  166. package/dist/session/session-timeout.d.ts.map +1 -1
  167. package/dist/session/session-timeout.js +4 -2
  168. package/dist/session/session-timeout.js.map +1 -1
  169. package/dist/session/shared-context-manager.d.ts.map +1 -1
  170. package/dist/session/shared-context-manager.js +31 -30
  171. package/dist/session/shared-context-manager.js.map +1 -1
  172. package/dist/tools/annotations.js +9 -9
  173. package/dist/tools/annotations.js.map +1 -1
  174. package/dist/tools/definitions/ask-question.d.ts.map +1 -1
  175. package/dist/tools/definitions/ask-question.js +35 -100
  176. package/dist/tools/definitions/ask-question.js.map +1 -1
  177. package/dist/tools/definitions/chat-history.d.ts +47 -1
  178. package/dist/tools/definitions/chat-history.d.ts.map +1 -1
  179. package/dist/tools/definitions/chat-history.js +10 -1
  180. package/dist/tools/definitions/chat-history.js.map +1 -1
  181. package/dist/tools/definitions/data-tables.d.ts.map +1 -1
  182. package/dist/tools/definitions/data-tables.js +2 -0
  183. package/dist/tools/definitions/data-tables.js.map +1 -1
  184. package/dist/tools/definitions/gemini.d.ts.map +1 -1
  185. package/dist/tools/definitions/gemini.js +40 -10
  186. package/dist/tools/definitions/gemini.js.map +1 -1
  187. package/dist/tools/definitions/notebook-management.d.ts.map +1 -1
  188. package/dist/tools/definitions/notebook-management.js +100 -70
  189. package/dist/tools/definitions/notebook-management.js.map +1 -1
  190. package/dist/tools/definitions/query-history.d.ts +47 -1
  191. package/dist/tools/definitions/query-history.d.ts.map +1 -1
  192. package/dist/tools/definitions/query-history.js +7 -0
  193. package/dist/tools/definitions/query-history.js.map +1 -1
  194. package/dist/tools/definitions/session-management.d.ts.map +1 -1
  195. package/dist/tools/definitions/session-management.js +5 -0
  196. package/dist/tools/definitions/session-management.js.map +1 -1
  197. package/dist/tools/definitions/system.d.ts.map +1 -1
  198. package/dist/tools/definitions/system.js +71 -100
  199. package/dist/tools/definitions/system.js.map +1 -1
  200. package/dist/tools/definitions/video.d.ts.map +1 -1
  201. package/dist/tools/definitions/video.js +3 -0
  202. package/dist/tools/definitions/video.js.map +1 -1
  203. package/dist/tools/definitions.d.ts.map +1 -1
  204. package/dist/tools/definitions.js +4 -0
  205. package/dist/tools/definitions.js.map +1 -1
  206. package/dist/tools/handlers/ask-question.d.ts +1 -1
  207. package/dist/tools/handlers/ask-question.d.ts.map +1 -1
  208. package/dist/tools/handlers/ask-question.js +56 -12
  209. package/dist/tools/handlers/ask-question.js.map +1 -1
  210. package/dist/tools/handlers/audio-video.d.ts.map +1 -1
  211. package/dist/tools/handlers/audio-video.js +15 -7
  212. package/dist/tools/handlers/audio-video.js.map +1 -1
  213. package/dist/tools/handlers/auth.d.ts +14 -19
  214. package/dist/tools/handlers/auth.d.ts.map +1 -1
  215. package/dist/tools/handlers/auth.js +77 -121
  216. package/dist/tools/handlers/auth.js.map +1 -1
  217. package/dist/tools/handlers/error-utils.d.ts +7 -0
  218. package/dist/tools/handlers/error-utils.d.ts.map +1 -0
  219. package/dist/tools/handlers/error-utils.js +17 -0
  220. package/dist/tools/handlers/error-utils.js.map +1 -0
  221. package/dist/tools/handlers/gemini.d.ts +1 -0
  222. package/dist/tools/handlers/gemini.d.ts.map +1 -1
  223. package/dist/tools/handlers/gemini.js +81 -51
  224. package/dist/tools/handlers/gemini.js.map +1 -1
  225. package/dist/tools/handlers/index.d.ts +39 -47
  226. package/dist/tools/handlers/index.d.ts.map +1 -1
  227. package/dist/tools/handlers/index.js +13 -2
  228. package/dist/tools/handlers/index.js.map +1 -1
  229. package/dist/tools/handlers/notebook-creation.d.ts.map +1 -1
  230. package/dist/tools/handlers/notebook-creation.js +99 -20
  231. package/dist/tools/handlers/notebook-creation.js.map +1 -1
  232. package/dist/tools/handlers/notebook-management.d.ts +8 -8
  233. package/dist/tools/handlers/notebook-management.d.ts.map +1 -1
  234. package/dist/tools/handlers/notebook-management.js +34 -80
  235. package/dist/tools/handlers/notebook-management.js.map +1 -1
  236. package/dist/tools/handlers/session-management.d.ts.map +1 -1
  237. package/dist/tools/handlers/session-management.js +12 -5
  238. package/dist/tools/handlers/session-management.js.map +1 -1
  239. package/dist/tools/handlers/system.d.ts.map +1 -1
  240. package/dist/tools/handlers/system.js +45 -10
  241. package/dist/tools/handlers/system.js.map +1 -1
  242. package/dist/tools/handlers/types.d.ts +1 -1
  243. package/dist/tools/handlers/types.d.ts.map +1 -1
  244. package/dist/tools/handlers/webhooks.d.ts.map +1 -1
  245. package/dist/tools/handlers/webhooks.js +15 -13
  246. package/dist/tools/handlers/webhooks.js.map +1 -1
  247. package/dist/types.d.ts +7 -17
  248. package/dist/types.d.ts.map +1 -1
  249. package/dist/utils/audit-logger.d.ts +19 -1
  250. package/dist/utils/audit-logger.d.ts.map +1 -1
  251. package/dist/utils/audit-logger.js +193 -27
  252. package/dist/utils/audit-logger.js.map +1 -1
  253. package/dist/utils/cleanup-manager.d.ts.map +1 -1
  254. package/dist/utils/cleanup-manager.js +6 -3
  255. package/dist/utils/cleanup-manager.js.map +1 -1
  256. package/dist/utils/crypto.d.ts +4 -1
  257. package/dist/utils/crypto.d.ts.map +1 -1
  258. package/dist/utils/crypto.js +32 -21
  259. package/dist/utils/crypto.js.map +1 -1
  260. package/dist/utils/file-lock.d.ts.map +1 -1
  261. package/dist/utils/file-lock.js +80 -16
  262. package/dist/utils/file-lock.js.map +1 -1
  263. package/dist/utils/file-permissions.d.ts +2 -0
  264. package/dist/utils/file-permissions.d.ts.map +1 -1
  265. package/dist/utils/file-permissions.js +2 -1
  266. package/dist/utils/file-permissions.js.map +1 -1
  267. package/dist/utils/logger.d.ts +4 -0
  268. package/dist/utils/logger.d.ts.map +1 -1
  269. package/dist/utils/logger.js +16 -0
  270. package/dist/utils/logger.js.map +1 -1
  271. package/dist/utils/page-utils.d.ts.map +1 -1
  272. package/dist/utils/page-utils.js +22 -39
  273. package/dist/utils/page-utils.js.map +1 -1
  274. package/dist/utils/response-validator.d.ts.map +1 -1
  275. package/dist/utils/response-validator.js +27 -22
  276. package/dist/utils/response-validator.js.map +1 -1
  277. package/dist/utils/secrets-scanner.d.ts +11 -0
  278. package/dist/utils/secrets-scanner.d.ts.map +1 -1
  279. package/dist/utils/secrets-scanner.js +63 -15
  280. package/dist/utils/secrets-scanner.js.map +1 -1
  281. package/dist/utils/secure-memory.d.ts +9 -31
  282. package/dist/utils/secure-memory.d.ts.map +1 -1
  283. package/dist/utils/secure-memory.js +17 -102
  284. package/dist/utils/secure-memory.js.map +1 -1
  285. package/dist/utils/security.d.ts +4 -3
  286. package/dist/utils/security.d.ts.map +1 -1
  287. package/dist/utils/security.js +41 -11
  288. package/dist/utils/security.js.map +1 -1
  289. package/dist/utils/stealth-utils.d.ts.map +1 -1
  290. package/dist/utils/stealth-utils.js +4 -4
  291. package/dist/utils/stealth-utils.js.map +1 -1
  292. package/dist/webhooks/types.d.ts +2 -0
  293. package/dist/webhooks/types.d.ts.map +1 -1
  294. package/dist/webhooks/webhook-dispatcher.d.ts +80 -12
  295. package/dist/webhooks/webhook-dispatcher.d.ts.map +1 -1
  296. package/dist/webhooks/webhook-dispatcher.js +472 -72
  297. package/dist/webhooks/webhook-dispatcher.js.map +1 -1
  298. package/docs/archive/ISSUES-legacy-2026-04-24.md +644 -0
  299. package/docs/dependency-risk.md +25 -0
  300. package/docs/testing-runbook.md +166 -0
  301. package/docs/usage-guide.md +2 -1
  302. 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
- constructor() {
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: {}, // Required for completion/complete handler
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.setupShutdownHandlers();
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
- this.toolRegistry = new Map([
245
+ this.toolRegistry = this.filterAdvancedToolRegistry(new Map([
109
246
  // Ask Question
110
- ["ask_question", (a, p) => this.toolHandlers.handleAskQuestion(a, p)],
247
+ ["ask_question", (args, progress) => this.toolHandlers.handleAskQuestion(asToolInput(args), progress)],
111
248
  // Notebook Management
112
- ["add_notebook", (a) => this.toolHandlers.handleAddNotebook(a)],
249
+ ["add_notebook", (args) => this.toolHandlers.handleAddNotebook(asToolInput(args))],
113
250
  ["list_notebooks", () => this.toolHandlers.handleListNotebooks()],
114
- ["get_notebook", (a) => this.toolHandlers.handleGetNotebook(a)],
115
- ["select_notebook", (a) => this.toolHandlers.handleSelectNotebook(a)],
116
- ["update_notebook", (a) => this.toolHandlers.handleUpdateNotebook(a)],
117
- ["remove_notebook", (a) => this.toolHandlers.handleRemoveNotebook(a)],
118
- ["search_notebooks", (a) => this.toolHandlers.handleSearchNotebooks(a)],
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", (a) => this.toolHandlers.handleExportLibrary(a)],
257
+ ["export_library", (args) => this.toolHandlers.handleExportLibrary(asToolInput(args))],
121
258
  // Quota & System
122
- ["get_quota", (a) => this.toolHandlers.handleGetQuota(a)],
123
- ["set_quota_tier", (a) => this.toolHandlers.handleSetQuotaTier(a)],
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", (a, p) => this.toolHandlers.handleCreateNotebook(a, p)],
127
- ["batch_create_notebooks", (a, p) => this.toolHandlers.handleBatchCreateNotebooks(a, p)],
128
- ["sync_library", (a) => this.toolHandlers.handleSyncLibrary(a)],
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", (a) => this.toolHandlers.handleCloseSession(a)],
132
- ["reset_session", (a) => this.toolHandlers.handleResetSession(a)],
133
- ["get_health", (a) => this.toolHandlers.handleGetHealth(a)],
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", (a, p) => this.toolHandlers.handleSetupAuth(a, p)],
136
- ["re_auth", (a, p) => this.toolHandlers.handleReAuth(a, p)],
137
- ["cleanup_data", (a) => this.toolHandlers.handleCleanupData(a)],
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", (a) => this.toolHandlers.handleListSources(a)],
140
- ["add_source", (a) => this.toolHandlers.handleAddSource(a)],
141
- ["add_folder", (a, p) => this.toolHandlers.handleAddFolder(a, p)],
142
- ["remove_source", (a) => this.toolHandlers.handleRemoveSource(a)],
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", (a) => this.toolHandlers.handleGenerateAudioOverview(a)],
145
- ["get_audio_status", (a) => this.toolHandlers.handleGetAudioStatus(a)],
146
- ["download_audio", (a) => this.toolHandlers.handleDownloadAudio(a)],
147
- ["generate_video_overview", (a) => this.toolHandlers.handleGenerateVideoOverview(a)],
148
- ["get_video_status", (a) => this.toolHandlers.handleGetVideoStatus(a)],
149
- ["generate_data_table", (a) => this.toolHandlers.handleGenerateDataTable(a)],
150
- ["get_data_table", (a) => this.toolHandlers.handleGetDataTable(a)],
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", (a) => this.toolHandlers.handleConfigureWebhook(a)],
289
+ ["configure_webhook", (args) => this.toolHandlers.handleConfigureWebhook(asToolInput(args))],
153
290
  ["list_webhooks", () => this.toolHandlers.handleListWebhooks()],
154
- ["test_webhook", (a) => this.toolHandlers.handleTestWebhook(a)],
155
- ["remove_webhook", (a) => this.toolHandlers.handleRemoveWebhook(a)],
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", (a, p) => this.toolHandlers.handleDeepResearch(a, p)],
158
- ["gemini_query", (a) => this.toolHandlers.handleGeminiQuery(a)],
159
- ["get_research_status", (a) => this.toolHandlers.handleGetResearchStatus(a)],
160
- ["upload_document", (a) => this.toolHandlers.handleUploadDocument(a)],
161
- ["query_document", (a) => this.toolHandlers.handleQueryDocument(a)],
162
- ["list_documents", (a) => this.toolHandlers.handleListDocuments(a)],
163
- ["delete_document", (a) => this.toolHandlers.handleDeleteDocument(a)],
164
- ["query_chunked_document", (a) => this.toolHandlers.handleQueryChunkedDocument(a)],
165
- ["get_query_history", (a) => this.toolHandlers.handleGetQueryHistory(a)],
166
- ["get_notebook_chat_history", (a) => this.toolHandlers.handleGetNotebookChatHistory(a)],
167
- ]);
168
- // List available tools
169
- this.server.setRequestHandler(ListToolsRequestSchema, async () => {
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: this.toolDefinitions,
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
- const progressToken = args?._meta?.progressToken;
179
- const authToken = args?._meta?.authToken || process.env.NLMCP_AUTH_TOKEN;
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 access the filesystem always require auth, even if globally disabled
186
- const TOOLS_REQUIRING_AUTH = ["add_folder", "cleanup_data", "export_library"];
187
- const requiresAuth = TOOLS_REQUIRING_AUTH.includes(name);
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({ success: false, error: `Unknown tool: ${name}` }, null, 2),
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 errorMessage = error instanceof Error ? error.message : String(error);
246
- log.error(`❌ [MCP] Tool execution error: ${errorMessage}`);
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 shutdown = async (signal) => {
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
- // Run the server
398
- main();
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