Add live web search to a Google ADK agent

Superhighway guides

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

PathBest forWhat you need
MCPToolsetAll five tools, fully autonomous — agent pays per call via x402A funded Base wallet + npx
Function tools + API keyTyped Python functions, controlled spendA 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 / endpointWhat it returnsPrice
web_search / /searchRanked web results — title, URL, description$0.001
news_search / /newsRecent news with published dates$0.001
image_search / /imagesImage URLs, thumbnails, source pages$0.001
scrape / /scrapeAny URL → clean markdown text$0.002
research / /researchSearch + read top pages in one call$0.005

Full API reference: /openapi.json. Get a free API key or learn the x402 wallet flow.