Feedback

Chat Icon

Practical MCP with FastMCP & LangChain

Engineering the Agentic Experience

Session State
68%

Implementing Session State Using FastMCP

To implement session state in our server, we will add a new parameter to the tool called name. This parameter will allow users to specify a name for their dog, and we will store that name in the session state so that it can be retrieved in future calls.

The parameter is optional, so users can still call the tool without providing a name. If a name is provided, we store it in session state under the key "dog_name". On subsequent calls where no name is given, we read it back from state — so the user only needs to introduce their dog once per session. We also modify the response to include the dog's name when it is available.

Here is the full updated server code with session state implemented:

cat > $HOME/workspace/puppy_guide/server/main.py << EOF
import os
from difflib import get_close_matches
from typing import Annotated

from dotenv import load_dotenv
from pydantic import Field

from fastmcp import Context
from fastmcp import FastMCP
from fastmcp.exceptions import ToolError

load_dotenv()

mcp = FastMCP(
    name=os.getenv("MCP_NAME"),
    instructions=os.getenv("MCP_INSTRUCTIONS"),
)


BREED_MULTIPLIERS = {
    "labrador": 7,
    "chihuahua": 5,
    "german shepherd": 8,
    "bulldog": 6,
}


def get_breed_multiplier(breed: str) -> int | None:
    """Fetch the age multiplier for a given dog breed."""
    return BREED_MULTIPLIERS.get(breed.lower())


@mcp.tool
async def dog_to_human_age(
    age: Annotated[int, Field(ge=0, le=30, description="The dog's age in years")],
    breed: Annotated[str, Field(description="The dog's breed")],
    ctx: Context,
    # New optional parameter for the dog's name
    name: Annotated[str | None, Field(description="The dog's name")] = None,
) -> str:
    """Calculate the real age of a dog in human years based on its breed."""

    total_steps = 4

    # If a name is provided, store it in session state for future calls
    if name:
        await ctx.set_state("dog_name", name)
    else:
        # No name provided — check if we stored one in a previous call
        name = await ctx.get_state("dog_name")

    # Progress: breed lookup
    await ctx.report_progress(
        progress=0,
        total=total_steps,
        message=f"Looking up breed multiplier for '{breed}'",
    )
    await ctx.info(f"Looking up breed multiplier for '{breed}'")

    multiplier = get_breed_multiplier(breed)

    if multiplier is None:
        # Look for 1 similar breed name
        suggestions = get_close_matches(
            breed.lower(), BREED_MULTIPLIERS.keys(), n=1, cutoff=0.6
        )

        if suggestions:
            await ctx.info(
                f"Breed '{breed}' not found, but found similar: {suggestions}"
            )
            result

Practical MCP with FastMCP & LangChain

Engineering the Agentic Experience

Enroll now to unlock current content and receive all future updates for free. Your purchase supports the author and fuels the creation of more exciting content. Act fast, as the price will rise as the course nears completion!