{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 🛠️ Level 1.5 · Station 8 — Build an Agent IN CODE (Google ADK)\n",
    "### AI Agents track · Google Colab · **Google ADK** + **Gemini (free)** + **MCP**\n",
    "\n",
    "You'll build a real agent in Python that **calls a live REST API** *and* **calls an MCP server** to solve a problem — using **Google's Agent Development Kit (ADK)**, which is free with Gemini.\n",
    "\n",
    "| Part | You'll do | ✅ Done when |\n",
    "|---|---|---|\n",
    "| **A · Setup** | Install ADK + connect Gemini | The runner helper prints ✅ |\n",
    "| **B · API tool** | Agent calls a live currency API | It converts money using the tool |\n",
    "| **C · MCP tool** | Agent calls YOUR MCP server | It answers from the MCP server's data |\n",
    "| **D · Combine** | One agent, API **+** MCP | It solves a two-part problem |\n",
    "\n",
    "> Run each cell in order with ▶️ / Shift+Enter. This notebook uses `await` in cells — that's normal in Colab."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 🅰️ Part A — Setup\n",
    "### 🔑 Get your free Gemini key (once)\n",
    "1. Open **https://aistudio.google.com/apikey** → **Create API key** → copy it.\n",
    "2. Run the cells below; paste the key when asked.\n",
    "\n",
    "🔒 An API key is a password — never paste it into a public doc, chat, or GitHub."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Install Google's Agent Development Kit (this also installs Gemini + MCP support)\n",
    "!pip -q install google-adk"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "os.environ['GOOGLE_API_KEY'] = getpass.getpass('Paste your Gemini API key: ').strip()\n",
    "os.environ['GOOGLE_GENAI_USE_VERTEXAI'] = 'FALSE'   # use the free AI Studio API, not Vertex\n",
    "print('Key set ✅')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### A tiny helper to run an agent\n",
    "ADK runs agents asynchronously. This helper sends one message to an agent and returns its final reply — you'll reuse it all notebook long."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "import asyncio\n",
    "from google.adk.runners import InMemoryRunner\n",
    "from google.genai import types\n",
    "\n",
    "MODEL = 'gemini-2.0-flash'   # a fast, free model. If it errors, try 'gemini-2.5-flash'.\n",
    "\n",
    "async def run_agent(agent, prompt, user='student'):\n",
    "    runner = InMemoryRunner(agent=agent, app_name='lab')\n",
    "    session = await runner.session_service.create_session(app_name='lab', user_id=user)\n",
    "    message = types.Content(role='user', parts=[types.Part(text=prompt)])\n",
    "    final = ''\n",
    "    async for event in runner.run_async(user_id=user, session_id=session.id, new_message=message):\n",
    "        if event.is_final_response() and event.content and event.content.parts:\n",
    "            final = event.content.parts[0].text or final\n",
    "    return final\n",
    "\n",
    "print('Runner helper ready ✅')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 🅱️ Part B — Give the agent a REST API tool\n",
    "A **tool** in ADK is just a Python function with a docstring. Here's one that calls a **free, no-key currency API** (frankfurter.app). Let's test the plain function first — no AI yet."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "import requests\n",
    "\n",
    "def convert_currency(amount: float, base: str, target: str) -> dict:\n",
    "    \"\"\"Convert money from one currency to another using live exchange rates.\n",
    "    Args: amount (number), base (e.g. 'USD'), target (e.g. 'EUR').\"\"\"\n",
    "    url = f'https://api.frankfurter.app/latest?amount={amount}&from={base}&to={target}'\n",
    "    data = requests.get(url, timeout=10).json()\n",
    "    return {'amount': amount, 'from': base, 'to': target, 'result': data.get('rates', {}).get(target.upper())}\n",
    "\n",
    "# 🧪 test the tool by itself (this is a real API call):\n",
    "print(convert_currency(50, 'USD', 'EUR'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now hand that function to an ADK **Agent** as a tool. The agent decides *when* to call it."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "from google.adk.agents import Agent\n",
    "\n",
    "api_agent = Agent(\n",
    "    name='money_helper',\n",
    "    model=MODEL,\n",
    "    instruction=('You help with money questions. For ANY currency conversion you MUST call the '\n",
    "                 'convert_currency tool — never guess exchange rates.'),\n",
    "    tools=[convert_currency],\n",
    ")\n",
    "\n",
    "print(await run_agent(api_agent, 'How much is 50 US dollars in euros right now?'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** the agent answered with a real, live number — because it *called your API tool* instead of guessing. That's an agent making an **API call**. 🎉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 🅲 Part C — Give the agent an MCP tool\n",
    "**MCP** (Model Context Protocol) is the 'USB-C for AI tools' from Station 7 — a standard way to plug tools and data into any agent.\n",
    "\n",
    "First we **write our own tiny MCP server**: a Python file exposing a private 'team database'. (`%%writefile` saves the cell to a file.)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "%%writefile team_server.py\n",
    "# A minimal MCP SERVER — exposes your own private data/tools over MCP.\n",
    "from mcp.server.fastmcp import FastMCP\n",
    "\n",
    "mcp = FastMCP('team')\n",
    "ROLES = {'maya': 'Team Lead', 'leo': 'Data Analyst', 'ada': 'Designer'}\n",
    "\n",
    "@mcp.tool()\n",
    "def get_member_role(name: str) -> str:\n",
    "    \"\"\"Look up a team member's role by first name.\"\"\"\n",
    "    return ROLES.get(name.lower(), 'Unknown member')\n",
    "\n",
    "@mcp.tool()\n",
    "def list_members() -> list:\n",
    "    \"\"\"List every team member's first name.\"\"\"\n",
    "    return list(ROLES.keys())\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    mcp.run(transport='stdio')   # talk to clients over standard input/output"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now connect that MCP server to an ADK agent with **`MCPToolset`**. ADK launches the server, discovers its tools, and lets the agent call them — a real **MCP call**."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset\n",
    "from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams\n",
    "from mcp import StdioServerParameters\n",
    "\n",
    "team_tools = MCPToolset(\n",
    "    connection_params=StdioConnectionParams(\n",
    "        server_params=StdioServerParameters(command='python3', args=['team_server.py'])\n",
    "    )\n",
    ")\n",
    "\n",
    "mcp_agent = Agent(\n",
    "    name='team_helper',\n",
    "    model=MODEL,\n",
    "    instruction=('Answer questions about the team. The MCP tools (get_member_role, list_members) '\n",
    "                 'are your ONLY source of truth about the team — always use them.'),\n",
    "    tools=[team_tools],\n",
    ")\n",
    "\n",
    "print(await run_agent(mcp_agent, \"What is Maya's role, and who else is on the team?\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** the agent answered from data it could only get by **calling your MCP server** (`get_member_role` + `list_members`). You just built an agent that makes MCP calls. 🔌"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 🅳 Part D — Solve a problem with BOTH (API + MCP)\n",
    "Real agents mix tools. Here's ONE agent with the **currency API** *and* the **MCP team server**, solving a two-part problem in a single run."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "assistant = Agent(\n",
    "    name='assistant',\n",
    "    model=MODEL,\n",
    "    instruction=('You are a helpful assistant. Use the MCP tools for team info and '\n",
    "                 'convert_currency for money. Always use tools instead of guessing.'),\n",
    "    tools=[convert_currency, team_tools],\n",
    ")\n",
    "\n",
    "print(await run_agent(assistant,\n",
    "    \"Maya earned a 500 USD bonus. What is her role, and how much is 500 USD in euros?\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** one agent, one question, **two kinds of tool call** — an MCP call (Maya's role) *and* an API call (the conversion). That's a real problem-solving agent. 🏆"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# 🎯 YOUR TURN: add a new tool and a new MCP function, then ask a question that needs both.\n",
    "# Ideas: add a get_joke() REST tool (https://official-joke-api.appspot.com/random_joke)\n",
    "#        add a @mcp.tool() get_member_email(name) to team_server.py\n",
    "print('Extend the agent above with your own API tool + MCP tool!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 🚀 Run it in Replit with a full UI\n",
    "Colab is great for learning; **Replit** is great for building & sharing. To run this as a real app with ADK's built-in chat UI:\n",
    "1. Create a Python Repl and `pip install google-adk`.\n",
    "2. Put your agent in `agent.py` and set `GOOGLE_API_KEY` in Replit **Secrets**.\n",
    "3. Run `adk web` — you get a browser chat UI to test the agent, watch every tool call, and share the link.\n",
    "\n",
    "## 🩺 Troubleshooting\n",
    "- **Model error?** Swap `MODEL = 'gemini-2.0-flash'` for `'gemini-2.5-flash'`.\n",
    "- **MCP cell hangs?** Re-run the `%%writefile team_server.py` cell first, then the MCPToolset cell.\n",
    "- **`await` error?** This notebook is built for Colab (top-level `await` works). In plain Python, wrap calls in `asyncio.run(...)`.\n",
    "- **Version note:** built for `google-adk` 2.x. If imports move, check `from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset`.\n",
    "\n",
    "## 🎉 You built an agent in code\n",
    "It made a **REST API call** and an **MCP call** to solve a problem — with a real framework (ADK), free on Gemini.\n",
    "\n",
    "**Next:** Station 8's sibling, [Build it Visually (Langflow)](station-7-visual.html) — the same idea with zero code. Then go build a full project in **Level 2**.\n",
    "\n",
    "_AI Agents track · LearnAI Level 1.5 · part of MillionRoots._"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "L1.5 Station 8 — Build an Agent in Code (ADK)",
   "provenance": []
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}