#
tokens: 7002/50000 12/12 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── build
│   ├── config.js
│   ├── handlers.js
│   ├── index.js
│   ├── prompts.js
│   ├── resource-templates.js
│   ├── resources.js
│   ├── tmdb-api.js
│   └── tools.js
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── config.ts
│   ├── handlers.ts
│   ├── index.ts
│   ├── prompts.ts
│   ├── resource-templates.ts
│   ├── resources.ts
│   ├── tmdb-api.ts
│   └── tools.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | .env
2 | /node_modules
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # TMDB MCP Server
  2 | 
  3 | This project implements a Model Context Protocol (MCP) server that integrates with The Movie Database (TMDB) API. It enables AI assistants like Claude to interact with movie data, providing capabilities for searching, retrieving details, and generating content related to movies.
  4 | 
  5 | ## Features
  6 | 
  7 | ### Resources
  8 | - **Static Resources**:
  9 |   - `tmdb://info` - Information about TMDB API
 10 |   - `tmdb://trending` - Currently trending movies
 11 |   
 12 | - **Resource Templates**:
 13 |   - `tmdb://movie/{id}` - Detailed information about a specific movie
 14 | 
 15 | ### Prompts
 16 | - **Movie Review**: Generate a customized movie review with specified style and rating
 17 | - **Movie Recommendation**: Get personalized movie recommendations based on genres and mood
 18 | 
 19 | ### Tools
 20 | - **Search Movies**: Find movies by title or keywords
 21 | - **Get Trending Movies**: Retrieve trending movies for day or week
 22 | - **Get Similar Movies**: Find movies similar to a specified movie
 23 | 
 24 | ## Setup Instructions
 25 | 
 26 | ### Prerequisites
 27 | - Node.js (v16 or later)
 28 | - npm or yarn
 29 | - TMDB API key
 30 | 
 31 | ### Installation
 32 | 
 33 | 1. Clone this repository
 34 |    ```
 35 |    git clone https://github.com/your-username/tmdb-mcp.git
 36 |    cd tmdb-mcp
 37 |    ```
 38 | 
 39 | 2. Install dependencies
 40 |    ```
 41 |    npm install
 42 |    ```
 43 | 
 44 | 3. Configure your TMDB API key
 45 |    - Create a `.env` file in the project root (alternative: edit `src/config.ts` directly)
 46 |    - Add your TMDB API key: `TMDB_API_KEY=your_api_key_here`
 47 | 
 48 | 4. Build the project
 49 |    ```
 50 |    npm run build
 51 |    ```
 52 | 
 53 | 5. Start the server
 54 |    ```
 55 |    npm start
 56 |    ```
 57 | 
 58 | ### Setup for Claude Desktop
 59 | 
 60 | 1. Open Claude Desktop
 61 | 2. Go to Settings > Developer tab
 62 | 3. Click "Edit Config" to open the configuration file
 63 | 4. Add the following to your configuration:
 64 | 
 65 | ```json
 66 | {
 67 |   "mcpServers": {
 68 |     "tmdb-mcp": {
 69 |       "command": "node",
 70 |       "args": ["/absolute/path/to/your/tmdb-mcp/build/index.js"]
 71 |     }
 72 |   }
 73 | }
 74 | ```
 75 | 
 76 | 5. Restart Claude Desktop
 77 | 
 78 | ## Usage Examples
 79 | 
 80 | ### Using Static Resources
 81 | 
 82 | - "What is TMDB?"
 83 | - "Show me currently trending movies"
 84 | 
 85 | ### Using Resource Templates
 86 | 
 87 | - "Get details about movie with ID 550" (Fight Club)
 88 | - "Tell me about the movie with ID 155" (The Dark Knight)
 89 | 
 90 | ### Using Prompts
 91 | 
 92 | - "Write a detailed review for Inception with a rating of 9/10"
 93 | - "Recommend sci-fi movies for a thoughtful mood"
 94 | 
 95 | ### Using Tools
 96 | 
 97 | - "Search for movies about space exploration"
 98 | - "What are the trending movies today?"
 99 | - "Find movies similar to The Matrix"
