UCLA Extension - Agentic AI Course
| Part | Focus | Points |
|---|---|---|
| Part 1 | Build a Google Workspace agent with ADK | 70 |
| Part 2 | Connect to GitHub via MCP Server | 30 |
workspace_assistant/
├── agent.py # STUDENT: Main agent definition
├── main.py # Given: CLI entry point
├── tools/
│ ├── auth.py # Given: Google OAuth helper
│ ├── calendar_tools.py # STUDENT (Option A): Calendar tools
│ ├── tasks_tools.py # STUDENT (Option B): Tasks tools
│ └── mcp_tools.py # STUDENT: GitHub MCP integration
├── config/
│ ├── settings.py # Given: Configuration from .env
│ ├── mcp_servers.json # Given: MCP server config (optional)
│ └── credentials/ # Your OAuth files (gitignored)
├── tests/
│ ├── test_tools.py # Part 1 verification
│ └── test_mcp.py # Part 2 verification
└── docs/
├── google_auth_setup.md # Google OAuth setup guide
├── github_mcp_setup.md # GitHub token setup guide
└── reflection_template.md
| File | What to implement |
|---|---|
tools/calendar_tools.py |
Option A: 3+ calendar tools |
tools/tasks_tools.py |
Option B: 3+ tasks tools |
agent.py |
System instruction + LlmAgent with your tools |
tools/mcp_tools.py |
Part 2: GitHub McpToolset configuration |
| File | What it provides |
|---|---|
tools/auth.py |
get_calendar_service() and get_tasks_service() — authenticated Google API clients |
config/settings.py |
Settings class — loads model name, credentials path, and options from .env |
main.py |
CLI with --interactive mode using rich |
tests/test_tools.py |
Checks tool count, docstrings, type hints, naming, error handling |
tests/test_mcp.py |
Checks GitHub token and McpToolset presence in agent |
Build an AI agent using Agent Development Kit (ADK).
Choose ONE option:
| Option | Focus |
|---|---|
| A | Calendar Assistant - Schedule meetings, find free time, manage conflicts |
| B | Tasks Manager - Create tasks, track to-dos, manage deadlines |
- Minimum 3 tools with clear names and descriptions
- Error handling with user-friendly messages
- System instruction that guides the agent's behavior
The tools/auth.py file is already implemented and gives you authenticated Google API clients:
from tools.auth import get_calendar_service # Option A
from tools.auth import get_tasks_service # Option B
# Use inside your tools:
service = get_calendar_service() # Returns authorized Calendar API client
service = get_tasks_service() # Returns authorized Tasks API clientFirst-time use triggers an OAuth browser flow. See docs/google_auth_setup.md for setup.
Define each tool as a plain Python function with a clear docstring and type hints — ADK reads these to build the tool schema and auto-wraps the function when you pass it to the agent. Each tool should call the Google API and return a dict:
from tools.auth import get_calendar_service
def list_upcoming_events(max_results: int = 10) -> dict:
"""List upcoming calendar events.
Args:
max_results: Maximum number of events to return.
Returns:
dict with 'status' and 'events' keys.
"""
try:
service = get_calendar_service()
events_result = service.events().list(
calendarId='primary', maxResults=max_results, ...
).execute()
return {"status": "success", "events": events_result.get("items", [])}
except Exception as e:
return {"status": "error", "message": str(e)}Add your completed tools to the list at the bottom of the file:
calendar_tools = [list_upcoming_events, find_available_slots, create_event]Implement in tools/calendar_tools.py using get_calendar_service():
| Tool | Purpose |
|---|---|
list_upcoming_events |
Get events for a date range |
find_available_slots |
Find free time between participants |
create_event |
Schedule a new meeting |
check_conflicts |
Detect scheduling conflicts |
reschedule_event |
Move an existing event |
Implement in tools/tasks_tools.py using get_tasks_service():
| Tool | Purpose |
|---|---|
list_tasks |
Get tasks from a list |
create_task |
Add a new task |
complete_task |
Mark a task as done |
update_task |
Modify task details |
delete_task |
Remove a task |
In agent.py, wire your tools into an LlmAgent:
from google.adk.agents import LlmAgent
from config.settings import Settings
from tools.calendar_tools import calendar_tools # or tasks_tools
def create_agent() -> LlmAgent:
settings = Settings()
instruction = """You are a Google Workspace assistant that helps users
manage their calendar. You can list events, find free time, create
meetings, check for conflicts, and reschedule events. Always confirm
before making changes..."""
return LlmAgent(
name="workspace_assistant",
model=settings.model_name,
instruction=instruction,
tools=calendar_tools,
)Extend your agent to interact with GitHub using McpToolset.
- Create a GitHub Personal Access Token with
reposcope - Configure
McpToolsetto connect to the GitHub MCP server - Update your system instruction to include GitHub capabilities
from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool import McpToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
from mcp import StdioServerParametersserver_params = StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-github"],
env={"GITHUB_PERSONAL_ACCESS_TOKEN": token}
)
toolset = McpToolset(
connection_params=StdioConnectionParams(server_params=server_params)
)Alternatively, load from config/mcp_servers.json:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}"
}
}
}
}Use the provided load_mcp_config() helper to load and parse this config.
Your agent should handle:
- "List my GitHub repositories"
- "Show open issues in [repo]"
- "Create an issue in [repo] about [topic]"
| Component | Points |
|---|---|
| Tool Design | 18 |
| Agent Instructions | 14 |
| Error Handling | 14 |
| Functionality | 14 |
| Code Quality | 10 |
| Component | Points |
|---|---|
| McpToolset Configured | 10 |
| GitHub Queries Work | 15 |
| Error Handling | 5 |
Bonus (+25): Implement tool search with defer_loading
What: Implement the defer_loading pattern so your agent doesn't load all 15+ GitHub MCP tools upfront—only searches and loads what it needs.
Why: Reduces token usage by ~80% (from ~8K to ~1.5K tokens for GitHub tools).
How:
- Add a
search_github_toolstool to your agent (10 pts) - Configure McpToolset with
defer_loading=True(10 pts) - Implement
create_agent_with_tool_search()in agent.py (5 pts)
Required Comparison: In your reflection, include a comparison showing token/context bloat:
- Run your agent without
defer_loadingand note the context size - Run your agent with
defer_loadingand note the context size - Calculate the % reduction in tokens
Example comparison:
| Mode | Tools Loaded | Approx Tokens |
|---|---|---|
| Without defer_loading | 15+ GitHub tools | ~8,000 |
| With defer_loading | 1-2 tools + search | ~1,500 |
| Savings | ~80% |
See tools/mcp_tools.py for scaffolding
conda create -n workspace-agent python=3.11 -y
conda activate workspace-agent
pip install -r requirements.txt
cp .env.example .envFollow docs/google_auth_setup.md to:
- Create a Google Cloud project with Calendar/Tasks API enabled
- Download OAuth credentials to
config/credentials/credentials.json - The first tool call will open a browser for authorization
- Implement 3+ tools in
tools/calendar_tools.pyortools/tasks_tools.py - Create your agent in
agent.pywith system instruction + tools - Verify:
cd workspace_assistant
python -m tests.test_tools- Test interactively:
adk web # ADK Web UI
# or
python main.py --interactive # CLI mode- Get a GitHub Personal Access Token (see docs/github_mcp_setup.md)
- Add
GITHUB_PERSONAL_ACCESS_TOKEN=ghp_...to.env - Implement
get_github_mcp_toolset()intools/mcp_tools.py - Add the toolset to your agent in
agent.pyand update the system instruction - Verify:
python -m tests.test_mcpADK provides a built-in web interface for testing your agent interactively.
cd workspace_assistant
adk webThis launches a local web server (typically at http://localhost:8000).
- Select your agent from the dropdown (should show your
workspace_assistant) - Type queries in the chat interface to test your tools
- View tool calls - the UI shows which tools are invoked and their responses
- Debug errors - see full error messages and stack traces
Part 1 - Calendar (Option A):
- "What meetings do I have tomorrow?"
- "Find a free slot for a 1-hour meeting next week"
- "Schedule a team sync for Monday at 2pm"
Part 1 - Tasks (Option B):
- "Show my tasks"
- "Add a task: Review PR by Friday"
- "Mark the first task as complete"
Part 2 - GitHub MCP:
- "List my GitHub repositories"
- "Show open issues in octocat/Hello-World"
- "Create an issue in my-repo about fixing the login bug"
| Issue | Solution |
|---|---|
| Agent not appearing | Ensure agent.py has a valid create_agent() function |
| Tools not showing | Check that tools are added to the agent's tools list |
| MCP connection failed | Verify GITHUB_PERSONAL_ACCESS_TOKEN in .env |
| Import errors | Run pip install -r requirements.txt |
This assignment uses GitHub Classroom. Your work is submitted by pushing to your repository's main branch.
- Test your agent with
adk weband verify all functionality works - Run the verification tests:
cd workspace_assistant python -m tests.test_tools python -m tests.test_mcp - Complete
docs/reflection_template.md - Commit and push your changes:
git add -A git commit -m "Submit assignment 2" git push origin main - Verify your submission on GitHub — go to your repository page and confirm all files are present
- Your submission is graded from the code pushed to
mainin your repository - You can push multiple times before the deadline; the latest state on
mainat the deadline is what's graded - Make sure your final code is committed and pushed before the deadline
- Do NOT commit sensitive files —
credentials/,token.json, and.envare already in.gitignore - Make sure your code passes the local tests before pushing