Skip to content
BuildLog
>2 posts shipped-30m ago-videos
Artificial Intelligence

Building an MCP Movie Assistant with TMDB and STDIO

A practical walkthrough of building a local MCP movie server with TypeScript, Zod, TMDB, STDIO transport, and MCP Inspector.

S

Samuel Adams

Editorial

4 min read.Jul 26
MCP Movie Assistant: an AI client connects through an MCP server to a movie discovery interface.

AI models are good at understanding requests such as “recommend a science-fiction movie from 2024,” but they do not automatically have access to live movie data. To retrieve current information, they need a controlled way to use external tools.

This project is a local MCP server that connects an AI client to the TMDB API. It can search movies, retrieve details, show trending titles, find recommendations, and discover movies with filters such as genre, rating, runtime, language, and streaming provider.

What is MCP?

Model Context Protocol (MCP) is a standard way for AI applications to connect with tools, data sources, and external services.

An MCP server exposes capabilities. An MCP client—such as an AI application—connects to that server, discovers its available tools, and calls the right tool when needed. In this project, the client does not need to know TMDB endpoints, authentication methods, query parameters, or response formats. The server owns those details.

Architectureplaintext
AI application
    ↓
MCP client
    ↓ JSON-RPC over STDIO
Movie Assistant MCP server
    ↓ HTTPS
TMDB API

Why use MCP?

Without MCP, every AI application needs a custom integration for every service it uses. MCP provides a shared contract: clients can discover tools, tools declare their expected inputs, servers validate and execute requests, and servers return results or structured errors.

The client discovers tools through tools/list and calls them through tools/call. MCP’s server concepts guide explains how tools are schema-defined interfaces with typed inputs and outputs.

The MCP handshake

Before a client can search for movies, it connects to the server. MCP has three broad lifecycle phases: initialization, normal operation, and shutdown. During initialization, the client and server negotiate a compatible protocol version and exchange capabilities.

After initialization, the client asks which tools the server provides. This movie server exposes search-movies, get-movie-details, get-trending-movies, get-movie-recommendations, and discover-movies. Only after tool discovery does the client invoke a tool.

src/index.tstypescript
const transport = new StdioServerTransport();
await server.connect(transport);

The client initiates the handshake; the SDK handles protocol mechanics after the transport is connected. Read the MCP lifecycle specification for the precise initialization sequence.

Why STDIO transport?

This project uses StdioServerTransport. The AI client launches the MCP server as a local child process, sends JSON-RPC messages through stdin, and receives protocol messages through stdout.

  • No HTTP server or open port is required.
  • The client controls the server process lifecycle.
  • TMDB credentials remain in the local server environment.
  • It is simple to run and test during development.

One important rule: stdout is reserved for valid MCP messages. Regular logs must go to stderr; otherwise they can corrupt the protocol stream.

src/index.tstypescript
console.error("Movie Assistant MCP Server running on stdio");

The MCP transport specification describes STDIO messages, logging, and the stdout requirement.

Creating the server

src/server.tstypescript
import { McpServer } from "@modelcontextprotocol/server";

export const server = new McpServer({
  name: "movie-assistant-server",
  version: "1.0.0",
});

The entry file imports the modules that register tools and prompts. That keeps startup and transport concerns separate from movie-related functionality.

Why Zod?

Every tool needs a clear input contract: which arguments are accepted, which are optional, and which values are valid. Zod defines runtime validation schemas that also work naturally with TypeScript.

src/tools.tstypescript
inputSchema: z.object({
  query: z.string().min(1).describe("Movie title to search for"),
  year: z.number().int().optional().describe("Optional release year"),
  page: z.number().int().min(1).default(1),
}),

This schema states that query must be a non-empty string, year is optional but must be an integer, and page must be an integer of at least one. If page is omitted, it defaults to one.

The MCP TypeScript SDK can expose a JSON Schema to the client, validate arguments before the handler runs, and infer handler types. Read the MCP schema-library guide and the Zod schema documentation for more detail.

Why are there curly braces in the handler?

src/tools.tstypescript
async ({ query, year, page }) => {
  // query, year, and page are ready to use
}

Those braces use JavaScript object destructuring; they are not extracting the schema. MCP passes validated input as an object, and destructuring pulls query, year, and page into local variables.

The two roles of bracestypescript
z.object({ ... })              // defines the expected input object
async ({ query, year, page })  // extracts properties from that object

Turning a tool call into a TMDB request

The search-movies tool receives validated input and turns it into TMDB query parameters before making an HTTPS request.

src/tools.tstypescript
const params = new URLSearchParams({
  query,
  page: page.toString(),
  include_adult: "false",
  language: "en-US",
});

if (year) {
  params.set("primary_release_year", year.toString());
}

The helper supports a preferred TMDB v4 bearer token and a v3 API-key fallback. Credentials live in .env, keeping secrets out of source code and tool schemas.

A realistic example

For a request like “Find popular science-fiction movies from 2024 with a rating of at least 7.5,” the AI client can select discover-movies and send structured arguments.

Example tool argumentsjson
{
  "genreIds": [878],
  "year": 2024,
  "minimumRating": 7.5,
  "sortBy": "popularity.desc",
  "page": 1
}

The server validates the request, maps the values to TMDB’s movie-discovery endpoint, and returns results. The AI handles conversation and interpretation; the MCP server handles controlled access to live data.

Testing with MCP Inspector

MCP Inspector is the official interactive tool for testing and debugging MCP servers. It can launch a local server, display capabilities, list tools and prompts, submit arguments, show responses, and surface logs.

This project already includes an Inspector command:

Terminalbash
npm run inspector

The script builds the TypeScript source and then launches the compiled server:

What the Inspector script runsbash
npm run build
npx @modelcontextprotocol/inspector node build/index.js

Once Inspector opens, connect to the server, confirm initialization, open the Tools tab, and verify that each movie tool appears with its description and schema. Test valid inputs, invalid inputs, and TMDB error responses.

Valid search-movies inputjson
{
  "query": "Interstellar",
  "year": 2014,
  "page": 1
}
Invalid search-movies inputjson
{
  "query": "",
  "page": 0
}

The first request should return movie data. The second should fail validation because the title is empty and the page is invalid. Inspector also helps confirm that logs go to stderr instead of stdout.

Related