Deploy a Finance Agent on Kubernetes

Build a Kubernetes-native AI agent that fetches live stock market data.

Introduction

30 Second Summary

Imagine asking a single question about a stock and getting back live prices, analyst trends, and a clear summary of what it all means. That is the kind of research assistant you are about to build.

In this project, you will deploy an AI-powered finance research agent on a local Kubernetes cluster using the kagent framework. Your agent will fetch real-time stock market data from Finnhub and respond with informed analysis powered by Google Gemini.

What You'll Build

You type a question like "What's the current price of AAPL and how does it compare to MSFT?" into a chat interface, and your agent calls out to Finnhub for live market data, then delivers a structured analysis with real numbers.

By the end of this project, you'll have:

  • A running AI agent you can chat with through a dashboard or CLI to ask market research questions and get back answers grounded in live data.
  • A custom MCP tool server that fetches real-time stock quotes, company profiles, and analyst recommendation trends from Finnhub's API.
  • A fully declarative deployment defined in YAML, with your agent and tool server running as proper Kubernetes workloads with secrets managed securely.
  • Secret Mission: Add a company news analysis tool that lets your agent fetch and summarize the latest headlines for any stock ticker.

Are there any prerequisites?

None. This guide walks you through installing every tool from scratch, including Docker, Minikube, Helm, and the kagent CLI. You will also create free API keys for Google Gemini and Finnhub as part of the project.

Before We Start

Before We Start...

Before we dive in, let's lock in what you're about to build and why it matters.

Install Docker, Minikube, and Python

Your finance agent needs three foundational tools to run. Docker packages your agent into containers. Minikube gives you a local Kubernetes cluster to deploy those containers onto. Python is the language you will write your agent logic in.

Without these three foundations, nothing else in the project works. In this step, you will install all three on your machine and verify each one is ready.

In this step, get ready to:
  • Install Docker Desktop and confirm the Docker engine is running.
  • Install Minikube using your platform's package manager.
  • Install Python 3 and verify it is on your PATH.
Install Docker Desktop

Docker Desktop is a GUI application that bundles the Docker engine, CLI, and container runtime into one install. It is available for macOS, Windows, and Linux.

macOS

  • Open your web browser and go to the Docker Desktop download page.
  • Click Download for Mac – Apple Silicon if you have an M-series Mac, or Download for Mac – Intel Chip if you have an Intel Mac.
  • Open the downloaded Docker.dmg file.
  • Drag the Docker icon into the Applications folder.
  • Press Cmd+Space to open Spotlight search.
  • Type Docker and press Enter to launch Docker Desktop.

Windows

  • Open your web browser and go to the Docker Desktop download page.
  • Click Download for Windows.
  • Run the downloaded Docker Desktop Installer.exe file.
  • Follow the installer prompts and accept the defaults. When asked, ensure Use WSL 2 instead of Hyper-V is checked.
  • When the installer finishes, click Close and restart to reboot your machine.
  • After restarting, press the Windows key, type Docker, and press Enter to launch Docker Desktop.

Linux

  • Open your web browser and go to the Docker Desktop download page.
  • Select the package for your distribution (Debian/Ubuntu .deb or Fedora/RHEL .rpm).
  • Install the downloaded package using your package manager. For Debian/Ubuntu, run:
sudo apt install ./docker-desktop-amd64.deb
  • Launch Docker Desktop from your application menu, or run systemctl --user start docker-desktop in your terminal.

What happens on first launch?

Docker Desktop will present a setup window asking you to accept the license agreement and choose default settings. Accept the defaults and grant privileged access when prompted. A Docker icon will appear in your menu bar (macOS) or system tray (Windows/Linux) once the engine is running.

Wait until the Docker icon in your menu bar (macOS) or system tray (Windows/Linux) shows a steady state. This means the engine is ready.

  • Open a terminal by pressing Cmd+Space (macOS) or the Windows key (Windows) to open your search bar, typing Terminal (macOS/Linux) or PowerShell (Windows), and pressing Enter.
  • Verify Docker is installed by running this command:
docker --version

macOS

Docker is installed and the CLI is available. You are ready to move on.

Windows

If the command hangs or prints an error about the Docker daemon, Docker Desktop may still be starting up.

  • Check your menu bar (macOS) or system tray (Windows/Linux) for the Docker icon. If it is still animating, wait another 30 seconds and try again.
  • If the Docker icon is missing, open Docker Desktop again from your application launcher.
Install Minikube

Minikube creates a single-node Kubernetes cluster on your laptop. It gives you a real cluster to deploy to without needing a cloud account.

macOS

  • Install Minikube using Homebrew by running this command:
brew install minikube

Don't have Homebrew?

If you see "command not found" when running brew, install Homebrew first by running:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Follow the on-screen instructions, close and reopen Terminal, then retry the Minikube install command above.

Windows

  • Install Minikube using winget by running this command in PowerShell:
winget install Kubernetes.minikube
  • Close and reopen PowerShell after the install finishes so the PATH updates take effect.

