SERP API

Google Search MCP Server: Give Claude, Cursor or Any AI Agent Live Google Results (Python Tutorial)

Build a Google search MCP server in Python that gives Claude Desktop, Cursor or any MCP client live Google results, AI Overviews and People Also Ask as JSON.

A Google search MCP server is a small program that lets an AI agent search Google and read the results. You run it on your computer. Then you point Claude Desktop, Cursor or another MCP client at it, and the agent gets a google_search tool it can call whenever it needs fresh facts.

Below is a complete one in about 75 lines of Python. It uses the official MCP SDK and a SERP API, and it works with any MCP client.

What is MCP, in plain words?

MCP, the Model Context Protocol, is an open standard for connecting AI apps to outside tools and data. Anthropic introduced it in November 2024. The official docs compare it to a USB-C port for AI apps: one plug that fits many devices.

There are two sides:

  • The server offers tools. Ours offers one tool, google_search.
  • The client is the AI app, such as Claude Desktop, Cursor or Claude Code. The model reads each tool’s name and description and decides when to call it.

In this setup, the client starts the server as a small background program. They talk over standard input and output, which MCP calls the stdio transport. Nothing is exposed to the internet.

Claude and ChatGPT can already search the web. That is fine for quick answers. It falls short when you need what Google shows:

  • You cannot pick the country or device. Rankings in London on mobile differ from New York on desktop.
  • You do not see the page. No positions, no AI Overview, no People Also Ask.
  • Google’s own API is going away. The Custom Search JSON API is closed to new customers and shuts down on January 1, 2027. Our guide to the Custom Search API shutdown covers the options.
  • Scraping Google yourself breaks. A script on your laptop hits captchas within minutes. We explain why in how to scrape Google search results.

A SERP API fetches the real results page from the country and device you choose, and returns it as JSON. The MCP server is the thin layer that hands that JSON to the agent.

What you need

  • Python 3.10 or newer and uv, a fast Python package manager.
  • An MCP client: Claude Desktop, Cursor, Claude Code, or any other.
  • A SERP API key. This guide uses Serplify’s POST /v1/serp/search endpoint. New accounts get $1 of free credit, about 200 searches.

Step 1: Create the project

uv init google-search-mcp
cd google-search-mcp
uv add "mcp[cli]"

That installs the official MCP Python SDK. We tested this guide with version 2.3.0. Version 2 renamed the main class to MCPServer, so older tutorials that import FastMCP will not run on it.

Step 2: Write the server

Create server.py and paste this in:

import asyncio
import os

import httpx2
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError

API = "https://api.serplify.io"
KEY = os.environ["SERPLIFY_KEY"]

mcp = MCPServer("google-search")


@mcp.tool()
async def google_search(
    query: str,
    location_code: int = 2840,
    language: str = "en",
    device: str = "desktop",
) -> dict:
    """Search Google and return the live first page of results.

    Use it for recent events, prices, rankings, or anything you are unsure of.

    Args:
        query: The search, typed the way a person would type it.
        location_code: Google location code. 2840 = United States, 2826 = United Kingdom.
        language: Language code, such as "en" or "de".
        device: "desktop" or "mobile".
    """
    body = {
        "keyword": query,
        "location": {"code": location_code},
        "language": {"code": language},
        "device": device,
        "depth": 10,
    }
    headers = {"Authorization": f"Bearer {KEY}"}
    async with httpx2.AsyncClient(base_url=API, headers=headers, timeout=300) as http:
        res = await http.post("/v1/serp/search", json=body)
        # A slow search comes back as HTTP 202 with a task to poll.
        for _ in range(20):
            if res.status_code != 202:
                break
            await asyncio.sleep(3)
            res = await http.get(res.json()["data"]["status_url"])
    payload = res.json()
    if res.status_code != 200:
        message = payload.get("error", {}).get("message", "still running, try again")
        raise ToolError(f"Search failed ({res.status_code}): {message}")
    return summarize(payload["data"])


def summarize(data: dict) -> dict:
    """Keep what an agent needs from the page and drop the rest."""
    page = {"query": data["keyword"], "ai_overview": None, "results": [], "people_also_ask": []}
    for item in data["items"]:
        if item["type"] == "organic":
            page["results"].append({
                "position": item["rank_group"],
                "title": item["title"],
                "url": item["url"],
                "snippet": item.get("description"),
            })
        elif item["type"] == "ai_overview":
            parts = item.get("items") or []
            page["ai_overview"] = {
                "text": "\n".join(p.get("text") or "" for p in parts),
                "sources": [ref["url"] for ref in item.get("references") or []],
            }
        elif item["type"] == "people_also_ask":
            page["people_also_ask"] = [q["title"] for q in item.get("items") or []]
    return page


if __name__ == "__main__":
    mcp.run(transport="stdio")

What each part does:

  • @mcp.tool() turns the function into a tool. The SDK builds the input schema from the type hints. The docstring becomes the description the model reads, so write it for the model.
  • The request body follows the API docs: keyword, location, language and device. depth: 10 asks for the first page only. The price is per page either way, and a smaller page loads faster.
  • The polling loop handles slow searches. If a live search cannot finish in time, the API answers HTTP 202 with a status_url. The loop checks it every 3 seconds for up to a minute. Checking is free.
  • ToolError sends a readable message, like “Missing or invalid API key”, back to the model. In SDK version 2, other errors reach the model only as a generic failure.
  • summarize() keeps three things: organic results, the AI Overview with its sources, and People Also Ask questions. A full page can hold dozens of block types. Sending all of them wastes the model’s context.

One rule matters for every stdio server: never use print(). Standard output carries the protocol messages, and a stray print breaks the connection. Use the logging module, which writes to standard error.

Step 3: Connect it to Claude Desktop

