@alexspalato/youtube-transcript-mcp 1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 alexadark
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # YouTube Transcript MCP
2
+
3
+ Get YouTube video transcripts directly in Claude. Just share a YouTube link - no technical syntax needed.
4
+
5
+ ## What It Does
6
+
7
+ Share any YouTube URL with Claude, and this MCP lets Claude fetch the transcript for you. Claude will naturally ask how you'd like to view it:
8
+
9
+ - **Readable text** - Clean paragraphs, perfect for reading and analysis
10
+ - **With timestamps** - Each line shows when it was said (great for referencing specific moments)
11
+ - **Structured data** - JSON format with full metadata (for developers)
12
+
13
+ Works with any YouTube video that has captions, in any language.
14
+
15
+ ## Installation
16
+
17
+ **One command:**
18
+
19
+ ```bash
20
+ npm install -g @alexspalato/youtube-transcript-mcp
21
+ ```
22
+
23
+ ## Setup
24
+
25
+ ### For Claude Code
26
+
27
+ ```bash
28
+ claude mcp add youtube-transcript
29
+ ```
30
+
31
+ Restart Claude Code and you're done!
32
+
33
+ ### For Claude Desktop
34
+
35
+ 1. Open `~/Library/Application Support/Claude/claude_desktop_config.json`
36
+ 2. Add this:
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "youtube-transcript": {
42
+ "command": "npx",
43
+ "args": ["-y", "@alexspalato/youtube-transcript-mcp"]
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ 3. Restart Claude Desktop
50
+
51
+ ## How to Use
52
+
53
+ Just chat naturally with Claude:
54
+
55
+ > "Can you get the transcript from https://youtu.be/dQw4w9WgXcQ?"
56
+
57
+ > "Summarize this video: https://youtu.be/eT_6uaHNlk8"
58
+
59
+ > "Get me the transcript with timestamps from this tutorial..."
60
+
61
+ Claude will handle everything - fetching the transcript, asking about your format preference, and displaying it clearly.
62
+
63
+ ### Multiple Languages
64
+
65
+ Works with any language that has captions:
66
+
67
+ > "Get the Spanish transcript from this video"
68
+
69
+ > "Show me this video's transcript in French"
70
+
71
+ Common language codes: `en`, `es`, `fr`, `de`, `ja`, `ko`, `zh`, `pt`, `ru`, `ar`, `hi`
72
+
73
+ ## License
74
+
75
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/build/index.js ADDED
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { fetchTranscript } from "youtube-transcript-plus";
6
+ /**
7
+ * Formats seconds into HH:MM:SS or MM:SS format
8
+ */
9
+ function formatTimestamp(seconds) {
10
+ const hours = Math.floor(seconds / 3600);
11
+ const minutes = Math.floor((seconds % 3600) / 60);
12
+ const secs = Math.floor(seconds % 60);
13
+ if (hours > 0) {
14
+ return `${hours.toString().padStart(2, "0")}:${minutes
15
+ .toString()
16
+ .padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
17
+ }
18
+ return `${minutes.toString().padStart(2, "0")}:${secs
19
+ .toString()
20
+ .padStart(2, "0")}`;
21
+ }
22
+ /**
23
+ * Extracts video ID from YouTube URL or returns the input if it's already an ID
24
+ */
25
+ function extractVideoId(urlOrId) {
26
+ // If it's already just an ID (no URL patterns), return it
27
+ if (!/[/:.]/.test(urlOrId)) {
28
+ return urlOrId;
29
+ }
30
+ // Handle various YouTube URL formats
31
+ const patterns = [
32
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\?\/]+)/,
33
+ /youtube\.com\/shorts\/([^&\?\/]+)/,
34
+ ];
35
+ for (const pattern of patterns) {
36
+ const match = urlOrId.match(pattern);
37
+ if (match) {
38
+ return match[1];
39
+ }
40
+ }
41
+ // If no pattern matched, assume it's an ID
42
+ return urlOrId;
43
+ }
44
+ /**
45
+ * Formats transcript segments into plain text paragraphs
46
+ */
47
+ function formatPlainText(segments) {
48
+ return segments.map((segment) => segment.text.trim()).join(" ");
49
+ }
50
+ /**
51
+ * Formats transcript segments with timestamps
52
+ */
53
+ function formatTimestamped(segments) {
54
+ return segments
55
+ .map((segment) => {
56
+ const timestamp = formatTimestamp(segment.offset);
57
+ return `[${timestamp}] ${segment.text.trim()}`;
58
+ })
59
+ .join("\n");
60
+ }
61
+ /**
62
+ * Formats transcript segments as JSON
63
+ */
64
+ function formatJson(segments) {
65
+ const formatted = segments.map((segment) => ({
66
+ timestamp: formatTimestamp(segment.offset),
67
+ offset: segment.offset,
68
+ duration: segment.duration,
69
+ text: segment.text.trim(),
70
+ }));
71
+ return JSON.stringify(formatted, null, 2);
72
+ }
73
+ const server = new Server({
74
+ name: "youtube-transcript-mcp",
75
+ version: "1.0.0",
76
+ }, {
77
+ capabilities: {
78
+ tools: {},
79
+ },
80
+ });
81
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
82
+ return {
83
+ tools: [
84
+ {
85
+ name: "get_transcript",
86
+ description: "Fetches YouTube video transcripts. When a user shares a YouTube URL, use this tool to retrieve the transcript. If the user doesn't specify a format preference, ask them how they'd like to view it: as readable text (plain), with timestamps for referencing specific moments (timestamped), or as structured data (json).",
87
+ inputSchema: {
88
+ type: "object",
89
+ properties: {
90
+ url: {
91
+ type: "string",
92
+ description: "YouTube video URL or video ID",
93
+ },
94
+ lang: {
95
+ type: "string",
96
+ description: "Language code for transcript (e.g., 'en', 'es', 'fr'). Defaults to 'en'.",
97
+ default: "en",
98
+ },
99
+ format: {
100
+ type: "string",
101
+ enum: ["plain", "timestamped", "json"],
102
+ description: "Output format: 'plain' for readable text (default), 'timestamped' for text with [00:00] timestamps, 'json' for structured data with metadata",
103
+ default: "plain",
104
+ },
105
+ },
106
+ required: ["url"],
107
+ },
108
+ },
109
+ ],
110
+ };
111
+ });
112
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
113
+ if (request.params.name !== "get_transcript") {
114
+ throw new Error(`Unknown tool: ${request.params.name}`);
115
+ }
116
+ const url = String(request.params.arguments?.url || "");
117
+ const lang = String(request.params.arguments?.lang || "en");
118
+ const format = (request.params.arguments?.format || "plain");
119
+ if (!url) {
120
+ throw new Error("URL parameter is required");
121
+ }
122
+ try {
123
+ const videoId = extractVideoId(url);
124
+ const transcript = await fetchTranscript(videoId, { lang });
125
+ let formattedText;
126
+ switch (format) {
127
+ case "timestamped":
128
+ formattedText = formatTimestamped(transcript);
129
+ break;
130
+ case "json":
131
+ formattedText = formatJson(transcript);
132
+ break;
133
+ case "plain":
134
+ default:
135
+ formattedText = formatPlainText(transcript);
136
+ break;
137
+ }
138
+ return {
139
+ content: [
140
+ {
141
+ type: "text",
142
+ text: formattedText,
143
+ },
144
+ ],
145
+ };
146
+ }
147
+ catch (error) {
148
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
149
+ return {
150
+ content: [
151
+ {
152
+ type: "text",
153
+ text: `Error fetching transcript: ${errorMessage}`,
154
+ },
155
+ ],
156
+ isError: true,
157
+ };
158
+ }
159
+ });
160
+ async function main() {
161
+ const transport = new StdioServerTransport();
162
+ await server.connect(transport);
163
+ console.error("YouTube Transcript MCP Server running on stdio");
164
+ }
165
+ main().catch((error) => {
166
+ console.error("Server error:", error);
167
+ process.exit(1);
168
+ });
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@alexspalato/youtube-transcript-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Get YouTube transcripts in Claude with natural language - no technical syntax needed",
5
+ "type": "module",
6
+ "main": "build/index.js",
7
+ "bin": {
8
+ "youtube-transcript-mcp": "build/index.js"
9
+ },
10
+ "files": [
11
+ "build",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=18.0.0"
17
+ },
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "watch": "tsc --watch",
21
+ "prepare": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "model-context-protocol",
26
+ "youtube",
27
+ "transcript",
28
+ "claude",
29
+ "anthropic",
30
+ "ai",
31
+ "video",
32
+ "captions"
33
+ ],
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/alexadark/youtube-transcript-mcp.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/alexadark/youtube-transcript-mcp/issues"
40
+ },
41
+ "homepage": "https://github.com/alexadark/youtube-transcript-mcp#readme",
42
+ "author": "alexadark",
43
+ "license": "MIT",
44
+ "dependencies": {
45
+ "@modelcontextprotocol/sdk": "^1.0.4",
46
+ "youtube-transcript-plus": "^1.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.10.2",
50
+ "typescript": "^5.7.2"
51
+ }
52
+ }