Lesson 4 · Phase 2 · Agents

Your First Agent: Brain + Hands

Wire an Ollama-served Gemma 4 E4B brain to FastMCP hands with Pydantic AI. The agent writes a song and saves it to disk — then we prove it with plain Python, no AI. Tool design deep-dive: what makes the LLM actually pick your tool.

What you'll learn

  • Connect Pydantic AI Agent to an MCP toolset
  • Run a local Gemma 4 E4B brain through Ollama
  • Write tool descriptions the model reliably chooses

Watch the loop run

This is the exact run from the notebook, replayed beat by beat. Press Run agent and watch the request travel: model → tool call → real Python → result → final answer.

agent.run("Write a 4-verse song about AI and save it as song.txt")
You

· · ·

Gemma 4 E4B (thinking)

· · ·

Tool call → MCP server

· · ·

FileServer (Python actually ran)

· · ·

Agent — final answer

· · ·

The hands: one tool

The docstring is not decoration — it is the sentence the model reads when deciding whether to use your tool. Write it like an instruction to the model, because it is one:

from fastmcp import FastMCP
from pathlib import Path

mcp = FastMCP("FileServer")

@mcp.tool()
def write_file(filename: str, content: str) -> str:
    """Save text to a file. Use this whenever the user asks for something to be
    written, saved, or created as a file."""
    print(f"  [TOOL RAN] write_file({filename!r}, {len(content)} chars)")   # proof it fired
    Path(filename).write_text(content)
    return f"Saved {len(content)} characters to {filename}."   # <- goes BACK to the model

The brain, wired to the hands

Pydantic AI's Agent takes a model (Gemma 4 E4B served locally by Ollama) and a toolset (our MCP server) — the whole wiring is three arguments:

from pydantic_ai import Agent
from pydantic_ai.mcp import MCPToolset

agent = Agent(
    model,                                        # the brain (Ollama)
    system_prompt="You are a helpful assistant. Use your tool to save files.",
    toolsets=[MCPToolset(mcp)],               # the hands (our MCP server)
)

result = await agent.run("Write a 4-verse song about AI and save it as song.txt")
print("\nAGENT:", result.output)

Proof — plain Python, no AI

print(Path("song.txt").read_text())

The strongest habit this course will teach you: verify agent work outside the agent. If the file reads back with plain Python, the tool really ran — no screenshot of a chat window can prove that.