Skip to main content
Glama
youtube.ts5.36 kB
import type { McpToolContext, YouTubeSearchResults, YouTubeSearchResultItem } from '../types' import { exec } from 'node:child_process' import { promisify } from 'node:util' import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js' import * as dotenv from 'dotenv' import { ofetch } from 'ofetch' import { z } from 'zod' dotenv.config() const execPromise = promisify(exec) const YOUTUBE_API_KEY = process.env.YOUTUBE_API_KEY const YOUTUBE_API_BASE_URL = 'https://www.googleapis.com/youtube/v3' const YOUTUBE_MUSIC_WATCH_URL_PREFIX = 'https://music.youtube.com/watch?v=' async function searchYoutubeVideo( apiKey: string, query: string, maxResults: number = 5, ): Promise<YouTubeSearchResultItem[]> { try { const searchResults = await ofetch<YouTubeSearchResults>('/search', { baseURL: YOUTUBE_API_BASE_URL, query: { key: apiKey, part: 'snippet', maxResults, type: 'video', q: query, }, }) return searchResults?.items ?? [] } catch (error: unknown) { console.error('Error fetching YouTube search results:', error) const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred during YouTube search' throw new McpError(ErrorCode.InternalError, `YouTube API search failed: ${errorMessage}`) } } async function openUrlInBrowser(url: string, platform: NodeJS.Platform = process.platform): Promise<void> { let command: string = '' try { if (platform === 'darwin') { const appleScript = `tell application "Google Chrome" to open location "${url}"` command = `osascript -e '${appleScript.replace(/'/g, "'\''")}'` console.log(`Executing command for macOS: ${command}`) const { stderr } = await execPromise(command) if (stderr) { console.warn('osascript stderr:', stderr) } } else if (platform === 'win32') { command = `start "" "${url}"` console.log(`Executing command for Windows: ${command}`) await execPromise(command) } else { // Assume Linux/other POSIX-like command = `xdg-open "${url}"` console.log(`Executing command for Linux/Other: ${command}`) await execPromise(command) } } catch (execError: unknown) { console.error(`Error executing command "${command}":`, execError) const errorMsg = execError instanceof Error ? execError.message : 'Unknown execution error' throw new McpError(ErrorCode.InternalError, `Failed to open URL in browser: ${errorMsg}`) } } export function registerToolYoutubeMusic({ mcp }: McpToolContext): void { if (!YOUTUBE_API_KEY) { console.error('YOUTUBE_API_KEY environment variable is not set. YouTube tools will not be registered.') return } const apiKey = YOUTUBE_API_KEY // Make it const within the scope mcp.tool( 'searchTrack', 'Search for tracks on YouTube Music by name.', { trackName: z.string().describe('The name of the track to search for'), }, async ({ trackName }) => { try { const searchResults = await searchYoutubeVideo(apiKey, trackName, 5) return { content: [ { type: 'text', text: JSON.stringify(searchResults, null, 2) }, ], } } catch (error: unknown) { console.error('Error in searchTrack tool:', error) const message = error instanceof McpError ? error.message : 'An unexpected error occurred during search.' return { content: [{ type: 'text', text: `Error searching YouTube: ${message}` }], isError: true, } } }, ) mcp.tool( 'playTrack', 'Search for a track on YouTube Music and open the top result in the default browser.', { trackName: z.string().describe('The name of the track to search for and play'), }, async ({ trackName }) => { try { const searchResults = await searchYoutubeVideo(apiKey, trackName, 1) if (searchResults.length === 0) { return { content: [{ type: 'text', text: `No search results found for: ${trackName}` }], } } const topResult = searchResults[0] const videoId = topResult?.id?.videoId const title = topResult?.snippet?.title ?? 'Unknown Title' if (!videoId) { console.error('Could not find video ID in top search result:', topResult) throw new McpError(ErrorCode.InternalError, 'Could not extract video ID from YouTube search result.') } const youtubeMusicUrl = `${YOUTUBE_MUSIC_WATCH_URL_PREFIX}${videoId}` await openUrlInBrowser(youtubeMusicUrl) return { content: [{ type: 'text', text: `Attempting to play in browser: ${title} (${youtubeMusicUrl})` }], } } catch (error: unknown) { console.error('Error in playTrack tool:', error) if (error instanceof McpError && error.code === ErrorCode.InternalError) { throw error } const message = error instanceof McpError ? error.message : error instanceof Error ? error.message : 'An unexpected error occurred during track playback.' return { content: [{ type: 'text', text: `Error playing track: ${message}` }], isError: true, } } }, ) }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/instructa/mcp-youtube-music'

If you have feedback or need assistance with the MCP directory API, please join our Discord server