youtube-knowledge-mcp 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -34
- package/dist/brain-schemas.d.ts +10 -10
- package/dist/brain-schemas.d.ts.map +1 -1
- package/dist/brain-schemas.js +0 -4
- package/dist/brain-schemas.js.map +1 -1
- package/dist/http/app.d.ts +16 -0
- package/dist/http/app.d.ts.map +1 -0
- package/dist/http/app.js +176 -0
- package/dist/http/app.js.map +1 -0
- package/dist/http/auth.d.ts +38 -0
- package/dist/http/auth.d.ts.map +1 -0
- package/dist/http/auth.js +79 -0
- package/dist/http/auth.js.map +1 -0
- package/dist/http/config.d.ts +41 -0
- package/dist/http/config.d.ts.map +1 -0
- package/dist/http/config.js +65 -0
- package/dist/http/config.js.map +1 -0
- package/dist/http/rate-limiter.d.ts +24 -0
- package/dist/http/rate-limiter.d.ts.map +1 -0
- package/dist/http/rate-limiter.js +55 -0
- package/dist/http/rate-limiter.js.map +1 -0
- package/dist/http/sessions.d.ts +34 -0
- package/dist/http/sessions.d.ts.map +1 -0
- package/dist/http/sessions.js +62 -0
- package/dist/http/sessions.js.map +1 -0
- package/dist/http.d.ts +6 -48
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +35 -252
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -517
- package/dist/index.js.map +1 -1
- package/dist/prompts-brain.d.ts +4 -0
- package/dist/prompts-brain.d.ts.map +1 -0
- package/dist/prompts-brain.js +48 -0
- package/dist/prompts-brain.js.map +1 -0
- package/dist/prompts-research.d.ts +4 -0
- package/dist/prompts-research.d.ts.map +1 -0
- package/dist/prompts-research.js +74 -0
- package/dist/prompts-research.js.map +1 -0
- package/dist/prompts-shared.d.ts +13 -0
- package/dist/prompts-shared.d.ts.map +1 -0
- package/dist/prompts-shared.js +7 -0
- package/dist/prompts-shared.js.map +1 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +5 -100
- package/dist/prompts.js.map +1 -1
- package/dist/registry/brain-tools.d.ts +4 -0
- package/dist/registry/brain-tools.d.ts.map +1 -0
- package/dist/registry/brain-tools.js +104 -0
- package/dist/registry/brain-tools.js.map +1 -0
- package/dist/registry/channel-tools.d.ts +4 -0
- package/dist/registry/channel-tools.d.ts.map +1 -0
- package/dist/registry/channel-tools.js +53 -0
- package/dist/registry/channel-tools.js.map +1 -0
- package/dist/registry/health-tools.d.ts +4 -0
- package/dist/registry/health-tools.d.ts.map +1 -0
- package/dist/registry/health-tools.js +21 -0
- package/dist/registry/health-tools.js.map +1 -0
- package/dist/registry/index.d.ts +8 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +27 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/library-tools.d.ts +4 -0
- package/dist/registry/library-tools.d.ts.map +1 -0
- package/dist/registry/library-tools.js +116 -0
- package/dist/registry/library-tools.js.map +1 -0
- package/dist/registry/media-tools.d.ts +4 -0
- package/dist/registry/media-tools.d.ts.map +1 -0
- package/dist/registry/media-tools.js +100 -0
- package/dist/registry/media-tools.js.map +1 -0
- package/dist/registry/thumbnail-tools.d.ts +7 -0
- package/dist/registry/thumbnail-tools.d.ts.map +1 -0
- package/dist/registry/thumbnail-tools.js +72 -0
- package/dist/registry/thumbnail-tools.js.map +1 -0
- package/dist/registry/transcript-tools.d.ts +4 -0
- package/dist/registry/transcript-tools.d.ts.map +1 -0
- package/dist/registry/transcript-tools.js +68 -0
- package/dist/registry/transcript-tools.js.map +1 -0
- package/dist/registry/types.d.ts +41 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +17 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/registry/video-tools.d.ts +4 -0
- package/dist/registry/video-tools.d.ts.map +1 -0
- package/dist/registry/video-tools.js +101 -0
- package/dist/registry/video-tools.js.map +1 -0
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +13 -10
- package/dist/resources.js.map +1 -1
- package/dist/schemas.d.ts +11 -2
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +5 -0
- package/dist/schemas.js.map +1 -1
- package/dist/thumbnail-resource.d.ts +8 -0
- package/dist/thumbnail-resource.d.ts.map +1 -0
- package/dist/thumbnail-resource.js +38 -0
- package/dist/thumbnail-resource.js.map +1 -0
- package/dist/thumbnail-schemas.d.ts +512 -0
- package/dist/thumbnail-schemas.d.ts.map +1 -0
- package/dist/thumbnail-schemas.js +95 -0
- package/dist/thumbnail-schemas.js.map +1 -0
- package/dist/tools/batch.js +1 -1
- package/dist/tools/batch.js.map +1 -1
- package/dist/tools/build-brain.js +1 -1
- package/dist/tools/build-brain.js.map +1 -1
- package/dist/tools/check-health.d.ts +2 -0
- package/dist/tools/check-health.d.ts.map +1 -1
- package/dist/tools/check-health.js +9 -0
- package/dist/tools/check-health.js.map +1 -1
- package/dist/tools/clips.d.ts +9 -41
- package/dist/tools/clips.d.ts.map +1 -1
- package/dist/tools/clips.js +52 -128
- package/dist/tools/clips.js.map +1 -1
- package/dist/tools/fetch-channel-thumbnails.d.ts +179 -0
- package/dist/tools/fetch-channel-thumbnails.d.ts.map +1 -0
- package/dist/tools/fetch-channel-thumbnails.js +123 -0
- package/dist/tools/fetch-channel-thumbnails.js.map +1 -0
- package/dist/tools/fetch-videos.d.ts +3 -0
- package/dist/tools/fetch-videos.d.ts.map +1 -1
- package/dist/tools/fetch-videos.js +3 -0
- package/dist/tools/fetch-videos.js.map +1 -1
- package/dist/tools/frames-subtitles.d.ts +44 -0
- package/dist/tools/frames-subtitles.d.ts.map +1 -0
- package/dist/tools/frames-subtitles.js +104 -0
- package/dist/tools/frames-subtitles.js.map +1 -0
- package/dist/tools/get-channel-info.d.ts +2 -0
- package/dist/tools/get-channel-info.d.ts.map +1 -1
- package/dist/tools/get-channel-info.js +8 -1
- package/dist/tools/get-channel-info.js.map +1 -1
- package/dist/tools/get-playlist-info.d.ts.map +1 -1
- package/dist/tools/get-playlist-info.js +1 -0
- package/dist/tools/get-playlist-info.js.map +1 -1
- package/dist/tools/get-thumbnail.d.ts +38 -0
- package/dist/tools/get-thumbnail.d.ts.map +1 -0
- package/dist/tools/get-thumbnail.js +152 -0
- package/dist/tools/get-thumbnail.js.map +1 -0
- package/dist/tools/get-transcript.d.ts.map +1 -1
- package/dist/tools/get-transcript.js +2 -14
- package/dist/tools/get-transcript.js.map +1 -1
- package/dist/tools/library.d.ts.map +1 -1
- package/dist/tools/library.js +13 -3
- package/dist/tools/library.js.map +1 -1
- package/dist/tools/list-channel-thumbnails.d.ts +235 -0
- package/dist/tools/list-channel-thumbnails.d.ts.map +1 -0
- package/dist/tools/list-channel-thumbnails.js +79 -0
- package/dist/tools/list-channel-thumbnails.js.map +1 -0
- package/dist/tools/save-to-library.d.ts.map +1 -1
- package/dist/tools/save-to-library.js +5 -1
- package/dist/tools/save-to-library.js.map +1 -1
- package/dist/tools/search-channels.d.ts +6 -0
- package/dist/tools/search-channels.d.ts.map +1 -1
- package/dist/tools/search-transcript.d.ts.map +1 -1
- package/dist/tools/search-transcript.js +6 -13
- package/dist/tools/search-transcript.js.map +1 -1
- package/dist/tools/search-videos.d.ts +3 -0
- package/dist/tools/search-videos.d.ts.map +1 -1
- package/dist/utils/batches.d.ts +15 -0
- package/dist/utils/batches.d.ts.map +1 -0
- package/dist/utils/batches.js +31 -0
- package/dist/utils/batches.js.map +1 -0
- package/dist/utils/brain-build.d.ts.map +1 -1
- package/dist/utils/brain-build.js +1 -12
- package/dist/utils/brain-build.js.map +1 -1
- package/dist/utils/brain-index.d.ts.map +1 -1
- package/dist/utils/brain-index.js +3 -2
- package/dist/utils/brain-index.js.map +1 -1
- package/dist/utils/brain-lock.d.ts +2 -18
- package/dist/utils/brain-lock.d.ts.map +1 -1
- package/dist/utils/brain-lock.js +4 -80
- package/dist/utils/brain-lock.js.map +1 -1
- package/dist/utils/brain-lookup.d.ts +8 -0
- package/dist/utils/brain-lookup.d.ts.map +1 -1
- package/dist/utils/brain-lookup.js +5 -31
- package/dist/utils/brain-lookup.js.map +1 -1
- package/dist/utils/brain-storage.d.ts.map +1 -1
- package/dist/utils/brain-storage.js +5 -5
- package/dist/utils/brain-storage.js.map +1 -1
- package/dist/utils/caption-probe.d.ts +7 -0
- package/dist/utils/caption-probe.d.ts.map +1 -0
- package/dist/utils/caption-probe.js +36 -0
- package/dist/utils/caption-probe.js.map +1 -0
- package/dist/utils/channel-images.d.ts +16 -0
- package/dist/utils/channel-images.d.ts.map +1 -0
- package/dist/utils/channel-images.js +35 -0
- package/dist/utils/channel-images.js.map +1 -0
- package/dist/utils/channel-lookup.d.ts +5 -0
- package/dist/utils/channel-lookup.d.ts.map +1 -0
- package/dist/utils/channel-lookup.js +37 -0
- package/dist/utils/channel-lookup.js.map +1 -0
- package/dist/utils/chapters.d.ts +11 -0
- package/dist/utils/chapters.d.ts.map +1 -0
- package/dist/utils/chapters.js +24 -0
- package/dist/utils/chapters.js.map +1 -0
- package/dist/utils/clips.d.ts.map +1 -1
- package/dist/utils/clips.js +18 -26
- package/dist/utils/clips.js.map +1 -1
- package/dist/utils/env.d.ts +26 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +72 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/errors.d.ts +1 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +20 -2
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/file-lock.d.ts +34 -0
- package/dist/utils/file-lock.d.ts.map +1 -0
- package/dist/utils/file-lock.js +91 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/dist/utils/flat-listing.d.ts +89 -0
- package/dist/utils/flat-listing.d.ts.map +1 -0
- package/dist/utils/flat-listing.js +68 -0
- package/dist/utils/flat-listing.js.map +1 -0
- package/dist/utils/format.d.ts +1 -3
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +1 -5
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/guard.d.ts +21 -0
- package/dist/utils/guard.d.ts.map +1 -0
- package/dist/utils/guard.js +105 -0
- package/dist/utils/guard.js.map +1 -0
- package/dist/utils/image-dimensions.d.ts +19 -0
- package/dist/utils/image-dimensions.d.ts.map +1 -0
- package/dist/utils/image-dimensions.js +93 -0
- package/dist/utils/image-dimensions.js.map +1 -0
- package/dist/utils/image-fetch.d.ts +31 -0
- package/dist/utils/image-fetch.d.ts.map +1 -0
- package/dist/utils/image-fetch.js +141 -0
- package/dist/utils/image-fetch.js.map +1 -0
- package/dist/utils/json-file.d.ts +14 -5
- package/dist/utils/json-file.d.ts.map +1 -1
- package/dist/utils/json-file.js +24 -9
- package/dist/utils/json-file.js.map +1 -1
- package/dist/utils/paths.d.ts +10 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +14 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/pattern.d.ts +30 -0
- package/dist/utils/pattern.d.ts.map +1 -0
- package/dist/utils/pattern.js +130 -0
- package/dist/utils/pattern.js.map +1 -0
- package/dist/utils/search-index.d.ts.map +1 -1
- package/dist/utils/search-index.js +9 -4
- package/dist/utils/search-index.js.map +1 -1
- package/dist/utils/storage.d.ts.map +1 -1
- package/dist/utils/storage.js +14 -8
- package/dist/utils/storage.js.map +1 -1
- package/dist/utils/subtitles.d.ts +7 -0
- package/dist/utils/subtitles.d.ts.map +1 -0
- package/dist/utils/subtitles.js +30 -0
- package/dist/utils/subtitles.js.map +1 -0
- package/dist/utils/thumbnail-entry.d.ts +19 -0
- package/dist/utils/thumbnail-entry.d.ts.map +1 -0
- package/dist/utils/thumbnail-entry.js +119 -0
- package/dist/utils/thumbnail-entry.js.map +1 -0
- package/dist/utils/thumbnail-fetch.d.ts +37 -0
- package/dist/utils/thumbnail-fetch.d.ts.map +1 -0
- package/dist/utils/thumbnail-fetch.js +89 -0
- package/dist/utils/thumbnail-fetch.js.map +1 -0
- package/dist/utils/thumbnail-ladder.d.ts +44 -0
- package/dist/utils/thumbnail-ladder.d.ts.map +1 -0
- package/dist/utils/thumbnail-ladder.js +68 -0
- package/dist/utils/thumbnail-ladder.js.map +1 -0
- package/dist/utils/thumbnail-listing.d.ts +16 -0
- package/dist/utils/thumbnail-listing.d.ts.map +1 -0
- package/dist/utils/thumbnail-listing.js +26 -0
- package/dist/utils/thumbnail-listing.js.map +1 -0
- package/dist/utils/thumbnail-paths.d.ts +20 -0
- package/dist/utils/thumbnail-paths.d.ts.map +1 -0
- package/dist/utils/thumbnail-paths.js +35 -0
- package/dist/utils/thumbnail-paths.js.map +1 -0
- package/dist/utils/thumbnail-store.d.ts +19 -0
- package/dist/utils/thumbnail-store.d.ts.map +1 -0
- package/dist/utils/thumbnail-store.js +125 -0
- package/dist/utils/thumbnail-store.js.map +1 -0
- package/dist/utils/transcript-cache.d.ts +20 -0
- package/dist/utils/transcript-cache.d.ts.map +1 -0
- package/dist/utils/transcript-cache.js +127 -0
- package/dist/utils/transcript-cache.js.map +1 -0
- package/dist/utils/transcript-search.d.ts +20 -0
- package/dist/utils/transcript-search.d.ts.map +1 -0
- package/dist/utils/transcript-search.js +35 -0
- package/dist/utils/transcript-search.js.map +1 -0
- package/dist/utils/transcript.d.ts +10 -31
- package/dist/utils/transcript.d.ts.map +1 -1
- package/dist/utils/transcript.js +2 -64
- package/dist/utils/transcript.js.map +1 -1
- package/dist/utils/validate-youtube.d.ts +35 -0
- package/dist/utils/validate-youtube.d.ts.map +1 -0
- package/dist/utils/validate-youtube.js +87 -0
- package/dist/utils/validate-youtube.js.map +1 -0
- package/dist/utils/validate.d.ts +11 -4
- package/dist/utils/validate.d.ts.map +1 -1
- package/dist/utils/validate.js +43 -36
- package/dist/utils/validate.js.map +1 -1
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +19 -0
- package/dist/utils/version.js.map +1 -0
- package/dist/utils/youtube-channel.d.ts +44 -0
- package/dist/utils/youtube-channel.d.ts.map +1 -0
- package/dist/utils/youtube-channel.js +61 -0
- package/dist/utils/youtube-channel.js.map +1 -0
- package/dist/utils/youtube-download.d.ts +27 -0
- package/dist/utils/youtube-download.d.ts.map +1 -0
- package/dist/utils/youtube-download.js +118 -0
- package/dist/utils/youtube-download.js.map +1 -0
- package/dist/utils/youtube-schemas.d.ts +394 -0
- package/dist/utils/youtube-schemas.d.ts.map +1 -0
- package/dist/utils/youtube-schemas.js +118 -0
- package/dist/utils/youtube-schemas.js.map +1 -0
- package/dist/utils/youtube-search.d.ts +14 -0
- package/dist/utils/youtube-search.d.ts.map +1 -0
- package/dist/utils/youtube-search.js +39 -0
- package/dist/utils/youtube-search.js.map +1 -0
- package/dist/utils/youtube-url.d.ts +25 -0
- package/dist/utils/youtube-url.d.ts.map +1 -0
- package/dist/utils/youtube-url.js +75 -0
- package/dist/utils/youtube-url.js.map +1 -0
- package/dist/utils/youtube-video.d.ts +47 -0
- package/dist/utils/youtube-video.d.ts.map +1 -0
- package/dist/utils/youtube-video.js +98 -0
- package/dist/utils/youtube-video.js.map +1 -0
- package/dist/utils/youtube.d.ts +10 -122
- package/dist/utils/youtube.d.ts.map +1 -1
- package/dist/utils/youtube.js +11 -486
- package/dist/utils/youtube.js.map +1 -1
- package/dist/utils/ytdlp-env.d.ts +33 -0
- package/dist/utils/ytdlp-env.d.ts.map +1 -0
- package/dist/utils/ytdlp-env.js +121 -0
- package/dist/utils/ytdlp-env.js.map +1 -0
- package/dist/utils/ytdlp-limiter.d.ts +24 -0
- package/dist/utils/ytdlp-limiter.d.ts.map +1 -0
- package/dist/utils/ytdlp-limiter.js +69 -0
- package/dist/utils/ytdlp-limiter.js.map +1 -0
- package/dist/utils/ytdlp-parse.d.ts +13 -0
- package/dist/utils/ytdlp-parse.d.ts.map +1 -0
- package/dist/utils/ytdlp-parse.js +48 -0
- package/dist/utils/ytdlp-parse.js.map +1 -0
- package/dist/utils/ytdlp.d.ts +13 -18
- package/dist/utils/ytdlp.d.ts.map +1 -1
- package/dist/utils/ytdlp.js +21 -110
- package/dist/utils/ytdlp.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { VideoListItem } from './youtube-channel.js';
|
|
3
|
+
/**
|
|
4
|
+
* Reading a flat channel or playlist listing.
|
|
5
|
+
*
|
|
6
|
+
* `--flat-playlist` lists a channel in one request instead of one per video,
|
|
7
|
+
* and each entry carries its thumbnail URLs — which is what makes a channel's
|
|
8
|
+
* thumbnails reachable without extracting every video. Printed as one JSON
|
|
9
|
+
* object per line with exactly these fields, so a title containing a delimiter
|
|
10
|
+
* cannot shift the others, and the parser checks each field's type.
|
|
11
|
+
*
|
|
12
|
+
* Verified against yt-dlp 2026.07.04: a flat entry carries `thumbnails` but no
|
|
13
|
+
* `upload_date`, so the date stays empty here and is resolved per video by the
|
|
14
|
+
* callers that need it.
|
|
15
|
+
*/
|
|
16
|
+
export declare const FLAT_PRINT_TEMPLATE = "%(.{id,title,duration,upload_date,timestamp,view_count,live_status,availability,thumbnails})j";
|
|
17
|
+
/** One entry of a thumbnails array; sizes are absent for some channel images. */
|
|
18
|
+
export declare const listedImageSchema: z.ZodObject<{
|
|
19
|
+
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
20
|
+
url: z.ZodString;
|
|
21
|
+
width: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
22
|
+
height: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
23
|
+
}, "strip", z.ZodTypeAny, {
|
|
24
|
+
url: string;
|
|
25
|
+
id?: string | null | undefined;
|
|
26
|
+
width?: number | null | undefined;
|
|
27
|
+
height?: number | null | undefined;
|
|
28
|
+
}, {
|
|
29
|
+
url: string;
|
|
30
|
+
id?: string | null | undefined;
|
|
31
|
+
width?: number | null | undefined;
|
|
32
|
+
height?: number | null | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
export type ListedImage = z.infer<typeof listedImageSchema>;
|
|
35
|
+
export declare const flatEntrySchema: z.ZodObject<{
|
|
36
|
+
id: z.ZodString;
|
|
37
|
+
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
38
|
+
duration: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
39
|
+
upload_date: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
40
|
+
view_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
41
|
+
live_status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
42
|
+
thumbnails: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
43
|
+
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
44
|
+
url: z.ZodString;
|
|
45
|
+
width: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
46
|
+
height: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
|
48
|
+
url: string;
|
|
49
|
+
id?: string | null | undefined;
|
|
50
|
+
width?: number | null | undefined;
|
|
51
|
+
height?: number | null | undefined;
|
|
52
|
+
}, {
|
|
53
|
+
url: string;
|
|
54
|
+
id?: string | null | undefined;
|
|
55
|
+
width?: number | null | undefined;
|
|
56
|
+
height?: number | null | undefined;
|
|
57
|
+
}>, "many">>>;
|
|
58
|
+
}, "strip", z.ZodTypeAny, {
|
|
59
|
+
id: string;
|
|
60
|
+
title?: string | null | undefined;
|
|
61
|
+
duration?: number | null | undefined;
|
|
62
|
+
upload_date?: string | null | undefined;
|
|
63
|
+
view_count?: number | null | undefined;
|
|
64
|
+
live_status?: string | null | undefined;
|
|
65
|
+
thumbnails?: {
|
|
66
|
+
url: string;
|
|
67
|
+
id?: string | null | undefined;
|
|
68
|
+
width?: number | null | undefined;
|
|
69
|
+
height?: number | null | undefined;
|
|
70
|
+
}[] | null | undefined;
|
|
71
|
+
}, {
|
|
72
|
+
id: string;
|
|
73
|
+
title?: string | null | undefined;
|
|
74
|
+
duration?: number | null | undefined;
|
|
75
|
+
upload_date?: string | null | undefined;
|
|
76
|
+
view_count?: number | null | undefined;
|
|
77
|
+
live_status?: string | null | undefined;
|
|
78
|
+
thumbnails?: {
|
|
79
|
+
url: string;
|
|
80
|
+
id?: string | null | undefined;
|
|
81
|
+
width?: number | null | undefined;
|
|
82
|
+
height?: number | null | undefined;
|
|
83
|
+
}[] | null | undefined;
|
|
84
|
+
}>;
|
|
85
|
+
export type FlatEntry = z.infer<typeof flatEntrySchema>;
|
|
86
|
+
/** The largest image by area, then by width; `undefined` for an empty list. */
|
|
87
|
+
export declare function largestThumbnail(thumbnails: ListedImage[]): ListedImage | undefined;
|
|
88
|
+
export declare function toVideoListItem(entry: FlatEntry): VideoListItem;
|
|
89
|
+
//# sourceMappingURL=flat-listing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flat-listing.d.ts","sourceRoot":"","sources":["../../src/utils/flat-listing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,mBAAmB,kGACiE,CAAC;AAElG,iFAAiF;AACjF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAK5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQ1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,WAAW,GAAG,SAAS,CAQnF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,aAAa,CAqB/D"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatYouTubeDate } from './format.js';
|
|
3
|
+
import { formatDuration, watchUrl } from './youtube-url.js';
|
|
4
|
+
/**
|
|
5
|
+
* Reading a flat channel or playlist listing.
|
|
6
|
+
*
|
|
7
|
+
* `--flat-playlist` lists a channel in one request instead of one per video,
|
|
8
|
+
* and each entry carries its thumbnail URLs — which is what makes a channel's
|
|
9
|
+
* thumbnails reachable without extracting every video. Printed as one JSON
|
|
10
|
+
* object per line with exactly these fields, so a title containing a delimiter
|
|
11
|
+
* cannot shift the others, and the parser checks each field's type.
|
|
12
|
+
*
|
|
13
|
+
* Verified against yt-dlp 2026.07.04: a flat entry carries `thumbnails` but no
|
|
14
|
+
* `upload_date`, so the date stays empty here and is resolved per video by the
|
|
15
|
+
* callers that need it.
|
|
16
|
+
*/
|
|
17
|
+
export const FLAT_PRINT_TEMPLATE = '%(.{id,title,duration,upload_date,timestamp,view_count,live_status,availability,thumbnails})j';
|
|
18
|
+
/** One entry of a thumbnails array; sizes are absent for some channel images. */
|
|
19
|
+
export const listedImageSchema = z.object({
|
|
20
|
+
id: z.string().nullish(),
|
|
21
|
+
url: z.string(),
|
|
22
|
+
width: z.number().nullish(),
|
|
23
|
+
height: z.number().nullish(),
|
|
24
|
+
});
|
|
25
|
+
export const flatEntrySchema = z.object({
|
|
26
|
+
id: z.string(),
|
|
27
|
+
title: z.string().nullish(),
|
|
28
|
+
duration: z.number().nullish(),
|
|
29
|
+
upload_date: z.string().nullish(),
|
|
30
|
+
view_count: z.number().nullish(),
|
|
31
|
+
live_status: z.string().nullish(),
|
|
32
|
+
thumbnails: z.array(listedImageSchema).nullish(),
|
|
33
|
+
});
|
|
34
|
+
/** The largest image by area, then by width; `undefined` for an empty list. */
|
|
35
|
+
export function largestThumbnail(thumbnails) {
|
|
36
|
+
const area = (image) => (image.width ?? 0) * (image.height ?? 0);
|
|
37
|
+
return thumbnails.reduce((best, image) => {
|
|
38
|
+
if (best === undefined)
|
|
39
|
+
return image;
|
|
40
|
+
if (area(image) > area(best))
|
|
41
|
+
return image;
|
|
42
|
+
if (area(image) === area(best) && (image.width ?? 0) > (best.width ?? 0))
|
|
43
|
+
return image;
|
|
44
|
+
return best;
|
|
45
|
+
}, undefined);
|
|
46
|
+
}
|
|
47
|
+
export function toVideoListItem(entry) {
|
|
48
|
+
const duration = entry.duration ?? 0;
|
|
49
|
+
const thumbnails = entry.thumbnails ?? [];
|
|
50
|
+
const largest = largestThumbnail(thumbnails);
|
|
51
|
+
return {
|
|
52
|
+
id: entry.id,
|
|
53
|
+
title: entry.title ?? 'Unknown title',
|
|
54
|
+
duration,
|
|
55
|
+
durationFormatted: formatDuration(duration),
|
|
56
|
+
uploadDate: formatYouTubeDate(entry.upload_date ?? ''),
|
|
57
|
+
url: watchUrl(entry.id),
|
|
58
|
+
...(largest === undefined ? {} : { thumbnailUrl: largest.url }),
|
|
59
|
+
...(thumbnails.length === 0 ? {} : { thumbnails }),
|
|
60
|
+
...(entry.view_count === null || entry.view_count === undefined
|
|
61
|
+
? {}
|
|
62
|
+
: { viewCount: entry.view_count }),
|
|
63
|
+
...(entry.live_status === null || entry.live_status === undefined
|
|
64
|
+
? {}
|
|
65
|
+
: { liveStatus: entry.live_status }),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=flat-listing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flat-listing.js","sourceRoot":"","sources":["../../src/utils/flat-listing.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/format.d.ts
CHANGED
|
@@ -2,13 +2,11 @@ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
|
2
2
|
export declare function formatCount(count: number): string;
|
|
3
3
|
export declare function formatYouTubeDate(yyyymmdd: string): string;
|
|
4
4
|
export declare function formatFilesize(bytes?: number): string;
|
|
5
|
-
export declare function textContent(text: string): CallToolResult;
|
|
6
5
|
/**
|
|
7
6
|
* A tool result carrying both a human-readable rendering and machine-readable
|
|
8
7
|
* data.
|
|
9
8
|
*
|
|
10
|
-
* The text block is
|
|
11
|
-
* nothing a client already displays changes; `structuredContent` is additive and
|
|
9
|
+
* The text block is what a client displays; `structuredContent` is additive and
|
|
12
10
|
* is what the tool's `outputSchema` describes. Every tool that declares an
|
|
13
11
|
* output schema must return this, because the SDK validates the two against
|
|
14
12
|
* each other.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAOD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAOD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAK5F;AAED,wEAAwE;AACxE,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,QAAQ,CAS3E;AAED,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAOzE;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD,cAAc,CAQhB"}
|
package/dist/utils/format.js
CHANGED
|
@@ -25,15 +25,11 @@ export function formatFilesize(bytes) {
|
|
|
25
25
|
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
26
26
|
return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
27
27
|
}
|
|
28
|
-
export function textContent(text) {
|
|
29
|
-
return { content: [{ type: 'text', text }] };
|
|
30
|
-
}
|
|
31
28
|
/**
|
|
32
29
|
* A tool result carrying both a human-readable rendering and machine-readable
|
|
33
30
|
* data.
|
|
34
31
|
*
|
|
35
|
-
* The text block is
|
|
36
|
-
* nothing a client already displays changes; `structuredContent` is additive and
|
|
32
|
+
* The text block is what a client displays; `structuredContent` is additive and
|
|
37
33
|
* is what the tool's `outputSchema` describes. Every tool that declares an
|
|
38
34
|
* output schema must return this, because the SDK validates the two against
|
|
39
35
|
* each other.
|
package/dist/utils/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,KAAK,IAAI,aAAa;QAAE,OAAO,GAAG,gBAAgB,CAAC,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC;IACjF,IAAI,KAAK,IAAI,SAAS;QAAE,OAAO,GAAG,gBAAgB,CAAC,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC;IACzE,IAAI,KAAK,IAAI,KAAK;QAAE,OAAO,GAAG,gBAAgB,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC;IACjE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,gBAAgB,CAAC,CAAS;IACjC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAClE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAChF,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,KAAK,IAAI,aAAa;QAAE,OAAO,GAAG,gBAAgB,CAAC,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC;IACjF,IAAI,KAAK,IAAI,SAAS;QAAE,OAAO,GAAG,gBAAgB,CAAC,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC;IACzE,IAAI,KAAK,IAAI,KAAK;QAAE,OAAO,GAAG,gBAAgB,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC;IACjE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,gBAAgB,CAAC,CAAS;IACjC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAClE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAChF,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,UAAmC;IAC1E,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;QAC1C,iBAAiB,EAAE,UAAU;KAC9B,CAAC;AACJ,CAAC;AAWD,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,KAAa,EAAE,MAAM,GAAG,CAAC;IAC/D,MAAM,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACvC,OAAO;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,OAAO;QACP,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnD,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAC9B,QAAgB,EAChB,IAAY,EACZ,QAAiB;IAEjB,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,GAAG,EAAE,UAAU,QAAQ,EAAE;QACzB,IAAI;QACJ,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;KAChD,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,UAAU,CACxB,IAAY,EACZ,UAAmC,EACnC,IAAuD;IAEvD,OAAO;QACL,OAAO,EAAE;YACP,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE;YAC/B,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC;SACtD;QACD,iBAAiB,EAAE,UAAU;KAC9B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
/** Any tool handler: whatever it takes, it answers with a tool result. */
|
|
3
|
+
type ToolLike = (...args: never[]) => CallToolResult | Promise<CallToolResult>;
|
|
4
|
+
/**
|
|
5
|
+
* Wrap a tool handler, keeping its exact type.
|
|
6
|
+
*
|
|
7
|
+
* A Proxy rather than an arrow function because the SDK's `registerTool` is
|
|
8
|
+
* generic over the input schema and demands a callback of the precise type it
|
|
9
|
+
* derives from it; a new function would need a cast to claim that type, while
|
|
10
|
+
* a Proxy over the original *is* that type.
|
|
11
|
+
*/
|
|
12
|
+
export declare function guarded<F extends ToolLike>(handler: F): F;
|
|
13
|
+
/**
|
|
14
|
+
* The resource counterpart. A read that fails used to propagate whatever was
|
|
15
|
+
* thrown — an `ENOENT` carrying an absolute local path, for instance — as the
|
|
16
|
+
* JSON-RPC error message. It now carries the same `[CODE] message` a tool
|
|
17
|
+
* would show, and nothing else.
|
|
18
|
+
*/
|
|
19
|
+
export declare function guardedResource<A extends unknown[], R>(handler: (...args: A) => Promise<R>): (...args: A) => Promise<R>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/utils/guard.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAoEzE,0EAA0E;AAC1E,KAAK,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAE/E;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,QAAQ,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAazD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,CAAC,EACpD,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAClC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAc5B"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { runWithRequestContext } from './context.js';
|
|
4
|
+
import { asYouTubeError, toToolError } from './errors.js';
|
|
5
|
+
/**
|
|
6
|
+
* Wrapping every handler the server registers, once, so that each call gets a
|
|
7
|
+
* request context and no call can throw out of the server.
|
|
8
|
+
*
|
|
9
|
+
* The MCP request's AbortSignal is published to the request context so an
|
|
10
|
+
* in-flight yt-dlp is killed when the client cancels, and anything thrown is
|
|
11
|
+
* rendered as an actionable message rather than a raw stack trace, a yt-dlp
|
|
12
|
+
* command line, or a local path. Tools report failures inside the result with
|
|
13
|
+
* `isError`; resources have no such envelope, so theirs become protocol errors
|
|
14
|
+
* carrying the same normalised text.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The parts of the SDK's `RequestHandlerExtra` this server uses.
|
|
18
|
+
*
|
|
19
|
+
* `sendNotification` is described with `z.custom` because a function is not
|
|
20
|
+
* something Zod can take apart structurally — the predicate is a real runtime
|
|
21
|
+
* check, and it is what lets the property be called without asserting a type
|
|
22
|
+
* over it.
|
|
23
|
+
*/
|
|
24
|
+
const requestExtraSchema = z.object({
|
|
25
|
+
signal: z.instanceof(AbortSignal).optional(),
|
|
26
|
+
sendNotification: z
|
|
27
|
+
.custom((value) => typeof value === 'function')
|
|
28
|
+
.optional(),
|
|
29
|
+
_meta: z.object({ progressToken: z.union([z.string(), z.number()]).optional() }).optional(),
|
|
30
|
+
});
|
|
31
|
+
/** The request context for a call, read from the SDK extra that arrives last. */
|
|
32
|
+
function contextOf(args) {
|
|
33
|
+
// Handlers declare only their own parameters; the SDK appends
|
|
34
|
+
// RequestHandlerExtra to every call, so it is whatever arrived last.
|
|
35
|
+
const parsed = requestExtraSchema.safeParse(args[args.length - 1]);
|
|
36
|
+
const extra = parsed.success ? parsed.data : undefined;
|
|
37
|
+
return {
|
|
38
|
+
signal: extra?.signal,
|
|
39
|
+
// Progress is only meaningful when the client asked for it by sending a
|
|
40
|
+
// token; without one the notification would be dropped anyway.
|
|
41
|
+
reportProgress: extra?.sendNotification && extra._meta?.progressToken !== undefined
|
|
42
|
+
? (progress, total, message) => {
|
|
43
|
+
void extra.sendNotification?.({
|
|
44
|
+
method: 'notifications/progress',
|
|
45
|
+
params: {
|
|
46
|
+
progressToken: extra._meta?.progressToken,
|
|
47
|
+
progress,
|
|
48
|
+
...(total === undefined ? {} : { total }),
|
|
49
|
+
...(message === undefined ? {} : { message }),
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
: undefined,
|
|
54
|
+
log: extra?.sendNotification
|
|
55
|
+
? (level, message) => {
|
|
56
|
+
void extra.sendNotification?.({
|
|
57
|
+
method: 'notifications/message',
|
|
58
|
+
params: { level, logger: 'youtube-knowledge-mcp', data: message },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
: undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Wrap a tool handler, keeping its exact type.
|
|
66
|
+
*
|
|
67
|
+
* A Proxy rather than an arrow function because the SDK's `registerTool` is
|
|
68
|
+
* generic over the input schema and demands a callback of the precise type it
|
|
69
|
+
* derives from it; a new function would need a cast to claim that type, while
|
|
70
|
+
* a Proxy over the original *is* that type.
|
|
71
|
+
*/
|
|
72
|
+
export function guarded(handler) {
|
|
73
|
+
const run = (args) => runWithRequestContext(contextOf(args), async () => {
|
|
74
|
+
try {
|
|
75
|
+
return await handler(...args);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return toToolError(error);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return new Proxy(handler, {
|
|
82
|
+
apply: (_target, _thisArg, args) => run(args),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The resource counterpart. A read that fails used to propagate whatever was
|
|
87
|
+
* thrown — an `ENOENT` carrying an absolute local path, for instance — as the
|
|
88
|
+
* JSON-RPC error message. It now carries the same `[CODE] message` a tool
|
|
89
|
+
* would show, and nothing else.
|
|
90
|
+
*/
|
|
91
|
+
export function guardedResource(handler) {
|
|
92
|
+
return async (...args) => runWithRequestContext(contextOf(args), async () => {
|
|
93
|
+
try {
|
|
94
|
+
return await handler(...args);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const failure = asYouTubeError(error);
|
|
98
|
+
const code = failure.code === 'INVALID_INPUT' || failure.code === 'NOT_FOUND'
|
|
99
|
+
? ErrorCode.InvalidParams
|
|
100
|
+
: ErrorCode.InternalError;
|
|
101
|
+
throw new McpError(code, `[${failure.code}] ${failure.toToolMessage()}`);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/utils/guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading an image's format and size from its first bytes.
|
|
3
|
+
*
|
|
4
|
+
* The manifest a thumbnail job writes records what was actually saved — a
|
|
5
|
+
* JPEG of 1280 by 720, say — and it must not take that from the URL's name or
|
|
6
|
+
* the server's Content-Type, both of which describe what was asked for rather
|
|
7
|
+
* than what arrived. Three container formats cover what YouTube's image hosts
|
|
8
|
+
* serve; anything else is reported as unknown rather than guessed.
|
|
9
|
+
*/
|
|
10
|
+
export type ImageFormat = 'jpg' | 'png' | 'webp';
|
|
11
|
+
export interface ImageProbe {
|
|
12
|
+
format: ImageFormat;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
mimeType: string;
|
|
16
|
+
}
|
|
17
|
+
/** The format and pixel size of an image, or `undefined` when the bytes are not one this reads. */
|
|
18
|
+
export declare function probeImage(bytes: Uint8Array): ImageProbe | undefined;
|
|
19
|
+
//# sourceMappingURL=image-dimensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-dimensions.d.ts","sourceRoot":"","sources":["../../src/utils/image-dimensions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAEjD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AA4FD,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAKpE"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading an image's format and size from its first bytes.
|
|
3
|
+
*
|
|
4
|
+
* The manifest a thumbnail job writes records what was actually saved — a
|
|
5
|
+
* JPEG of 1280 by 720, say — and it must not take that from the URL's name or
|
|
6
|
+
* the server's Content-Type, both of which describe what was asked for rather
|
|
7
|
+
* than what arrived. Three container formats cover what YouTube's image hosts
|
|
8
|
+
* serve; anything else is reported as unknown rather than guessed.
|
|
9
|
+
*/
|
|
10
|
+
const JPEG_SIGNATURE = [0xff, 0xd8, 0xff];
|
|
11
|
+
const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
12
|
+
function startsWith(bytes, signature, at = 0) {
|
|
13
|
+
return signature.every((byte, index) => bytes[at + index] === byte);
|
|
14
|
+
}
|
|
15
|
+
function ascii(bytes, from, length) {
|
|
16
|
+
return String.fromCharCode(...bytes.subarray(from, from + length));
|
|
17
|
+
}
|
|
18
|
+
/** Every caller checks the length first, so a read here is always in range. */
|
|
19
|
+
function view(bytes) {
|
|
20
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
21
|
+
}
|
|
22
|
+
function be16(bytes, at) {
|
|
23
|
+
return view(bytes).getUint16(at);
|
|
24
|
+
}
|
|
25
|
+
function be32(bytes, at) {
|
|
26
|
+
return view(bytes).getUint32(at);
|
|
27
|
+
}
|
|
28
|
+
function le16(bytes, at) {
|
|
29
|
+
return view(bytes).getUint16(at, true);
|
|
30
|
+
}
|
|
31
|
+
function le24(bytes, at) {
|
|
32
|
+
return le16(bytes, at) | (view(bytes).getUint8(at + 2) << 16);
|
|
33
|
+
}
|
|
34
|
+
/** Start-of-frame markers carry the dimensions; C4, C8 and CC are other things. */
|
|
35
|
+
function isStartOfFrame(marker) {
|
|
36
|
+
return marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;
|
|
37
|
+
}
|
|
38
|
+
function probeJpeg(bytes) {
|
|
39
|
+
let at = 2;
|
|
40
|
+
while (at + 9 < bytes.length) {
|
|
41
|
+
if (bytes[at] !== 0xff)
|
|
42
|
+
return undefined;
|
|
43
|
+
const marker = view(bytes).getUint8(at + 1);
|
|
44
|
+
// Standalone markers have no length field.
|
|
45
|
+
if (marker === 0xd8 || marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {
|
|
46
|
+
at += 2;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// Scan data or end of image: no frame header was found before it.
|
|
50
|
+
if (marker === 0xda || marker === 0xd9)
|
|
51
|
+
return undefined;
|
|
52
|
+
if (isStartOfFrame(marker)) {
|
|
53
|
+
return sized('jpg', 'image/jpeg', be16(bytes, at + 7), be16(bytes, at + 5));
|
|
54
|
+
}
|
|
55
|
+
at += 2 + be16(bytes, at + 2);
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
function probePng(bytes) {
|
|
60
|
+
if (bytes.length < 24 || ascii(bytes, 12, 4) !== 'IHDR')
|
|
61
|
+
return undefined;
|
|
62
|
+
return sized('png', 'image/png', be32(bytes, 16), be32(bytes, 20));
|
|
63
|
+
}
|
|
64
|
+
function probeWebp(bytes) {
|
|
65
|
+
if (bytes.length < 30 || ascii(bytes, 8, 4) !== 'WEBP')
|
|
66
|
+
return undefined;
|
|
67
|
+
switch (ascii(bytes, 12, 4)) {
|
|
68
|
+
case 'VP8 ':
|
|
69
|
+
return sized('webp', 'image/webp', le16(bytes, 26) & 0x3fff, le16(bytes, 28) & 0x3fff);
|
|
70
|
+
case 'VP8L': {
|
|
71
|
+
const bits = view(bytes).getUint32(21, true);
|
|
72
|
+
return sized('webp', 'image/webp', (bits & 0x3fff) + 1, ((bits >>> 14) & 0x3fff) + 1);
|
|
73
|
+
}
|
|
74
|
+
case 'VP8X':
|
|
75
|
+
return sized('webp', 'image/webp', le24(bytes, 24) + 1, le24(bytes, 27) + 1);
|
|
76
|
+
default:
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function sized(format, mimeType, width, height) {
|
|
81
|
+
return width > 0 && height > 0 ? { format, mimeType, width, height } : undefined;
|
|
82
|
+
}
|
|
83
|
+
/** The format and pixel size of an image, or `undefined` when the bytes are not one this reads. */
|
|
84
|
+
export function probeImage(bytes) {
|
|
85
|
+
if (startsWith(bytes, JPEG_SIGNATURE))
|
|
86
|
+
return probeJpeg(bytes);
|
|
87
|
+
if (startsWith(bytes, PNG_SIGNATURE))
|
|
88
|
+
return probePng(bytes);
|
|
89
|
+
if (bytes.length >= 12 && ascii(bytes, 0, 4) === 'RIFF')
|
|
90
|
+
return probeWebp(bytes);
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=image-dimensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-dimensions.js","sourceRoot":"","sources":["../../src/utils/image-dimensions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place this server fetches anything over HTTP itself.
|
|
3
|
+
*
|
|
4
|
+
* Everything else goes through yt-dlp. Thumbnails are the exception because
|
|
5
|
+
* their URLs are known from a single channel listing, and asking yt-dlp to
|
|
6
|
+
* extract each video just to download its image would multiply the requests
|
|
7
|
+
* YouTube sees by the size of the channel. The fetch is deliberately narrow:
|
|
8
|
+
* an allowlist of image hosts, https only, redirects re-checked hop by hop, a
|
|
9
|
+
* timeout, a byte cap enforced while reading, and an image content type.
|
|
10
|
+
*/
|
|
11
|
+
export declare const IMAGE_HOSTS: readonly ["i.ytimg.com", "yt3.googleusercontent.com", "yt3.ggpht.com"];
|
|
12
|
+
export declare const IMAGE_TIMEOUT_MS = 20000;
|
|
13
|
+
/** Cap for images written to disk. */
|
|
14
|
+
export declare const MAX_IMAGE_BYTES: number;
|
|
15
|
+
/** Cap for images returned inline as base64; a 1280x720 JPEG is about 140 KB. */
|
|
16
|
+
export declare const MAX_INLINE_IMAGE_BYTES: number;
|
|
17
|
+
export interface FetchImageOptions {
|
|
18
|
+
maxBytes?: number;
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface FetchedImage {
|
|
22
|
+
bytes: Buffer;
|
|
23
|
+
contentType: string;
|
|
24
|
+
/** The URL the bytes came from, after any redirects. */
|
|
25
|
+
url: string;
|
|
26
|
+
}
|
|
27
|
+
/** Validate an image URL against the allowlist, returning it parsed. */
|
|
28
|
+
export declare function assertImageUrl(input: string): URL;
|
|
29
|
+
/** Fetch one image into memory, or throw a typed error saying why not. */
|
|
30
|
+
export declare function fetchImage(input: string, options?: FetchImageOptions): Promise<FetchedImage>;
|
|
31
|
+
//# sourceMappingURL=image-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-fetch.d.ts","sourceRoot":"","sources":["../../src/utils/image-fetch.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AAEH,eAAO,MAAM,WAAW,wEAAyE,CAAC;AAClG,eAAO,MAAM,gBAAgB,QAAS,CAAC;AACvC,sCAAsC;AACtC,eAAO,MAAM,eAAe,QAAkB,CAAC;AAC/C,iFAAiF;AACjF,eAAO,MAAM,sBAAsB,QAAkB,CAAC;AAKtD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAcjD;AAgBD,0EAA0E;AAC1E,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,YAAY,CAAC,CA0CvB"}
|