@thenavidm/youtube-mcp-cli 2.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.
- package/LICENSE +21 -0
- package/README.md +877 -0
- package/SKILL.md +162 -0
- package/dist/accounts/store.d.ts +41 -0
- package/dist/accounts/store.js +110 -0
- package/dist/accounts/store.js.map +1 -0
- package/dist/auth.d.ts +14 -0
- package/dist/auth.js +206 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +55 -0
- package/dist/cli.js +439 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +42 -0
- package/dist/config.js +130 -0
- package/dist/config.js.map +1 -0
- package/dist/doctor.d.ts +7 -0
- package/dist/doctor.js +78 -0
- package/dist/doctor.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +134 -0
- package/dist/index.js.map +1 -0
- package/dist/safety.d.ts +50 -0
- package/dist/safety.js +84 -0
- package/dist/safety.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +28 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/account.d.ts +10 -0
- package/dist/tools/account.js +222 -0
- package/dist/tools/account.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.js +12 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/kit.d.ts +57 -0
- package/dist/tools/kit.js +78 -0
- package/dist/tools/kit.js.map +1 -0
- package/dist/tools/research.d.ts +16 -0
- package/dist/tools/research.js +232 -0
- package/dist/tools/research.js.map +1 -0
- package/dist/tools/transcripts.d.ts +9 -0
- package/dist/tools/transcripts.js +123 -0
- package/dist/tools/transcripts.js.map +1 -0
- package/dist/tools/types.d.ts +6 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/transport/http.d.ts +23 -0
- package/dist/transport/http.js +83 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/youtube/api.d.ts +47 -0
- package/dist/youtube/api.js +176 -0
- package/dist/youtube/api.js.map +1 -0
- package/dist/youtube/transcripts.d.ts +65 -0
- package/dist/youtube/transcripts.js +321 -0
- package/dist/youtube/transcripts.js.map +1 -0
- package/dist/youtube/ytdlp.d.ts +35 -0
- package/dist/youtube/ytdlp.js +145 -0
- package/dist/youtube/ytdlp.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* yt-dlp transport for caption tracks.
|
|
3
|
+
*
|
|
4
|
+
* As of 2026 YouTube answers a bare `timedtext` URL with 200 and an empty body
|
|
5
|
+
* unless the request carries a proof-of-origin token tied to a real player
|
|
6
|
+
* session. Scraping the watch page still lists which tracks exist, but it can
|
|
7
|
+
* no longer read their contents, so the track list and the track body now come
|
|
8
|
+
* from different places.
|
|
9
|
+
*
|
|
10
|
+
* yt-dlp already solves the hard half and is maintained against YouTube's
|
|
11
|
+
* changes far faster than this repo could be, so we shell out to it rather than
|
|
12
|
+
* reimplement token minting. It is an optional dependency: without it you can
|
|
13
|
+
* still list languages, and every other tool in this server is unaffected.
|
|
14
|
+
*/
|
|
15
|
+
import { spawn } from "node:child_process";
|
|
16
|
+
import { mkdtemp, readdir, readFile, rm } from "node:fs/promises";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
const BINARY = process.env.YOUTUBE_YTDLP_PATH?.trim() || "yt-dlp";
|
|
20
|
+
export class YtDlpMissingError extends Error {
|
|
21
|
+
constructor() {
|
|
22
|
+
super("yt-dlp is not installed. Transcripts need it because YouTube no longer serves caption text directly. Install it with `brew install yt-dlp` (macOS) or `pipx install yt-dlp`, or set YOUTUBE_YTDLP_PATH to its location.");
|
|
23
|
+
this.name = "YtDlpMissingError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function run(args, timeoutMs) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const child = spawn(BINARY, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
29
|
+
let stdout = "";
|
|
30
|
+
let stderr = "";
|
|
31
|
+
const timer = setTimeout(() => {
|
|
32
|
+
child.kill("SIGKILL");
|
|
33
|
+
reject(new Error(`yt-dlp timed out after ${timeoutMs}ms`));
|
|
34
|
+
}, timeoutMs);
|
|
35
|
+
child.stdout.on("data", (d) => (stdout += d.toString()));
|
|
36
|
+
child.stderr.on("data", (d) => (stderr += d.toString()));
|
|
37
|
+
child.on("error", (err) => {
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
if (err.code === "ENOENT")
|
|
40
|
+
reject(new YtDlpMissingError());
|
|
41
|
+
else
|
|
42
|
+
reject(err);
|
|
43
|
+
});
|
|
44
|
+
child.on("close", (code) => {
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** True when yt-dlp can actually be launched. Used by `doctor` and by tool errors. */
|
|
51
|
+
export async function isAvailable() {
|
|
52
|
+
try {
|
|
53
|
+
const { code } = await run(["--version"], 10000);
|
|
54
|
+
return code === 0;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** `en-orig` and `en-en` both just mean `en`. */
|
|
61
|
+
function normalizeLang(code) {
|
|
62
|
+
const bare = code.replace(/-orig$/, "");
|
|
63
|
+
const halves = bare.split("-");
|
|
64
|
+
if (halves.length === 2 && halves[0] && halves[0].toLowerCase() === halves[1]?.toLowerCase()) {
|
|
65
|
+
return halves[0];
|
|
66
|
+
}
|
|
67
|
+
return bare;
|
|
68
|
+
}
|
|
69
|
+
function parseJson3(raw) {
|
|
70
|
+
const doc = JSON.parse(raw);
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const ev of doc.events ?? []) {
|
|
73
|
+
const text = (ev.segs ?? [])
|
|
74
|
+
.map((s) => s.utf8 ?? "")
|
|
75
|
+
.join("")
|
|
76
|
+
.replace(/\s+/g, " ")
|
|
77
|
+
.trim();
|
|
78
|
+
// Auto-caption streams include empty timing events used for on-screen
|
|
79
|
+
// positioning. They carry no words and would show up as blank lines.
|
|
80
|
+
if (!text)
|
|
81
|
+
continue;
|
|
82
|
+
out.push({
|
|
83
|
+
start: (ev.tStartMs ?? 0) / 1000,
|
|
84
|
+
duration: (ev.dDurationMs ?? 0) / 1000,
|
|
85
|
+
text,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Download one caption track.
|
|
92
|
+
*
|
|
93
|
+
* `--sub-langs` accepts a glob, so `en.*` picks up `en`, `en-GB` and the
|
|
94
|
+
* auto-generated `en-orig` in one pass. We take whichever file lands and report
|
|
95
|
+
* the language from its name rather than assuming we got the exact code asked
|
|
96
|
+
* for.
|
|
97
|
+
*/
|
|
98
|
+
export async function fetchCaptions(videoId, language = "en", timeoutMs = 90000) {
|
|
99
|
+
const dir = await mkdtemp(join(tmpdir(), "youtube-mcp-"));
|
|
100
|
+
try {
|
|
101
|
+
const lang = language.includes("*") ? language : `${language}.*`;
|
|
102
|
+
const { code, stderr } = await run([
|
|
103
|
+
"--skip-download",
|
|
104
|
+
"--write-subs",
|
|
105
|
+
"--write-auto-subs",
|
|
106
|
+
"--sub-langs",
|
|
107
|
+
lang,
|
|
108
|
+
"--sub-format",
|
|
109
|
+
"json3",
|
|
110
|
+
"--no-warnings",
|
|
111
|
+
"--no-playlist",
|
|
112
|
+
"-o",
|
|
113
|
+
join(dir, "cap"),
|
|
114
|
+
`https://www.youtube.com/watch?v=${videoId}`,
|
|
115
|
+
], timeoutMs);
|
|
116
|
+
const files = (await readdir(dir)).filter((f) => f.endsWith(".json3"));
|
|
117
|
+
if (files.length === 0) {
|
|
118
|
+
if (/HTTP Error 429|Too Many Requests/i.test(stderr)) {
|
|
119
|
+
throw new Error("YouTube is rate limiting this IP (HTTP 429). Wait a few minutes before retrying, or run with a proxy.");
|
|
120
|
+
}
|
|
121
|
+
if (/Sign in to confirm|bot/i.test(stderr)) {
|
|
122
|
+
throw new Error("YouTube asked yt-dlp to prove it is not a bot. Pass cookies with YOUTUBE_YTDLP_COOKIES, or retry later.");
|
|
123
|
+
}
|
|
124
|
+
throw new Error(code === 0
|
|
125
|
+
? "No captions were downloaded. The video probably has none in that language."
|
|
126
|
+
: `yt-dlp failed: ${stderr.trim().split("\n").pop() ?? "unknown error"}`);
|
|
127
|
+
}
|
|
128
|
+
// Prefer a human-written track over the auto one when both arrive. yt-dlp
|
|
129
|
+
// names auto-generated files `<id>.<lang>-orig.json3`.
|
|
130
|
+
const chosen = files.find((f) => !f.includes("-orig.")) ?? files[0];
|
|
131
|
+
const raw = await readFile(join(dir, chosen), "utf8");
|
|
132
|
+
const matched = /\.([A-Za-z0-9-]+)\.json3$/.exec(chosen);
|
|
133
|
+
return {
|
|
134
|
+
segments: parseJson3(raw),
|
|
135
|
+
// yt-dlp writes variant tracks as `<lang>-<lang>` (e.g. `en-en`) and its
|
|
136
|
+
// auto track as `<lang>-orig`. Both mean the plain language to a caller.
|
|
137
|
+
language: normalizeLang(matched?.[1] ?? language),
|
|
138
|
+
autoGenerated: chosen.includes("-orig."),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
await rm(dir, { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=ytdlp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ytdlp.js","sourceRoot":"","sources":["../../src/youtube/ytdlp.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@thenavidm/youtube-mcp-cli",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "YouTube MCP server and CLI for Claude Code, Codex and AI agents. 16 tools for transcripts, search, channel research, comments, analytics and multi-channel management.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"youtube-mcp": "dist/index.js",
|
|
9
|
+
"youtube-cli": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=20"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"build:mcpb": "bash desktop-extension/build.sh",
|
|
23
|
+
"check:counts": "node scripts/check-counts.mjs"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.18.0",
|
|
27
|
+
"zod": "^3.23.8",
|
|
28
|
+
"zod-to-json-schema": "^3.24.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^22.10.0",
|
|
32
|
+
"typescript": "^5.7.0",
|
|
33
|
+
"vitest": "^2.1.0"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"author": "Navid Moazzez (https://navid.me)",
|
|
37
|
+
"homepage": "https://navid.me",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/thenavidm/youtube-mcp-cli.git"
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/thenavidm/youtube-mcp-cli/issues"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"mcp",
|
|
47
|
+
"model-context-protocol",
|
|
48
|
+
"youtube",
|
|
49
|
+
"youtube-api",
|
|
50
|
+
"transcript",
|
|
51
|
+
"mcp-server",
|
|
52
|
+
"claude",
|
|
53
|
+
"claude-code",
|
|
54
|
+
"ai-agents",
|
|
55
|
+
"llm-tools",
|
|
56
|
+
"cli",
|
|
57
|
+
"youtube-cli"
|
|
58
|
+
],
|
|
59
|
+
"files": [
|
|
60
|
+
"dist",
|
|
61
|
+
"README.md",
|
|
62
|
+
"SKILL.md",
|
|
63
|
+
"LICENSE"
|
|
64
|
+
]
|
|
65
|
+
}
|