ellokillox

mcp-local-leads

Community ellokillox
Updated

MCP server: find local businesses without a website in any city (Google Places API New). Standard library only, no dependencies.

mcp-local-leads

Docs & live demo: https://ellokillox.github.io/ — install, the exact test output and what is not tested.

testsLicense: MITdeps

An MCP server (Model Context Protocol) that finds local businesses that appear on Google Maps but have no website — the most obvious prospect list there is if you sell websites, SEO or marketing to local businesses.

Ask Claude Desktop / Claude Code / Cursor / any MCP client:

"Find dental clinics without a website in Temuco, Chile and save them to a CSV."

and the AI calls this server, which queries Google Places API (New), paginates, drops duplicates by placeId and returns only the businesses whose websiteUri is empty.

  • One file, zero dependencies. Standard library only, Python 3.9+ (tested in CI on 3.9, 3.12 and 3.13). No pip install, no Node, no build step.
  • Narrow field mask (11 fields). Places bills by the fields you request, so asking for less costs less.
  • Two tools: find_businesses_without_website and export_leads_csv.
  • No scraping, no third-party data brokers. It reads the field Google already returns.

Install

  1. Save server.py anywhere, e.g. ~/mcp-local-leads/server.py.
  2. Get a Google Places API (New) key with billing enabled: https://console.cloud.google.com/google/maps-apis. Enable Places API (New).
  3. Point your MCP client at it.

Claude Desktop (claude_desktop_config.json, macOS ~/Library/Application Support/Claude/, Windows %APPDATA%\Claude\):

{
  "mcpServers": {
    "local-leads": {
      "command": "python3",
      "args": ["/ABSOLUTE/PATH/TO/server.py"],
      "env": { "GOOGLE_PLACES_API_KEY": "YOUR_KEY_HERE" }
    }
  }
}

Claude Code / any client that takes a stdio command:

GOOGLE_PLACES_API_KEY=your_key python3 server.py

Restart the client and the tools show up.

Docker (this is also how directory checks build it):

docker build -t mcp-local-leads .
docker run -i --rm -e GOOGLE_PLACES_API_KEY=your_key mcp-local-leads

-i keeps stdin open and there is no -t: an MCP stdio server speaks one JSON line per message, and a pseudo-TTY would corrupt it. The image is python:3.12-slim plus server.py — nothing to install, runs as an unprivileged user.

Tools

find_businesses_without_website

Argument Type Default Meaning
city string (required) — e.g. "Temuco, Chile", "Austin, TX"
niche string — "dental clinics", "plumbers", "restaurants"… without it, generic local businesses
country string — appended to the query when the city does not include it
limit int 1–60 20 how many businesses without a website to return
page_size int 1–20 20 results per Places request (Google's cap is 20)
max_pages int 1–3 3 pages to read — each page is a billed request
language string — languageCode for results, e.g. "es", "en"

Returns leads (name, address, city, phone, rating, review count, type, Maps URL, coordinates, placeId), plus businesses_seen, with_website, without_website, requests, pages and next_page_token so the model knows what it actually did and what it cost in requests.

The city field is parsed from Google's formattedAddress, which ends with the country: for "Av. Alemania 123, Temuco, La Araucanía, Chile" the city column is La Araucanía, not Chile.

export_leads_csv

Writes the leads array to a CSV (12 columns, UTF-8 with BOM so Excel opens it correctly). Excel, Numbers and LibreOffice all import it as-is.

What was actually tested

python3 test_mcp_local_leads.py — 45 checks against a mock of Places API (New) served on localhost, driving server.py over real stdio JSON-RPC:

3) Busqueda real contra el mock (2 paginas)
  OK    negocios vistos = 7
  OK    con web = 3
  OK    sin web = 4
  OK    ids sin web y sin duplicados: ['p1', 'p3', 'p5', 'p7']
  OK    textQuery = dental clinics in Temuco, Chile
  OK    la 2a peticion envia pageToken
  OK    field mask estrecha: 11 campos
  OK    ciudad = penultima parte de la direccion ('La Araucanía', no 'Chile')
...
== 45 comprobaciones · 0 fallos ==

