Add live web search to a Google ADK agent
Google ADK (Agent Development Kit) is Python-first and treats plain functions as first-class tools — pass any typed function to tools=[...] and the agent can call it. For all five Superhighway tools with autonomous x402 payments, the MCPToolset path is faster to set up.
Two paths
| Path | Best for | What you need |
|---|---|---|
| MCPToolset | All five tools, fully autonomous — agent pays per call via x402 | A funded Base wallet + npx |
| Function tools + API key | Typed Python functions, controlled spend | A free API key from /pricing |
Path 1: MCP via MCPToolset (recommended for autonomous agents)
pip install google-adk mcp
import asyncio, os
from google.adk import Agent, Runner
from google.adk.tools.mcp_tool import MCPToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
from google.adk.sessions import InMemorySessionService
from mcp import StdioServerParameters
from google.genai import types
agent = Agent(
model="gemini-2.5-flash",
name="search_agent",
instruction="You are a research assistant with access to live web search.",
tools=[
MCPToolset(
connection_params=StdioConnectionParams(
server_params=StdioServerParameters(
command="npx",
args=["-y", "superhighway-mcp"],
env={
"AGENT_PRIVATE_KEY": os.environ["AGENT_PRIVATE_KEY"],
"X402_NETWORK": "base",
},
)
)
)
],
)
runner = Runner(
agent=agent,
app_name="search_app",
session_service=InMemorySessionService(),
)
async def main():
session = await runner.session_service.create_session(
app_name="search_app", user_id="user1"
)
async for event in runner.run_async(
user_id="user1",
session_id=session.id,
new_message=types.Content(
role="user",
parts=[types.Part(text="What are the latest developments in AI agent frameworks?")],
),
):
if event.is_final_response() and event.content:
print(event.content.parts[0].text)
asyncio.run(main())
AGENT_PRIVATE_KEY is the private key of a wallet with USDC on Base. Each search call costs $0.001 and is settled automatically via the x402 protocol — no signup, no API key, no human in the loop. The MCPToolset exposes all five tools: web_search, news_search, image_search, scrape, and research.
Path 2: Function tools with an API key
ADK treats any typed Python function with a docstring as a tool — no decorator needed. The type annotations and Args: section of the docstring are the schema the model reads.
pip install google-adk requests
import asyncio, os, requests
from google.adk import Agent, Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
BASE = "https://superhighway.walls.sh"
HEADERS = {"X-Api-Key": os.environ["SUPERHIGHWAY_API_KEY"]}
def web_search(query: str, limit: int = 5) -> dict:
"""Search the live web for current information.
Args:
query: The search query string.
limit: Maximum number of results to return.
Returns:
Search results with titles, URLs, and snippets.
"""
return requests.get(f"{BASE}/search", params={"q": query, "limit": limit}, headers=HEADERS).json()
def news_search(query: str, limit: int = 5) -> dict:
"""Search for recent news articles about a topic.
Args:
query: The news search query.
limit: Maximum number of articles to return.
Returns:
Recent news articles with titles, sources, and summaries.
"""
return requests.get(f"{BASE}/news", params={"q": query, "limit": limit}, headers=HEADERS).json()
def research(query: str, pages: int = 3) -> dict:
"""Perform deep research by searching and reading multiple web pages.
Args:
query: The research topic or question.
pages: Number of pages to read for synthesis.
Returns:
Synthesized research summary from multiple sources.
"""
return requests.get(f"{BASE}/research", params={"q": query, "pages": pages}, headers=HEADERS).json()
agent = Agent(
model="gemini-2.5-flash",
name="search_agent",
instruction="You are a research assistant with access to live web search.",
tools=[web_search, news_search, research],
)
runner = Runner(
agent=agent,
app_name="search_app",
session_service=InMemorySessionService(),
)
async def main():
session = await runner.session_service.create_session(
app_name="search_app", user_id="user1"
)
async for event in runner.run_async(
user_id="user1",
session_id=session.id,
new_message=types.Content(
role="user",
parts=[types.Part(text="What changed in the latest Python release?")],
),
):
if event.is_final_response() and event.content:
print(event.content.parts[0].text)
asyncio.run(main())
The key ADK pattern: plain functions are tools. The Args: section of the docstring maps directly to the tool's parameter descriptions — no @tool decorator, no Pydantic model, no class hierarchy needed.
Which tools are available
| Tool / endpoint | What it returns | Price |
|---|---|---|
web_search / /search | Ranked web results — title, URL, description | $0.001 |
news_search / /news | Recent news with published dates | $0.001 |
image_search / /images | Image URLs, thumbnails, source pages | $0.001 |
scrape / /scrape | Any URL → clean markdown text | $0.002 |
research / /research | Search + read top pages in one call | $0.005 |
Full API reference: /openapi.json. Get a free API key or learn the x402 wallet flow.