@aitofy/youtube 0.1.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 +28 -0
- package/README.md +278 -0
- package/dist/chunk-A6HJFYPT.mjs +847 -0
- package/dist/index.d.mts +241 -0
- package/dist/index.d.ts +241 -0
- package/dist/index.js +884 -0
- package/dist/index.mjs +28 -0
- package/dist/mcp.d.mts +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +1008 -0
- package/dist/mcp.mjs +251 -0
- package/package.json +75 -0
package/dist/mcp.mjs
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getChannelInfo,
|
|
4
|
+
getChannelVideos,
|
|
5
|
+
getTranscript,
|
|
6
|
+
getTranscriptText,
|
|
7
|
+
getVideoInfo,
|
|
8
|
+
listTranscripts,
|
|
9
|
+
searchVideos
|
|
10
|
+
} from "./chunk-A6HJFYPT.mjs";
|
|
11
|
+
|
|
12
|
+
// src/mcp.ts
|
|
13
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
14
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
+
import {
|
|
16
|
+
CallToolRequestSchema,
|
|
17
|
+
ListToolsRequestSchema
|
|
18
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
19
|
+
var server = new Server(
|
|
20
|
+
{
|
|
21
|
+
name: "youtube-tools",
|
|
22
|
+
version: "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
capabilities: {
|
|
26
|
+
tools: {}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
31
|
+
tools: [
|
|
32
|
+
{
|
|
33
|
+
name: "get_youtube_transcript",
|
|
34
|
+
description: "Get the transcript/subtitles of a YouTube video. Returns timestamped segments.",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
video_id: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "YouTube video ID or URL (e.g., dQw4w9WgXcQ)"
|
|
41
|
+
},
|
|
42
|
+
language: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "Preferred language code (e.g., en, vi). Default: en"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
required: ["video_id"]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "get_youtube_transcript_text",
|
|
52
|
+
description: "Get the transcript of a YouTube video as plain text (no timestamps).",
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {
|
|
56
|
+
video_id: {
|
|
57
|
+
type: "string",
|
|
58
|
+
description: "YouTube video ID or URL"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
required: ["video_id"]
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "list_youtube_transcripts",
|
|
66
|
+
description: "List available transcript languages for a YouTube video.",
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
video_id: {
|
|
71
|
+
type: "string",
|
|
72
|
+
description: "YouTube video ID or URL"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
required: ["video_id"]
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "get_youtube_video_info",
|
|
80
|
+
description: "Get detailed information about a YouTube video (title, views, duration, etc).",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
video_id: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "YouTube video ID or URL"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
required: ["video_id"]
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "get_youtube_channel_videos",
|
|
94
|
+
description: "Get a list of videos from a YouTube channel.",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
type: "object",
|
|
97
|
+
properties: {
|
|
98
|
+
channel: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "Channel ID, URL, or @handle (e.g., @Fireship)"
|
|
101
|
+
},
|
|
102
|
+
limit: {
|
|
103
|
+
type: "number",
|
|
104
|
+
description: "Maximum number of videos to return. Default: 15"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
required: ["channel"]
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "get_youtube_channel_info",
|
|
112
|
+
description: "Get information about a YouTube channel.",
|
|
113
|
+
inputSchema: {
|
|
114
|
+
type: "object",
|
|
115
|
+
properties: {
|
|
116
|
+
channel: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "Channel ID, URL, or @handle"
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
required: ["channel"]
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "search_youtube_videos",
|
|
126
|
+
description: "Search for YouTube videos.",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: {
|
|
130
|
+
query: {
|
|
131
|
+
type: "string",
|
|
132
|
+
description: "Search query"
|
|
133
|
+
},
|
|
134
|
+
limit: {
|
|
135
|
+
type: "number",
|
|
136
|
+
description: "Maximum number of results. Default: 10"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
required: ["query"]
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}));
|
|
144
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
145
|
+
const { name, arguments: args } = request.params;
|
|
146
|
+
try {
|
|
147
|
+
switch (name) {
|
|
148
|
+
case "get_youtube_transcript": {
|
|
149
|
+
const videoId = args?.video_id;
|
|
150
|
+
const language = args?.language || "en";
|
|
151
|
+
const segments = await getTranscript(videoId, { languages: [language] });
|
|
152
|
+
return {
|
|
153
|
+
content: [
|
|
154
|
+
{
|
|
155
|
+
type: "text",
|
|
156
|
+
text: JSON.stringify(segments, null, 2)
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
case "get_youtube_transcript_text": {
|
|
162
|
+
const videoId = args?.video_id;
|
|
163
|
+
const text = await getTranscriptText(videoId);
|
|
164
|
+
return {
|
|
165
|
+
content: [{ type: "text", text }]
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
case "list_youtube_transcripts": {
|
|
169
|
+
const videoId = args?.video_id;
|
|
170
|
+
const tracks = await listTranscripts(videoId);
|
|
171
|
+
return {
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: JSON.stringify(tracks, null, 2)
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
case "get_youtube_video_info": {
|
|
181
|
+
const videoId = args?.video_id;
|
|
182
|
+
const info = await getVideoInfo(videoId);
|
|
183
|
+
return {
|
|
184
|
+
content: [
|
|
185
|
+
{
|
|
186
|
+
type: "text",
|
|
187
|
+
text: JSON.stringify(info, null, 2)
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
case "get_youtube_channel_videos": {
|
|
193
|
+
const channel = args?.channel;
|
|
194
|
+
const limit = args?.limit || 15;
|
|
195
|
+
const videos = await getChannelVideos({ channel, limit });
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: JSON.stringify(videos, null, 2)
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
case "get_youtube_channel_info": {
|
|
206
|
+
const channel = args?.channel;
|
|
207
|
+
const info = await getChannelInfo(channel);
|
|
208
|
+
return {
|
|
209
|
+
content: [
|
|
210
|
+
{
|
|
211
|
+
type: "text",
|
|
212
|
+
text: JSON.stringify(info, null, 2)
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
case "search_youtube_videos": {
|
|
218
|
+
const query = args?.query;
|
|
219
|
+
const limit = args?.limit || 10;
|
|
220
|
+
const results = await searchVideos({ query, limit });
|
|
221
|
+
return {
|
|
222
|
+
content: [
|
|
223
|
+
{
|
|
224
|
+
type: "text",
|
|
225
|
+
text: JSON.stringify(results, null, 2)
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
default:
|
|
231
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
232
|
+
}
|
|
233
|
+
} catch (error) {
|
|
234
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
235
|
+
return {
|
|
236
|
+
content: [
|
|
237
|
+
{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: `Error: ${message}`
|
|
240
|
+
}
|
|
241
|
+
],
|
|
242
|
+
isError: true
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
async function main() {
|
|
247
|
+
const transport = new StdioServerTransport();
|
|
248
|
+
await server.connect(transport);
|
|
249
|
+
console.error("YouTube Tools MCP Server running on stdio");
|
|
250
|
+
}
|
|
251
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aitofy/youtube",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Free YouTube utilities - get transcripts, channel videos, and more without API key",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"youtube-mcp": "./dist/mcp.js"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "tsup src/index.ts src/mcp.ts --watch",
|
|
25
|
+
"build": "tsup src/index.ts src/mcp.ts --format cjs,esm --dts --clean",
|
|
26
|
+
"test": "npx tsx tests/test-typescript.ts",
|
|
27
|
+
"prepublishOnly": "npm run build",
|
|
28
|
+
"lint": "eslint src/",
|
|
29
|
+
"typecheck": "tsc --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"youtube",
|
|
33
|
+
"transcript",
|
|
34
|
+
"subtitles",
|
|
35
|
+
"captions",
|
|
36
|
+
"channel",
|
|
37
|
+
"videos",
|
|
38
|
+
"scraper",
|
|
39
|
+
"api",
|
|
40
|
+
"no-api-key",
|
|
41
|
+
"youtube-transcript",
|
|
42
|
+
"youtube-captions",
|
|
43
|
+
"youtube-subtitles",
|
|
44
|
+
"video-transcript",
|
|
45
|
+
"typescript",
|
|
46
|
+
"nodejs",
|
|
47
|
+
"mcp",
|
|
48
|
+
"claude",
|
|
49
|
+
"chatgpt",
|
|
50
|
+
"ai"
|
|
51
|
+
],
|
|
52
|
+
"author": "Aitofy",
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "https://github.com/aitofy-dev/youtube"
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://aitofy.dev",
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/aitofy-dev/youtube/issues"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=18.0.0"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
67
|
+
"fast-xml-parser": "^4.3.0"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@types/node": "^20.0.0",
|
|
71
|
+
"tsup": "^8.5.1",
|
|
72
|
+
"tsx": "^4.0.0",
|
|
73
|
+
"typescript": "^5.3.0"
|
|
74
|
+
}
|
|
75
|
+
}
|