In the previous lessons, you learned about building tools with the @mcp.tool() decorator and using the Context object to access lifespan resources and provide logging.
In this challenge, you will build a tool that searches for movies in a specific genre, using nodes and relationships from the graph to provide relevant context to the LLM - the essence of GraphRAG.
Challenge Goals
To complete this challenge, you will:
- 
Create a tool that accepts a genre parameter
 - 
Use the Context object to access the Neo4j driver
 - 
Query Neo4j for movies in the specified genre
 - 
Add logging to provide feedback during execution
 - 
Return structured output with movie information
 - 
Test the tool with the interactive client
 
Solution Available
If you get stuck, you can review the complete solution in the repository at solutions/6c-build-database-tool/main.py.
To see the solution in action, run:
uv --directory solutions/6c-build-database-tool run main.pyStep 1: Create the Tool Function
Add a new tool to your server/main.py file:
from mcp.server.fastmcp import Context
@mcp.tool()
async def get_movies_by_genre(genre: str, limit: int = 10, ctx: Context = None) -> list[dict]:
    """
    Get movies by genre from the Neo4j database.
    Args:
        genre: The genre to search for (e.g., "Action", "Drama", "Comedy")
        limit: Maximum number of movies to return (default: 10)
        ctx: Context object (injected automatically)
    Returns:
        List of movies with title, tagline, and release year
    """
    # Log the request
    await ctx.info(f"Searching for {genre} movies (limit: {limit})...")
    # Access the Neo4j driver from lifespan context
    driver = ctx.request_context.lifespan_context.driver
    # Log the query execution
    await ctx.debug(f"Executing Cypher query for genre: {genre}")
    try:
        # Execute the query
        records, summary, keys = await driver.execute_query(
            """
            MATCH (m:Movie)-[:IN_GENRE]->(g:Genre {name: $genre})
            RETURN m.title AS title,
                   m.tagline AS tagline,
                   m.released AS released
            ORDER BY m.imdbRating DESC
            LIMIT $limit
            """,
            genre=genre,
            limit=limit
        )
        # Convert records to list of dictionaries
        movies = [record.data() for record in records]
        # Log the result
        await ctx.info(f"Found {len(movies)} {genre} movies")
        if len(movies) == 0:
            await ctx.warning(f"No movies found for genre: {genre}")
        return movies
    except Exception as e:
        # Log any errors
        await ctx.error(f"Query failed: {str(e)}")
        raiseStructured Output:
Notice that the tool returns a list[dict].
FastMCP will automatically convert this to structured output that clients can parse and use programmatically.
The type hints help the LLM understand what the tool returns!
Step 2: 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.pySelect the get_movies_by_genre tool from the menu. The client will prompt you for parameters:
genre (required)
  Type: string
  Enter value: Action
limit (optional, default: 10)
  Type: integer
  Enter value: 5Test with different genres: Action, Comedy, Drama, Sci-Fi
Try different limit values (5, 10, 20) to see how it affects the results.
Step 3: Observe the Logging
While testing, check the server terminal window to see the logging messages:
- 
Look for
infomessages showing the search and results - 
Check for
debugmessages showing the Cypher query execution - 
Try an invalid genre to see the
warningmessage 
Step 4: Verify the Output
The tool should return structured data like:
[
  {
    "title": "The Matrix",
    "tagline": "Welcome to the Real World",
    "released": 1999
  },
  {
    "title": "The Matrix Reloaded",
    "tagline": "Free your mind",
    "released": 2003
  }
]Verify Your Implementation
Once you’ve implemented and tested the tool:
- 
The tool should appear in the interactive client’s tool list
 - 
It should accept
genreand optionallimitparameters - 
It should return a list of movies with title, tagline, and released year
 - 
Logging messages should appear in the server terminal
 - 
The tool should handle invalid genres gracefully
 
Experiment Further
Try enhancing your tool:
- 
Add progress reporting for large queries
 - 
Include more movie properties (director, actors, rating)
 - 
Add error handling for connection issues
 - 
Create additional tools for other queries (by year, by rating, etc.)
 
Summary
In this challenge, you successfully built a Neo4j-backed tool using the Context object:
- 
Context parameter - Added
ctx: Contextto access MCP capabilities - 
Driver access - Retrieved the Neo4j driver from
ctx.request_context.lifespan_context - 
Logging - Used
ctx.info(),ctx.debug(),ctx.warning(), andctx.error()for feedback - 
Structured output - Returned typed data (
list[dict]) for client consumption - 
Error handling - Caught and logged exceptions appropriately
 
Your tool now provides a great user experience with informative logging and structured data output.
In the next lesson, you’ll learn about resources and how to expose Neo4j data in a different way.