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:
-
Add a movie resource with a dynamic URI pattern
-
Query Neo4j for comprehensive movie details
-
Return structured data in a consistent format
-
Include cast, genres, directors, and metadata
-
Test the resource with the interactive client
Solution Available
If you get stuck, you can review the complete solution in the repository at solutions/8c-create-resource/main.py.
To see the solution in action, run:
uv --directory solutions/8c-create-resource run main.pyStep 1: Understanding the Resource URI
Resources use URI patterns to identify what data to fetch.
For a movie resource, we’ll use the pattern: movie://{tmdb_id}
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 Function
Add this resource to your server/main.py file, after your existing tools:
@mcp.resource("movie://{tmdb_id}")
async def get_movie(tmdb_id: str, ctx: Context) -> str:
"""
Get detailed information about a specific movie by TMDB ID.
Args:
tmdb_id: The TMDB ID of the movie (e.g., "603" for The Matrix)
Returns:
Formatted string with movie details including title, plot, cast, and genres
"""
await ctx.info(f"Fetching movie details for TMDB ID: {tmdb_id}")
context = ctx.request_context.lifespan_context
try:
records, _, _ = await context.driver.execute_query(
"""
MATCH (m:Movie {tmdbId: $tmdb_id})
RETURN m.title AS title,
m.released AS released,
m.tagline AS tagline,
[ (m)-[:IN_GENRE]->(g:Genre) | g.name ] AS genres,
[ (p)-[:ACTED_IN]->(m) | p.name ] AS actors,
[ (d)-[:DIRECTED]->(m) | d.name ] AS directors
""",
tmdb_id=tmdb_id,
database_=context.database
)
if not records:
await ctx.warning(f"Movie with TMDB ID {tmdb_id} not found")
return f"Movie with TMDB ID {tmdb_id} not found in database"
movie = records[0].data()
# Format the output
output = []
output.append(f"# {movie['title']} ({movie['released']})")
output.append("")
if movie['tagline']:
output.append(f"_{movie['tagline']}_")
output.append("")
output.append(f"**Rating:** {movie['rating']}/10")
output.append(f"**Runtime:** {movie['runtime']} minutes")
output.append(f"**Genres:** {', '.join(movie['genres'])}")
if movie['directors']:
output.append(f"**Director(s):** {', '.join(movie['directors'])}")
output.append("")
output.append("## Plot")
output.append(movie['plot'])
if movie['cast']:
output.append("")
output.append("## Cast")
for actor in movie['cast']:
if actor['role']:
output.append(f"- {actor['name']} as {actor['role']}")
else:
output.append(f"- {actor['name']}")
result = "\n".join(output)
await ctx.info(f"Successfully fetched details for '{movie['title']}'")
return result
except Exception as e:
await ctx.error(f"Failed to fetch movie: {str(e)}")
raiseKey points in this code:
-
URI pattern:
movie://{tmdb_id}- Thetmdb_idparameter is extracted from the URI -
Comprehensive query: Fetches movie details, genres, cast, and directors in one query
-
Structured data: Returns a consistent JSON-compatible dictionary
-
Error handling: Handles missing movies gracefully
Step 3: Test with the Interactive Client
Start your server in one terminal:
uv --directory server run main.pyIn a separate terminal, run the interactive client from the project root:
uv --directory client run main.pyTest Popular Movies
The client will show available resource templates. Select the movie resource template and enter a TMDB ID when prompted.
Try these TMDB IDs:
-
603- The Matrix (1999) -
13- Forrest Gump (1994) -
550- Fight Club (1999) -
680- Pulp Fiction (1994)
The client will prompt you:
tmdb_id (required)
Type: string
Enter value: 603You should see structured output like:
{
"title": "The Matrix",
"released": 1999,
"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 sampling in advanced features.
Summary
In this challenge, you successfully created a movie resource:
-
Dynamic URI pattern -
movie://{tmdb_id}for flexible resource access -
Comprehensive query - Fetched movie details, cast, genres, and directors
-
Structured data - Returned consistent, JSON-compatible output
-
Error handling - Gracefully handled missing movies
-
Resource design - Understood when to use resources vs tools
Your server now exposes data in two ways: tools for searches and resources for specific entities.
In the next lesson, you’ll learn about pagination for handling large datasets.