Linux

  • Download and install the Minikube binary by running these commands:
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
rm minikube-linux-amd64
  • Verify the install succeeded by running:
minikube version

✔️ I see a version number

Minikube is installed and ready.

ⓧ Command not found

  • Close and reopen your terminal so your PATH refreshes.
  • Run minikube version again.
  • If it still fails, re-run the install command for your platform from the tab above.
Install Python 3

Your agent code and MCP tool server are both written in Python. You need Python 3.10 or later on your PATH.

macOS

  • Install Python 3 using Homebrew by running this command:
brew install python@3

Windows

  • Install Python 3 using winget by running this command in PowerShell:
winget install Python.Python.3.12
  • Close and reopen PowerShell after the install finishes so the PATH updates take effect.

Linux

  • Most Linux distributions include Python 3. Confirm it is installed, or install it by running:
sudo apt install python3 python3-pip python3-venv
  • Verify the install by running:
python3 --version

✔️ I see a version number

Python 3 is installed and ready.

ⓧ Command not found

  • Close and reopen your terminal to refresh your PATH.
  • Run python3 --version again.
  • On Windows, try python --version instead (Windows installs often omit the 3 suffix).

Before you move on, confirm all three tools are available in a single check. What do you think will happen when you run all three version commands together?

  • Run all three version checks together:
docker --version && minikube version && python3 --version

You should see three version numbers printed one after the other. Each line confirms one of your tools is installed and on your PATH.

One of the commands failed?

  • The && operator stops at the first failure. Whichever tool's version did not print is the one that needs fixing.
  • Scroll up to the relevant install section above and follow the troubleshooting steps for that specific tool.
  • If Docker prints an error about the daemon, make sure Docker Desktop is running (check the icon in your menu bar or system tray).

Why is one of my version commands failing after I installed everything?

Your development environment is ready. Next up, you will start your Kubernetes cluster and install the kagent framework that turns it into an AI agent platform.

Start Your Cluster and Install Kagent

You have Docker, Minikube, and Python installed. But right now there is no cluster running. Without a live cluster, there is nowhere for your AI agent to live.

In this step, you will start a local Kubernetes cluster with Minikube, install the agent runtime (kagent), and connect it to Google's Gemini API. By the end, your cluster will be a fully operational AI agent platform with demo agents already running.

In this step, get ready to:
  • Start a Minikube cluster and install Helm and kubectl.
  • Install the kagent CLI and deploy kagent onto your cluster.
  • Store your Gemini API key as a Kubernetes secret and verify the agent platform is running.
Start the Minikube cluster and install Helm and kubectl

Your agent needs a Kubernetes cluster to run on. Minikube creates a single-node cluster locally, so you get the full Kubernetes experience without paying for cloud resources.

  • Start your cluster with enough memory and CPU for the agent workloads by running this command:
minikube start --memory=4096 --cpus=2

This takes 1-2 minutes. You will see Minikube download the Kubernetes components and start the cluster node.

What does this command do?

The --memory=4096 flag gives your cluster 4 GB of RAM. The --cpus=2 flag allocates 2 CPU cores. Kagent runs multiple pods (the controller, the dashboard, and your agents), so these resources prevent slowdowns.

Kagent uses Helm (a Kubernetes package manager) under the hood, and kubectl is how you interact with your cluster directly. You need both installed before proceeding.

  • Install both Helm and kubectl by running the commands for your platform:

macOS

brew install helm
brew install kubectl

Windows

winget install Helm.Helm
winget install Kubernetes.kubectl

Linux

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
sudo snap install kubectl --classic
  • Verify both installed correctly and confirm the cluster is running:
helm version
kubectl cluster-info

