@mcpflow.io/mcp-tmdb-mcp-server
Version:
镜像源
210 lines (209 loc) • 7.95 kB
JavaScript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import fetch from 'node-fetch';
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
const TMDB_API_KEY = process.env.TMDB_API_KEY;
const TMDB_BASE_URL = "https://api.themoviedb.org/3";
const server = new Server({
name: "example-servers/tmdb",
version: "0.1.0",
}, {
capabilities: {
resources: {},
tools: {},
},
});
async function fetchFromTMDB(endpoint, params = {}) {
const url = new URL(`${TMDB_BASE_URL}${endpoint}`);
url.searchParams.append("api_key", TMDB_API_KEY);
for (const [key, value] of Object.entries(params)) {
url.searchParams.append(key, value);
}
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error(`TMDB API error: ${response.statusText}`);
}
return response.json();
}
async function getMovieDetails(movieId) {
return fetchFromTMDB(`/movie/${movieId}`, { append_to_response: "credits,reviews" });
}
server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
const params = {
page: request.params?.cursor || "1",
};
const data = await fetchFromTMDB("/movie/popular", params);
return {
resources: data.results.map((movie) => ({
uri: `tmdb:///movie/${movie.id}`,
mimeType: "application/json",
name: `${movie.title} (${movie.release_date.split("-")[0]})`,
})),
nextCursor: data.page < data.total_pages ? String(data.page + 1) : undefined,
};
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const movieId = request.params.uri.replace("tmdb:///movie/", "");
const movie = await getMovieDetails(movieId);
const movieInfo = {
title: movie.title,
releaseDate: movie.release_date,
rating: movie.vote_average,
overview: movie.overview,
genres: movie.genres?.map(g => g.name).join(", "),
posterUrl: movie.poster_path ?
`https://image.tmdb.org/t/p/w500${movie.poster_path}` :
"No poster available",
cast: movie.credits?.cast?.slice(0, 5).map(actor => `${actor.name} as ${actor.character}`),
director: movie.credits?.crew?.find(person => person.job === "Director")?.name,
reviews: movie.reviews?.results?.slice(0, 3).map(review => ({
author: review.author,
content: review.content,
rating: review.rating
}))
};
return {
contents: [
{
uri: request.params.uri,
mimeType: "application/json",
text: JSON.stringify(movieInfo, null, 2),
},
],
};
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "search_movies",
description: "Search for movies by title or keywords",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Search query for movie titles",
},
},
required: ["query"],
},
},
{
name: "get_recommendations",
description: "Get movie recommendations based on a movie ID",
inputSchema: {
type: "object",
properties: {
movieId: {
type: "string",
description: "TMDB movie ID to get recommendations for",
},
},
required: ["movieId"],
},
},
{
name: "get_trending",
description: "Get trending movies for a time window",
inputSchema: {
type: "object",
properties: {
timeWindow: {
type: "string",
enum: ["day", "week"],
description: "Time window for trending movies",
},
},
required: ["timeWindow"],
},
},
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case "search_movies": {
const query = request.params.arguments?.query;
const data = await fetchFromTMDB("/search/movie", { query });
const results = data.results
.map((movie) => `${movie.title} (${movie.release_date?.split("-")[0]}) - ID: ${movie.id}\n` +
`Rating: ${movie.vote_average}/10\n` +
`Overview: ${movie.overview}\n`)
.join("\n---\n");
return {
content: [
{
type: "text",
text: `Found ${data.results.length} movies:\n\n${results}`,
},
],
isError: false,
};
}
case "get_recommendations": {
const movieId = request.params.arguments?.movieId;
const data = await fetchFromTMDB(`/movie/${movieId}/recommendations`);
const recommendations = data.results
.slice(0, 5)
.map((movie) => `${movie.title} (${movie.release_date?.split("-")[0]})\n` +
`Rating: ${movie.vote_average}/10\n` +
`Overview: ${movie.overview}\n`)
.join("\n---\n");
return {
content: [
{
type: "text",
text: `Top 5 recommendations:\n\n${recommendations}`,
},
],
isError: false,
};
}
case "get_trending": {
const timeWindow = request.params.arguments?.timeWindow;
const data = await fetchFromTMDB(`/trending/movie/${timeWindow}`);
const trending = data.results
.slice(0, 10)
.map((movie) => `${movie.title} (${movie.release_date?.split("-")[0]})\n` +
`Rating: ${movie.vote_average}/10\n` +
`Overview: ${movie.overview}\n`)
.join("\n---\n");
return {
content: [
{
type: "text",
text: `Trending movies for the ${timeWindow}:\n\n${trending}`,
},
],
isError: false,
};
}
default:
throw new Error("Tool not found");
}
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`,
},
],
isError: true,
};
}
});
// Start the server
if (!TMDB_API_KEY) {
console.error("TMDB_API_KEY environment variable is required");
process.exit(1);
}
const transport = new StdioServerTransport();
server.connect(transport).catch((error) => {
console.error("Server connection error:", error);
process.exit(1);
});