python3 test_mcp_sdk_client.py — the same server driven by the official Python MCP SDK (stdio_client + ClientSession), i.e. the same code path Claude Desktop uses: initialize → list_tools → call_tool for both tools, 4 leads returned, CSV written.

Both scripts are in this repo's tests/ folder and run in CI on every push (.github/workflows/test.yml, Python 3.9 / 3.12 / 3.13). The SDK interop step needs Python 3.10+, because that is the SDK's own minimum; the server itself does not.

test_mcp_docker.py — the same handshake inside the container, from the published Dockerfile (this is the check directories that build servers run: the image must start and answer introspection):

1) docker build                    OK  imagen construida
2) arranque sin clave              OK  isError=true y el mensaje nombra GOOGLE_PLACES_API_KEY
3) initialize / tools/list         OK  serverInfo.name = mcp-local-leads · 2 herramientas
4) tools/call contra el mock       OK  3 negocios vistos · 2 sin web · ids ['p1','p3']
                                   OK  field mask 11 campos · clave en X-Goog-Api-Key
5) export_leads_csv                OK  2 filas escritas dentro del contenedor
== RESULTADO: TODO OK (16 comprobaciones) ==

Limits (read this before you buy a key)

  • Places has no emails. It returns phone and address; finding a decision-maker's email is a separate step (and a separate problem).
  • Every page is billed by Google. Set max_pages low while you experiment. Check current Places pricing before running this at scale — prices are Google's to change, so this repo does not quote them.
  • "No website" means Places has no websiteUri. Some businesses have a Facebook page instead; that is still a business that needs a website.
  • Results honour Google's own ranking for the text query. Different niches and cities behave differently; there is no magic query.

Related

  • Free n8n version of the same first step (Places → Google Sheets, built-in nodes only): n8n-find-local-businesses-without-website
  • If you want the rest of the pipeline — lead scoring rubric, per-business research prompts, first-contact templates in ES/EN, industry configurations and the n8n workflows — that is a paid kit at https://b2b.medicpc51.cl (Spanish, local payment in Chile).

This server is free and MIT on purpose: MIT means take it and use it.

Español

Servidor MCP que encuentra negocios locales que aparecen en Google Maps pero no tienen sitio web: la lista de prospectos más obvia que existe si vendes webs, SEO o marketing a negocios locales. Un solo archivo, sin dependencias, Python 3.9+.

Le pides a tu cliente de IA: «busca clínicas dentales sin web en Temuco y guárdalas en un CSV» y el modelo llama a este servidor, que consulta Places API (New), pagina, descarta duplicados por placeId y devuelve solo los que no tienen websiteUri.

Instalación: guarda server.py, pon tu clave en GOOGLE_PLACES_API_KEY y añade el servidor a claude_desktop_config.json (el ejemplo está arriba). El campo city se toma de la penúltima parte de la dirección, porque Google termina en el país.

Limitaciones: Places no devuelve correos (solo teléfono y dirección), cada página se factura y «sin web» significa que Places no tiene websiteUri — puede tener una página de Facebook, que sigue siendo un negocio sin web.

License

MIT — see LICENSE.

MCP Server · Populars

MCP Server · New

    FROWNINGdev

    ORM Lens

    ER diagrams, N+1 detection, schema drift & migration-risk linting for Django and SQLAlchemy (Alembic) — VS Code, CLI and MCP server. Static analysis: no database, no app boot. Free & MIT. Formerly django-orm-lens.

    Community FROWNINGdev
    M-Pineapple

    Dev Project Coordinator

    Local desk for AI agents: track coding and non-coding projects as JSON, with Analytics and Kanban views. MCP server, Mac-first, Docker for Linux.

    Community M-Pineapple
    gimp-mcp

    GIMP MCP

    GIMP MCP server

    Community gimp-mcp
    PrismorSec

    Prismor

    Self-hosted runtime control plane for AI agents. Observe or HITL approve or Block rogue tool calls before it executes: secret leaks, prompt injection, supply chain etc in a local dashboard. Agent agnostic (Claude, codex, langchain etc.)

    Community PrismorSec
    grafana-cold-storage

    Loki MCP Server

    An MCP ( Model Context Protocol ) Server for Grafana Loki