Open the Claude Desktop config file. Create it if it does not exist.

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Add the server:

{
  "mcpServers": {
    "google-search": {
      "command": "uv",
      "args": ["--directory", "/ABSOLUTE/PATH/TO/google-search-mcp", "run", "server.py"],
      "env": { "SERPLIFY_KEY": "live_your_key_here" }
    }
  }
}

Use the full path to your project folder. Save the file and restart Claude Desktop. If it cannot find uv, put the full path from which uv in the command field.

Step 4: Connect Cursor or Claude Code

Cursor reads the same JSON shape. Put it in ~/.cursor/mcp.json for every project, or .cursor/mcp.json for one. If the tool does not appear, open the Output panel and pick “MCP Logs”.

Claude Code adds it with one command:

claude mcp add --env SERPLIFY_KEY=live_your_key_here --transport stdio google-search \
  -- uv --directory /ABSOLUTE/PATH/TO/google-search-mcp run server.py

Keep your key out of code you commit. The env field and the --env flag pass it to the server at start.

Step 5: Try it

Ask the agent something it cannot know from training. For example: “Search Google for search api for ai agents. Which sites rank first, and what does the AI Overview cite?” It should call google_search and answer from the page.

Here is what summarize() made of the real US desktop page for that query on October 9, 2026. We trimmed it to two results, three sources and four questions:

{
  "query": "search api for ai agents",
  "ai_overview": {
    "text": "A web search API for AI agents evaluates a natural language query from an autonomous workflow and returns clean, machine-readable data ...",
    "sources": [
      "https://tavily.com/",
      "https://www.firecrawl.dev/glossary/web-search-apis/web-search-tool-ai-agents",
      "https://www.vellum.ai/blog/best-web-search-apis-and-mcps-for-ai-agents"
    ]
  },
  "results": [
    {
      "position": 1,
      "title": "Tavily",
      "url": "https://tavily.com/",
      "snippet": "Tavily is the real-time search engine for AI agents ..."
    },
    {
      "position": 2,
      "title": "What's the best tool/API for web search in an agentic stack?",
      "url": "https://www.reddit.com/r/AI_Agents/comments/1pf9avo/whats_the_best_toolapi_for_web_search_in_an/",
      "snippet": "I'm building an agentic stack where one component needs to run real web searches ..."
    }
  ],
  "people_also_ask": [
    "Which search API is best for AI agents?",
    "Do AI agents use APIs?",
    "What is the best web search tool for AI agents?",
    "Is there a free AI agent API available?"
  ]
}

That page had nine organic results and an AI Overview with 16 sources. The agent gets all of it as compact JSON. To pull more block types, like videos or the local pack, add branches to summarize(). Our post on how we parse SERP features into JSON lists the shapes.

What it costs

Each successful search costs $0.005, so 1,000 searches cost $5. A failed or blocked search costs nothing. If your agent runs 300 searches a day, that is $1.50 a day.

There is no plan or monthly fee. The $1 of free credit covers about 200 searches, which is plenty to test the server. Our Google SERP API page has the full price and feature list. For a walkthrough of the API itself, see how to use the Serplify SERP API.

A Google results page is not always what an agent needs. These are the main options as of October 2026.

OptionWhere results come fromBest when
Built-in web search in Claude or ChatGPTThe app’s own search providerYou only need answers, not Google’s page
Brave Search MCP server (official, from Brave)Brave’s own indexYou want a ready-made server and do not need Google
Exa or Tavily MCP serversAI-first search engines that also return page textResearch and RAG agents that need page content
Google Custom Search JSON APIA Google custom search engineNot for new projects: closed to new customers, ends January 1, 2027
Community servers that scrape google.comGoogle, fetched from your own IPHobby use; they break on captchas
Your own server plus a SERP API (this guide)The real Google page, by country and deviceYou need rankings, AI Overviews or People Also Ask

Pick by the question your agent answers. “What does Google show for this keyword?” needs Google’s page. “What do sources say about this topic?” can use any good index.

Frequently asked questions

Is there an official Google Search MCP server?

Not for public web results, as of October 2026. Google's official MCP servers, announced in December 2025, cover services like Maps, BigQuery and Compute Engine. To give an agent the public Google results page, you need a SERP API behind your own MCP server, or a different search engine.

Which search API is best for AI agents?

It depends on what the agent needs. For what Google shows, including rankings, AI Overviews and People Also Ask, use a SERP API. For page text to feed a RAG app, AI-first engines like Exa or Tavily return content directly. For simple answers, the built-in web search in Claude or ChatGPT may be enough.

Is there a free web search API for AI agents?

Most search APIs have a free tier or trial credit. Serplify gives $1 of free credit at signup, which covers about 200 Google searches. Google's Custom Search JSON API had 100 free queries a day, but it is closed to new customers and shuts down on January 1, 2027.

Can I write the server in TypeScript instead?

Yes. The official TypeScript SDK works the same way: create a server, register a tool with an input schema, and connect it over stdio. The API request and the client config stay the same.

Sources and further reading

  1. Model Context Protocol: What is MCP? modelcontextprotocol.io
  2. Model Context Protocol: Build an MCP server modelcontextprotocol.io
  3. MCP Python SDK documentation py.sdk.modelcontextprotocol.io
  4. Anthropic: Introducing the Model Context Protocol (November 2024) anthropic.com
  5. Serplify docs: Live search docs.serplify.io
  6. Google for Developers: Custom Search JSON API overview developers.google.com
  7. Cursor docs: Model Context Protocol cursor.com

From the team that built it

Put this into practice with the SERP API.

Real-time Google SERP API with AI-ready JSON — 26+ SERP feature types, location and device targeting, 99.9% uptime — full live SERPs at a fraction of the going rate.

Start on the free balance — no card required.