Add live web search to a smolagents agent
smolagents' @tool decorator and MCP support make it easy to give any agent live web search — either via an autonomous x402 wallet or a free API key.
Two paths
| Path | Best for | What you need |
|---|---|---|
| MCP via ToolCollection | All five tools, fully autonomous — agent pays per call | A funded Base wallet + npx |
@tool functions + API key | Explicit typed tools, controlled spend | A free API key from /pricing |
Path 1: MCP via ToolCollection (recommended for autonomous agents)
pip install smolagents mcp
from smolagents import CodeAgent, HfApiModel, ToolCollection
from mcp import StdioServerParameters
server_params = StdioServerParameters(
command="npx",
args=["-y", "superhighway-mcp"],
env={
"AGENT_PRIVATE_KEY": "0xYOUR_FUNDED_BASE_WALLET_KEY",
"X402_NETWORK": "base",
},
)
tool_collection = ToolCollection.from_mcp(server_params, trust_remote_code=True)
agent = CodeAgent(
tools=[*tool_collection.tools],
model=HfApiModel("Qwen/Qwen2.5-72B-Instruct"),
)
agent.run("What are the latest developments in AI agent frameworks?")
The MCP server exposes all five tools — web_search, news_search, image_search, scrape, research — and each call settles a $0.001–$0.005 USDC payment automatically via x402. See wallet setup or the x402 flow.
Path 2: @tool functions with an API key
Get a free API key (1,000 calls/month) and define typed tools with the @tool decorator:
pip install smolagents requests
import os
import requests
from smolagents import CodeAgent, HfApiModel, tool
API_KEY = os.environ["SUPERHIGHWAY_API_KEY"]
BASE = "https://superhighway.walls.sh"
@tool
def web_search(query: str, limit: int = 5) -> str:
"""Search the live web for current information, facts, and news.
Args:
query: The search query.
limit: Number of results to return (1-10, default 5).
"""
r = requests.get(
f"{BASE}/search",
params={"q": query, "limit": limit},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10,
)
r.raise_for_status()
results = r.json()["results"]
return "\n\n".join(
f"**{x['title']}**\n{x['url']}\n{x['description']}" for x in results
)
@tool
def news_search(query: str) -> str:
"""Search recent news articles with published dates.
Args:
query: The news search query.
"""
r = requests.get(
f"{BASE}/news",
params={"q": query},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10,
)
r.raise_for_status()
results = r.json()["results"]
return "\n\n".join(
f"**{x['title']}** ({x.get('publishedDate', '')})\n{x['url']}\n{x['description']}"
for x in results
)
@tool
def research(query: str, pages: int = 2) -> str:
"""Search + read the top matching pages in one call. Returns full page content.
Args:
query: Research topic or question.
pages: Number of pages to read (1-5, default 2).
"""
r = requests.get(
f"{BASE}/research",
params={"q": query, "pages": pages},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
)
r.raise_for_status()
data = r.json()
return "\n\n---\n\n".join(
f"## {p['title']}\n{p['url']}\n\n{p['markdown'][:3000]}"
for p in data["pages"] if not p.get("error")
)
agent = CodeAgent(
tools=[web_search, news_search, research],
model=HfApiModel("Qwen/Qwen2.5-72B-Instruct"),
)
agent.run("Summarize the top AI news from the last 48 hours.")
smolagents' @tool decorator uses the docstring as the tool description — write it as if the model will read it, because it will. The Args: section documents each parameter. Add only the tools your agent needs.
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.