youtube-knowledge-mcp 2.1.0 → 3.0.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 (478) hide show
  1. package/README.md +153 -38
  2. package/dist/brain-schemas.d.ts +10 -10
  3. package/dist/brain-schemas.d.ts.map +1 -1
  4. package/dist/brain-schemas.js +0 -4
  5. package/dist/brain-schemas.js.map +1 -1
  6. package/dist/harvest-schemas.d.ts +75 -0
  7. package/dist/harvest-schemas.d.ts.map +1 -0
  8. package/dist/harvest-schemas.js +106 -0
  9. package/dist/harvest-schemas.js.map +1 -0
  10. package/dist/http/app.d.ts +16 -0
  11. package/dist/http/app.d.ts.map +1 -0
  12. package/dist/http/app.js +176 -0
  13. package/dist/http/app.js.map +1 -0
  14. package/dist/http/auth.d.ts +38 -0
  15. package/dist/http/auth.d.ts.map +1 -0
  16. package/dist/http/auth.js +79 -0
  17. package/dist/http/auth.js.map +1 -0
  18. package/dist/http/config.d.ts +41 -0
  19. package/dist/http/config.d.ts.map +1 -0
  20. package/dist/http/config.js +65 -0
  21. package/dist/http/config.js.map +1 -0
  22. package/dist/http/rate-limiter.d.ts +24 -0
  23. package/dist/http/rate-limiter.d.ts.map +1 -0
  24. package/dist/http/rate-limiter.js +55 -0
  25. package/dist/http/rate-limiter.js.map +1 -0
  26. package/dist/http/sessions.d.ts +34 -0
  27. package/dist/http/sessions.d.ts.map +1 -0
  28. package/dist/http/sessions.js +62 -0
  29. package/dist/http/sessions.js.map +1 -0
  30. package/dist/http.d.ts +6 -48
  31. package/dist/http.d.ts.map +1 -1
  32. package/dist/http.js +35 -252
  33. package/dist/http.js.map +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +11 -517
  36. package/dist/index.js.map +1 -1
  37. package/dist/prompts-brain.d.ts +4 -0
  38. package/dist/prompts-brain.d.ts.map +1 -0
  39. package/dist/prompts-brain.js +48 -0
  40. package/dist/prompts-brain.js.map +1 -0
  41. package/dist/prompts-research.d.ts +4 -0
  42. package/dist/prompts-research.d.ts.map +1 -0
  43. package/dist/prompts-research.js +74 -0
  44. package/dist/prompts-research.js.map +1 -0
  45. package/dist/prompts-shared.d.ts +13 -0
  46. package/dist/prompts-shared.d.ts.map +1 -0
  47. package/dist/prompts-shared.js +7 -0
  48. package/dist/prompts-shared.js.map +1 -0
  49. package/dist/prompts.d.ts.map +1 -1
  50. package/dist/prompts.js +5 -100
  51. package/dist/prompts.js.map +1 -1
  52. package/dist/registry/brain-tools.d.ts +4 -0
  53. package/dist/registry/brain-tools.d.ts.map +1 -0
  54. package/dist/registry/brain-tools.js +104 -0
  55. package/dist/registry/brain-tools.js.map +1 -0
  56. package/dist/registry/channel-tools.d.ts +4 -0
  57. package/dist/registry/channel-tools.d.ts.map +1 -0
  58. package/dist/registry/channel-tools.js +53 -0
  59. package/dist/registry/channel-tools.js.map +1 -0
  60. package/dist/registry/harvest-tools.d.ts +10 -0
  61. package/dist/registry/harvest-tools.d.ts.map +1 -0
  62. package/dist/registry/harvest-tools.js +109 -0
  63. package/dist/registry/harvest-tools.js.map +1 -0
  64. package/dist/registry/health-tools.d.ts +4 -0
  65. package/dist/registry/health-tools.d.ts.map +1 -0
  66. package/dist/registry/health-tools.js +39 -0
  67. package/dist/registry/health-tools.js.map +1 -0
  68. package/dist/registry/index.d.ts +8 -0
  69. package/dist/registry/index.d.ts.map +1 -0
  70. package/dist/registry/index.js +29 -0
  71. package/dist/registry/index.js.map +1 -0
  72. package/dist/registry/library-tools.d.ts +4 -0
  73. package/dist/registry/library-tools.d.ts.map +1 -0
  74. package/dist/registry/library-tools.js +116 -0
  75. package/dist/registry/library-tools.js.map +1 -0
  76. package/dist/registry/media-tools.d.ts +4 -0
  77. package/dist/registry/media-tools.d.ts.map +1 -0
  78. package/dist/registry/media-tools.js +100 -0
  79. package/dist/registry/media-tools.js.map +1 -0
  80. package/dist/registry/thumbnail-tools.d.ts +7 -0
  81. package/dist/registry/thumbnail-tools.d.ts.map +1 -0
  82. package/dist/registry/thumbnail-tools.js +72 -0
  83. package/dist/registry/thumbnail-tools.js.map +1 -0
  84. package/dist/registry/transcript-tools.d.ts +4 -0
  85. package/dist/registry/transcript-tools.d.ts.map +1 -0
  86. package/dist/registry/transcript-tools.js +68 -0
  87. package/dist/registry/transcript-tools.js.map +1 -0
  88. package/dist/registry/types.d.ts +41 -0
  89. package/dist/registry/types.d.ts.map +1 -0
  90. package/dist/registry/types.js +17 -0
  91. package/dist/registry/types.js.map +1 -0
  92. package/dist/registry/video-tools.d.ts +4 -0
  93. package/dist/registry/video-tools.d.ts.map +1 -0
  94. package/dist/registry/video-tools.js +101 -0
  95. package/dist/registry/video-tools.js.map +1 -0
  96. package/dist/resources.d.ts.map +1 -1
  97. package/dist/resources.js +13 -10
  98. package/dist/resources.js.map +1 -1
  99. package/dist/schemas.d.ts +250 -2
  100. package/dist/schemas.d.ts.map +1 -1
  101. package/dist/schemas.js +61 -0
  102. package/dist/schemas.js.map +1 -1
  103. package/dist/thumbnail-resource.d.ts +8 -0
  104. package/dist/thumbnail-resource.d.ts.map +1 -0
  105. package/dist/thumbnail-resource.js +38 -0
  106. package/dist/thumbnail-resource.js.map +1 -0
  107. package/dist/thumbnail-schemas.d.ts +512 -0
  108. package/dist/thumbnail-schemas.d.ts.map +1 -0
  109. package/dist/thumbnail-schemas.js +95 -0
  110. package/dist/thumbnail-schemas.js.map +1 -0
  111. package/dist/tools/ask-brain.js +1 -1
  112. package/dist/tools/ask-brain.js.map +1 -1
  113. package/dist/tools/batch.js +1 -1
  114. package/dist/tools/batch.js.map +1 -1
  115. package/dist/tools/brain-info.js +1 -1
  116. package/dist/tools/brain-info.js.map +1 -1
  117. package/dist/tools/build-brain.js +1 -1
  118. package/dist/tools/build-brain.js.map +1 -1
  119. package/dist/tools/check-health.d.ts +61 -0
  120. package/dist/tools/check-health.d.ts.map +1 -1
  121. package/dist/tools/check-health.js +94 -6
  122. package/dist/tools/check-health.js.map +1 -1
  123. package/dist/tools/clips.d.ts +9 -41
  124. package/dist/tools/clips.d.ts.map +1 -1
  125. package/dist/tools/clips.js +52 -128
  126. package/dist/tools/clips.js.map +1 -1
  127. package/dist/tools/fetch-channel-thumbnails.d.ts +179 -0
  128. package/dist/tools/fetch-channel-thumbnails.d.ts.map +1 -0
  129. package/dist/tools/fetch-channel-thumbnails.js +123 -0
  130. package/dist/tools/fetch-channel-thumbnails.js.map +1 -0
  131. package/dist/tools/fetch-videos.d.ts +18 -4
  132. package/dist/tools/fetch-videos.d.ts.map +1 -1
  133. package/dist/tools/fetch-videos.js +64 -20
  134. package/dist/tools/fetch-videos.js.map +1 -1
  135. package/dist/tools/frames-subtitles.d.ts +44 -0
  136. package/dist/tools/frames-subtitles.d.ts.map +1 -0
  137. package/dist/tools/frames-subtitles.js +104 -0
  138. package/dist/tools/frames-subtitles.js.map +1 -0
  139. package/dist/tools/get-channel-info.d.ts +2 -0
  140. package/dist/tools/get-channel-info.d.ts.map +1 -1
  141. package/dist/tools/get-channel-info.js +8 -1
  142. package/dist/tools/get-channel-info.js.map +1 -1
  143. package/dist/tools/get-comments.d.ts +238 -6
  144. package/dist/tools/get-comments.d.ts.map +1 -1
  145. package/dist/tools/get-comments.js +85 -21
  146. package/dist/tools/get-comments.js.map +1 -1
  147. package/dist/tools/get-coverage.d.ts +122 -0
  148. package/dist/tools/get-coverage.d.ts.map +1 -0
  149. package/dist/tools/get-coverage.js +105 -0
  150. package/dist/tools/get-coverage.js.map +1 -0
  151. package/dist/tools/get-playlist-info.d.ts.map +1 -1
  152. package/dist/tools/get-playlist-info.js +1 -0
  153. package/dist/tools/get-playlist-info.js.map +1 -1
  154. package/dist/tools/get-thumbnail.d.ts +38 -0
  155. package/dist/tools/get-thumbnail.d.ts.map +1 -0
  156. package/dist/tools/get-thumbnail.js +152 -0
  157. package/dist/tools/get-thumbnail.js.map +1 -0
  158. package/dist/tools/get-transcript.d.ts.map +1 -1
  159. package/dist/tools/get-transcript.js +2 -14
  160. package/dist/tools/get-transcript.js.map +1 -1
  161. package/dist/tools/harvest-channel.d.ts +92 -0
  162. package/dist/tools/harvest-channel.d.ts.map +1 -0
  163. package/dist/tools/harvest-channel.js +62 -0
  164. package/dist/tools/harvest-channel.js.map +1 -0
  165. package/dist/tools/harvest-comments.d.ts +81 -0
  166. package/dist/tools/harvest-comments.d.ts.map +1 -0
  167. package/dist/tools/harvest-comments.js +115 -0
  168. package/dist/tools/harvest-comments.js.map +1 -0
  169. package/dist/tools/library.d.ts.map +1 -1
  170. package/dist/tools/library.js +14 -4
  171. package/dist/tools/library.js.map +1 -1
  172. package/dist/tools/list-channel-thumbnails.d.ts +235 -0
  173. package/dist/tools/list-channel-thumbnails.d.ts.map +1 -0
  174. package/dist/tools/list-channel-thumbnails.js +79 -0
  175. package/dist/tools/list-channel-thumbnails.js.map +1 -0
  176. package/dist/tools/list-library.js +1 -1
  177. package/dist/tools/list-library.js.map +1 -1
  178. package/dist/tools/prune-harvest.d.ts +59 -0
  179. package/dist/tools/prune-harvest.d.ts.map +1 -0
  180. package/dist/tools/prune-harvest.js +108 -0
  181. package/dist/tools/prune-harvest.js.map +1 -0
  182. package/dist/tools/query-comments.d.ts +125 -0
  183. package/dist/tools/query-comments.d.ts.map +1 -0
  184. package/dist/tools/query-comments.js +106 -0
  185. package/dist/tools/query-comments.js.map +1 -0
  186. package/dist/tools/query-videos.d.ts +118 -0
  187. package/dist/tools/query-videos.d.ts.map +1 -0
  188. package/dist/tools/query-videos.js +97 -0
  189. package/dist/tools/query-videos.js.map +1 -0
  190. package/dist/tools/repair-store.d.ts +34 -0
  191. package/dist/tools/repair-store.d.ts.map +1 -0
  192. package/dist/tools/repair-store.js +58 -0
  193. package/dist/tools/repair-store.js.map +1 -0
  194. package/dist/tools/save-to-library.d.ts.map +1 -1
  195. package/dist/tools/save-to-library.js +5 -1
  196. package/dist/tools/save-to-library.js.map +1 -1
  197. package/dist/tools/search-channels.d.ts +6 -0
  198. package/dist/tools/search-channels.d.ts.map +1 -1
  199. package/dist/tools/search-channels.js +5 -1
  200. package/dist/tools/search-channels.js.map +1 -1
  201. package/dist/tools/search-transcript.d.ts.map +1 -1
  202. package/dist/tools/search-transcript.js +6 -13
  203. package/dist/tools/search-transcript.js.map +1 -1
  204. package/dist/tools/search-videos.d.ts +3 -0
  205. package/dist/tools/search-videos.d.ts.map +1 -1
  206. package/dist/tools/search-videos.js +1 -1
  207. package/dist/tools/search-videos.js.map +1 -1
  208. package/dist/utils/batches.d.ts +15 -0
  209. package/dist/utils/batches.d.ts.map +1 -0
  210. package/dist/utils/batches.js +31 -0
  211. package/dist/utils/batches.js.map +1 -0
  212. package/dist/utils/brain-build.d.ts.map +1 -1
  213. package/dist/utils/brain-build.js +1 -12
  214. package/dist/utils/brain-build.js.map +1 -1
  215. package/dist/utils/brain-index.d.ts.map +1 -1
  216. package/dist/utils/brain-index.js +3 -2
  217. package/dist/utils/brain-index.js.map +1 -1
  218. package/dist/utils/brain-lock.d.ts +2 -18
  219. package/dist/utils/brain-lock.d.ts.map +1 -1
  220. package/dist/utils/brain-lock.js +4 -80
  221. package/dist/utils/brain-lock.js.map +1 -1
  222. package/dist/utils/brain-lookup.d.ts +8 -0
  223. package/dist/utils/brain-lookup.d.ts.map +1 -1
  224. package/dist/utils/brain-lookup.js +5 -31
  225. package/dist/utils/brain-lookup.js.map +1 -1
  226. package/dist/utils/brain-storage.d.ts.map +1 -1
  227. package/dist/utils/brain-storage.js +5 -5
  228. package/dist/utils/brain-storage.js.map +1 -1
  229. package/dist/utils/caption-probe.d.ts +7 -0
  230. package/dist/utils/caption-probe.d.ts.map +1 -0
  231. package/dist/utils/caption-probe.js +36 -0
  232. package/dist/utils/caption-probe.js.map +1 -0
  233. package/dist/utils/channel-images.d.ts +16 -0
  234. package/dist/utils/channel-images.d.ts.map +1 -0
  235. package/dist/utils/channel-images.js +35 -0
  236. package/dist/utils/channel-images.js.map +1 -0
  237. package/dist/utils/channel-lookup.d.ts +5 -0
  238. package/dist/utils/channel-lookup.d.ts.map +1 -0
  239. package/dist/utils/channel-lookup.js +37 -0
  240. package/dist/utils/channel-lookup.js.map +1 -0
  241. package/dist/utils/chapters.d.ts +11 -0
  242. package/dist/utils/chapters.d.ts.map +1 -0
  243. package/dist/utils/chapters.js +24 -0
  244. package/dist/utils/chapters.js.map +1 -0
  245. package/dist/utils/clips.d.ts.map +1 -1
  246. package/dist/utils/clips.js +18 -26
  247. package/dist/utils/clips.js.map +1 -1
  248. package/dist/utils/comment-store.d.ts +71 -0
  249. package/dist/utils/comment-store.d.ts.map +1 -0
  250. package/dist/utils/comment-store.js +129 -0
  251. package/dist/utils/comment-store.js.map +1 -0
  252. package/dist/utils/comment-threads.d.ts +51 -0
  253. package/dist/utils/comment-threads.d.ts.map +1 -0
  254. package/dist/utils/comment-threads.js +76 -0
  255. package/dist/utils/comment-threads.js.map +1 -0
  256. package/dist/utils/coverage-text.d.ts +3 -0
  257. package/dist/utils/coverage-text.d.ts.map +1 -0
  258. package/dist/utils/coverage-text.js +62 -0
  259. package/dist/utils/coverage-text.js.map +1 -0
  260. package/dist/utils/coverage.d.ts +57 -0
  261. package/dist/utils/coverage.d.ts.map +1 -0
  262. package/dist/utils/coverage.js +142 -0
  263. package/dist/utils/coverage.js.map +1 -0
  264. package/dist/utils/env.d.ts +26 -0
  265. package/dist/utils/env.d.ts.map +1 -0
  266. package/dist/utils/env.js +72 -0
  267. package/dist/utils/env.js.map +1 -0
  268. package/dist/utils/errors.d.ts +1 -1
  269. package/dist/utils/errors.d.ts.map +1 -1
  270. package/dist/utils/errors.js +28 -2
  271. package/dist/utils/errors.js.map +1 -1
  272. package/dist/utils/file-lock.d.ts +34 -0
  273. package/dist/utils/file-lock.d.ts.map +1 -0
  274. package/dist/utils/file-lock.js +91 -0
  275. package/dist/utils/file-lock.js.map +1 -0
  276. package/dist/utils/flat-listing.d.ts +89 -0
  277. package/dist/utils/flat-listing.d.ts.map +1 -0
  278. package/dist/utils/flat-listing.js +68 -0
  279. package/dist/utils/flat-listing.js.map +1 -0
  280. package/dist/utils/format.d.ts +18 -4
  281. package/dist/utils/format.d.ts.map +1 -1
  282. package/dist/utils/format.js +2 -6
  283. package/dist/utils/format.js.map +1 -1
  284. package/dist/utils/guard.d.ts +21 -0
  285. package/dist/utils/guard.d.ts.map +1 -0
  286. package/dist/utils/guard.js +105 -0
  287. package/dist/utils/guard.js.map +1 -0
  288. package/dist/utils/harvest-catalog.d.ts +44 -0
  289. package/dist/utils/harvest-catalog.d.ts.map +1 -0
  290. package/dist/utils/harvest-catalog.js +164 -0
  291. package/dist/utils/harvest-catalog.js.map +1 -0
  292. package/dist/utils/harvest-lock.d.ts +3 -0
  293. package/dist/utils/harvest-lock.d.ts.map +1 -0
  294. package/dist/utils/harvest-lock.js +26 -0
  295. package/dist/utils/harvest-lock.js.map +1 -0
  296. package/dist/utils/harvest-receipts.d.ts +14 -0
  297. package/dist/utils/harvest-receipts.d.ts.map +1 -0
  298. package/dist/utils/harvest-receipts.js +110 -0
  299. package/dist/utils/harvest-receipts.js.map +1 -0
  300. package/dist/utils/image-dimensions.d.ts +19 -0
  301. package/dist/utils/image-dimensions.d.ts.map +1 -0
  302. package/dist/utils/image-dimensions.js +93 -0
  303. package/dist/utils/image-dimensions.js.map +1 -0
  304. package/dist/utils/image-fetch.d.ts +31 -0
  305. package/dist/utils/image-fetch.d.ts.map +1 -0
  306. package/dist/utils/image-fetch.js +141 -0
  307. package/dist/utils/image-fetch.js.map +1 -0
  308. package/dist/utils/json-file.d.ts +14 -5
  309. package/dist/utils/json-file.d.ts.map +1 -1
  310. package/dist/utils/json-file.js +24 -9
  311. package/dist/utils/json-file.js.map +1 -1
  312. package/dist/utils/listing-cursor.d.ts +31 -0
  313. package/dist/utils/listing-cursor.d.ts.map +1 -0
  314. package/dist/utils/listing-cursor.js +39 -0
  315. package/dist/utils/listing-cursor.js.map +1 -0
  316. package/dist/utils/paths.d.ts +10 -0
  317. package/dist/utils/paths.d.ts.map +1 -1
  318. package/dist/utils/paths.js +14 -0
  319. package/dist/utils/paths.js.map +1 -1
  320. package/dist/utils/pattern.d.ts +30 -0
  321. package/dist/utils/pattern.d.ts.map +1 -0
  322. package/dist/utils/pattern.js +130 -0
  323. package/dist/utils/pattern.js.map +1 -0
  324. package/dist/utils/pot-preflight.d.ts +53 -0
  325. package/dist/utils/pot-preflight.d.ts.map +1 -0
  326. package/dist/utils/pot-preflight.js +146 -0
  327. package/dist/utils/pot-preflight.js.map +1 -0
  328. package/dist/utils/search-index.d.ts.map +1 -1
  329. package/dist/utils/search-index.js +9 -4
  330. package/dist/utils/search-index.js.map +1 -1
  331. package/dist/utils/sleep.d.ts +9 -0
  332. package/dist/utils/sleep.d.ts.map +1 -0
  333. package/dist/utils/sleep.js +25 -0
  334. package/dist/utils/sleep.js.map +1 -0
  335. package/dist/utils/storage.d.ts.map +1 -1
  336. package/dist/utils/storage.js +14 -8
  337. package/dist/utils/storage.js.map +1 -1
  338. package/dist/utils/store-health.d.ts +37 -0
  339. package/dist/utils/store-health.d.ts.map +1 -0
  340. package/dist/utils/store-health.js +92 -0
  341. package/dist/utils/store-health.js.map +1 -0
  342. package/dist/utils/store-migrations.d.ts +33 -0
  343. package/dist/utils/store-migrations.d.ts.map +1 -0
  344. package/dist/utils/store-migrations.js +41 -0
  345. package/dist/utils/store-migrations.js.map +1 -0
  346. package/dist/utils/store-paths.d.ts +33 -0
  347. package/dist/utils/store-paths.d.ts.map +1 -0
  348. package/dist/utils/store-paths.js +48 -0
  349. package/dist/utils/store-paths.js.map +1 -0
  350. package/dist/utils/store-rows.d.ts +22 -0
  351. package/dist/utils/store-rows.d.ts.map +1 -0
  352. package/dist/utils/store-rows.js +62 -0
  353. package/dist/utils/store-rows.js.map +1 -0
  354. package/dist/utils/store-schema.d.ts +16 -0
  355. package/dist/utils/store-schema.d.ts.map +1 -0
  356. package/dist/utils/store-schema.js +168 -0
  357. package/dist/utils/store-schema.js.map +1 -0
  358. package/dist/utils/store.d.ts +34 -0
  359. package/dist/utils/store.d.ts.map +1 -0
  360. package/dist/utils/store.js +157 -0
  361. package/dist/utils/store.js.map +1 -0
  362. package/dist/utils/subtitles.d.ts +7 -0
  363. package/dist/utils/subtitles.d.ts.map +1 -0
  364. package/dist/utils/subtitles.js +30 -0
  365. package/dist/utils/subtitles.js.map +1 -0
  366. package/dist/utils/thumbnail-entry.d.ts +19 -0
  367. package/dist/utils/thumbnail-entry.d.ts.map +1 -0
  368. package/dist/utils/thumbnail-entry.js +119 -0
  369. package/dist/utils/thumbnail-entry.js.map +1 -0
  370. package/dist/utils/thumbnail-fetch.d.ts +37 -0
  371. package/dist/utils/thumbnail-fetch.d.ts.map +1 -0
  372. package/dist/utils/thumbnail-fetch.js +89 -0
  373. package/dist/utils/thumbnail-fetch.js.map +1 -0
  374. package/dist/utils/thumbnail-ladder.d.ts +44 -0
  375. package/dist/utils/thumbnail-ladder.d.ts.map +1 -0
  376. package/dist/utils/thumbnail-ladder.js +68 -0
  377. package/dist/utils/thumbnail-ladder.js.map +1 -0
  378. package/dist/utils/thumbnail-listing.d.ts +16 -0
  379. package/dist/utils/thumbnail-listing.d.ts.map +1 -0
  380. package/dist/utils/thumbnail-listing.js +26 -0
  381. package/dist/utils/thumbnail-listing.js.map +1 -0
  382. package/dist/utils/thumbnail-paths.d.ts +20 -0
  383. package/dist/utils/thumbnail-paths.d.ts.map +1 -0
  384. package/dist/utils/thumbnail-paths.js +35 -0
  385. package/dist/utils/thumbnail-paths.js.map +1 -0
  386. package/dist/utils/thumbnail-store.d.ts +19 -0
  387. package/dist/utils/thumbnail-store.d.ts.map +1 -0
  388. package/dist/utils/thumbnail-store.js +125 -0
  389. package/dist/utils/thumbnail-store.js.map +1 -0
  390. package/dist/utils/transcript-cache.d.ts +20 -0
  391. package/dist/utils/transcript-cache.d.ts.map +1 -0
  392. package/dist/utils/transcript-cache.js +127 -0
  393. package/dist/utils/transcript-cache.js.map +1 -0
  394. package/dist/utils/transcript-search.d.ts +20 -0
  395. package/dist/utils/transcript-search.d.ts.map +1 -0
  396. package/dist/utils/transcript-search.js +35 -0
  397. package/dist/utils/transcript-search.js.map +1 -0
  398. package/dist/utils/transcript.d.ts +10 -31
  399. package/dist/utils/transcript.d.ts.map +1 -1
  400. package/dist/utils/transcript.js +2 -64
  401. package/dist/utils/transcript.js.map +1 -1
  402. package/dist/utils/validate-youtube.d.ts +35 -0
  403. package/dist/utils/validate-youtube.d.ts.map +1 -0
  404. package/dist/utils/validate-youtube.js +87 -0
  405. package/dist/utils/validate-youtube.js.map +1 -0
  406. package/dist/utils/validate.d.ts +11 -4
  407. package/dist/utils/validate.d.ts.map +1 -1
  408. package/dist/utils/validate.js +43 -36
  409. package/dist/utils/validate.js.map +1 -1
  410. package/dist/utils/version.d.ts +2 -0
  411. package/dist/utils/version.d.ts.map +1 -0
  412. package/dist/utils/version.js +19 -0
  413. package/dist/utils/version.js.map +1 -0
  414. package/dist/utils/video-stats-cache.d.ts +16 -0
  415. package/dist/utils/video-stats-cache.d.ts.map +1 -0
  416. package/dist/utils/video-stats-cache.js +81 -0
  417. package/dist/utils/video-stats-cache.js.map +1 -0
  418. package/dist/utils/video-stats.d.ts +186 -0
  419. package/dist/utils/video-stats.d.ts.map +1 -0
  420. package/dist/utils/video-stats.js +168 -0
  421. package/dist/utils/video-stats.js.map +1 -0
  422. package/dist/utils/video-store.d.ts +69 -0
  423. package/dist/utils/video-store.d.ts.map +1 -0
  424. package/dist/utils/video-store.js +77 -0
  425. package/dist/utils/video-store.js.map +1 -0
  426. package/dist/utils/youtube-channel.d.ts +51 -0
  427. package/dist/utils/youtube-channel.d.ts.map +1 -0
  428. package/dist/utils/youtube-channel.js +91 -0
  429. package/dist/utils/youtube-channel.js.map +1 -0
  430. package/dist/utils/youtube-download.d.ts +27 -0
  431. package/dist/utils/youtube-download.d.ts.map +1 -0
  432. package/dist/utils/youtube-download.js +118 -0
  433. package/dist/utils/youtube-download.js.map +1 -0
  434. package/dist/utils/youtube-schemas.d.ts +492 -0
  435. package/dist/utils/youtube-schemas.d.ts.map +1 -0
  436. package/dist/utils/youtube-schemas.js +148 -0
  437. package/dist/utils/youtube-schemas.js.map +1 -0
  438. package/dist/utils/youtube-search.d.ts +14 -0
  439. package/dist/utils/youtube-search.d.ts.map +1 -0
  440. package/dist/utils/youtube-search.js +39 -0
  441. package/dist/utils/youtube-search.js.map +1 -0
  442. package/dist/utils/youtube-url.d.ts +25 -0
  443. package/dist/utils/youtube-url.d.ts.map +1 -0
  444. package/dist/utils/youtube-url.js +75 -0
  445. package/dist/utils/youtube-url.js.map +1 -0
  446. package/dist/utils/youtube-video.d.ts +68 -0
  447. package/dist/utils/youtube-video.d.ts.map +1 -0
  448. package/dist/utils/youtube-video.js +110 -0
  449. package/dist/utils/youtube-video.js.map +1 -0
  450. package/dist/utils/youtube.d.ts +10 -122
  451. package/dist/utils/youtube.d.ts.map +1 -1
  452. package/dist/utils/youtube.js +11 -486
  453. package/dist/utils/youtube.js.map +1 -1
  454. package/dist/utils/ytdlp-env.d.ts +33 -0
  455. package/dist/utils/ytdlp-env.d.ts.map +1 -0
  456. package/dist/utils/ytdlp-env.js +121 -0
  457. package/dist/utils/ytdlp-env.js.map +1 -0
  458. package/dist/utils/ytdlp-limiter.d.ts +27 -0
  459. package/dist/utils/ytdlp-limiter.d.ts.map +1 -0
  460. package/dist/utils/ytdlp-limiter.js +85 -0
  461. package/dist/utils/ytdlp-limiter.js.map +1 -0
  462. package/dist/utils/ytdlp-pacer.d.ts +64 -0
  463. package/dist/utils/ytdlp-pacer.d.ts.map +1 -0
  464. package/dist/utils/ytdlp-pacer.js +165 -0
  465. package/dist/utils/ytdlp-pacer.js.map +1 -0
  466. package/dist/utils/ytdlp-parse.d.ts +13 -0
  467. package/dist/utils/ytdlp-parse.d.ts.map +1 -0
  468. package/dist/utils/ytdlp-parse.js +48 -0
  469. package/dist/utils/ytdlp-parse.js.map +1 -0
  470. package/dist/utils/ytdlp-stream.d.ts +32 -0
  471. package/dist/utils/ytdlp-stream.d.ts.map +1 -0
  472. package/dist/utils/ytdlp-stream.js +61 -0
  473. package/dist/utils/ytdlp-stream.js.map +1 -0
  474. package/dist/utils/ytdlp.d.ts +23 -18
  475. package/dist/utils/ytdlp.d.ts.map +1 -1
  476. package/dist/utils/ytdlp.js +56 -134
  477. package/dist/utils/ytdlp.js.map +1 -1
  478. package/package.json +3 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/youtube-knowledge-mcp.svg)](https://www.npmjs.com/package/youtube-knowledge-mcp)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Node.js](https://img.shields.io/badge/Node.js-22%2B-green.svg)](https://nodejs.org/)
5
+ [![Node.js](https://img.shields.io/badge/Node.js-22.13%2B-green.svg)](https://nodejs.org/)
6
6
  [![GitHub stars](https://img.shields.io/github/stars/teobouancheau/youtube-knowledge-mcp?style=social)](https://github.com/teobouancheau/youtube-knowledge-mcp)
7
7
 
8
8
  A Model Context Protocol (MCP) server that gives AI assistants the ability to search, analyze, and extract knowledge from YouTube videos. Works with Claude Desktop, Claude Code, Claude.ai, Cursor and any MCP-compatible client.
@@ -18,6 +18,7 @@ Supports both **local** (stdio) and **remote** (Streamable HTTP) transports.
18
18
  - **Search** videos and channels by keyword
19
19
  - **Fetch** videos from a playlist or channel
20
20
  - **Video, channel and playlist metadata**, chapters, and top comments
21
+ - **Thumbnails**: look at any video's thumbnail or a channel's avatar or banner as an image, or save every thumbnail of a channel to disk
21
22
  - **Transcripts with timestamps**, sliced by time range or chapter, and capped so a
22
23
  three-hour video cannot flood your context
23
24
  - **Search inside a transcript** and get back `?t=` links that open the video at
@@ -39,6 +40,19 @@ Supports both **local** (stdio) and **remote** (Streamable HTTP) transports.
39
40
  - **Read them back**, and **search across all of them** with full-text ranking
40
41
  - **Tag, retag and delete**
41
42
 
43
+ **Extract exhaustively, and prove it** (local mode)
44
+
45
+ - **Catalogue a whole channel** across every tab — videos, shorts, streams,
46
+ releases, podcasts — into a local SQLite store, resumably
47
+ - **Harvest comments with their replies**, every field YouTube returns, and
48
+ search them locally with full-text ranking and real pagination
49
+ - **A completeness receipt on every extraction**: `complete` is true only when
50
+ the server can _prove_ it holds everything in scope, so a partial harvest can
51
+ never be mistaken for a full history
52
+ - **Ask what you actually have** with `get_coverage`, which never touches the
53
+ network — cheap enough to call before every claim
54
+ - **Remove what you hold**, including one author's comments everywhere
55
+
42
56
  **Build a brain for a channel** (local mode)
43
57
 
44
58
  - **Read a whole channel** into a searchable corpus of timestamped passages, in
@@ -50,18 +64,25 @@ Supports both **local** (stdio) and **remote** (Streamable HTTP) transports.
50
64
  speaking rate, and the phrases it repeats across videos
51
65
  - **Keep a written profile** beside the corpus, grounded in passages you can cite
52
66
 
67
+ **Study a channel's thumbnails** (local mode)
68
+
69
+ - **Save every thumbnail** of a channel, plus its avatar and banner, resumably, at the largest size YouTube serves, with each image's real pixel size recorded
70
+
53
71
  **Built to stay working**
54
72
 
55
73
  - WebVTT parsed by the W3C reference implementation, not a hand-written matcher
56
74
  - Typed, actionable errors — "no captions in en, try: fr, es, de" rather than a
57
75
  wall of yt-dlp stderr
58
- - Timeouts, retry with backoff, and a concurrency limit on every yt-dlp call
76
+ - Timeouts, adaptive backoff and a concurrency limit on every yt-dlp call, with
77
+ cooldowns and a circuit breaker so a long harvest survives being throttled
59
78
  - `check_health` diagnoses missing or outdated yt-dlp and ffmpeg
79
+ - Bearer auth, a loopback default bind, an allowlist on every URL and image host, and typed error codes on every failure
60
80
  - Structured output on every tool, plus MCP resources, prompts and completions
81
+ - An end-to-end suite runs the built server against real yt-dlp over both transports every week
61
82
 
62
83
  ## Prerequisites
63
84
 
64
- - Node.js 22+
85
+ - Node.js 22.13+ — the version where `node:sqlite`, which the harvest store uses, is available without a flag
65
86
  - [yt-dlp](https://github.com/yt-dlp/yt-dlp#installation) — required by every tool.
66
87
  `brew install yt-dlp` (macOS) or `pip install -U yt-dlp`
67
88
  - [ffmpeg](https://ffmpeg.org/) — required for downloads, clip extraction and frame
@@ -154,7 +175,11 @@ npm run build
154
175
  npm run start:http
155
176
  ```
156
177
 
157
- The server listens on `PORT` (default 3000). Set `PORT` environment variable to change.
178
+ The server listens on `127.0.0.1:PORT` (default 3000). To reach it from another
179
+ machine set `MCP_BIND_HOST=0.0.0.0` together with `MCP_AUTH_TOKEN`; the server
180
+ refuses to start exposed and unauthenticated unless you also set
181
+ `MCP_ALLOW_UNAUTHENTICATED=true`. Behind a reverse proxy, set `MCP_TRUST_PROXY`
182
+ to the number of hops and `MCP_PUBLIC_URL` to the address clients use.
158
183
 
159
184
  #### Docker
160
185
 
@@ -210,7 +235,7 @@ a cold start. Any paid plan removes that.
210
235
 
211
236
  ## MCP Tools
212
237
 
213
- 33 tools. The 14 read-only ones work over both transports; the 19 that touch
238
+ 44 tools. The 15 read-only ones work over both transports; the 29 that touch
214
239
  your filesystem are registered only in local (stdio) mode, so a remote
215
240
  deployment cannot reach the host's disk.
216
241
 
@@ -220,18 +245,19 @@ available for this video. Call get_transcript again with one of: fr, es, de.`
220
245
 
221
246
  ### Discovery — remote + local
222
247
 
223
- | Tool | Key parameters | Returns |
224
- | ------------------- | ---------------- | ---------------------------------------------------------------- |
225
- | `search_videos` | `query`, `limit` | Matching videos with durations, channels and view counts |
226
- | `search_channels` | `query`, `limit` | Matching channels with subscriber counts |
227
- | `fetch_videos` | `url`, `limit` | Videos in a playlist or channel |
228
- | `get_video_info` | `video` | Title, channel, duration, views, likes, description, tags |
229
- | `get_channel_info` | `channel` | Name, handle, subscriber count, description |
230
- | `get_playlist_info` | `url` | Title, channel, video count, last updated |
231
- | `get_chapters` | `video` | Chapter titles with start/end times and deep links |
232
- | `get_comments` | `video`, `limit` | Top-level comments by popularity |
233
- | `list_formats` | `video` | Available formats grouped by video+audio, video-only, audio-only |
234
- | `check_health` | — | yt-dlp and ffmpeg status, versions, and staleness warnings |
248
+ | Tool | Key parameters | Returns |
249
+ | ------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------- |
250
+ | `search_videos` | `query`, `limit` | Matching videos with durations, channels and view counts |
251
+ | `search_channels` | `query`, `limit` | Matching channels with subscriber counts |
252
+ | `fetch_videos` | `url`, `limit`, `cursor` | One page of a playlist or channel, with a cursor. `total` is present only when YouTube states one |
253
+ | `get_video_info` | `video` | Title, channel, duration, views, likes, description, tags |
254
+ | `get_channel_info` | `channel` | Name, handle, subscriber count, description, avatar and banner |
255
+ | `get_playlist_info` | `url` | Title, channel, video count, last updated |
256
+ | `get_chapters` | `video` | Chapter titles with start/end times and deep links |
257
+ | `get_comments` | `video`, `limit`, `includeReplies`, `sort` | Comments with replies and a completeness receipt saying how many of the video's comments this is |
258
+ | `list_formats` | `video` | Available formats grouped by video+audio, video-only, audio-only |
259
+ | `get_thumbnail` | `video`, `channel`, `image`, `quality` | A video thumbnail, channel avatar or banner as an image, with its real size |
260
+ | `check_health` | — | yt-dlp and ffmpeg status, versions, staleness warnings, and which session options are set |
235
261
 
236
262
  ### Transcripts — remote + local
237
263
 
@@ -274,6 +300,35 @@ keyframe-aligned cut. All of these require ffmpeg.
274
300
  | `delete_library_item` | `videoId`, `contentType` | What was deleted |
275
301
  | `rebuild_library_index` | — | Number of notes reindexed |
276
302
 
303
+ ### Exhaustive harvest — local only
304
+
305
+ Extraction that stores what it reads, and proves what it holds. Every tool here
306
+ returns a **completeness receipt**: `complete` is true only when the server can
307
+ prove it has everything in scope, and a consumer must not describe the data as a
308
+ full history while it is false.
309
+
310
+ | Tool | Key parameters | Returns |
311
+ | ------------------ | ---------------------------------------------------------------------- | ----------------------------------------------------------- |
312
+ | `harvest_channel` | `channel`, `maxVideos` | Every video across all tabs, catalogued, with a receipt |
313
+ | `harvest_comments` | `video`, `maxComments`, `sort`, `maxRepliesPerThread` | Comments and replies stored locally, with a receipt |
314
+ | `get_coverage` | `scope`, `incompleteOnly`, `verify`, `limit`, `offset` | What the server can prove it holds, and what it cannot |
315
+ | `query_comments` | `video`, `channel`, `match`, `author`, `threadOf`, `order`, `limit` | Full-text search over stored comments, with real pagination |
316
+ | `query_videos` | `channel`, `match`, `since`, `until`, `withComments`, `order`, `limit` | The catalogued videos, filtered and sorted |
317
+ | `prune_harvest` | `channel`, `video`, `author`, `scope`, `confirm`, `vacuum` | What was removed from the store |
318
+ | `repair_store` | `confirm` | Moves a damaged store aside and starts a fresh one |
319
+
320
+ `get_coverage` never contacts YouTube, so it is cheap enough to call before
321
+ making any claim about the data. Check `anyIncomplete` first.
322
+
323
+ There is no cursor for comments: YouTube exposes none for a live read, so
324
+ `harvest_comments` cannot page. To get more, run it again with a larger
325
+ `maxComments` — rows are upserted on the comment id, so a re-run costs requests
326
+ and never data.
327
+
328
+ `prune_harvest` exists because the store holds personal data at scale: display
329
+ names, channel ids and free-text opinions from people who never used this tool.
330
+ The `author` filter is the per-person erasure path.
331
+
277
332
  ### Channel brains — local only
278
333
 
279
334
  | Tool | Key parameters | Returns |
@@ -289,6 +344,24 @@ keyframe-aligned cut. All of these require ffmpeg.
289
344
  channel from what is already on disk, so they work offline and cost nothing to
290
345
  call.
291
346
 
347
+ ### Channel thumbnails — local only
348
+
349
+ | Tool | Key parameters | Returns |
350
+ | --------------------------- | -------------------------------------------- | --------------------------------------------------------- |
351
+ | `fetch_channel_thumbnails` | `channel`, `maxVideos`, `tabs`, `quality` | Every thumbnail saved to disk, plus the avatar and banner |
352
+ | `list_channel_thumbnails` | `channel`, `tab`, `state`, `limit`, `offset` | Saved entries with paths and decoded sizes |
353
+ | `delete_channel_thumbnails` | `channel` | What was removed |
354
+
355
+ One yt-dlp listing per tab, then the images from YouTube's image hosts
356
+ directly, so a five-hundred-video channel costs a handful of listings and not
357
+ five hundred extractions. For each video the fetch tries the largest image
358
+ YouTube serves (`maxresdefault`, 1280 wide where it exists) and keeps it only if
359
+ it decodes wider than the image the listing offered; otherwise it keeps the
360
+ listed image, and failing that the small `hqdefault`. Every width and height in
361
+ the manifest was decoded from the saved bytes, never read off a URL. Shorts are
362
+ listed with portrait thumbnails and saved under `shorts/`. The run is resumable:
363
+ interrupt it and call again, and only what is missing or truncated is fetched.
364
+
292
365
  A brain holds one caption language; pass `language` to read another, and build a
293
366
  separate brain per language.
294
367
 
@@ -309,16 +382,18 @@ skipped forever as already done.
309
382
 
310
383
  Reusable workflows your client can invoke directly: `summarize_video`,
311
384
  `extract_skill`, `compare_videos`, `research_topic`, `channel_deep_dive`,
312
- `clip_from_quote` (find a phrase, then cut the clip around it), and — local
313
- only — `review_library`, `create_brain` (build a channel's corpus, then write
314
- its profile from it) and `ask_creator` (answer a question strictly from a
315
- brain, with citations).
385
+ `clip_from_quote` (find a phrase, then cut the clip around it),
386
+ `study_thumbnails` (save a channel's thumbnails, look at a sample, describe
387
+ what recurs), and — local only — `review_library`, `create_brain` (build a
388
+ channel's corpus, then write its profile from it) and `ask_creator` (answer a
389
+ question strictly from a brain, with citations).
316
390
 
317
391
  ## Resources
318
392
 
319
393
  - `youtube://transcript/{videoId}` — a timestamped transcript, fetched and cached on first read
320
394
  - `youtube://library/{videoId}/{summary|skill}` — a saved note (local only, and enumerable)
321
395
  - `youtube://brain/{channelId}/{manifest|profile}` — what a channel brain covers, or the profile written from it (local only, and enumerable)
396
+ - `youtube://thumbnails/{channelId}/manifest` — what `fetch_channel_thumbnails` saved for a channel (local only, and enumerable)
322
397
 
323
398
  ## Error codes
324
399
 
@@ -334,25 +409,51 @@ them, each prefixed with a code and followed by a next step.
334
409
  | `RATE_LIMITED`, `TIMEOUT` | Transient; retried automatically with backoff before surfacing |
335
410
  | `YTDLP_MISSING`, `FFMPEG_MISSING`, `YTDLP_FAILED` | A tooling problem; the message says how to fix it |
336
411
  | `INVALID_INPUT` | A bad argument, caught before any network call |
412
+ | `BOT_CHECK` | YouTube asked this address to sign in; set the cookie settings below |
413
+ | `FETCH_FAILED` | An image could not be downloaded from YouTube's image hosts |
337
414
  | `CANCELLED` | The client cancelled the request |
338
415
 
339
416
  ## Environment variables
340
417
 
341
418
  All optional.
342
419
 
343
- | Variable | Default | Purpose |
344
- | ------------------------------- | --------- | -------------------------------------------------------------------------------------------------------- |
345
- | `MCP_AUTH_TOKEN` | unset | Require this bearer token on the HTTP transport. **Set this if you expose the server beyond localhost.** |
346
- | `MCP_ALLOWED_HOSTS` | unset | Comma-separated Host allowlist; enables DNS-rebinding protection |
347
- | `MCP_ALLOWED_ORIGINS` | unset | Comma-separated Origin allowlist |
348
- | `MCP_BIND_HOST` | `0.0.0.0` | Interface to bind |
349
- | `PORT` | `3000` | HTTP port. The Docker image sets `10000`; Render and similar platforms inject their own |
350
- | `MCP_RATE_LIMIT` | `60` | Requests per window, per client |
351
- | `MCP_RATE_WINDOW_MS` | `60000` | Rate-limit window |
352
- | `MCP_SESSION_IDLE_MS` | `1800000` | Close HTTP sessions idle this long |
353
- | `MCP_MAX_SESSIONS` | `1000` | Reject new sessions past this many |
354
- | `YOUTUBE_MCP_MAX_CONCURRENCY` | `3` | Concurrent yt-dlp processes |
355
- | `YOUTUBE_MCP_TRANSCRIPT_TTL_MS` | 30 days | Transcript cache lifetime |
420
+ | Variable | Default | Purpose |
421
+ | ---------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
422
+ | `MCP_AUTH_TOKEN` | unset | Require this bearer token on the HTTP transport. **Set this if you expose the server beyond localhost.** |
423
+ | `MCP_ALLOWED_HOSTS` | unset | Comma-separated Host allowlist; enables DNS-rebinding protection |
424
+ | `MCP_ALLOWED_ORIGINS` | unset | Comma-separated Origin allowlist |
425
+ | `MCP_BIND_HOST` | `127.0.0.1` | Interface to bind. The Docker image sets `0.0.0.0`. A non-loopback bind refuses to start without `MCP_AUTH_TOKEN` unless `MCP_ALLOW_UNAUTHENTICATED=true` |
426
+ | `MCP_ALLOW_UNAUTHENTICATED` | `false` | Explicit consent to listen on a network interface with no token |
427
+ | `MCP_TRUST_PROXY` | `false` | `true`, `false`, or the number of reverse-proxy hops whose `X-Forwarded-*` headers to believe. Off, forwarded headers are ignored for rate limiting and URLs |
428
+ | `MCP_PUBLIC_URL` | unset | The URL clients reach the server at; used in the OAuth metadata it publishes instead of the request's Host |
429
+ | `MCP_MAX_BODY_BYTES` | `1048576` | Largest request body accepted |
430
+ | `MCP_MODE` | unset | `http` selects the HTTP transport when no `--http` flag is given |
431
+ | `PORT` | `3000` | HTTP port. The Docker image sets `10000`; Render and similar platforms inject their own |
432
+ | `MCP_RATE_LIMIT` | `60` | Requests per window, per client |
433
+ | `MCP_RATE_WINDOW_MS` | `60000` | Rate-limit window |
434
+ | `MCP_SESSION_IDLE_MS` | `1800000` | Close HTTP sessions idle this long |
435
+ | `MCP_MAX_SESSIONS` | `1000` | Reject new sessions past this many |
436
+ | `YOUTUBE_MCP_MAX_CONCURRENCY` | `3` | Concurrent yt-dlp processes |
437
+ | `YOUTUBE_MCP_TRANSCRIPT_TTL_MS` | 30 days | Transcript cache lifetime |
438
+ | `YOUTUBE_MCP_STATS_TTL_MS` | 1 day | Per-video stats cache lifetime |
439
+ | `YOUTUBE_MCP_COOKIES_FROM_BROWSER` | unset | Browser whose cookies yt-dlp should read: `brave`, `chrome`, `chromium`, `edge`, `firefox`, `opera`, `safari`, `vivaldi` or `whale`. See Signed-in content |
440
+ | `YOUTUBE_MCP_COOKIES_FILE` | unset | A Netscape-format cookies file inside your home directory, as an alternative to a browser |
441
+ | `YOUTUBE_MCP_PROXY` | unset | An `http`, `https`, `socks4` or `socks5` proxy URL for yt-dlp |
442
+ | `YOUTUBE_MCP_SLEEP_REQUESTS_S` | unset | Seconds yt-dlp sleeps between its own requests, to stay under YouTube's limits |
443
+
444
+ ### Signed-in content
445
+
446
+ Some videos need a signed-in session — age-restricted, members-only, private
447
+ ones you have access to — and YouTube sometimes asks an address to prove it is
448
+ not a bot before serving any video at all. Those failures arrive as
449
+ `LOGIN_REQUIRED`, `AGE_GATED` or `BOT_CHECK`, and each names the fix: set
450
+ `YOUTUBE_MCP_COOKIES_FROM_BROWSER` to a browser you are signed in with, or
451
+ `YOUTUBE_MCP_COOKIES_FILE` to a cookies file, and restart the server.
452
+
453
+ Cookies are your account. Use them in local (stdio) mode, keep a cookies file
454
+ readable by you alone, and know that content read this way may be personal.
455
+ The server validates both settings at boot, never logs the file's path or
456
+ contents, and never surfaces yt-dlp's output.
356
457
 
357
458
  ## Library Storage
358
459
 
@@ -372,6 +473,13 @@ Content is stored in `~/.youtube-knowledge/`:
372
473
  │ ├── manifest.json # What the brain covers, and where a build stopped
373
474
  │ ├── chunks.json # The timestamped passages
374
475
  │ └── profile.md # The written account, if one was saved
476
+ ├── thumbnails/ # Saved channel thumbnails
477
+ │ └── {channel_id}/
478
+ │ ├── manifest.json # Every image, its decoded size, where it is
479
+ │ ├── channel/ # avatar.* and banner.*
480
+ │ ├── videos/ # {video_id}.jpg (or .png / .webp)
481
+ │ ├── shorts/
482
+ │ └── streams/
375
483
  ├── downloads/ # Full downloads
376
484
  ├── clips/ # Extracted clips
377
485
  ├── frames/ # Captured stills
@@ -469,6 +577,11 @@ client over an in-memory transport, exercises the library against a real
469
577
  temporary filesystem, and snapshots the tool manifest so any change to the
470
578
  public surface shows up as a reviewable diff.
471
579
 
580
+ An end-to-end lane (`E2E=1 npm run test:e2e`) drives the built server through
581
+ real MCP clients over both transports against real yt-dlp and the real network,
582
+ in an isolated temporary home. It runs weekly in CI and on every release before
583
+ anything is published; see CONTRIBUTING.md.
584
+
472
585
  ## Development
473
586
 
474
587
  ```bash
@@ -480,7 +593,7 @@ npm run start:http # Run server (HTTP)
480
593
  npm run validate # Typecheck + lint + format check + test
481
594
  ```
482
595
 
483
- CI runs the same gate on Node 22 and 24 for every push and pull request,
596
+ CI runs the same gate on Node 22.22, 22 and 24 for every push and pull request,
484
597
  then boots the built server as a real MCP client to verify the manifest.
485
598
 
486
599
  ## Contributing
@@ -490,9 +603,11 @@ project layout, coding standards, and how to add a tool.
490
603
 
491
604
  ## Security
492
605
 
493
- The HTTP transport is unauthenticated unless you set `MCP_AUTH_TOKEN`. See
494
- [SECURITY.md](SECURITY.md) before exposing it beyond localhost, and to report a
495
- vulnerability.
606
+ The HTTP transport binds loopback and refuses to listen on a network interface
607
+ without `MCP_AUTH_TOKEN`. Caller-supplied URLs must be on a YouTube host before
608
+ anything is handed to yt-dlp, every yt-dlp target follows a `--` terminator, and
609
+ ids are validated before they become paths. See [SECURITY.md](SECURITY.md) for
610
+ the deployment checklist and how to report a vulnerability.
496
611
 
497
612
  ## License
498
613
 
@@ -226,6 +226,8 @@ export declare const brainManifestSchema: z.ZodObject<{
226
226
  subscriberCount: z.ZodNumber;
227
227
  channelUrl: z.ZodString;
228
228
  description: z.ZodString;
229
+ avatarUrl: z.ZodOptional<z.ZodString>;
230
+ bannerUrl: z.ZodOptional<z.ZodString>;
229
231
  }, "strip", z.ZodTypeAny, {
230
232
  description: string;
231
233
  name: string;
@@ -233,6 +235,8 @@ export declare const brainManifestSchema: z.ZodObject<{
233
235
  handle: string;
234
236
  subscriberCount: number;
235
237
  channelUrl: string;
238
+ avatarUrl?: string | undefined;
239
+ bannerUrl?: string | undefined;
236
240
  }, {
237
241
  description: string;
238
242
  name: string;
@@ -240,6 +244,8 @@ export declare const brainManifestSchema: z.ZodObject<{
240
244
  handle: string;
241
245
  subscriberCount: number;
242
246
  channelUrl: string;
247
+ avatarUrl?: string | undefined;
248
+ bannerUrl?: string | undefined;
243
249
  }>;
244
250
  /**
245
251
  * A brain is built from one caption language. Defaulted rather than required
@@ -345,6 +351,8 @@ export declare const brainManifestSchema: z.ZodObject<{
345
351
  handle: string;
346
352
  subscriberCount: number;
347
353
  channelUrl: string;
354
+ avatarUrl?: string | undefined;
355
+ bannerUrl?: string | undefined;
348
356
  };
349
357
  version: number;
350
358
  videos: Record<string, {
@@ -391,6 +399,8 @@ export declare const brainManifestSchema: z.ZodObject<{
391
399
  handle: string;
392
400
  subscriberCount: number;
393
401
  channelUrl: string;
402
+ avatarUrl?: string | undefined;
403
+ bannerUrl?: string | undefined;
394
404
  };
395
405
  version: number;
396
406
  videos: Record<string, unknown>;
@@ -489,14 +499,4 @@ export declare const brainSummarySchema: z.ZodObject<{
489
499
  updatedAt: string;
490
500
  hasProfile: boolean;
491
501
  }>;
492
- export declare const brainLockSchema: z.ZodObject<{
493
- pid: z.ZodNumber;
494
- startedAt: z.ZodString;
495
- }, "strip", z.ZodTypeAny, {
496
- pid: number;
497
- startedAt: string;
498
- }, {
499
- pid: number;
500
- startedAt: string;
501
- }>;
502
502
  //# sourceMappingURL=brain-schemas.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"brain-schemas.d.ts","sourceRoot":"","sources":["../src/brain-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;GAMG;AAEH,qDAAqD;AACrD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAchC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,uDAAuD;AACvD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;EAO3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG/B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;EAI5B,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;EAGpC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,4BAA4B;AAC5B,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;IAG9B;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMH,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,8BAA8B;AAC9B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAY7B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;EAG1B,CAAC"}
1
+ {"version":3,"file":"brain-schemas.d.ts","sourceRoot":"","sources":["../src/brain-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;GAMG;AAEH,qDAAqD;AACrD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAchC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,uDAAuD;AACvD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;EAO3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG/B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;EAI5B,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;EAGpC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,4BAA4B;AAC5B,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAG9B;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMH,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,8BAA8B;AAC9B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAY7B,CAAC"}
@@ -106,8 +106,4 @@ export const brainSummarySchema = z.object({
106
106
  indexedCount: z.number().int(),
107
107
  chunkCount: z.number().int(),
108
108
  });
109
- export const brainLockSchema = z.object({
110
- pid: z.number().int(),
111
- startedAt: z.string(),
112
- });
113
109
  //# sourceMappingURL=brain-schemas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"brain-schemas.js","sourceRoot":"","sources":["../src/brain-schemas.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"brain-schemas.js","sourceRoot":"","sources":["../src/brain-schemas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The shapes a completeness receipt is made of.
4
+ *
5
+ * This server exists to extract everything YouTube will serve, and the failure
6
+ * mode that matters is not "we got 3%" — it is "we got 3% and reported 100%".
7
+ * A receipt travels with every extraction result so a consumer can never
8
+ * describe a sample as a full history by accident.
9
+ *
10
+ * The invariants that make `complete` mean something live in
11
+ * `utils/coverage.ts`, which is the only place allowed to build one.
12
+ */
13
+ export declare const harvestScopeSchema: z.ZodEnum<["channel-catalog", "video-details", "video-comments"]>;
14
+ export type HarvestScope = z.infer<typeof harvestScopeSchema>;
15
+ export declare const completenessReasonSchema: z.ZodEnum<["COMPLETE", "CAP_REACHED", "BUDGET_SPENT", "RATE_LIMITED", "CANCELLED", "TIMEOUT", "SOURCE_REFUSED", "COMMENTS_DISABLED", "SOURCE_SILENT", "PARTIAL_ERROR", "NOT_ATTEMPTED"]>;
16
+ export type CompletenessReason = z.infer<typeof completenessReasonSchema>;
17
+ /**
18
+ * Where a total came from.
19
+ *
20
+ * `source-exhausted` is the one value that is not a number YouTube reported:
21
+ * it means the listing ended on its own, which is its own kind of proof.
22
+ */
23
+ export declare const expectedSourceSchema: z.ZodEnum<["youtube:comment_count", "youtube:playlist_count", "source-exhausted"]>;
24
+ export type ExpectedSource = z.infer<typeof expectedSourceSchema>;
25
+ export declare const coverageSchema: z.ZodObject<{
26
+ scope: z.ZodEnum<["channel-catalog", "video-details", "video-comments"]>;
27
+ targetId: z.ZodString;
28
+ complete: z.ZodBoolean;
29
+ reason: z.ZodEnum<["COMPLETE", "CAP_REACHED", "BUDGET_SPENT", "RATE_LIMITED", "CANCELLED", "TIMEOUT", "SOURCE_REFUSED", "COMMENTS_DISABLED", "SOURCE_SILENT", "PARTIAL_ERROR", "NOT_ATTEMPTED"]>;
30
+ have: z.ZodNumber;
31
+ expected: z.ZodOptional<z.ZodNumber>;
32
+ expectedSource: z.ZodOptional<z.ZodEnum<["youtube:comment_count", "youtube:playlist_count", "source-exhausted"]>>;
33
+ ranToExhaustion: z.ZodOptional<z.ZodBoolean>;
34
+ source: z.ZodString;
35
+ harvestedAt: z.ZodString;
36
+ staleAfter: z.ZodOptional<z.ZodString>;
37
+ limitApplied: z.ZodOptional<z.ZodNumber>;
38
+ sortApplied: z.ZodOptional<z.ZodEnum<["top", "new"]>>;
39
+ resumeToken: z.ZodOptional<z.ZodString>;
40
+ note: z.ZodOptional<z.ZodString>;
41
+ }, "strip", z.ZodTypeAny, {
42
+ have: number;
43
+ scope: "channel-catalog" | "video-details" | "video-comments";
44
+ targetId: string;
45
+ complete: boolean;
46
+ reason: "RATE_LIMITED" | "TIMEOUT" | "CANCELLED" | "COMPLETE" | "CAP_REACHED" | "BUDGET_SPENT" | "SOURCE_REFUSED" | "COMMENTS_DISABLED" | "SOURCE_SILENT" | "PARTIAL_ERROR" | "NOT_ATTEMPTED";
47
+ source: string;
48
+ harvestedAt: string;
49
+ expected?: number | undefined;
50
+ note?: string | undefined;
51
+ ranToExhaustion?: boolean | undefined;
52
+ expectedSource?: "youtube:playlist_count" | "source-exhausted" | "youtube:comment_count" | undefined;
53
+ staleAfter?: string | undefined;
54
+ limitApplied?: number | undefined;
55
+ sortApplied?: "top" | "new" | undefined;
56
+ resumeToken?: string | undefined;
57
+ }, {
58
+ have: number;
59
+ scope: "channel-catalog" | "video-details" | "video-comments";
60
+ targetId: string;
61
+ complete: boolean;
62
+ reason: "RATE_LIMITED" | "TIMEOUT" | "CANCELLED" | "COMPLETE" | "CAP_REACHED" | "BUDGET_SPENT" | "SOURCE_REFUSED" | "COMMENTS_DISABLED" | "SOURCE_SILENT" | "PARTIAL_ERROR" | "NOT_ATTEMPTED";
63
+ source: string;
64
+ harvestedAt: string;
65
+ expected?: number | undefined;
66
+ note?: string | undefined;
67
+ ranToExhaustion?: boolean | undefined;
68
+ expectedSource?: "youtube:playlist_count" | "source-exhausted" | "youtube:comment_count" | undefined;
69
+ staleAfter?: string | undefined;
70
+ limitApplied?: number | undefined;
71
+ sortApplied?: "top" | "new" | undefined;
72
+ resumeToken?: string | undefined;
73
+ }>;
74
+ export type Coverage = z.infer<typeof coverageSchema>;
75
+ //# sourceMappingURL=harvest-schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harvest-schemas.d.ts","sourceRoot":"","sources":["../src/harvest-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,kBAAkB,mEAO7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,wBAAwB,0LAqBnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,oFAI/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4EzB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
@@ -0,0 +1,106 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The shapes a completeness receipt is made of.
4
+ *
5
+ * This server exists to extract everything YouTube will serve, and the failure
6
+ * mode that matters is not "we got 3%" — it is "we got 3% and reported 100%".
7
+ * A receipt travels with every extraction result so a consumer can never
8
+ * describe a sample as a full history by accident.
9
+ *
10
+ * The invariants that make `complete` mean something live in
11
+ * `utils/coverage.ts`, which is the only place allowed to build one.
12
+ */
13
+ export const harvestScopeSchema = z.enum([
14
+ /** Which videos a channel has. */
15
+ 'channel-catalog',
16
+ /** Full per-video metadata snapshots. */
17
+ 'video-details',
18
+ /** Comments and replies for one video. */
19
+ 'video-comments',
20
+ ]);
21
+ export const completenessReasonSchema = z.enum([
22
+ /** Provably everything: the source stated a total and we hold it, or the source ran out. */
23
+ 'COMPLETE',
24
+ /** The caller's own limit stopped it. */
25
+ 'CAP_REACHED',
26
+ /** The wall-clock budget for this call ran out. */
27
+ 'BUDGET_SPENT',
28
+ /** YouTube throttled this client. */
29
+ 'RATE_LIMITED',
30
+ /** The MCP client aborted the request. */
31
+ 'CANCELLED',
32
+ 'TIMEOUT',
33
+ /** BOT_CHECK, LOGIN_REQUIRED, PRIVATE, AGE_GATED or MEMBERS_ONLY. */
34
+ 'SOURCE_REFUSED',
35
+ /** Comments are turned off for this video, so zero is the true total. */
36
+ 'COMMENTS_DISABLED',
37
+ /** The source states no total, so exhaustion is unprovable. */
38
+ 'SOURCE_SILENT',
39
+ /** Some items failed; each cost only itself. */
40
+ 'PARTIAL_ERROR',
41
+ 'NOT_ATTEMPTED',
42
+ ]);
43
+ /**
44
+ * Where a total came from.
45
+ *
46
+ * `source-exhausted` is the one value that is not a number YouTube reported:
47
+ * it means the listing ended on its own, which is its own kind of proof.
48
+ */
49
+ export const expectedSourceSchema = z.enum([
50
+ 'youtube:comment_count',
51
+ 'youtube:playlist_count',
52
+ 'source-exhausted',
53
+ ]);
54
+ export const coverageSchema = z.object({
55
+ scope: harvestScopeSchema,
56
+ targetId: z.string().describe('Channel id for a catalog, video id for details or comments'),
57
+ complete: z
58
+ .boolean()
59
+ .describe('True ONLY when this server can prove it holds every item in scope. False means the ' +
60
+ 'data is a SAMPLE. A consumer must not describe it as a full history while this is false.'),
61
+ reason: completenessReasonSchema.describe('Why complete is what it is. Always present, including when complete is true.'),
62
+ have: z
63
+ .number()
64
+ .int()
65
+ .nonnegative()
66
+ .describe('Items this store actually holds for this target, counted from the store.'),
67
+ expected: z
68
+ .number()
69
+ .int()
70
+ .nonnegative()
71
+ .optional()
72
+ .describe('The total the SOURCE states. Absent when the source states none — never inferred, ' +
73
+ 'never copied from have. Absent with complete:false means "unknown how much is missing".'),
74
+ expectedSource: expectedSourceSchema
75
+ .optional()
76
+ .describe('Where expected came from. Absent if and only if expected is absent.'),
77
+ ranToExhaustion: z
78
+ .boolean()
79
+ .optional()
80
+ .describe("Whether the source's own iteration finished rather than being cut off. Necessary for " +
81
+ 'completeness but not sufficient: a cap also ends iteration.'),
82
+ source: z.string().describe('The exact extraction path, including tool version and flags.'),
83
+ harvestedAt: z.string().describe('ISO-8601 of the last item written for this target'),
84
+ staleAfter: z
85
+ .string()
86
+ .optional()
87
+ .describe('ISO-8601 after which this should be re-checked. Comments accrue, so a complete ' +
88
+ 'receipt goes stale without becoming wrong.'),
89
+ limitApplied: z
90
+ .number()
91
+ .int()
92
+ .positive()
93
+ .optional()
94
+ .describe('The cap the caller asked for. Present if and only if one was applied.'),
95
+ sortApplied: z
96
+ .enum(['top', 'new'])
97
+ .optional()
98
+ .describe('Ordering the source applied. A capped "top" harvest is a BIASED PREFIX of a stream, ' +
99
+ 'not a random sample.'),
100
+ resumeToken: z
101
+ .string()
102
+ .optional()
103
+ .describe('Opaque. Pass to the same tool to continue. Absent if and only if complete.'),
104
+ note: z.string().optional(),
105
+ });
106
+ //# sourceMappingURL=harvest-schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harvest-schemas.js","sourceRoot":"","sources":["../src/harvest-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC;IACvC,kCAAkC;IAClC,iBAAiB;IACjB,yCAAyC;IACzC,eAAe;IACf,0CAA0C;IAC1C,gBAAgB;CACjB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,IAAI,CAAC;IAC7C,4FAA4F;IAC5F,UAAU;IACV,yCAAyC;IACzC,aAAa;IACb,mDAAmD;IACnD,cAAc;IACd,qCAAqC;IACrC,cAAc;IACd,0CAA0C;IAC1C,WAAW;IACX,SAAS;IACT,qEAAqE;IACrE,gBAAgB;IAChB,yEAAyE;IACzE,mBAAmB;IACnB,+DAA+D;IAC/D,eAAe;IACf,gDAAgD;IAChD,eAAe;IACf,eAAe;CAChB,CAAC,CAAC;AAGH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC;IACzC,uBAAuB;IACvB,wBAAwB;IACxB,kBAAkB;CACnB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,KAAK,EAAE,kBAAkB;IACzB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4DAA4D,CAAC;IAE3F,QAAQ,EAAE,CAAC;SACR,OAAO,EAAE;SACT,QAAQ,CACP,qFAAqF;QACnF,0FAA0F,CAC7F;IAEH,MAAM,EAAE,wBAAwB,CAAC,QAAQ,CACvC,8EAA8E,CAC/E;IAED,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,WAAW,EAAE;SACb,QAAQ,CAAC,0EAA0E,CAAC;IAEvF,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,GAAG,EAAE;SACL,WAAW,EAAE;SACb,QAAQ,EAAE;SACV,QAAQ,CACP,oFAAoF;QAClF,yFAAyF,CAC5F;IAEH,cAAc,EAAE,oBAAoB;SACjC,QAAQ,EAAE;SACV,QAAQ,CAAC,qEAAqE,CAAC;IAElF,eAAe,EAAE,CAAC;SACf,OAAO,EAAE;SACT,QAAQ,EAAE;SACV,QAAQ,CACP,uFAAuF;QACrF,6DAA6D,CAChE;IAEH,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8DAA8D,CAAC;IAE3F,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;IAErF,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,iFAAiF;QAC/E,4CAA4C,CAC/C;IAEH,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,EAAE;SACV,QAAQ,CAAC,uEAAuE,CAAC;IAEpF,WAAW,EAAE,CAAC;SACX,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;SACpB,QAAQ,EAAE;SACV,QAAQ,CACP,sFAAsF;QACpF,sBAAsB,CACzB;IAEH,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,4EAA4E,CAAC;IAEzF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { Express } from 'express';
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { type HttpConfig } from './config.js';
4
+ import type { RateLimiter } from './rate-limiter.js';
5
+ import type { SessionStore } from './sessions.js';
6
+ /** The Express application: routes, authentication and rate limiting. */
7
+ export interface AppDependencies {
8
+ createServer: (mode: 'http') => McpServer;
9
+ sessions: SessionStore;
10
+ limiter: RateLimiter;
11
+ /** The bound port, known only once listening; read lazily for that reason. */
12
+ port: () => number;
13
+ startedAt: number;
14
+ }
15
+ export declare function buildApp(config: HttpConfig, deps: AppDependencies): Express;
16
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/http/app.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAmC,MAAM,SAAS,CAAC;AAOxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYzE,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,yEAAyE;AAEzE,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IAC1C,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAqK3E"}