100 | 
101 | ## Development
102 | 
103 | ### Project Structure
104 | 
105 | ```
106 | tmdb-mcp/
107 | ├── src/
108 | │   ├── index.ts                # Main server file
109 | │   ├── config.ts               # Configuration and API keys
110 | │   ├── handlers.ts             # Request handlers
111 | │   ├── resources.ts            # Static resources
112 | │   ├── resource-templates.ts   # Dynamic resource templates
113 | │   ├── prompts.ts              # Prompt definitions
114 | │   ├── tools.ts                # Tool implementations
115 | │   └── tmdb-api.ts             # TMDB API wrapper
116 | ├── package.json
117 | ├── tsconfig.json
118 | └── README.md
119 | ```
120 | 
121 | ### Testing
122 | 
123 | Use the MCP Inspector to test your server during development:
124 | 
125 | ```
126 | npx @modelcontextprotocol/inspector node build/index.js
127 | ```
128 | 
129 | ## License
130 | 
131 | MIT
132 | 
133 | ## Acknowledgements
134 | 
135 | - [The Movie Database (TMDB)](https://www.themoviedb.org/)
136 | - [Model Context Protocol](https://modelcontextprotocol.github.io/)
```

--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------

```typescript
1 | import dotenv from "dotenv";
2 | 
3 | dotenv.config();
4 | 
5 | export const TMDB_API_KEY = process.env.TMDB_API_KEY;
6 | export const TMDB_BASE_URL = "https://api.themoviedb.org/3";
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "Node16",
 5 |     "moduleResolution": "Node16",
 6 |     "outDir": "./build",
 7 |     "rootDir": "./src",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true
12 |   },
13 |   "include": ["src/**/*"]
14 | }
15 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "tmdb-mcp",
 3 |   "version": "1.0.0",
 4 |   "type": "module",
 5 |   "scripts": {
 6 |     "build": "tsc",
 7 |     "start": "node build/index.js",
 8 |     "dev": "tsc && node build/index.js"
 9 |   },
10 |   "dependencies": {
11 |     "@modelcontextprotocol/sdk": "^1.1.0",
12 |     "axios": "^1.8.3",
13 |     "dotenv": "^16.4.7",
14 |     "node-fetch": "^3.3.0"
15 |   },
16 |   "devDependencies": {
17 |     "@types/node": "^22.10.5",
18 |     "typescript": "^5.7.2"
19 |   }
20 | }
21 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 3 | import { setupHandlers } from "./handlers.js";
 4 | 
 5 | const server = new Server(
 6 |   {
 7 |     name: "tmdb-mcp",
 8 |     version: "1.0.0",
 9 |   },
10 |   {
11 |     capabilities: {
12 |       resources: {},
13 |       prompts: {},
14 |       tools: {},
15 |     },
16 |   },
17 | );
18 | 
19 | setupHandlers(server);
20 | 
21 | // Start server using stdio transport
22 | const transport = new StdioServerTransport();
23 | await server.connect(transport);
24 | 
25 | console.info(
26 |   '{"jsonrpc": "2.0", "method": "log", "params": { "message": "TMDB MCP Server running..." }}',
27 | );
```

--------------------------------------------------------------------------------
/src/resources.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { getTrendingMovies } from './tmdb-api.js';
 2 | 
 3 | export const resources = [
 4 |   {
 5 |     uri: "tmdb://info",
 6 |     name: "TMDB Info",
 7 |     description: "Information about The Movie Database API",
 8 |     mimeType: "text/plain",
 9 |   },
10 |   {
11 |     uri: "tmdb://trending",
12 |     name: "Trending Movies",
13 |     description: "Currently trending movies on TMDB",
14 |     mimeType: "application/json",
15 |   }
16 | ];
17 | 
18 | export const resourceHandlers = {
19 |   "tmdb://info": async () => ({
20 |     contents: [
21 |       {
22 |         uri: "tmdb://info",
23 |         text: "The Movie Database (TMDB) is a popular, user-editable database for movies and TV shows. This MCP server provides access to TMDB data through resources, prompts, and tools.",
24 |       },
25 |     ],
26 |   }),
27 |   "tmdb://trending": async () => {
28 |     const trendingData = await getTrendingMovies();
29 |     return {
30 |       contents: [
31 |         {
32 |           uri: "tmdb://trending",
33 |           text: JSON.stringify(trendingData, null, 2),
34 |         },
35 |       ],
36 |     };
37 |   },
38 | };
```

