Back to use cases

    Use case / Developers / Python 3 / Published

    Build an MCP Web Search Research Agent in Python

    Card for Build an MCP Web Search Research Agent in Python

    Agent Search Toolbelt shows how to give an agent real web search through MCP. It connects to the Desearch hosted MCP server from Python, uses four tools, answers a question with the links it used, and logs every tool call with its arguments, latency and cost.

    The problem

    Most agent demos either fake search or hide it behind a framework. Developers who want MCP web search in their own code need to see the plain steps: connect, list tools, call them, parse the result and keep track of cost. They also want a version that works without an LLM so they can test the search part on its own.

    What the app does

    • Takes a question in plain words.
    • Finds 10 links on the web for it.
    • Reads the top 3 pages as plain text, with one retry if a page comes back empty.
    • Pulls recent public X posts on the topic.
    • Writes a Markdown answer that quotes the most relevant sentences, each with its link.
    • Appends one JSON line per tool call to a run log (tool, arguments, latency, items, cost).

    How it works

    1. Connect to https://mcp.desearch.ai/mcp with the official MCP Python SDK over Streamable HTTP, sending your key as a Bearer token.
    2. Find links with the web-links-search tool.
    3. Read pages with the extract tool (js: false).
    4. Check the conversation with the x-search tool, optionally limited to the last N days.
    5. Answer. The default loop is scripted and needs no LLM. An optional --llm mode lets an OpenAI-compatible model choose the tools, including web-search.

    The code

    MCP_URL = os.environ.get("DESEARCH_MCP_URL", "https://mcp.desearch.ai/mcp")
    TOOLS = ("web-search", "web-links-search", "extract", "x-search")  # the only tools the agent may call
    
    
    class Toolbelt:
        def __init__(self, session, log_path, raw_dir=None):
            self.session, self.log_path, self.raw_dir, self.calls = session, pathlib.Path(log_path), raw_dir, []
    
        async def tools(self):
            listed = (await self.session.list_tools()).tools
            return [t for t in listed if t.name in TOOLS]
    
        async def call(self, name, args):
            if name not in TOOLS:
                raise ValueError(f"tool not allowed: {name}")
            t0 = time.perf_counter()
            res = await self.session.call_tool(name, args)
            latency = round(time.perf_counter() - t0, 3)
            data, err = h.parse_result(res), h.is_error(res)
            rec = {"ts": dt.datetime.now().astimezone().isoformat(timespec="seconds"), "tool": name, "args": args,
                   "latency_s": latency, "is_error": err, "items": 0 if err else h.count_items(data),
                   **h.cost_fields(name, None if err else data, getattr(res, "meta", None))}
            self.calls.append(rec)
            if self.raw_dir:  # full tool result, for debugging (holds no key)
                h.save_raw(self.raw_dir, len(self.calls), name, args, res)
            with self.log_path.open("a") as f:
                f.write(json.dumps(rec, ensure_ascii=False) + "\n")
            return data, rec
    

    toolbelt.py is the agent (about 60 lines of logic), helpers.py holds parsing, cost and Markdown helpers, llm_loop.py is the optional LLM mode, and test_toolbelt.py runs offline.

    Endpoints

    • MCP tool web-links-search
    • MCP tool extract
    • MCP tool x-search
    • MCP tool web-search

    Example output

    From a run on 2026-10-09 for "James Webb telescope exoplanet atmosphere findings" with X posts from the last 14 days:

    ToolCallsWhat came back
    web-links-search110 links
    extract3 or moretext of the top pages
    x-search1recent public posts

    The answer quotes sentences from the pages it read and lists every source link. The run log (run-log.sample.jsonl) shows each call's latency and cost.

    Run it yourself

    The public examples folder linked above includes the script and a README. Install its requirements, set DESEARCH_API_KEY from console.desearch.ai, and use the run command in that README.

    Useful flags: --top (pages to read, default 3), --links (10 to 200), --x-days (only X posts from the last N days), --llm (let a model pick tools; needs LLM_API_KEY, LLM_BASE_URL, LLM_MODEL).

    What it costs

    Over MCP, a 10-link search costs $0.004 (AI Search, $0.40 per 1,000 items), a web search $0.001 and an extracted page about $0.0005, per the published rates checked 2026-10-09. The sample run reported $0.004 from the tools plus about $0.003 estimated for calls that do not return a cost field. See pricing.

    Ideas to extend it

    • Swap the scripted plan for your own agent framework and keep the same four tools.
    • Add a step that writes the answer to a notes app or a ticket.
    • Use the run log to compare cost and latency across prompts.

    Use with AI agents

    Point an MCP client at the hosted Desearch server. Replace the placeholder with a key from the console. Do not commit the key.

    {
      "mcpServers": {
        "desearch": {
          "url": "https://mcp.desearch.ai/mcp",
          "headers": {
            "Authorization": "Bearer <DESEARCH_API_KEY>"
          }
        }
      }
    }

    Prompt you can paste for this example:

    Use the Desearch MCP server to research the question I ask. Search the web, open the top pages, and search X. Answer with the links and posts you actually retrieve. Do not invent sources.

    Build your own example.

    Give your agents access to web and social search. Get an API key to start building, or talk to us about your integration.

    Read docs