Challenge

Create a Movie Resource

In the previous lesson, you learned about resources and how they differ from tools.

In this challenge, you will add a resource to your Movies GraphRAG Server that exposes detailed movie information by its tmdbId property.

Challenge Goals

To complete this challenge, you will:

  1. Add a movie resource with a dynamic URI pattern using ResourceTemplate
  2. Query Neo4j for comprehensive movie details
  3. Return structured data in a consistent format
  4. Include cast, genres, directors, and metadata
  5. Test the resource with MCP Inspector
Solution Available

If you get stuck, you can review the complete solution in the repository at solutions/8c-create-resource/index.ts.

Step 1: Understanding the Resource URI

Resources use URI patterns to identify what data to fetch.

For a movie resource, you will use the pattern: movie://{tmdbId}

Examples:

  • movie://603 - The Matrix
  • movie://605 - The Matrix Reloaded
  • movie://13 - Forrest Gump

This allows clients to request specific movies by their TMDB ID.

Step 2: Create the Resource

Add this resource to your server/index.ts file, after your existing tools.

First, import ResourceTemplate from the SDK:

typescript:server/index.ts
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";

Then register the movie resource:

typescript:server/index.ts
server.registerResource(
  "movie",
  new ResourceTemplate("movie://{tmdbId}", { list: undefined }),
  { description: "Get detailed information about a specific movie by TMDB ID", mimeType: "application/json" },
  async (uri, { tmdbId }) => {
    console.error(`Fetching movie details for TMDB ID: ${tmdbId}`);
 
    try {
      const { records } = await driver.executeQuery(
        `
        MATCH (m:Movie {tmdbId: $tmdbId})
        RETURN m.title AS title,
               m.released AS released,
               m.tagline AS tagline,
               m.imdbRating AS rating,
               m.runtime AS runtime,
               m.plot AS plot,
               [ (m)-[:IN_GENRE]->(g:Genre) | g.name ] AS genres,
               [ (p)-[r:ACTED_IN]->(m) | { name: p.name, role: r.role } ][0..5] AS cast,
               [ (d)-[:DIRECTED]->(m) | d.name ] AS directors
        `,
        { tmdbId: String(tmdbId) },
        { database }
      );
 
      if (records.length === 0) {
        return {
          contents: [{
            uri: uri.href,
            mimeType: "application/json",
            text: JSON.stringify({ error: `Movie with TMDB ID ${tmdbId} not found` }),
          }],
        };
      }
 
      const movie = records[0].toObject();
      return {
        contents: [{
          uri: uri.href,
          mimeType: "application/json",
          text: JSON.stringify(movie, null, 2),
        }],
      };
    } catch (error) {
      console.error(`Failed to fetch movie: ${error}`);
      return {
        contents: [{
          uri: uri.href,
          mimeType: "application/json",
          text: JSON.stringify({ error: `Failed to fetch movie: ${error}` }),
        }],
      };
    }
  }
);

The handler receives two arguments:

  1. uri - The parsed URL object for the requested resource
  2. { tmdbId } - The extracted template parameters

The Cypher query fetches comprehensive movie details in a single query, including genres, cast (limited to the first 5), and directors using list comprehension patterns.

Step 3: Test with MCP Inspector

Start the MCP Inspector connected to your server:

bash
npx @modelcontextprotocol/inspector npx tsx index.ts

In the Inspector UI:

  1. Click the Resources tab
  2. You should see the movie resource template listed
  3. Enter a TMDB ID in the template parameter field

Try these TMDB IDs:

  • Copy - The Matrix (1999)
  • Copy - Forrest Gump (1994)
  • Copy - Fight Club (1999)
  • Copy - Pulp Fiction (1994)

You should see structured output like:

json
{
  "title": "The Matrix",
  "released": "1999-03-31",
  "tagline": "Welcome to the Real World",
  "rating": 8.7,
  "runtime": 136,
  "genres": ["Action", "Science Fiction"],
  "directors": ["Lilly Wachowski", "Lana Wachowski"],
  "plot": "Set in the 22nd century, The Matrix tells the story of a computer hacker...",
  "cast": [
    { "name": "Keanu Reeves", "role": "Neo" },
    { "name": "Laurence Fishburne", "role": "Morpheus" },
    { "name": "Carrie-Anne Moss", "role": "Trinity" },
    { "name": "Hugo Weaving", "role": "Agent Smith" },
    { "name": "Gloria Foster", "role": "Oracle" }
  ]
}

This structured format makes it easy to use the data programmatically and is perfect for loading into an LLM's context.