

A model can predict text. It cannot fetch market data, hit your API, or place an order without help. In the context of AI agents, that help is a tool. A tool is a callable capability with typed inputs and a structured result. The agent decides what to do next, calls the tool, and uses the result to continue the plan.
In this post, we will explore building an app called Finwise with Agent Development Kit (ADK) to show the pattern of creating tools for your agent in practice. By the end of this, you will learn to create four different types of tools so an agent can complete a multi step job in a predictable way, and you will learn to use your agent through ADK’s web UI.
Here’s what our Finwise app will look like:
We start off by giving a complex prompt to our agent and it makes use of the four tools at it’s disposal to complete the tasks.
1. Price lookup
The root agent called get_stock_price("GOOG")
. The function tool returned a structured result with the price, and the agent summarized it in the chat.
2. Open ended analysis
The root agent handed the CSV task to CodeAgent
. That agent used the built in code executor to load the data, compute daily return stats, and produce a short plain-English summary. Code work stays isolated on this helper agent so the main agent remains simple.
3. Risk simulation through MCP
The agent invoked run_monte_carlo
from the MCP toolset, passing the AAPL price from step one along with your parameters. ADK connect to the MCP server and returned a quick simulation summary within the time budget.
4. Safe trade preparation with auth
The agent called execute_trade
without confirmation. The tool validated the inputs and replied with a needs_confirmation
payload that shows exactly what to resend with confirm=true
. No credentials are requested and no order was placed until you explicitly confirm.
By the end you will know when to use a direct function, when to route work to a code execution agent, when to move heavy work to an MCP service, and how to add OAuth based authentication for sensitive actions like executing a trade.
Function Tool: get_stock_price
Function tools are the cleanest way to give an agent a new capability. In ADK you write a normal Python function with simple, JSON friendly inputs and outputs. When you place that function in the tools
list, ADK wraps it and the agent can call it through structured tool calls.
def get_stock_price(ticker: str) -> dict:
"""
Input: a ticker like 'AAPL'
Output: 'status': 'ok'
Replace the placeholder with your data source when ready.
"""
t = (ticker or "").upper().strip()
if not t:
return {"status": "error", "message": "ticker is required"}
# Placeholder price so the ADK pattern is clear; swap in your provider later
price = 123.45
return {"status": "ok", "ticker": t, "price": float(price)}
Attach the tool and guide the agent:
from google.adk.agents import Agent
root_agent = Agent(
name="Finwise",
model="gemini-2.0-flash",
instruction=(
"If the user asks for a quote or price, call get_stock_price(ticker). "
"On success, report the number and the ticker. "
"If the tool returns an error, ask for a valid ticker and try again."
),
tools=[get_stock_price], # ADK auto wraps this as a FunctionTool
)
Prompt to try
What is the price of AAPL right now
Built in code execution tool
Some questions are easiest to answer by writing a few lines of Python on the fly: load a tiny CSV, compute returns, fit a quick model, make a small table. ADK includes a built in code executor for exactly this. The recommended pattern is:
- put code execution on its own agent (so crashes, imports, or long prints don’t pollute your root agent)
- expose that agent to the root with
AgentTool
- teach the code agent to only run code when needed, then summarize the result in plain text
This separation also reflects an ADK rule of thumb: code execution agents should not carry your other tools. Keep them focused, then compose at the root.
Create the analysis agent
from google.adk.agents import Agent
from google.adk.code_executors import BuiltInCodeExecutorcoding_agent = Agent(
name="CodeAgent",
model="gemini-2.0-flash",
instruction=(
"Write and run Python only when it is the simplest way to answer. "
"If given a CSV string, load with pandas, compute the requested stats, "
"and finish with a one-line Summary."
),
code_executor=BuiltInCodeExecutor(), # enables sandboxed Python execution
)
Expose it to the root agent
from google.adk.agents import Agent
from google.adk.tools import agent_tool
root_agent = Agent(
name="Finwise",
model="gemini-2.0-flash",
instruction=(
"Use CodeAgent for any ad-hoc data analysis (CSV parsing, quick stats, small plots). "
"Otherwise answer directly or use other tools."
),
tools=[
agent_tool.AgentTool(agent=coding_agent), # CodeAgent becomes a tool
# ...other tools like get_stock_price, MCP, execute_trade
],
)
Prompt to try
Use CodeAgent. Here is a tiny CSV:
date,close
2025-08-12,100
2025-08-13,101.2
Compute the daily return mean and standard deviation, then give a one sentence Summary.
MCP server with ADK and the MCP toolset
Some work is better outside the agent process. Think heavy simulation, special native libraries, or services that many agents should share. This is where MCP fits. MCP is a standard way for an agent to discover a server’s tools and call them with structured inputs. In this project we do two things. We create a tiny MCP server that exposes a single tool, and we connect that server to our agent through ADK’s MCP toolset. ADK then discovers the tool, calls it with structured inputs, and returns a structured result. You write the server once and any MCP capable client can use it.
Why use MCP here
- Keep heavy or specialized code in its own process or service
- Reuse the same tool from multiple agents and clients
- Control resources and permissions separately from the agent
- Keep the root agent small and easy to reason about
Wire an MCP STDIO server into your agent
This setup tells ADK to launch your local server when the tool is first used. No need to run the server by hand.
import os, sys
from pathlib import Path
from google.adk.agents import Agent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
from mcp import StdioServerParameters# Resolve the server path safely
here = Path(__file__).resolve().parent
server_path = os.getenv(
"MONTE_CARLO_MCP_SERVER",
str(here.parent / "external" / "monte_carlo_mcp_server.py"),
)
if not Path(server_path).is_file():
raise FileNotFoundError(f"MCP server not found: {server_path}")
mcp_server = StdioServerParameters(
command=sys.executable,
args=[server_path],
env={"PYTHONUNBUFFERED": "1"},
)
monte_carlo_mcp = MCPToolset(
connection_params=StdioConnectionParams(server_params=mcp_server)
)
root_agent = Agent(
name="Finwise",
model="gemini-2.0-flash",
instruction=(
"For risk simulation, call the MCP tool run_monte_carlo with the current price, "
"a one year horizon, a reasonable number of trials, and a five second time budget. "
"Return a short summary of outcomes."
),
tools=[monte_carlo_mcp],
)
How the agent actually calls it
Give the agent plain parameters. Keep names simple so auto function calling is reliable.
# Example of what the agent will send through the MCP toolset
# run_monte_carlo(
# start_price=price_from_step_one,
# days=252,
# trials=8000,
# annual_drift=0.07,
# annual_vol=0.20,
# time_budget_s=5
# )
Prompt to try
Run the MCP tool run_monte_carlo with start price 100, 252 trading days, 8000 trials, drift 7 percent, volatility 20 percent, and a five second time budget. Give me a brief summary of the result.
Execute trade tool and authentication in ADK
Many useful tools need credentials. ADK gives you a consistent way to request credentials from the user, exchange them, and cache them in the session. The pattern uses a ToolContext
plus AuthConfig
, AuthCredential
, and request_credential
or get_auth_response
. See the authentication guide for the exact API and flow.
Define the OAuth scheme and a demo credential
from google.adk.auth import AuthConfig, AuthCredential, AuthCredentialTypes, OAuth2Auth
from fastapi.openapi.models import OAuth2, OAuthFlowAuthorizationCode, OAuthFlows
auth_scheme = OAuth2(
flows=OAuthFlows(
authorizationCode=OAuthFlowAuthorizationCode(
authorizationUrl=os.getenv("TRADE_OAUTH_AUTH_URL"),
tokenUrl=os.getenv("TRADE_OAUTH_TOKEN_URL"),
scopes={"write:orders": "place orders", "read:portfolio": "read portfolio"},
)
)
)
auth_credential = AuthCredential(
auth_type=AuthCredentialTypes.OAUTH2,
oauth2=OAuth2Auth(
client_id=os.getenv("TRADE_OAUTH_CLIENT_ID", "DEMO_CLIENT_ID"),
client_secret=os.getenv("TRADE_OAUTH_CLIENT_SECRET", "DEMO_CLIENT_SECRET"),
scopes=["read:portfolio", "write:orders"],
),
)
Prompt to try
Place a demo buy for one share of AAPL with confirm true. If authentication is required, request it and then return the final trade status.
Conclusion
Finwise is a small project with a big lesson: agents stay reliable when you give them clear, composable tools. You saw the core patterns in ADK — direct function calls, a dedicated code-execution agent, an MCP service for heavier work, and an authenticated action — and how they fit together in one flow. Start with this skeleton, then swap in your data sources, attach your own services, and wire real OAuth for sensitive actions.
Next steps worth trying:
- Replace the demo price tool with your provider
- Point the OAuth settings at your brokerage and send a real test order
- Deploy the MCP server and call it from multiple agents
- Add an OpenAPI or Google Cloud toolset to grow capabilities without bloating the root agent
Grab the code from github: https://github.com/GoogleCloudPlatform/devrel-demos/tree/main/ai-ml/agent-labs/adk-custom-tools-finwise
Happy building!
Source Credit: https://medium.com/google-cloud/building-finwise-with-agent-development-kit-four-tool-patterns-that-matter-bd3257e51914?source=rss—-e52cf94d98af—4