@mkterswingman/5mghost-yonder 0.0.18 → 0.0.19
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/dist/cli/index.js +6 -0
- package/dist/cli/installSkills.d.ts +1 -0
- package/dist/cli/installSkills.js +39 -0
- package/dist/cli/setup.js +13 -0
- package/dist/utils/launcher.js +8 -0
- package/dist/utils/openClaw.d.ts +1 -0
- package/dist/utils/openClaw.js +3 -0
- package/dist/utils/skills.d.ts +16 -0
- package/dist/utils/skills.js +56 -0
- package/package.json +2 -1
- package/skills/use-yt-mcp/SKILL.md +117 -0
package/dist/cli/index.js
CHANGED
|
@@ -24,6 +24,7 @@ Commands:
|
|
|
24
24
|
setup Run first-time setup (OAuth + cookies + MCP registration)
|
|
25
25
|
serve Start the MCP server (stdio transport)
|
|
26
26
|
smoke Run installer smoke checks
|
|
27
|
+
install-skills Install the bundled yt-mcp analysis skill into supported AI clients
|
|
27
28
|
setup-cookies Refresh YouTube cookies using browser login
|
|
28
29
|
runtime Manage required runtimes
|
|
29
30
|
check Check auth, runtime, cookies, and connectivity
|
|
@@ -81,6 +82,11 @@ async function main() {
|
|
|
81
82
|
await runSmoke(process.argv.slice(3));
|
|
82
83
|
break;
|
|
83
84
|
}
|
|
85
|
+
case "install-skills": {
|
|
86
|
+
const { runInstallSkills } = await import("./installSkills.js");
|
|
87
|
+
await runInstallSkills();
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
84
90
|
case "setup-cookies": {
|
|
85
91
|
const { runSetupCookies, parseSetupCookiesArgs } = await import("./setupCookies.js");
|
|
86
92
|
await runSetupCookies({}, parseSetupCookiesArgs(process.argv.slice(3)));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runInstallSkills(): Promise<void>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { buildSkillInstallPlan, installSkillTarget, } from "../utils/skills.js";
|
|
5
|
+
function detectCli(name) {
|
|
6
|
+
try {
|
|
7
|
+
execFileSync(name, ["--version"], { stdio: "pipe" });
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function resolvePackageRoot() {
|
|
15
|
+
return join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
16
|
+
}
|
|
17
|
+
export async function runInstallSkills() {
|
|
18
|
+
const packageRoot = resolvePackageRoot();
|
|
19
|
+
const availableCliNames = [
|
|
20
|
+
"claude-internal",
|
|
21
|
+
"claude",
|
|
22
|
+
"codex-internal",
|
|
23
|
+
"codex",
|
|
24
|
+
"gemini-internal",
|
|
25
|
+
"gemini",
|
|
26
|
+
].filter(detectCli);
|
|
27
|
+
const plan = buildSkillInstallPlan(packageRoot, {
|
|
28
|
+
availableCliNames,
|
|
29
|
+
includeOpenClaw: process.env.YT_MCP_INSTALL_OPENCLAW_SKILL === "1",
|
|
30
|
+
});
|
|
31
|
+
if (plan.length === 0) {
|
|
32
|
+
console.log("[yt-mcp] No supported AI client detected for bundled skill install.");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
for (const target of plan) {
|
|
36
|
+
installSkillTarget(target);
|
|
37
|
+
console.log(`[yt-mcp] Installed bundled skill for ${target.label}: ${target.targetDir}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
package/dist/cli/setup.js
CHANGED
|
@@ -6,6 +6,7 @@ import { runOAuthFlow } from "../auth/oauthFlow.js";
|
|
|
6
6
|
import { hasSIDCookies } from "../utils/cookies.js";
|
|
7
7
|
import { buildLauncherCommand, writeLauncherFile } from "../utils/launcher.js";
|
|
8
8
|
import { checkAll } from "../runtime/installers.js";
|
|
9
|
+
import { runInstallSkills } from "./installSkills.js";
|
|
9
10
|
import { getOpenClawConfigPath, isOpenClawInstallLikelyInstalled, writeOpenClawConfig, } from "../utils/openClaw.js";
|
|
10
11
|
import { getCodexInternalConfigPath, writeCodexInternalConfig, } from "../utils/codexInternal.js";
|
|
11
12
|
import { MCP_REGISTER_TIMEOUT_MS, classifyRegistrationFailure, } from "../utils/mcpRegistration.js";
|
|
@@ -267,6 +268,7 @@ export async function runSetup() {
|
|
|
267
268
|
console.log("Step 5/5: Registering MCP in AI clients...");
|
|
268
269
|
const launcherCommand = buildLauncherCommand();
|
|
269
270
|
let registered = false;
|
|
271
|
+
let skillsInstalled = false;
|
|
270
272
|
for (const { bin, label, command } of buildSetupCliCandidates({
|
|
271
273
|
file: launcherCommand.command,
|
|
272
274
|
args: launcherCommand.args,
|
|
@@ -299,6 +301,14 @@ export async function runSetup() {
|
|
|
299
301
|
console.log(` ⚠️ Codex CLI (internal) auto-register failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
300
302
|
}
|
|
301
303
|
}
|
|
304
|
+
try {
|
|
305
|
+
process.env.YT_MCP_INSTALL_OPENCLAW_SKILL = isOpenClawInstallLikelyInstalled(detectCli) ? "1" : "0";
|
|
306
|
+
await runInstallSkills();
|
|
307
|
+
skillsInstalled = true;
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
console.log(` ⚠️ Bundled skill install failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
311
|
+
}
|
|
302
312
|
if (!registered) {
|
|
303
313
|
console.log(" ℹ️ No supported CLI found. Add manually to your AI client:");
|
|
304
314
|
}
|
|
@@ -329,6 +339,9 @@ export async function runSetup() {
|
|
|
329
339
|
}
|
|
330
340
|
`);
|
|
331
341
|
console.log(` OpenClaw uses ${PATHS.sharedAuthJson} for PAT/JWT, so env.YT_MCP_TOKEN is optional after setup.`);
|
|
342
|
+
if (skillsInstalled) {
|
|
343
|
+
console.log(" ✅ Installed bundled yt-mcp analysis skill for detected AI clients");
|
|
344
|
+
}
|
|
332
345
|
console.log(" Media downloads:");
|
|
333
346
|
console.log(" - `start_download_job` / `poll_download_job` are job-based local tools");
|
|
334
347
|
console.log(" - Batch limit: 5 YouTube videos per job");
|
package/dist/utils/launcher.js
CHANGED
|
@@ -26,6 +26,10 @@ const npmCacheDir = ${JSON.stringify(npmCacheDir)};
|
|
|
26
26
|
const args = process.argv.slice(2);
|
|
27
27
|
const targetArgs = args.length > 0 ? args : ["serve"];
|
|
28
28
|
|
|
29
|
+
function isSkillInstallerMode(subArgs) {
|
|
30
|
+
return subArgs[0] === "install-skills";
|
|
31
|
+
}
|
|
32
|
+
|
|
29
33
|
function isRepairableNpxFailure(stderr) {
|
|
30
34
|
const lower = stderr.toLowerCase();
|
|
31
35
|
return (lower.includes("_npx/") || lower.includes("_npx\\\\"))
|
|
@@ -38,6 +42,7 @@ function runNpx(subArgs, captureStdErrOnly = false) {
|
|
|
38
42
|
return spawnSync(npxBin, ["--yes", packageSpec, ...subArgs], {
|
|
39
43
|
env: { ...process.env, npm_config_cache: npmCacheDir },
|
|
40
44
|
// Why: probe runs before MCP starts; stdout must stay silent or it will corrupt stdio transport.
|
|
45
|
+
// The skill installer is an explicit one-shot CLI path, so it can inherit stdio safely.
|
|
41
46
|
stdio: captureStdErrOnly ? ["ignore", "ignore", "pipe"] : "inherit",
|
|
42
47
|
});
|
|
43
48
|
}
|
|
@@ -70,6 +75,9 @@ function ensurePackageReady() {
|
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
ensurePackageReady();
|
|
78
|
+
if (isSkillInstallerMode(targetArgs)) {
|
|
79
|
+
process.env.YT_MCP_INSTALL_SKILLS = "1";
|
|
80
|
+
}
|
|
73
81
|
const finalRun = runNpx(targetArgs, false);
|
|
74
82
|
process.exit(finalRun.status ?? 0);
|
|
75
83
|
`;
|
package/dist/utils/openClaw.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface OpenClawServerConfig {
|
|
|
6
6
|
env?: Record<string, string>;
|
|
7
7
|
}
|
|
8
8
|
export declare function getOpenClawConfigPath(homeDir?: string): string;
|
|
9
|
+
export declare function getOpenClawSkillsDir(homeDir?: string): string;
|
|
9
10
|
export declare function buildOpenClawServerConfig(launcherCommand: LauncherCommand): OpenClawServerConfig;
|
|
10
11
|
export declare function upsertOpenClawConfigText(currentText: string | null, serverName: string, launcherCommand: LauncherCommand): string;
|
|
11
12
|
export declare function removeOpenClawConfigEntryText(currentText: string | null, serverName: string): {
|
package/dist/utils/openClaw.js
CHANGED
|
@@ -4,6 +4,9 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
export function getOpenClawConfigPath(homeDir = homedir()) {
|
|
5
5
|
return join(homeDir, ".openclaw", "workspace", "config", "mcporter.json");
|
|
6
6
|
}
|
|
7
|
+
export function getOpenClawSkillsDir(homeDir = homedir()) {
|
|
8
|
+
return join(homeDir, ".openclaw", "skills");
|
|
9
|
+
}
|
|
7
10
|
export function buildOpenClawServerConfig(launcherCommand) {
|
|
8
11
|
return {
|
|
9
12
|
transport: "stdio",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LauncherCommand } from "./launcher.js";
|
|
2
|
+
export interface SkillInstallTarget {
|
|
3
|
+
client: string;
|
|
4
|
+
label: string;
|
|
5
|
+
sourceDir: string;
|
|
6
|
+
targetDir: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SkillInstallPlanOptions {
|
|
9
|
+
homeDir?: string;
|
|
10
|
+
availableCliNames?: string[];
|
|
11
|
+
includeOpenClaw?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function getSkillPackageSourcePath(packageRoot: string): string;
|
|
14
|
+
export declare function buildSkillLauncherCommand(launcherPath: string): LauncherCommand;
|
|
15
|
+
export declare function buildSkillInstallPlan(packageRoot: string, options?: SkillInstallPlanOptions): SkillInstallTarget[];
|
|
16
|
+
export declare function installSkillTarget(target: SkillInstallTarget): void;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { getOpenClawSkillsDir } from "./openClaw.js";
|
|
5
|
+
const SKILL_NAME = "use-yt-mcp";
|
|
6
|
+
export function getSkillPackageSourcePath(packageRoot) {
|
|
7
|
+
return join(packageRoot, "skills", SKILL_NAME);
|
|
8
|
+
}
|
|
9
|
+
export function buildSkillLauncherCommand(launcherPath) {
|
|
10
|
+
return {
|
|
11
|
+
command: "node",
|
|
12
|
+
args: [launcherPath, "install-skills"],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function buildSkillInstallPlan(packageRoot, options = {}) {
|
|
16
|
+
const homeDir = options.homeDir ?? homedir();
|
|
17
|
+
const availableCliNames = new Set(options.availableCliNames ?? []);
|
|
18
|
+
const sourceDir = getSkillPackageSourcePath(packageRoot);
|
|
19
|
+
const plan = [];
|
|
20
|
+
const cliTargets = [
|
|
21
|
+
{ client: "claude-internal", label: "Claude Code (internal)", dir: join(homeDir, ".claude-internal", "skills") },
|
|
22
|
+
{ client: "claude", label: "Claude Code", dir: join(homeDir, ".claude", "skills") },
|
|
23
|
+
{ client: "codex-internal", label: "Codex CLI (internal)", dir: join(homeDir, ".codex-internal", "skills") },
|
|
24
|
+
{ client: "codex", label: "Codex CLI / Codex App", dir: join(homeDir, ".codex", "skills") },
|
|
25
|
+
{ client: "gemini-internal", label: "Gemini CLI (internal)", dir: join(homeDir, ".gemini-internal", "skills") },
|
|
26
|
+
{ client: "gemini", label: "Gemini CLI", dir: join(homeDir, ".gemini", "skills") },
|
|
27
|
+
];
|
|
28
|
+
for (const target of cliTargets) {
|
|
29
|
+
if (!availableCliNames.has(target.client)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
plan.push({
|
|
33
|
+
client: target.client,
|
|
34
|
+
label: target.label,
|
|
35
|
+
sourceDir,
|
|
36
|
+
targetDir: join(target.dir, SKILL_NAME),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (options.includeOpenClaw) {
|
|
40
|
+
plan.push({
|
|
41
|
+
client: "openclaw",
|
|
42
|
+
label: "OpenClaw",
|
|
43
|
+
sourceDir,
|
|
44
|
+
targetDir: join(getOpenClawSkillsDir(homeDir), SKILL_NAME),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return plan;
|
|
48
|
+
}
|
|
49
|
+
export function installSkillTarget(target) {
|
|
50
|
+
if (!existsSync(target.sourceDir)) {
|
|
51
|
+
throw new Error(`Bundled skill not found: ${target.sourceDir}`);
|
|
52
|
+
}
|
|
53
|
+
mkdirSync(dirname(target.targetDir), { recursive: true });
|
|
54
|
+
rmSync(target.targetDir, { recursive: true, force: true });
|
|
55
|
+
cpSync(target.sourceDir, target.targetDir, { recursive: true });
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mkterswingman/5mghost-yonder",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "Internal MCP client with local data tools and remote API proxy",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"dist/",
|
|
37
|
+
"skills/",
|
|
37
38
|
"scripts/download-ytdlp.mjs",
|
|
38
39
|
"scripts/install.sh",
|
|
39
40
|
"scripts/install.ps1",
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: use-yt-mcp
|
|
3
|
+
preamble-tier: 3
|
|
4
|
+
version: 1.2.0
|
|
5
|
+
description: |
|
|
6
|
+
Use when the user wants YouTube analysis through yt-mcp: channel performance,
|
|
7
|
+
video stats, subtitles, comments, trending, or batch YouTube data work.
|
|
8
|
+
Also use when the user pastes YouTube links such as youtube.com/watch,
|
|
9
|
+
youtube.com/shorts, youtube.com/live, or youtu.be URLs.
|
|
10
|
+
Keywords: YouTube, 频道, 视频, 字幕, 评论, 均播, 播放量, trending, yt-mcp, youtube.com, youtu.be.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Use yt-mcp
|
|
14
|
+
|
|
15
|
+
Focus on YouTube analysis with yt-mcp MCP tools. Answer the analysis question first. Only switch into setup or troubleshooting when the user explicitly asks for it or a tool call fails.
|
|
16
|
+
|
|
17
|
+
## 0. Hard Constraints
|
|
18
|
+
|
|
19
|
+
- For YouTube data tasks, always use `mcp__yt-mcp__*` tools first.
|
|
20
|
+
- Do not route YouTube queries to legacy MCPs or legacy setup skills such as `youtube-data`, `mcp_youtube_data`, `use-youtube-data-mcp`, or `Use YouTube Data MCP` when `yt-mcp` can answer.
|
|
21
|
+
- Do not fetch YouTube metrics, subtitles, comments, channel data, or trending data by browsing the web, scraping pages, or using generic web search. Use `yt-mcp` instead.
|
|
22
|
+
- If `yt-mcp` is unavailable or fails, stop and tell the user it needs setup or troubleshooting. Do not silently fall back to old MCPs or ad-hoc web lookup.
|
|
23
|
+
|
|
24
|
+
## Trigger Hints
|
|
25
|
+
|
|
26
|
+
- Treat pasted YouTube links as an automatic trigger for this skill, even if the user does not explicitly say "YouTube".
|
|
27
|
+
- Common URL forms: `https://www.youtube.com/watch?v=...`, `https://youtu.be/...`, `https://www.youtube.com/shorts/...`, `https://www.youtube.com/live/...`.
|
|
28
|
+
|
|
29
|
+
## 1. Intent -> Tool Routing
|
|
30
|
+
|
|
31
|
+
| User Intent | Tool(s) |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Search videos by keyword | `search_videos` |
|
|
34
|
+
| Get video metrics in bulk | `get_video_stats` |
|
|
35
|
+
| Large video stats job | `start_video_stats_job` -> `poll_video_stats_job` |
|
|
36
|
+
| Get subtitles / transcript | `get_subtitles`, `batch_get_subtitles` |
|
|
37
|
+
| Check subtitle languages | `list_available_subtitles` |
|
|
38
|
+
| Get comments | `get_comments` |
|
|
39
|
+
| Large comments export | `start_comments_job` -> `poll_comments_job` |
|
|
40
|
+
| Get comment replies | `get_comment_replies` |
|
|
41
|
+
| Channel stats | `get_channel_stats` |
|
|
42
|
+
| List channel uploads | `list_channel_uploads` |
|
|
43
|
+
| Trending videos | `get_trending` |
|
|
44
|
+
| Local download | `start_download_job` -> `poll_download_job` |
|
|
45
|
+
|
|
46
|
+
All tools are prefixed `mcp__yt-mcp__` in actual calls.
|
|
47
|
+
|
|
48
|
+
## 2. Analysis Rules
|
|
49
|
+
|
|
50
|
+
- Prefer batch-capable tools when comparing multiple videos or channels.
|
|
51
|
+
- `get_video_stats` accepts up to 1000 items. If the response returns `mode: "file"`, then inline `preview_items` is only a preview sample from a larger dataset and must not be used as the full basis of analysis. Tell the user the complete result is in `output_file` or `download_url`.
|
|
52
|
+
- For large video/comment analyses where completeness matters, prefer async jobs: `start_video_stats_job` -> `poll_video_stats_job`, or `start_comments_job` -> `poll_comments_job`.
|
|
53
|
+
- When using async jobs, poll until status is `completed`, and tell the user if the result is still partial or in progress.
|
|
54
|
+
- Use normalized response fields in analysis: `published_at`, `duration`, `view_count`, `like_count`, `comment_count`, `is_live_replay`.
|
|
55
|
+
- Default channel analysis window to 30 days unless the user specifies another range.
|
|
56
|
+
- Shorts heuristic: parsed ISO 8601 `duration` <= 90 seconds. Live replay: `is_live_replay === true`. Otherwise treat as long-form.
|
|
57
|
+
- Follow the user's language. Use thousand separators like `1,234,567`; avoid `K/M/B` unless the user asks.
|
|
58
|
+
- Do not dump raw JSON unless the user explicitly wants raw output.
|
|
59
|
+
|
|
60
|
+
## 3. Common Workflows
|
|
61
|
+
|
|
62
|
+
### Channel Average Views
|
|
63
|
+
|
|
64
|
+
1. `list_channel_uploads(channel or channel_id, max_results=N)`
|
|
65
|
+
2. For moderate sets, `get_video_stats(videos=[...ids])`; for large sets or when full coverage matters, `start_video_stats_job(videos=[...ids])` then `poll_video_stats_job(job_id)` until completed
|
|
66
|
+
3. Filter by `published_at` within the requested window
|
|
67
|
+
4. Segment into long-form, Shorts, live replay
|
|
68
|
+
5. Report video count, total views, average views, max, min
|
|
69
|
+
|
|
70
|
+
Default output:
|
|
71
|
+
|
|
72
|
+
```md
|
|
73
|
+
## {频道名} 均播分析(近 {N} 天)
|
|
74
|
+
|
|
75
|
+
| 类型 | 视频数 | 总播放 | 均播 | 最高 | 最低 |
|
|
76
|
+
|---|---:|---:|---:|---:|---:|
|
|
77
|
+
| 长视频 | 12 | 1,234,567 | 102,880 | 456,789 | 12,345 |
|
|
78
|
+
| Shorts | 24 | 2,345,678 | 97,736 | 234,567 | 23,456 |
|
|
79
|
+
| 直播回放 | 3 | 345,678 | 115,226 | 200,000 | 45,678 |
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Video Summary
|
|
83
|
+
|
|
84
|
+
1. `get_subtitles(video, format="csv")`
|
|
85
|
+
2. Build a timestamped summary from the transcript
|
|
86
|
+
3. Output section headers with timestamps, not a single long paragraph
|
|
87
|
+
|
|
88
|
+
### Comment Analysis
|
|
89
|
+
|
|
90
|
+
1. For normal analysis, `get_comments(video, max_comments=N, order="relevance")`; for larger exports, `start_comments_job` then `poll_comments_job`
|
|
91
|
+
2. Cluster by sentiment or topic
|
|
92
|
+
3. Quote representative comments and keep like counts when useful
|
|
93
|
+
|
|
94
|
+
### Channel Stability
|
|
95
|
+
|
|
96
|
+
1. Fetch uploads and stats
|
|
97
|
+
2. Measure posting frequency, average views, and volatility
|
|
98
|
+
3. Compare recent videos vs earlier videos to judge trend direction
|
|
99
|
+
|
|
100
|
+
## 4. Output Rules
|
|
101
|
+
|
|
102
|
+
- Answer the user's actual question first, then show supporting metrics.
|
|
103
|
+
- For comparisons, prefer compact tables over long prose.
|
|
104
|
+
- State assumptions when the time window, sample size, or language is inferred.
|
|
105
|
+
- If data is partial, say so directly.
|
|
106
|
+
|
|
107
|
+
## 5. Setup Boundary
|
|
108
|
+
|
|
109
|
+
Only discuss CLI when the user asks to install or troubleshoot yt-mcp, or when a tool call fails because of auth/cookies/runtime.
|
|
110
|
+
|
|
111
|
+
Useful commands:
|
|
112
|
+
|
|
113
|
+
- `yt-mcp setup`
|
|
114
|
+
- `yt-mcp setup-cookies`
|
|
115
|
+
- `yt-mcp check`
|
|
116
|
+
|
|
117
|
+
Do not turn a normal analysis request into a setup checklist.
|