UNPKG

node-csfd-api

Version:

ČSFD API in JavaScript. Amazing NPM library for scrapping csfd.cz :)

284 lines (282 loc) 8.51 kB
#!/usr/bin/env node import { csfd } from "../index.js"; import { name, version } from "../package.js"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; //#region src/bin/mcp-server.ts const server = new McpServer({ name, version }); /** * TOOL 1: Search * Description: Essential first step to get IDs for movies or creators. */ server.registerTool("search", { title: "Search", description: "Searches for a movie, TV series, or person on CSFD.cz. Returns a list of results with IDs. Use this tool FIRST to find the ID needed for other tools.", inputSchema: { query: z.string().describe("Search query (movie title, series, or actor, director, etc. name)") }, annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false } }, async ({ query }) => { try { const results = await csfd.search(query); return { structuredContent: results, content: [{ type: "text", text: `Found ${results.movies.length} movies, ${results.tvSeries.length} TV series, and ${results.users.length} users.` }] }; } catch (error) { return { content: [{ type: "text", text: `Error during search: ${error}` }], isError: true }; } }); /** * TOOL 2: Movie Details * Description: Returns detailed info about a specific movie/series. */ server.registerTool("get_movie", { title: "Get Movie", description: "Retrieves detailed information about a specific movie or series, including rating, plot, genres, and actors. Requires a numeric CSFD ID.", inputSchema: { id: z.number().describe("CSFD Movie ID (found using the 'search' tool)") }, annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false } }, async ({ id }) => { try { const movie = await csfd.movie(id); return { structuredContent: movie, content: [{ type: "text", text: `Movie ${movie.title} (${movie.year}) with rating ${movie.rating} retrieved successfully.` }] }; } catch (error) { return { content: [{ type: "text", text: `Error retrieving movie details: ${error}` }], isError: true }; } }); /** * TOOL 3: Creator Details * Description: Returns detailed info about a person (actor, director). */ server.registerTool("get_creator", { title: "Get Creator", description: "Retrieves information about a specific creator (actor, director, etc.), including their biography and filmography. Requires a numeric CSFD ID.", inputSchema: { id: z.number().describe("CSFD Creator ID") }, annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false } }, async ({ id }) => { try { const creator = await csfd.creator(id); return { structuredContent: creator, content: [{ type: "text", text: `Creator ${creator.name} retrieved successfully.` }] }; } catch (error) { return { content: [{ type: "text", text: `Error retrieving creator details: ${error}` }], isError: true }; } }); /** * TOOL 4: User Ratings * Description: Returns ratings from a specific CSFD user. */ server.registerTool("get_user_ratings", { title: "Get User Ratings", description: "Retrieves movie ratings from a specific CSFD user. Returns a list of movies with their user rating (0-5 stars). Supports pagination and filtering by film type.", inputSchema: { user: z.union([z.string(), z.number()]).describe("CSFD User ID (numeric) or username"), page: z.number().optional().describe("Page number to fetch (default: 1)"), allPages: z.boolean().optional().describe("Fetch all pages at once (use wisely, may be slow)"), allPagesDelay: z.number().optional().describe("Delay in ms between page requests when using allPages"), excludes: z.array(z.string()).optional().describe("Film types to exclude (e.g. \"series\", \"tv-film\")"), includesOnly: z.array(z.string()).optional().describe("Only include these film types (e.g. \"film\")") }, annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false } }, async ({ user, page, allPages, allPagesDelay, excludes, includesOnly }) => { try { const results = await csfd.userRatings(user, { page, allPages, allPagesDelay, excludes, includesOnly }); return { structuredContent: { results }, content: [{ type: "text", text: `Retrieved ${results.length} user ratings successfully.` }] }; } catch (error) { return { content: [{ type: "text", text: `Error retrieving user ratings: ${error}` }], isError: true }; } }); /** * TOOL 5: User Reviews * Description: Returns reviews written by a specific CSFD user. */ server.registerTool("get_user_reviews", { title: "Get User Reviews", description: "Retrieves movie reviews written by a specific CSFD user. Returns a list of movies with their review text and rating.", inputSchema: { user: z.union([z.string(), z.number()]).describe("CSFD User ID (numeric) or username"), page: z.number().optional().describe("Page number to fetch (default: 1)"), allPages: z.boolean().optional().describe("Fetch all pages at once (use wisely, may be slow)"), allPagesDelay: z.number().optional().describe("Delay in ms between page requests when using allPages"), excludes: z.array(z.string()).optional().describe("Film types to exclude (e.g. \"series\", \"tv-film\")"), includesOnly: z.array(z.string()).optional().describe("Only include these film types (e.g. \"film\")") }, annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false } }, async ({ user, page, allPages, allPagesDelay, excludes, includesOnly }) => { try { const results = await csfd.userReviews(user, { page, allPages, allPagesDelay, excludes, includesOnly }); return { structuredContent: { results }, content: [{ type: "text", text: `Retrieved ${results.length} user reviews successfully.` }] }; } catch (error) { return { content: [{ type: "text", text: `Error retrieving user reviews: ${error}` }], isError: true }; } }); /** * TOOL 6: Cinemas * Description: Returns cinema screenings for a given district and time period. */ server.registerTool("get_cinemas", { title: "Get Cinemas", description: "Retrieves cinema screenings for a given district in Czech Republic. Returns a list of cinemas with their current screenings, showtimes, and movie names and ids.", inputSchema: { district: z.union([z.number(), z.string()]).describe("District ID (numeric) or name"), period: z.enum([ "today", "tomorrow", "weekend", "week", "month" ]).describe("Time period for screenings") }, annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false } }, async ({ district, period }) => { try { const results = await csfd.cinema(district, period); return { structuredContent: { results }, content: [{ type: "text", text: `Retrieved ${results.length} cinema screenings successfully.` }] }; } catch (error) { return { content: [{ type: "text", text: `Error retrieving cinema data: ${error}` }], isError: true }; } }); /** * PROMPT: Actor's Top Rated Works * Example: "Find movies starring Mads Mikkelsen and give me the top 5 sorted by rating." */ server.registerPrompt("actor-top-rated", { title: "Actor Top Rated", description: "Finds and ranks the best movies of a specific actor or creator.", argsSchema: { actorName: z.string().describe("Name of the actor or director") } }, async ({ actorName }) => { return { messages: [{ role: "user", content: { type: "text", text: `First, search for the person "${actorName}" to get their ID. Then, retrieve their full profile to see their filmography. From the results, identify the top 5 highest-rated movies they acted in. Finally, provide a bulleted list with the movie titles, years, and their CSFD ratings.` } }] }; }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error(`--- CSFD MCP Server (v${version}) ---`); console.error("Status: Running via Stdio"); console.error("\nTo use this server in Claude Desktop, add this to your config:"); console.error(JSON.stringify({ "node-csfd-api": { command: "npx", args: [ "-y", "node-csfd-api", "mcp" ] } }, null, 2)); } main().catch((error) => { console.error("Fatal error in MCP server:", error); process.exit(1); }); //#endregion export { };