search_movies
Search for movies by title and optional year using the MCP server, retrieving detailed information to streamline your film discovery process.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | Movie title to search for | |
| year | No | Movie year (optional) |
Implementation Reference
- src/index.ts:23-82 (registration)Full registration of the 'search_movies' MCP tool, including input schema, handler logic that formats search results into a markdown-like text response, and error handling.server.tool( "search_movies", { title: z.string().describe("Movie title to search for"), year: z.string().optional().describe("Movie year (optional)"), }, async ({ title, year }) => { try { const results = await searchMovies(title, year); if (results.length === 0) { return { content: [ { type: "text", text: `No movies found with title "${title}"${ year ? ` from year ${year}` : "" }.`, }, ], }; } let response = `🎬 **Found ${results.length} movies for "${title}":**\n\n`; results.forEach((movie, index) => { response += `${index + 1}. **${movie.title}** (${movie.year})\n`; response += ` - ID: ${movie.id}\n`; response += ` - Source: ${movie.source.toUpperCase()}\n`; if (movie.poster !== "N/A") { response += ` - Poster: ${movie.poster}\n`; } response += "\n"; }); response += '\n💡 *Use "get_movie_details" with the ID for more information.*'; return { content: [ { type: "text", text: response, }, ], }; } catch (error) { return { content: [ { type: "text", text: `❌ Error searching movies: ${ error instanceof Error ? error.message : "Unknown error" }`, }, ], }; } } );
- src/index.ts:29-81 (handler)The handler function executing the core tool logic: calls the searchMovies helper, processes results, formats a rich text response with movie list, or handles empty/error cases.async ({ title, year }) => { try { const results = await searchMovies(title, year); if (results.length === 0) { return { content: [ { type: "text", text: `No movies found with title "${title}"${ year ? ` from year ${year}` : "" }.`, }, ], }; } let response = `🎬 **Found ${results.length} movies for "${title}":**\n\n`; results.forEach((movie, index) => { response += `${index + 1}. **${movie.title}** (${movie.year})\n`; response += ` - ID: ${movie.id}\n`; response += ` - Source: ${movie.source.toUpperCase()}\n`; if (movie.poster !== "N/A") { response += ` - Poster: ${movie.poster}\n`; } response += "\n"; }); response += '\n💡 *Use "get_movie_details" with the ID for more information.*'; return { content: [ { type: "text", text: response, }, ], }; } catch (error) { return { content: [ { type: "text", text: `❌ Error searching movies: ${ error instanceof Error ? error.message : "Unknown error" }`, }, ], }; } }
- src/index.ts:25-28 (schema)Zod input schema defining parameters for the search_movies tool.{ title: z.string().describe("Movie title to search for"), year: z.string().optional().describe("Movie year (optional)"), },
- src/movie-apis.ts:199-217 (helper)Core helper function that performs parallel searches on OMDb and TMDb APIs, combines results, deduplicates by title (case-insensitive), and limits to 20 unique movies.export async function searchMovies( title: string, year?: string ): Promise<SearchResult[]> { const [omdbResults, tmdbResults] = await Promise.all([ searchMoviesOMDb(title, year), searchMoviesTMDb(title, year), ]); // Combinar resultados y eliminar duplicados por título const allResults = [...omdbResults, ...tmdbResults]; const uniqueResults = allResults.filter( (movie, index, self) => index === self.findIndex((m) => m.title.toLowerCase() === movie.title.toLowerCase()) ); return uniqueResults.slice(0, 20); // Limitar a 20 resultados }
- src/movie-apis.ts:20-54 (helper)Helper for searching movies specifically using OMDb API.export async function searchMoviesOMDb( title: string, year?: string ): Promise<SearchResult[]> { try { const params = new URLSearchParams({ apikey: OMDB_API_KEY, s: title, type: "movie", }); if (year) { params.append("y", year); } const response = await fetch(`${OMDB_BASE_URL}?${params}`); const data = (await response.json()) as OMDbSearchResult; if (data.Response === "False") { return []; } return data.Search.map((movie) => ({ title: movie.Title, year: movie.Year, id: movie.imdbID, type: movie.Type, poster: movie.Poster, source: "omdb" as const, })); } catch (error) { console.error("Error searching movies in OMDb:", error); return []; } }
- src/movie-apis.ts:102-138 (helper)Helper for searching movies specifically using TMDb API.export async function searchMoviesTMDb( title: string, year?: string ): Promise<SearchResult[]> { if (!TMDB_API_KEY) { return []; } try { const params = new URLSearchParams({ api_key: TMDB_API_KEY, query: title, language: "en-US", }); if (year) { params.append("year", year); } const response = await fetch(`${TMDB_BASE_URL}/search/movie?${params}`); const data = (await response.json()) as TMDbSearchResult; return data.results.slice(0, 10).map((movie) => ({ title: movie.title, year: movie.release_date ? movie.release_date.split("-")[0] : "", id: movie.id.toString(), type: "movie", poster: movie.poster_path ? `https://image.tmdb.org/t/p/w500${movie.poster_path}` : "N/A", source: "tmdb" as const, })); } catch (error) { console.error("Error searching movies in TMDb:", error); return []; } }