--------------------------------------------------------------------------------
/src/resource-templates.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { getMovieDetails } from './tmdb-api.js';
 2 | 
 3 | export const resourceTemplates = [
 4 |   {
 5 |     uriTemplate: "tmdb://movie/{id}",
 6 |     name: "Movie Details",
 7 |     description: "Get details about a specific movie by ID",
 8 |     mimeType: "application/json",
 9 |   },
10 | ];
11 | 
12 | const movieDetailsExp = /^tmdb:\/\/movie\/(\d+)$/;
13 | 
14 | export const getResourceTemplate = async (uri: string) => {
15 |   const movieMatch = uri.match(movieDetailsExp);
16 |   if (movieMatch) {
17 |     const movieId = movieMatch[1];
18 |     
19 |     return async () => {
20 |       try {
21 |         // Get the raw movie details from your API
22 |         const movieDetails = await getMovieDetails(movieId);
23 |         
24 |         // Return in the correct format expected by the MCP SDK
25 |         return {
26 |           contents: [
27 |             {
28 |               uri,
29 |               text: JSON.stringify(movieDetails, null, 2), // This should be the raw movie data
30 |             },
31 |           ],
32 |         };
33 |       } catch (error: unknown) {
34 |         if (error instanceof Error) {
35 |           throw new Error(`Failed to fetch movie details: ${error.message}`);
36 |         }
37 |         throw new Error('Failed to fetch movie details: Unknown error');
38 |       }
39 |     };
40 |   }
41 |   return null;
42 | };
```

--------------------------------------------------------------------------------
/src/prompts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export const prompts = {
 2 |   "movie-review": {
 3 |     name: "movie-review",
 4 |     description: "Create a movie review based on provided details",
 5 |     arguments: [
 6 |       {
 7 |         name: "title",
 8 |         description: "Title of the movie",
 9 |         required: true,
10 |       },
11 |       {
12 |         name: "rating",
13 |         description: "Your rating of the movie (1-10)",
14 |         required: true,
15 |       },
16 |       {
17 |         name: "style",
18 |         description: "Review style (brief, detailed, critical)",
19 |         required: false,
20 |       },
21 |     ],
22 |   },
23 |   "movie-recommendation": {
24 |     name: "movie-recommendation",
25 |     description: "Get personalized movie recommendations",
26 |     arguments: [
27 |       {
28 |         name: "genres",
29 |         description: "Preferred genres (comma-separated)",
30 |         required: true,
31 |       },
32 |       {
33 |         name: "mood",
34 |         description: "Current mood (happy, thoughtful, excited, etc.)",
35 |         required: false,
36 |       },
37 |       {
38 |         name: "avoidGenres",
39 |         description: "Genres to avoid (comma-separated)",
40 |         required: false,
41 |       },
42 |     ],
43 |   },
44 | };
45 | 
46 | export const promptHandlers = {
47 |   "movie-review": ({
48 |     title,
49 |     rating,
50 |     style = "detailed",
51 |   }: {
52 |     title: string;
53 |     rating: number;
54 |     style?: string;
55 |   }) => {
56 |     return {
57 |       messages: [
58 |         {
59 |           role: "user",
60 |           content: {
61 |             type: "text",
62 |             text: `Write a ${style} review for the movie "${title}" with a rating of ${rating}/10. Include your thoughts on the plot, characters, direction, and overall experience.`,
63 |           },
64 |         },
65 |       ],
66 |     };
67 |   },
68 |   "movie-recommendation": ({
69 |     genres,
70 |     mood = "any",
71 |     avoidGenres = "",
72 |   }: {
73 |     genres: string[];
74 |     mood?: string;
75 |     avoidGenres?: string;
76 |   }) => {
77 |     return {
78 |       messages: [
79 |         {
80 |           role: "user",
81 |           content: {
82 |             type: "text",
83 |             text: `Recommend movies in the following genres: ${genres}. I'm in a ${mood} mood. Please avoid these genres if possible: ${avoidGenres}. Include a brief description of why you're recommending each movie.`,
84 |           },
85 |         },
86 |       ],
87 |     };
88 |   },
89 | };
90 | 
```

--------------------------------------------------------------------------------
/src/handlers.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import {
 2 |   CallToolRequestSchema,
 3 |   GetPromptRequestSchema,
 4 |   ListPromptsRequestSchema,
 5 |   ListResourcesRequestSchema,
 6 |   ListResourceTemplatesRequestSchema,
 7 |   ListToolsRequestSchema,
 8 |   ReadResourceRequestSchema,
 9 | } from "@modelcontextprotocol/sdk/types.js";