You should see a Helm version number and the Kubernetes control plane address (something like https://127.0.0.1:...).

Helm or kubectl install failed?

Close and reopen your terminal to refresh your PATH, then retry the install commands for your platform.

If kubectl cluster-info shows an error, confirm Docker Desktop is still running and that you ran minikube start earlier without errors.

Help me troubleshoot my Helm or kubectl installation.

Install kagent and deploy it to the cluster

With a cluster running, you now need the agent runtime. Kagent turns your generic Kubernetes cluster into an AI agent platform by adding Custom Resource Definitions (CRDs) for agents, tools, and model configs.

  • Install the kagent CLI by running the command for your platform:

macOS

brew install kagent

Windows

Open PowerShell and run:

curl https://raw.githubusercontent.com/kagent-dev/kagent/refs/heads/main/scripts/get-kagent | bash

Running bash scripts on Windows

This script requires Git Bash or WSL. If you installed Git for Windows, open Git Bash and run the command there. Alternatively, run it inside your WSL terminal.

Linux

curl https://raw.githubusercontent.com/kagent-dev/kagent/refs/heads/main/scripts/get-kagent | bash
  • Verify the CLI is available by running:
kagent version

You should see version 0.9.9 (or later) printed.

Seeing 'command not found' for kagent?

If your shell cannot find kagent after installing, try opening a new terminal window. The installer places the binary in /usr/local/bin (macOS/Linux) or $HOME/bin (if you used --no-sudo). Make sure that directory is on your PATH.

Help me fix kagent command not found after installing.

Kagent needs access to a large language model to power its agents. You will use Google's Gemini API, which provides a free tier with no billing setup required.

  • Open your browser and go to aistudio.google.com.
  • Sign in with your Google account.
  • Click Get API key in the left sidebar.
  • Click Create API key and copy the generated key.

What is this API key for?

This key lets your agents call Google's Gemini model to generate responses. The free tier includes generous limits for personal projects. No credit card is needed.

  • Tell kagent to use Gemini as its model provider by exporting these environment variables:
export KAGENT_DEFAULT_MODEL_PROVIDER=gemini
export GEMINI_API_KEY=your-gemini-api-key-here

What do these exports do?

By default, kagent install looks for an OpenAI key. Setting KAGENT_DEFAULT_MODEL_PROVIDER=gemini tells kagent to use Gemini instead. GEMINI_API_KEY passes your key so kagent can configure the Gemini model during installation.

  • Install kagent onto your cluster with the demo profile by running:
kagent install --profile demo

This command takes 2-3 minutes. It installs the kagent controller, dashboard, and demo agents as Kubernetes pods.

What does the demo profile include?

The --profile demo flag preloads your cluster with sample agents and MCP tools so you can explore kagent immediately. You will see agents like helm-agent, observability-agent, and istio-agent already running.

🙋‍♀️ Install timing out or pods stuck?

Make sure Docker Desktop is running and your Minikube cluster is healthy. Run minikube status to confirm everything is in the Running state.

Help me fix kagent install timing out on Minikube.

Create the Kubernetes secret and verify kagent is running

Your agents also need the key stored as a Kubernetes secret so they can access it at runtime without exposing it in your code.

  • Create the secret using the environment variable you exported earlier by running:
kubectl create secret generic kagent-gemini -n kagent --from-literal GOOGLE_API_KEY=$GEMINI_API_KEY

What does this command do?

This creates a Kubernetes Secret called kagent-gemini in the kagent namespace. It stores your API key under the key name GOOGLE_API_KEY. Kagent's Gemini provider looks for this exact secret name and key by default.

The command reads $GEMINI_API_KEY from the environment variable you exported earlier, so you don't need to paste the key again.

You should see secret/kagent-gemini created in the output.

Seeing 'namespace kagent not found'?

The kagent install --profile demo command creates the kagent namespace. If it does not exist, the install may not have completed successfully. Run kubectl get namespaces to check whether kagent appears in the list.

Help me fix the missing kagent namespace.

Time to verify the full platform is working. Before you run the next command, what do you think you will see if kagent installed correctly?

  • List the pre-installed demo agents by running:
kagent get agent

You should see a table listing the demo agents: helm-agent, observability-agent, and istio-agent. This confirms kagent is running and managing agents on your cluster.

Your Kubernetes cluster is now a live AI agent platform. Next up, you will scaffold your own finance research agent and see it respond to your first message.

Scaffold and Run Your Finance Agent Locally

Your cluster is running and kagent is installed, but you have no custom agents yet. Before deploying anything to Kubernetes, you want to see your agent come alive fast and get immediate feedback that it works.

The kagent CLI scaffolds a complete agent project in seconds. You will customize it with a finance research persona, then run it locally to chat with it. There is a catch though: without real tools connected, the agent can only talk generically about finance. That limitation is exactly what you will fix in the next step.

In this step, get ready to:
  • Scaffold a finance agent project using the kagent CLI.
  • Customize the agent with a finance research system prompt.
  • Run the agent locally and chat with it.
Scaffold your finance agent project

The kagent init command creates a ready-to-run agent project with all the boilerplate wired up. You choose the framework (ADK), language (Python), model provider (Gemini), and give your agent a name.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Scaffold your finance agent project by running this command:
kagent init adk python finance_agent --model-provider Gemini --model-name gemini-3.6-flash

What does this command do?

  • adk selects the Agent Development Kit framework (Google's ADK for Python).
  • python sets the language.
  • finance_agent is the project name and directory.
  • --model-provider Gemini --model-name gemini-3.6-flash configures the agent to use Google's Gemini 3.6 Flash model.

You will see output confirming the project was created, including a file tree and next-step hints.

  • Move into your new project folder and list the files by running:
cd finance_agent
ls

You should see files including kagent.yaml, pyproject.toml, docker-compose.yaml, and a finance_agent/ subdirectory containing your agent code.

What is in the scaffolded project?

  • finance_agent/agent.py contains the main agent logic (currently a dice-rolling placeholder).
  • finance_agent/mcp_tools.py wires up MCP tool discovery so the agent can use external tools.
  • finance_agent/agent-card.json holds metadata about your agent (name, description, version).
  • kagent.yaml is the project configuration that tells kagent which model and MCP servers to use.

🙋‍♀️ Command not found?

If you see a "command not found" error for kagent, make sure the kagent CLI was installed correctly in the previous step. Try running brew install kagent again.

help me troubleshoot kagent init failing

Configure the agent with a finance persona

The scaffolded agent.py has a generic dice-rolling prompt. You will replace it entirely with a finance research persona that instructs the agent how to behave when answering market questions.

  • Open finance_agent/agent.py in VS Code.
  • Select all the existing content and delete it.
  • Paste the following code, which defines the system prompt:
from google.adk.agents import Agent

from .mcp_tools import mcp_tools

SYSTEM_PROMPT = """You are a finance research assistant that helps users analyze stocks and market data.

# Instructions
- Use your available tools to fetch real-time stock data when users ask about prices, companies, or market trends
- Always provide context with your data: explain what the numbers mean
- If a user asks about a stock, fetch the current quote before answering
- Compare stocks when asked by fetching data for each ticker
- If you don't have a tool for something, say so clearly rather than guessing
- Format responses in Markdown with clear sections

# Response format
- Start with a brief summary of what you found
- Include specific numbers (price, change %, volume) when available
- Add your analysis of what the data suggests
- End with any caveats (e.g., "this is real-time data, not investment advice")
"""

What does this system prompt do?

The SYSTEM_PROMPT is a set of instructions that shapes how the agent behaves. It tells the agent to use tools for real data, format responses in Markdown, and never guess when tools are unavailable.

The imports pull in the Agent class from Google's ADK and the mcp_tools list that kagent auto-generates for MCP tool discovery.

  • Below the SYSTEM_PROMPT string, add the agent instantiation:
root_agent = Agent(
    model="gemini-3.6-flash",
    name="finance_research_agent",
    description="A finance research agent that fetches real-time stock data and provides market analysis.",
    instruction=SYSTEM_PROMPT,
    tools=mcp_tools,
)

What does this code do?

  • root_agent is the entry point kagent looks for when running your agent.
  • model tells it which LLM to call (Gemini 3.6 Flash).
  • instruction passes your system prompt so every conversation uses these rules.
  • tools=mcp_tools connects any MCP tool servers defined in kagent.yaml.
  • Save finance_agent/agent.py.

Next, update the project configuration to tell kagent about the MCP tool server you will build in the next step.

  • Open kagent.yaml in VS Code.
  • Replace the entire file content with:
name: finance_agent
framework: adk
language: python
model:
  provider: Gemini
  name: gemini-3.6-flash
mcp_servers:
  - name: finnhub-tools
    command: python
    args: ["-m", "finnhub_tools"]

What does the mcp_servers section do?

The mcp_servers section declares that this agent will use an MCP tool server called finnhub-tools. The server does not exist yet. You will build it in the next step. For now, the agent will start but have no tools available to call.

  • Save kagent.yaml.
  • Now open finance_agent/agent-card.json and replace its content with:
{
  "name": "finance_research_agent",
  "description": "A finance research agent that fetches real-time stock data and provides market analysis.",
  "version": "0.1.0"
}
  • Save finance_agent/agent-card.json.
  • Confirm your updated files exist by running:
ls finance_agent/

You should see __init__.py, agent.py, mcp_tools.py, and agent-card.json listed.

Create the environment file and run the agent

The agent needs your Gemini API key to call the LLM. kagent reads API keys from a .env.local file in the project root.

  • Create the .env.local file by running:
cat > .env.local << 'EOF'
GOOGLE_API_KEY=your-gemini-api-key-here
FINNHUB_API_KEY=your-finnhub-api-key-here
EOF
  • Open .env.local in VS Code and replace your-gemini-api-key-here with the actual Gemini API key you obtained from Google AI Studio in the previous step.
  • Leave your-finnhub-api-key-here as a placeholder for now. You will get the Finnhub key in the next step.
  • Save .env.local.

Why two keys?

GOOGLE_API_KEY authenticates with Gemini so the agent can generate responses. FINNHUB_API_KEY will authenticate with the Finnhub stock data API once you build the MCP tool server in the next step.

Time to see your agent in action. Before you run it, think about this: you told the agent it has tools for fetching stock data, but the finnhub-tools module does not exist yet. What do you think will happen when you ask it about stock prices?

  • Build and run your agent locally by running:
kagent build && kagent run

What happens when you run this?

kagent build packages your agent into a Docker image. kagent run launches the agent locally using docker-compose and opens a terminal chat interface where you can type messages.

The MCP server reference in kagent.yaml will fail to start since the finnhub_tools module does not exist yet. The agent itself will still start and respond using Gemini, but it will have no tools to call.

  • In the terminal chat interface, type:
Hello, what can you do?

The agent responds describing its finance research capabilities. It mentions it can fetch stock quotes, compare tickers, and analyze trends.

  • Now ask it something that requires real data:
What is the current price of AAPL?

The agent cannot fetch a real price. It either tells you the tool is unavailable or provides a disclaimer that it cannot access live data right now. This is the designed limitation: an LLM without tools is just a chatbot that can only guess.

  • Press Ctrl+C to exit the chat interface and stop the agent.

Agent not starting?

If you see errors about Docker not running, make sure Docker Desktop is open and the whale icon shows a green "running" status in your menu bar.

If the build fails with a Python import error, double-check that finance_agent/agent.py has the exact code from the previous substep and that finance_agent/__init__.py exists.

help me debug why kagent run is failing

✔️ Awesome, I've got everything!

Great. Double check you have saved all your files.

ⓧ I'd like to double check the full code

Here is the full finance_agent/agent.py file:

from google.adk.agents import Agent

from .mcp_tools import mcp_tools

SYSTEM_PROMPT = """You are a finance research assistant that helps users analyze stocks and market data.

# Instructions
- Use your available tools to fetch real-time stock data when users ask about prices, companies, or market trends
- Always provide context with your data: explain what the numbers mean
- If a user asks about a stock, fetch the current quote before answering
- Compare stocks when asked by fetching data for each ticker
- If you don't have a tool for something, say so clearly rather than guessing
- Format responses in Markdown with clear sections

# Response format
- Start with a brief summary of what you found
- Include specific numbers (price, change %, volume) when available
- Add your analysis of what the data suggests
- End with any caveats (e.g., this is real-time data, not investment advice)
"""

root_agent = Agent(
    model="gemini-3.6-flash",
    name="finance_research_agent",
    description="A finance research agent that fetches real-time stock data and provides market analysis.",
    instruction=SYSTEM_PROMPT,
    tools=mcp_tools,
)

Here is kagent.yaml:

name: finance_agent
framework: adk
language: python
model:
  provider: Gemini
  name: gemini-3.6-flash
mcp_servers:
  - name: finnhub-tools
    command: python
    args: ["-m", "finnhub_tools"]

Here is finance_agent/agent-card.json:

{
  "name": "finance_research_agent",
  "description": "A finance research agent that fetches real-time stock data and provides market analysis.",
  "version": "0.1.0"
}

Here is .env.local (with your actual Gemini key replacing the placeholder):

GOOGLE_API_KEY=your-gemini-api-key-here
FINNHUB_API_KEY=your-finnhub-api-key-here

Your agent is alive and can chat about finance topics using Gemini. The problem is clear: it has no way to fetch real market data, so it cannot give you an actual stock price. Next up, you will build a custom MCP tool server that connects to Finnhub's API and gives your agent real capabilities.

Build the Finnhub MCP Tool Server

In the last step, you chatted with your agent and saw its limitation firsthand. When you asked about stock prices, it could only guess because it has no access to real market data.

An LLM without tools is just a chatbot. In this step, you will build a custom MCP tool server that connects to Finnhub's free stock data API. This gives your agent the ability to fetch live prices, company profiles, and analyst recommendations on command.

In this step, get ready to:
  • Get a free Finnhub API key for real-time stock data.
  • Scaffold an MCP server project and implement three stock data tools.
  • Test the MCP server locally and see real price data returned.
Get your Finnhub API key

Finnhub provides free real-time stock market data with 60 API calls per minute. No credit card is required.

  • Open your browser and navigate to finnhub.io.
  • Click Get free API key and create an account with your email.
  • After signing in, copy your API key from the dashboard.

Your .env.local file already has a placeholder for this key from the scaffolding in the previous step.

  • In VS Code, open .env.local.
  • Find the line that says:
FINNHUB_API_KEY=your-finnhub-api-key-here
  • Replace your-finnhub-api-key-here with the API key you just copied from Finnhub.
  • Save .env.local.

Why a separate .env.local file?

Keeping API keys in .env.local means they never get committed to version control. The kagent runtime loads this file automatically when you run the agent or MCP server locally.

Scaffold and implement the MCP server

The kagent CLI includes dedicated MCP commands for building tool servers. You will scaffold a new server project, then replace the boilerplate with your custom Finnhub integration.

  • In your terminal, make sure you are inside the finance_agent directory by running:
cd ~/Desktop/finance_agent
  • Scaffold the MCP server project by running:
kagent mcp init finnhub-tools

What does this command do?

This creates a finnhub-tools directory with boilerplate files for a FastMCP Python server. It includes a sample echo tool, a Dockerfile, and configuration files.

The CLI may prompt you for a description and author. Press Enter to accept the defaults for each prompt.

  • Confirm the new directory was created by running:
ls finnhub-tools

You should see files like Dockerfile, pyproject.toml, and a src/ directory listed in the output.

Now you will replace the scaffolded code with your custom Finnhub tool implementation. The target structure is a simple Python package with three files.

  • Create the main tools file at finnhub-tools/tools.py in VS Code and paste the following setup code:
import os
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("finnhub-tools")

FINNHUB_API_KEY = os.environ.get("FINNHUB_API_KEY", "")
FINNHUB_BASE_URL = "https://finnhub.io/api/v1"

What does this setup do?

  • FastMCP("finnhub-tools") creates a new MCP server instance named finnhub-tools.
  • FINNHUB_API_KEY reads your API key from the environment variable at runtime.
  • FINNHUB_BASE_URL sets the base URL for all Finnhub API calls so you do not repeat it in every function.
  • Add the first tool function below the setup code in finnhub-tools/tools.py:
@mcp.tool()
async def get_stock_quote(symbol: str) -> dict:
    """Get the current stock quote for a given ticker symbol.

    Args:
        symbol: The stock ticker symbol (e.g., AAPL, MSFT, GOOGL)

    Returns:
        A dictionary with current price (c), change (d), percent change (dp),
        high (h), low (l), open (o), previous close (pc), and timestamp (t).
    """
    async with httpx.AsyncClient() as client:
        response = await client.get(
            f"{FINNHUB_BASE_URL}/quote",
            params={"symbol": symbol.upper(), "token": FINNHUB_API_KEY},
        )
        response.raise_for_status()
        data = response.json()
        return {
            "symbol": symbol.upper(),
            "current_price": data["c"],
            "change": data["d"],
            "percent_change": data["dp"],
            "high": data["h"],
            "low": data["l"],
            "open": data["o"],
            "previous_close": data["pc"],
        }

What does get_stock_quote do?

The @mcp.tool() decorator registers this function as a tool your agent can call. When invoked, it sends an HTTP GET request to Finnhub's /quote endpoint with the stock ticker symbol.

The docstring is important because the agent reads it to understand when and how to use the tool. Finnhub returns single-letter keys like c for current price, so the function maps them to readable names.

  • Before adding more tools, set up the package wiring files so you can test shortly. Create finnhub-tools/__init__.py with this content:
from .tools import mcp
  • Create finnhub-tools/__main__.py with this content:
from .tools import mcp

mcp.run()

Why __init__.py and __main__.py?

__init__.py imports the mcp server instance so other modules can access it. __main__.py makes the package executable with python -m finnhub_tools. This is exactly how kagent.yaml is configured to start the MCP server.

  • Save all three files (tools.py, __init__.py, __main__.py).

Before you run this, think about what you expect. You have one tool registered (get_stock_quote), a Finnhub API key in your environment, and a server that starts with mcp.run(). Do you think calling the tool with AAPL will return a real price?

  • Test your MCP server by running:
kagent mcp run

What happens when you run this?

This command builds your MCP server into a Docker image and launches the MCP Inspector tool in your browser. The Inspector lets you connect to your server and call tools manually to verify they work.

It may take a few seconds for the connection to establish. If you do not have the MCP Inspector installed, the command will prompt you to install it first.

  • In the MCP Inspector, click Connect to connect to your server.
  • Click the Tools tab, then click List Tools.
  • Select the get_stock_quote tool.
  • Enter AAPL in the symbol field and click Run Tool.

You should see real-time JSON data returned with Apple's current stock price, change amount, percent change, daily high, low, open, and previous close.

Not seeing your tool listed?

  • Make sure your FINNHUB_API_KEY in .env.local is a real key from finnhub.io, not the placeholder text.
  • Check that finnhub-tools/__main__.py contains mcp.run(). Without this line, the server does not start.
  • If the MCP Inspector shows a connection error, wait a few seconds and click Connect again.

Still stuck? Help me debug why my kagent MCP server tools are not showing up in the Inspector.

  • Stop the MCP server by pressing Ctrl+C in your terminal.

Your first tool works. Now add the remaining two tools to give your agent a fuller picture of any stock.

  • Open finnhub-tools/tools.py and add this second tool function below get_stock_quote:
@mcp.tool()
async def get_company_profile(symbol: str) -> dict:
    """Get the company profile for a given ticker symbol.

    Args:
        symbol: The stock ticker symbol (e.g., AAPL, MSFT, GOOGL)

    Returns:
        A dictionary with company name, industry, market cap, and other details.
    """
    async with httpx.AsyncClient() as client:
        response = await client.get(
            f"{FINNHUB_BASE_URL}/stock/profile2",
            params={"symbol": symbol.upper(), "token": FINNHUB_API_KEY},
        )
        response.raise_for_status()
        return response.json()

What does get_company_profile do?

This tool calls Finnhub's /stock/profile2 endpoint. It returns information like company name, industry, market capitalization, and exchange listing. Your agent uses this to provide context when comparing stocks.

  • Add the third and final tool function below get_company_profile in finnhub-tools/tools.py:
@mcp.tool()
async def get_recommendation_trends(symbol: str) -> list:
    """Get analyst recommendation trends for a given ticker symbol.

    Args:
        symbol: The stock ticker symbol (e.g., AAPL, MSFT, GOOGL)

    Returns:
        A list of monthly recommendation data with buy, hold, sell, strongBuy, strongSell counts.
    """
    async with httpx.AsyncClient() as client:
        response = await client.get(
            f"{FINNHUB_BASE_URL}/stock/recommendation",
            params={"symbol": symbol.upper(), "token": FINNHUB_API_KEY},
        )
        response.raise_for_status()
        return response.json()

What does get_recommendation_trends do?

This tool fetches analyst consensus data from Finnhub's /stock/recommendation endpoint. It returns monthly counts of buy, hold, sell, strong buy, and strong sell recommendations. Your agent can use this to provide market sentiment analysis.

  • Save finnhub-tools/tools.py.
Test the complete MCP server

Your kagent.yaml already references the finnhub-tools MCP server from the scaffolding in Step 3. Confirm the wiring is correct before the final test.

  • Open kagent.yaml and confirm you see this section under mcp_servers:
mcp_servers:
  - name: finnhub-tools
    command: python
    args: ["-m", "finnhub_tools"]

How does wiring work?

This YAML tells kagent to start your MCP server by running python -m finnhub_tools. The agent automatically discovers all tools registered with @mcp.tool() on that server and makes them available for use.

✔️ Awesome, I've got everything!

Great. Make sure you have saved all files in finnhub-tools/.

ⓧ I'd like to double check the full code

Your finnhub-tools/tools.py is over 30 lines, so check each section matches yours by scrolling through the file:

  • The imports and FastMCP setup at the top (import os, import httpx, from mcp.server.fastmcp import FastMCP, mcp = FastMCP("finnhub-tools"), and the two constants).
  • The get_stock_quote function calling /quote and returning a dictionary with readable key names.
  • The get_company_profile function calling /stock/profile2 and returning the raw JSON.
  • The get_recommendation_trends function calling /stock/recommendation and returning the raw JSON.

Also confirm __init__.py has from .tools import mcp and __main__.py has from .tools import mcp followed by mcp.run().

Now run the server one more time to verify all three tools appear.

  • Start the MCP server again by running:
kagent mcp run
  • In the MCP Inspector, click Connect.
  • Click the Tools tab, then click List Tools.

You should see all three tools listed: get_stock_quote, get_company_profile, and get_recommendation_trends.

  • Select get_stock_quote, enter AAPL in the symbol field, and click Run Tool.

You should see real price data returned as JSON with fields like current_price, change, and percent_change.

Seeing an error when running the tool?

  • If you see a 401 response, your Finnhub API key is invalid. Go back to finnhub.io and copy a fresh key into .env.local.
  • If a tool returns an empty result, check that you typed the ticker symbol correctly (e.g., AAPL not aapl). The function uppercases it, but the Inspector may have trailing spaces.
  • If only one tool appears instead of three, make sure you saved tools.py after adding all three functions, then restart with kagent mcp run.

Still stuck? Help me troubleshoot why my Finnhub MCP tool is returning an error instead of stock data.

Your agent now has real superpowers. Next up, you will deploy the whole stack to your Kubernetes cluster so it runs as a proper cloud-native workload.

Deploy to Kubernetes and Chat

Your agent works locally and fetches live market data from Finnhub. That proves your code is solid.

Now you will deploy it to your Minikube cluster so it runs as a real Kubernetes workload. The kagent CLI packages your agent and MCP server into container images, pushes them to the cluster registry, and creates the proper Custom Resource Definitions (CRDs) that tell Kubernetes how to manage them.

In this step, get ready to:
  • Build and push Docker images for your agent and MCP server to the Minikube registry.
  • Deploy everything to the cluster and verify with kubectl.
  • Chat with your deployed agent using the dashboard and CLI.
Build and push images to the Minikube registry

Kubernetes runs containers, not raw Python files. Before your cluster can run the agent, it needs container images stored in a registry it can access.

  • Make sure you are inside the finance_agent project folder in your terminal.
  • Build and push both the agent and MCP server images by running this command:
kagent build --push

What does this command do?

The kagent build command reads your kagent.yaml and builds Docker images for your agent and every MCP server listed in it.

The --push flag uploads the built images to the localhost:5001 registry that Minikube exposes. This makes the images available to your cluster without needing an external registry like Docker Hub.

  • Wait for the build to finish. You should see Docker build progress for both finance_agent and finnhub-tools, followed by push confirmation messages.

Build failed or Docker errors?

  • Make sure Docker Desktop is running. Check the Docker whale icon in your menu bar.
  • Confirm your Minikube cluster is still up by running minikube status. If it stopped, run minikube start to restart it.
  • help me debug a kagent build failure
Deploy to the cluster and verify

Your images are in the registry. Now you tell kagent to create the Kubernetes resources that run them.

  • Deploy your agent and MCP server to the cluster by running this command:
kagent deploy . --env-file .env.local

What does this command do?

This reads your kagent.yaml manifest and creates Kubernetes CRDs for your agent and MCP server. The --env-file .env.local flag injects your API keys as Kubernetes secrets in the kagent namespace so your pods can access them securely.

The kagent controller running on your cluster picks up these CRDs and spins up pods for both the agent and the finnhub-tools MCP server.

  • Verify the deployment by checking the CRDs in the kagent namespace:
kubectl get agents,mcpservers -n kagent
  • You should see finance_agent listed as an Agent resource and finnhub-tools listed as an MCPServer resource. Both will show a NAME and AGE column confirming they are registered.

Resources not showing up?

  • Give it 30 seconds. The kagent controller needs time to reconcile new CRDs after deployment.
  • If you see an error about the resource type not found, kagent may not be installed properly. Run kagent get agent to check whether the demo agents are still present.
  • help me troubleshoot my kagent deployment
Chat with your deployed finance agent

Your agent is live on the cluster. Time to talk to it and see real market data flow through the full Kubernetes stack.

  • Open the kagent dashboard by running:
kagent dashboard
  • Your browser opens to http://localhost:8082. You should see your finance_agent listed alongside the demo agents.
  • Click on finance_agent and try asking it a question like "What is Apple's current stock price?" or "Compare AAPL and MSFT for me."

The dashboard is great for exploration, but the CLI lets you invoke the agent directly from your terminal too.

Before you run the next command, predict: will the agent respond with a real stock price or a generic answer?

  • Invoke the agent from your terminal by running this command:
kagent invoke --agent finance_agent --task "What is the current stock price of AAPL?" --stream

You should see the agent stream a response that includes a real dollar amount for Apple's stock price, pulled live from Finnhub. The response includes the current price, daily change, and percentage change.

What just happened?

Your request traveled through the full cloud-native stack: the kagent CLI sent your task to the finance_agent pod, which used Gemini to decide it needed the get_stock_quote tool, called the finnhub-tools MCP server pod, which fetched live data from the Finnhub API, and returned it to the agent for analysis.

All of this ran as Kubernetes workloads with secrets managed by the cluster. This is the same pattern you would use to deploy to a production cloud cluster.

🙋‍♀️ Agent not responding with real data?

  • If the agent says it cannot fetch data or has no tools, your MCP server pod may not be running. Run kubectl get pods -n kagent and check for CrashLoopBackOff on the finnhub-tools pod.
  • If you see authentication errors, your Finnhub API key may be incorrect. Double-check the value in .env.local and redeploy with kagent deploy . --env-file .env.local.
  • help me debug my finance agent not fetching real stock data

Secret mission

Add Company News Analysis

Your agent can tell you the price of a stock, but it has no idea why the price is moving. Can you add a news analysis tool that fetches recent headlines and lets the agent explain what is driving a stock up or down?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally on your Minikube cluster, so there are no ongoing cloud costs.

Resources you used:

  • Minikube cluster running locally (no cloud cost).
  • Docker images (finance_agent and finnhub-tools) in the local Minikube registry.
  • Kubernetes secrets containing your Gemini and Finnhub API keys.
  • Local project files in the finance_agent directory on your Desktop.

Keep everything running

No action needed. Choose this if you are still actively building or want to keep experimenting with your finance agent.

  • Your Minikube cluster stays running and uses local CPU and memory only.
  • Your agent remains accessible via kagent dashboard and kagent invoke.
  • No cloud charges will accrue.

Pause - I'll come back to this later

Shut down the Minikube cluster to free up local CPU and memory. Your cluster state is preserved and you can resume later.

  • Stop the Minikube cluster by running:
minikube stop
  • When you are ready to resume, start it again by running:
minikube start

Everything comes back exactly as you left it. Your agent, MCP server, and secrets are all preserved.

Delete - I don't want to use this again

Remove all project resources and start fresh. This deletes the Minikube cluster, all Docker images in the local registry, and all Kubernetes secrets.

  • Delete the Minikube cluster by running:
minikube delete

This removes the cluster, all Docker images stored in its registry, and the Kubernetes secrets containing your API keys.

  • Remove the local project files by running:
rm -rf ~/Desktop/finance_agent

Your Gemini and Finnhub API keys still exist in your Google AI Studio and Finnhub accounts. You can revoke them from those dashboards if you no longer need them.

Nice Work!

Nice Work!

Nice work! You just built and deployed a fully functional AI finance research agent on a local Kubernetes cluster. Your agent fetches real-time stock data on command and runs as a proper cloud-native workload.

You've learned how to:

  • Built a finance research AI agent using the kagent CLI with Google Gemini as the LLM provider, scaffolding a complete agent project and deploying it to a local Minikube cluster.
  • Created a custom MCP tool server that fetches real-time stock quotes, company profiles, and analyst recommendation trends from Finnhub's API.
  • Deployed the agent and tool server to Kubernetes as CRD-managed workloads with proper secrets management, then chatted with the agent via the kagent dashboard and CLI to perform live market research.
  • Secret Mission: Extended the agent with a company news analysis tool, proving the MCP pattern scales to multiple tools without modifying agent logic.

Ready to quiz yourself?