Add live web search to a LangGraph agent

Superhighway guides

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

PathBest forWhat you need
MultiServerMCPClientAll five tools, fully autonomous — agent pays per call via x402A funded Base wallet + npx
@tool + API keyExplicit Python tool functions, controlled spendA 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 / 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.