10 | import { resourceHandlers, resources } from "./resources.js";
11 | import { getResourceTemplate, resourceTemplates } from "./resource-templates.js";
12 | import { promptHandlers, prompts } from "./prompts.js";
13 | import { toolHandlers, tools } from "./tools.js";
14 | import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
15 | 
16 | export const setupHandlers = (server: Server): void => {
17 |   // Resource handlers
18 |   server.setRequestHandler(
19 |     ListResourcesRequestSchema,
20 |     async () => ({ resources }),
21 |   );
22 |   
23 |   server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
24 |     resourceTemplates,
25 |   }));
26 |   
27 |   server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
28 |     const { uri } = request.params;
29 |     // Using type assertion to tell TypeScript this is a valid key
30 |     const resourceHandler = resourceHandlers[uri as keyof typeof resourceHandlers];
31 |     if (resourceHandler) return await resourceHandler();
32 |     
33 |     const resourceTemplateHandler = await getResourceTemplate(uri);
34 |     if (resourceTemplateHandler) return await resourceTemplateHandler();
35 |     
36 |     throw new Error(`Resource not found: ${uri}`);
37 |   });
38 | 
39 |   // Prompt handlers
40 |   server.setRequestHandler(ListPromptsRequestSchema, async () => ({
41 |     prompts: Object.values(prompts),
42 |   }));
43 |   
44 |   server.setRequestHandler(GetPromptRequestSchema, async (request) => {
45 |     const { name, arguments: args } = request.params;
46 |     // Using type assertion to tell TypeScript this is a valid key
47 |     const promptHandler = promptHandlers[name as keyof typeof promptHandlers];
48 |     
49 |     if (promptHandler) {
50 |       return promptHandler(args as any);
51 |     }
52 |     
53 |     throw new Error(`Prompt not found: ${name}`);
54 |   });
55 | 
56 |   // Tool handlers
57 |   server.setRequestHandler(ListToolsRequestSchema, async () => ({
58 |     tools: Object.values(tools),
59 |   }));
60 | 
61 |   // This is the key fix - we need to format the response properly
62 |   server.setRequestHandler(CallToolRequestSchema, async (request) => {
63 |     try {
64 |       const { name, arguments: args } = request.params;
65 |       // Using type assertion to tell TypeScript this is a valid key
66 |       const handler = toolHandlers[name as keyof typeof toolHandlers];
67 | 
68 |       if (!handler) throw new Error(`Tool not found: ${name}`);
69 | 
70 |       // Execute the handler but wrap the response in the expected format
71 |       const result = await handler(args as any);
72 |       
73 |       // Return in the format expected by the SDK
74 |       return {
75 |         tools: [{
76 |           name,
77 |           inputSchema: {
78 |             type: "object",
79 |             properties: {} // This would ideally be populated with actual schema
80 |           },
81 |           description: `Tool: ${name}`,
82 |           result
83 |         }]
84 |       };
85 |     } catch (error) {
86 |       // Properly handle errors
87 |       if (error instanceof Error) {
88 |         return {
89 |           tools: [],
90 |           error: error.message
91 |         };
92 |       }
93 |       return {
94 |         tools: [],
95 |         error: "An unknown error occurred"
96 |       };
97 |     }
98 |   });
99 | };
```

--------------------------------------------------------------------------------
/src/tools.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import {
  2 |   searchMovies,
  3 |   getTrendingMovies,
  4 |   getSimilarMovies,
  5 |   getMovieDetails,
  6 | } from "./tmdb-api.js";
  7 | 
  8 | export const tools = {
  9 |   "search-movies": {
 10 |     name: "search-movies",
 11 |     description: "Search for movies by title or keywords",
 12 |     inputSchema: {
 13 |       type: "object",
 14 |       properties: {
 15 |         query: {
 16 |           type: "string",
 17 |           description: "Search query",
 18 |         },
 19 |         page: {
 20 |           type: "number",
 21 |           description: "Page number for results",
 22 |         },
 23 |       },
 24 |       required: ["query"],
 25 |     },
 26 |   },
 27 |   "get-trending": {
 28 |     name: "get-trending",
 29 |     description: "Get trending movies",
 30 |     inputSchema: {
 31 |       type: "object",
 32 |       properties: {
 33 |         timeWindow: {
 34 |           type: "string",
 35 |           enum: ["day", "week"],
 36 |           description: "Time window for trending movies",
 37 |         },
 38 |       },
 39 |       required: [],
 40 |     },
 41 |   },
 42 |   "get-similar": {
 43 |     name: "get-similar",
 44 |     description: "Get similar movies to a given movie",
 45 |     inputSchema: {
 46 |       type: "object",
 47 |       properties: {
 48 |         movieId: {
 49 |           type: "string",
 50 |           description: "ID of the movie to find similar movies for",
 51 |         },
 52 |       },
 53 |       required: ["movieId"],
 54 |     },
 55 |   },
 56 |   "get-movie-details": {
 57 |     name: "get-movie-details",
 58 |     description: "Get detailed information about a specific movie",
 59 |     inputSchema: {
 60 |       type: "object",
 61 |       properties: {
 62 |         movieId: {
 63 |           type: "string",
 64 |           description: "ID of the movie to get details for",
 65 |         },
 66 |       },
 67 |       required: ["movieId"],
 68 |     },
 69 |   },
 70 | };
 71 | 
 72 | export const toolHandlers = {
 73 |   "search-movies": async ({
 74 |     query,
 75 |     page = 1,
 76 |   }: {
 77 |     query: string;
 78 |     page?: number;
 79 |   }) => {
 80 |     try {
 81 |       // Return the raw results directly
 82 |       return await searchMovies(query, page);
 83 |     } catch (error: unknown) {
 84 |       if (error instanceof Error) {
 85 |         throw new Error(`Failed to search movies: ${error.message}`);
 86 |       }
 87 |       throw new Error("Failed to search movies: Unknown error");
 88 |     }
 89 |   },
 90 |   "get-trending": async ({
 91 |     timeWindow = "week",
 92 |   }: {
 93 |     timeWindow?: "day" | "week";
 94 |   }) => {
 95 |     try {
 96 |       // Return the raw results directly
 97 |       return await getTrendingMovies(timeWindow);
 98 |     } catch (error: unknown) {
 99 |       if (error instanceof Error) {
100 |         throw new Error(`Failed to get trending movies: ${error.message}`);
101 |       }
102 |       throw new Error("Failed to get trending movies: Unknown error");
103 |     }
104 |   },
105 |   "get-similar": async ({ movieId }: { movieId: string }) => {
106 |     try {
107 |       // Return the raw results directly
108 |       return await getSimilarMovies(movieId);
109 |     } catch (error: unknown) {
110 |       if (error instanceof Error) {
111 |         throw new Error(`Failed to get similar movies: ${error.message}`);
112 |       }
113 |       throw new Error("Failed to get similar movies: Unknown error");
114 |     }
115 |   },
116 |   "get-movie-details": async ({ movieId }: { movieId: string }) => {
117 |     try {
118 |       const result = await getMovieDetails(movieId);
119 |       return result;
120 |     } catch (error: unknown) {
121 |       if (error instanceof Error) {
122 |         return { text: `Failed to get movie details: ${error.message}` };
123 |       }
124 |       return { text: "Failed to get movie details: Unknown error" };
125 |     }
126 |   },
127 | };
128 | 
```

