This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a hands-on workshop teaching how to build AI chat agents using Microsoft Agent Framework, Chainlit, and GitHub Models. The codebase is organized into progressive phases, each building on the previous one.
# Run any Chainlit app
chainlit run app.py -w
# Run specific phase solution
chainlit run solutions/phase-06/app.py -w
# Test GitHub Models connection
python solutions/phase-02/test_github_models.py
# Install dependencies
pip install -r requirements.txt
# or with uv (faster)
uv pip install -r requirements.txtThe workshop builds incrementally through 6 phases:
- Phase 1: Environment setup - Python venv, dependencies
- Phase 2: GitHub Models connection test - raw
AsyncOpenAIclient only (no frameworks) - Phase 3: Basic Chainlit chat with streaming - uses raw
AsyncOpenAIwith dict-based message history - Phase 4: Microsoft Agent Framework introduction - introduces
ChatAgentwithAgentThread - Phase 5: Tool calling -
ChatAgentwithFunctionCallContent/FunctionResultContenttool handling - Phase 6: MCP integration - combines local tools with
MCPStreamableHTTPToolfor remote servers
Phase 2-3: Raw OpenAI Client (no frameworks):
from openai import AsyncOpenAI
client = AsyncOpenAI(
api_key=os.getenv("GITHUB_TOKEN"),
base_url="https://models.github.ai/inference",
)
# Direct API calls
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
stream=True,
)Phase 4-6: Agent Framework Chat Client:
from openai import AsyncOpenAI
from agent_framework.openai import OpenAIChatClient
openai_client = AsyncOpenAI(
api_key=os.getenv("GITHUB_TOKEN"),
base_url="https://models.github.ai/inference",
)
chat_client = OpenAIChatClient(
async_client=openai_client,
model_id="gpt-4o-mini",
)Agent Creation (Phase 4+):
from agent_framework import ChatAgent
agent = ChatAgent(
chat_client=chat_client,
instructions=SYSTEM_PROMPT,
tools=TOOLS, # Empty in Phase 4, populated in Phase 5+
temperature=0.7,
)Thread Management (Phase 4+, for conversation history):
thread = agent.get_new_thread()
result = await agent.run("message", thread=thread)Streaming (Phase 3: raw OpenAI, Phase 4+: agent):
# Phase 3 (raw OpenAI)
async for chunk in stream:
if chunk.choices and len(chunk.choices) > 0:
content = chunk.choices[0].delta.content
if content:
await msg.stream_token(content)
# Phase 4+ (Agent Framework)
async for update in agent.run_stream("message", thread=thread):
if update.text:
await msg.stream_token(update.text)Tool Visualization (Phase 5+):
from agent_framework import FunctionCallContent, FunctionResultContent
if isinstance(content, FunctionCallContent):
if content.name and content.call_id not in tool_steps:
step = cl.Step(name=f"🔧 {content.name}", type="tool")
await step.send()
tool_steps[content.call_id] = step
elif isinstance(content, FunctionResultContent):
step = tool_steps.get(content.call_id)
if step:
step.output = content.result
await step.update()Tool Definition (Phase 5+):
from typing import Annotated
from pydantic import Field
def get_weather(
city: Annotated[str, Field(description="City name")]
) -> str:
"""Docstring is critical - agent uses it to decide when to call."""
...MCP Tool (Phase 6):
from agent_framework import MCPStreamableHTTPTool
mcp_tool = MCPStreamableHTTPTool(
name="microsoft_learn",
url="https://learn.microsoft.com/api/mcp"
)solutions/phase-XX/app.py- Main Chainlit application for each phasesolutions/phase-XX/tools.py- Tool definitions (phases 5-6)docs/01-06-*.md- Workshop documentation for each phase (6 files)docs/INDEX.md- Complete documentation index and learning guidedocs/07-troubleshooting.md- Comprehensive troubleshooting guide for all phasesREADME.md- Main project overview with quick linksCLAUDE.md- This file (guidance for Claude Code)
Required in .env:
GITHUB_TOKEN- GitHub personal access token for GitHub Models APIWEATHER_API_KEY- WeatherAPI key for tool demo (phase 5+)
When working with this repository, you may be asked to:
-
Compare docs with solution files - Ensure documentation matches working code
- Docs:
docs/XX-phase-name.md - Solutions:
solutions/phase-XX/app.pyandtools.py - Use diffs to check imports, function names, patterns
- Docs:
-
Fix sync issues between docs and code
- Solution file is the source of truth
- Update docs to match if code changed
- Check: imports, function signatures, streaming patterns, tool handling
-
Debug issues in workshop phases
- Check solution files for working examples
- Verify imports match (especially Phase 5:
FunctionCallContent,FunctionResultContent) - Test individual components (use Phase 2 test script as template)
-
Update documentation
- Add explanations or clarifications
- Update code examples to match current best practices
- Ensure all phases show correct imports and patterns
- Keep consistency: all agents have
temperature=0.7, all streams use safety checks
-
Add new content
- New phases should follow the established pattern
- Solution must work before documentation is written
- Documentation should match solution file exactly in final code section
- Add to
docs/INDEX.mdnavigation
- Solution source of truth: Each
solutions/phase-XX/app.pyis the reference - Documentation source of truth: Phases must match solutions
- Help first place:
docs/07-troubleshooting.mdfor user issues - Navigation hub:
docs/INDEX.mdfor learning paths - This file:
CLAUDE.mdfor technical context
When fixing sync issues or adding features:
- Solution file (
solutions/phase-XX/) exists and runs without errors - Documentation (
docs/XX-*.md) matches solution code exactly in final sections - Imports are correct and match phase-specific patterns
- Tool handling uses
isinstance()nothasattr()(Phase 5+) - Streaming patterns include safety checks (Phase 3:
if chunk.choices and len...) - All agents have
temperature=0.7(Phase 4+) - Threads are used for memory (Phase 4+)
-
docs/INDEX.mdupdated with new/changed content -
CLAUDE.mdupdated with patterns if changed
This section documents how the training materials are structured, useful for creating new workshops.
Each phase doc (docs/XX-phase-name.md) follows this pattern:
- Header with time estimate and learning objectives
- Concept explanation with ASCII diagrams where helpful
- Step-by-step instructions that build incrementally
- Complete code example at the end for reference
- Project structure showing expected files
- Checkpoint with testable scenarios
- Common issues section for troubleshooting
- Each phase builds on the previous one with minimal new concepts
- Early steps show simplified code, final code adds all parameters (e.g.,
temperature=0.7) - Code is introduced incrementally: first a minimal working version, then enhanced
- Explicit "What's new" tables comparing current phase to previous
Phase 2-3 (Raw OpenAI):
- No Agent Framework imports
- Direct API calls with
client.chat.completions.create() - Streaming with
stream=Trueparameter - Message history as dicts in lists
- Safety checks:
if chunk.choices and len(chunk.choices) > 0:
Phase 4-6 (Agent Framework):
- Solution files in
solutions/phase-XX/must match the final code in docs exactly - All agents include
temperature=0.7in final versions - Streaming pattern:
async for update in agent.run_stream(...) - Thread management: always pass
thread=threadfor conversation history - Tool handling: Use
isinstance(content, FunctionCallContent)nothasattr() - Tool tracking: Use
tool_steps = {}dict withcontent.call_idas key
- Each phase folder created with
mkdir -p phase-XX && cd phase-XX && touch app.py - Files to create are explicitly listed (no implicit file creation)
- Virtual environment tip: mention
deactivateto exit - Chainlit auto-creates
chainlit.md- no need to copy between phases - Time estimates are realistic (total ~85 minutes tested)
Each phase includes specific test scenarios:
- Concrete user inputs to try
- Expected outputs/behaviors
- Visual confirmations (e.g., "tool step appears in UI")
- LangChain Workshop: Uses
ChatOpenAIfrom LangChain - Agent Framework Workshop: Uses raw
AsyncOpenAIfrom OpenAI library- Reason: Teach OpenAI library first, then wrap with Agent Framework
- Simpler, more direct path to understanding LLMs
| Aspect | LangChain | Agent Framework |
|---|---|---|
| Agent creation | create_agent() helper |
ChatAgent() class |
| Conversation history | Dict-based messages | AgentThread |
| Streaming | astream() with stream_mode |
run_stream() |
| Tool definition | @tool decorator |
Function with Annotated |
| Tool handling | ToolCall objects |
FunctionCallContent/FunctionResultContent |
| Tool tracking | Tool name as key | call_id as key |
| MCP tools | langchain-mcp-adapters |
MCPStreamableHTTPTool |
| Message format | LangChain messages | Dict-based messages (OpenAI format) |
- LangChain: All 6 phases use LangChain
- Agent Framework: Phases 2-3 are framework-agnostic (raw OpenAI), then introduce Agent Framework in Phase 4