Add live web search to a LangGraph agent
LangGraph's create_react_agent builds a ready-to-run ReAct loop — the model reasons, calls a tool, reads the result, and repeats until it answers. It accepts any list of LangChain tools: load all five Superhighway tools over MCP with MultiServerMCPClient, or wrap individual REST calls with the @tool decorator for controlled spend.
Two paths
| Path | Best for | What you need |
|---|---|---|
| MultiServerMCPClient | All five tools, fully autonomous — agent pays per call via x402 | A funded Base wallet + npx |
| @tool + API key | Explicit Python tool functions, controlled spend | A free API key from /pricing |
Path 1: MCP via MultiServerMCPClient (recommended for autonomous agents)
pip install langchain-mcp-adapters langgraph langchain-openai
import asyncio, os
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
async def main():
client = MultiServerMCPClient({
"superhighway": {
"command": "npx",
"args": ["-y", "superhighway-mcp"],
"env": {
"AGENT_PRIVATE_KEY": os.environ["AGENT_PRIVATE_KEY"],
"X402_NETWORK": "base",
},
"transport": "stdio",
}
})
# get_tools() is async — it spawns the MCP server and lists its tools
tools = await client.get_tools()
agent = create_react_agent(ChatOpenAI(model="gpt-4o"), tools)
result = await agent.ainvoke({
"messages": [{"role": "user", "content": "What's happening in AI this week?"}]
})
print(result["messages"][-1].content)
asyncio.run(main())
AGENT_PRIVATE_KEY is the private key of a wallet holding USDC on Base. Each call costs $0.001 and settles automatically via the x402 protocol — no signup, no API key, no human in the loop. MultiServerMCPClient is instantiated directly (not as a context manager), and await client.get_tools() returns all five tools as LangChain tools: web_search, news_search, image_search, scrape, and research. Because the whole chain is async, run it inside asyncio.run(main()) and call the agent with await agent.ainvoke(...).
Path 2: @tool functions with an API key
LangChain's @tool decorator from langchain_core.tools turns any function into a tool. The function's docstring is the description the model reads when deciding which tool to call, and its type-annotated arguments become the input schema — so write a clear docstring and annotate every parameter.
pip install langgraph langchain-openai requests
import os, requests
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
BASE = "https://superhighway.walls.sh"
HEADERS = {"X-Api-Key": os.environ["SUPERHIGHWAY_API_KEY"]}
@tool
def web_search(query: str, limit: int = 5) -> dict:
"""Search the live web for current information and return ranked results."""
return requests.get(f"{BASE}/search", params={"q": query, "limit": limit}, headers=HEADERS).json()
@tool
def news_search(query: str, limit: int = 5) -> dict:
"""Search for recent news articles with published dates."""
return requests.get(f"{BASE}/news", params={"q": query, "limit": limit}, headers=HEADERS).json()
@tool
def research(query: str, pages: int = 3) -> dict:
"""Search the web and read the top pages for deep research synthesis."""
return requests.get(f"{BASE}/research", params={"q": query, "pages": pages}, headers=HEADERS).json()
agent = create_react_agent(ChatOpenAI(model="gpt-4o"), [web_search, news_search, research])
result = agent.invoke({"messages": [{"role": "user", "content": "What changed in Python 3.13?"}]})
print(result["messages"][-1].content)
Here the tools are plain synchronous functions, so you call the agent with agent.invoke(...) — no event loop required. (Use agent.ainvoke(...) only when your tools or model client are async, as in the MCP path above.)
The LangGraph pattern
create_react_agent(model, tools) returns a CompiledGraph — a LangChain Runnable — so it exposes the standard runnable interface: .invoke(), .ainvoke(), .stream(), and .astream(). You always call it with the graph's state, {"messages": [...]}, and read the final answer from result["messages"][-1].content. The same compiled graph works with both paths — only how you build the tool list (and whether you await) changes. (Newer LangChain versions also ship create_agent in langchain.agents as the successor to create_react_agent; both take the same (model, tools) shape.)
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.