--------------------------------------------------------------------------------
/src/tmdb-api.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import axios, { AxiosRequestConfig, AxiosError, AxiosResponse } from 'axios';
  2 | import { TMDB_API_KEY, TMDB_BASE_URL } from './config.js';
  3 | 
  4 | // Response types
  5 | interface MovieResult {
  6 |   id: number;
  7 |   title: string;
  8 |   poster_path: string | null;
  9 |   backdrop_path: string | null;
 10 |   overview: string;
 11 |   release_date: string;
 12 |   vote_average: number;
 13 |   [key: string]: any;
 14 | }
 15 | 
 16 | interface SearchMoviesResponse {
 17 |   page: number;
 18 |   results: MovieResult[];
 19 |   total_results: number;
 20 |   total_pages: number;
 21 | }
 22 | 
 23 | interface MovieDetailsResponse extends MovieResult {
 24 |   credits: {
 25 |     cast: Array<{
 26 |       id: number;
 27 |       name: string;
 28 |       character: string;
 29 |       profile_path: string | null;
 30 |       [key: string]: any;
 31 |     }>;
 32 |     crew: Array<{
 33 |       id: number;
 34 |       name: string;
 35 |       job: string;
 36 |       department: string;
 37 |       [key: string]: any;
 38 |     }>;
 39 |   };
 40 |   videos: {
 41 |     results: Array<{
 42 |       id: string;
 43 |       key: string;
 44 |       site: string;
 45 |       type: string;
 46 |       [key: string]: any;
 47 |     }>;
 48 |   };
 49 |   images: {
 50 |     backdrops: Array<{
 51 |       file_path: string;
 52 |       width: number;
 53 |       height: number;
 54 |       [key: string]: any;
 55 |     }>;
 56 |     posters: Array<{
 57 |       file_path: string;
 58 |       width: number;
 59 |       height: number;
 60 |       [key: string]: any;
 61 |     }>;
 62 |   };
 63 | }
 64 | 
 65 | // Axios instance with default configurations
 66 | const tmdbClient = axios.create({
 67 |   baseURL: TMDB_BASE_URL,
 68 |   timeout: 10000, // 10 seconds timeout
 69 |   params: {
 70 |     api_key: TMDB_API_KEY
 71 |   }
 72 | });
 73 | 
 74 | // Retry logic
 75 | const axiosWithRetry = async <T>(
 76 |   config: AxiosRequestConfig, 
 77 |   retries: number = 3, 
 78 |   backoff: number = 300
 79 | ): Promise<AxiosResponse<T>> => {
 80 |   try {
 81 |     return await tmdbClient(config);
 82 |   } catch (err) {
 83 |     const error = err as AxiosError;
 84 |     
 85 |     if (retries > 0 && (
 86 |       error.code === 'ECONNRESET' || 
 87 |       error.code === 'ETIMEDOUT' || 
 88 |       (error.response && (error.response.status >= 500 || error.response.status === 429))
 89 |     )) {
 90 |       console.log(`Request failed, retrying... (${retries} attempts left)`);
 91 |       await new Promise(resolve => setTimeout(resolve, backoff));
 92 |       return axiosWithRetry<T>(config, retries - 1, backoff * 2);
 93 |     }
 94 |     throw error;
 95 |   }
 96 | };
 97 | 
 98 | export async function searchMovies(query: string, page: number = 1): Promise<SearchMoviesResponse> {
 99 |   try {
100 |     const response = await axiosWithRetry<SearchMoviesResponse>({
101 |       url: '/search/movie',
102 |       params: {
103 |         query: query,
104 |         page: page
105 |       }
106 |     });
107 |     return response.data;
108 |   } catch (error) {
109 |     const err = error as Error;
110 |     console.error('Error searching movies:', err.message);
111 |     throw new Error(`Failed to search movies: ${err.message}`);
112 |   }
113 | }
114 | 
115 | export async function getMovieDetails(movieId: number | string): Promise<MovieDetailsResponse> {
116 |   try {
117 |     const response = await axiosWithRetry<MovieDetailsResponse>({
118 |       url: `/movie/${movieId}`,
119 |       params: {
120 |         append_to_response: 'credits,videos,images'
121 |       }
122 |     });
123 |     return response.data;
124 |   } catch (error) {
125 |     const err = error as Error;
126 |     console.error('Error getting movie details:', err.message);
127 |     throw new Error(`Failed to get movie details: ${err.message}`);
128 |   }
129 | }
130 | 
131 | export async function getTrendingMovies(timeWindow: 'day' | 'week' = 'week'): Promise<SearchMoviesResponse> {
132 |   try {
133 |     const response = await axiosWithRetry<SearchMoviesResponse>({
134 |       url: `/trending/movie/${timeWindow}`
135 |     });
136 |     return response.data;
137 |   } catch (error) {
138 |     const err = error as Error;
139 |     console.error('Error getting trending movies:', err.message);
140 |     throw new Error(`Failed to get trending movies: ${err.message}`);
141 |   }
142 | }
143 | 
144 | export async function getSimilarMovies(movieId: number | string): Promise<SearchMoviesResponse> {
145 |   try {
146 |     const response = await axiosWithRetry<SearchMoviesResponse>({
147 |       url: `/movie/${movieId}/similar`
148 |     });
149 |     return response.data;
150 |   } catch (error) {
151 |     const err = error as Error;
152 |     console.error('Error getting similar movies:', err.message);
153 |     throw new Error(`Failed to get similar movies: ${err.message}`);
154 |   }
155 | }
```