#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import 'dotenv/config';
import { searchVideos, searchVideosSchema, formatSearchResults } from './tools/search.js';
import { playPlaylist, playPlaylistSchema, formatPlayResult } from './tools/play.js';
const server = new Server(
{
name: 'youtube-mcp',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'searchVideos',
description: 'Search for YouTube videos by query. Returns video IDs, titles, channels, and descriptions.',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query for YouTube videos (e.g., "best english songs", "relaxing piano music")',
},
maxResults: {
type: 'number',
description: 'Maximum number of results to return (1-50, default: 10)',
default: 10,
minimum: 1,
maximum: 50,
},
},
required: ['query'],
},
},
{
name: 'playPlaylist',
description: 'Play YouTube videos in the browser. Provide video IDs directly or a search query to find and play videos automatically.',
inputSchema: {
type: 'object',
properties: {
videoIds: {
type: 'array',
items: { type: 'string' },
description: 'Array of YouTube video IDs to play as a playlist',
},
query: {
type: 'string',
description: 'Search query to find and play videos (used if videoIds not provided)',
},
maxResults: {
type: 'number',
description: 'Number of videos to play when using query (default: 10)',
default: 10,
minimum: 1,
maximum: 50,
},
},
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'searchVideos': {
const parsed = searchVideosSchema.parse(args);
const result = await searchVideos(parsed);
return {
content: [
{
type: 'text',
text: formatSearchResults(result),
},
],
isError: !result.success,
};
}
case 'playPlaylist': {
const parsed = playPlaylistSchema.parse(args);
const result = await playPlaylist(parsed);
return {
content: [
{
type: 'text',
text: formatPlayResult(result),
},
],
isError: !result.success,
};
}
default:
return {
content: [
{
type: 'text',
text: `Unknown tool: ${name}`,
},
],
isError: true,
};
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: 'text',
text: `Error executing ${name}: ${errorMessage}`,
},
],
isError: true,
};
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('YouTube MCP Server started successfully');
}
main().catch((error) => {
console.error('Failed to start YouTube MCP Server:', error);
process.exit(1);
});