npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rog-studios/google-maps-mcp

v1.3.0

Published

The complete Google Maps Platform as an MCP server: places, Google Business listings, photos, routes with multi-stop optimization, map & Street View images, geocoding, address validation, autocomplete, weather, air quality, pollen, solar — with layered ra

Readme

google-maps-mcp

The complete Google Maps Platform as a production-grade MCP server: places & Google Business listings, photos, routes with multi-stop optimization, map & Street View images the model can actually see, geocoding, address validation, autocomplete, weather, air quality, pollen and rooftop solar — built for agents, hardened for operators.

  • 23 read-only tools (+ a resource template, a prompt, and a shipped agent skill), all with strict Zod input validation, typed structured output and agent-oriented "use when" descriptions.
  • Images as first-class results: maps_static_map, maps_street_view and maps_place_photo return real MCP image content blocks — the agent sees the map, the route, the storefront. URLs with embedded keys are never exposed.
  • Composite tools that answer in one call what normally takes 5-10 (maps_explore_area, maps_compare_places), with graceful partial-failure semantics.
  • Layered abuse protection: per-client inbound rate limiting, global outbound rate limiting, concurrency caps, per-host circuit breakers, retry with exponential backoff + jitter (honoring Retry-After), hard timeouts, optional daily budget, request coalescing.
  • Security first: the Google key never leaks (logs, errors and URLs are scrubbed), upstream content is sanitized and treated as data — never instructions, photo CDN fetches are host-allowlisted (SSRF defense), and the HTTP transport ships authenticated-only with DNS-rebinding and brute-force defenses.
  • Two transports: stdio (local, default) and Streamable HTTP (remote, bearer-authenticated). The deprecated SSE transport is intentionally not implemented.

Documentation

| Guide | What's inside | | -------------------------------------------- | --------------------------------------------------------------------------------- | | Getting started | Zero to first tool call: API key, enabling APIs, client setup, verification | | Tools reference | All 23 tools A→Z: every parameter, outputs, examples, billing notes | | Configuration | Every environment variable + tuning recipes (low-cost, high-traffic, locked-down) | | Deployment | stdio vs HTTP, TLS/reverse proxy, Docker, observability, production checklist | | Security | Threat model, key lifecycle, prompt-injection & SSRF defenses, privacy | | Architecture | Codebase layout, request lifecycle, design decisions, how to add a tool | | Troubleshooting | Symptom → cause → fix tables and FAQ | | Changelog | Release history |

Tools

Geocoding & addresses

| Tool | What it does | | ----------------------- | ------------------------------------------------------------------------- | | maps_geocode | Address → coordinates + canonical place_id (up to 5 candidates) | | maps_batch_geocode | Up to 20 addresses in one call, per-address error isolation | | maps_reverse_geocode | Coordinates → addresses + place_ids | | maps_validate_address | Address Validation API: completeness verdict, standardized form, metadata | | maps_autocomplete | Partial/fuzzy text → concrete place suggestions with place_ids |

Places & Google Business listings

| Tool | What it does | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | maps_search_places | Free-text search; filters (open now, min rating, bias) + search along a route with detour costs | | maps_search_nearby | Places around a point, filterable by type, rank by popularity or distance | | maps_place_details | Full Google Business listing: hours, phone, website + detail_level: full facets (delivery, payment, parking, accessibility…); reviews & photo refs opt-in | | maps_place_photo | Downloads a place photo as an image the model sees |

Routes & roads

| Tool | What it does | | -------------------- | ------------------------------------------------------------------------------------ | | maps_compute_route | Directions with up to 23 intermediate stops + automatic visit-order optimization | | maps_route_matrix | Distance/duration for every origin × destination pair (≤ 625 pairs) | | maps_snap_to_roads | Snaps noisy GPS traces to the road network |

Imagery

| Tool | What it does | | ------------------ | ------------------------------------------------------------------------------- | | maps_static_map | Renders a map image: markers, route polylines, satellite/terrain styles | | maps_street_view | Street View panorama (availability probed first via the free metadata endpoint) |

Environment

| Tool | What it does | | ---------------------- | ---------------------------------------------------------------------------------- | | maps_weather | Current conditions, ≤ 10-day daily / ≤ 240-hour hourly forecast, 24 h history | | maps_air_quality | Universal + local AQI, pollutants, health advice; ≤ 96 h forecast, ≤ 720 h history | | maps_pollen_forecast | ≤ 5-day grass/tree/weed pollen indexes and in-season plants | | maps_solar_potential | Rooftop solar potential for the nearest building (Solar API) | | maps_timezone | IANA time zone + UTC/DST offsets at a given instant | | maps_elevation | Ground elevation for up to 100 coordinates |

Composites (one call instead of many)

| Tool | What it does | | ------------------------- | -------------------------------------------------------------------------------------------------------- | | maps_explore_area | Neighborhood overview: top places per category + area name + optional weather/AQI snapshot | | maps_compare_places | 2-5 places side by side, with travel time/distance from an origin | | maps_local_rank_tracker | Local-SEO grid audit: where a business ranks for a keyword across an area, visibility %, top competitors |

Also exposed: the gmaps://place/{placeId} resource template, the plan_trip prompt, and an agent skill (skills/google-maps/SKILL.md) teaching effective tool-chaining patterns — drop it into your agent's skills directory.

Requirements

  • Node.js ≥ 20 (or Docker).
  • A Google Maps Platform API key. Enable the APIs for the tools you'll use (each tool fails cleanly with an auth_error naming the missing API otherwise):
    • Core: Geocoding API, Places API (New), Routes API, Time Zone API, Elevation API
    • Imagery: Maps Static API, Street View Static API
    • Environment: Weather API, Air Quality API, Pollen API, Solar API
    • Extras: Address Validation API, Roads API

Restrict the key to exactly those APIs in the Google Cloud Console and add an IP restriction when running server-side. Billing must be active. maps_place_details with detail_level: "full" or include_reviews hits higher-priced Places SKUs — the defaults stay on the lean masks.

Quickstart (stdio)

GOOGLE_MAPS_API_KEY=AIza... npx -y @rog-studios/google-maps-mcp

The server speaks MCP on stdout and logs JSON to stderr.

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "google-maps": {
      "command": "npx",
      "args": ["-y", "@rog-studios/google-maps-mcp"],
      "env": { "GOOGLE_MAPS_API_KEY": "AIza..." }
    }
  }
}

Claude Code

claude mcp add google-maps --env GOOGLE_MAPS_API_KEY=AIza... -- npx -y @rog-studios/google-maps-mcp

Cursor (~/.cursor/mcp.json)

{
  "mcpServers": {
    "google-maps": {
      "command": "npx",
      "args": ["-y", "@rog-studios/google-maps-mcp"],
      "env": { "GOOGLE_MAPS_API_KEY": "AIza..." }
    }
  }
}

VS Code (.vscode/mcp.json)

{
  "servers": {
    "google-maps": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@rog-studios/google-maps-mcp"],
      "env": { "GOOGLE_MAPS_API_KEY": "AIza..." }
    }
  }
}

Running from a clone instead of npm? Build with npm run build, then use "command": "node", "args": ["/absolute/path/to/google-maps-mcp/dist/index.js"].

Agent integration

  • Vision-grade results: image tools return MCP image content blocks plus a JSON metadata mirror — multimodal agents reason about what they see (a route drawn on a map, a storefront photo) instead of imagining it.
  • Chainable by design: maps_compute_route returns encodedPolyline, accepted by maps_static_map (draw it) and maps_search_places.route_polyline (search along it with detour costs). maps_place_details.photos[].name feeds maps_place_photo.
  • Token-context control: MCP_TOOLS_ENABLED / MCP_TOOLS_DISABLED (CSV) shrink the advertised tool surface; unknown names fail at startup.
  • Server instructions teach clients the typical flows, and skills/google-maps/SKILL.md ships ready-to-use chaining patterns for Claude Code and compatible agents.
  • Actionable errors: every failure is one line with a machine-readable code, a hint, and retry_after_ms when waiting helps.

Remote mode (Streamable HTTP)

GOOGLE_MAPS_API_KEY=AIza... \
MCP_TRANSPORT=http \
MCP_SERVER_API_KEY="$(openssl rand -hex 32)" \
npx -y @rog-studios/google-maps-mcp
  • Endpoint: POST/GET/DELETE http://127.0.0.1:3000/mcpbearer auth is mandatory, the server refuses to start without MCP_SERVER_API_KEY.
  • GET /healthz — unauthenticated liveness. GET /metrics — JSON counters, bearer-authenticated, opt-in via METRICS_ENABLED=true.
  • Comma-separate several keys in MCP_SERVER_API_KEY to give each client its own identity, rate-limit bucket and revocation path.
  • Sessions are bound to the key that created them, capped (MCP_MAX_SESSIONS) and evicted when idle.
  • Binding to anything but loopback is your explicit choice (MCP_HTTP_HOST=0.0.0.0): put TLS termination in front and set MCP_ALLOWED_HOSTS to your public hostname.

Smoke test:

curl -s http://127.0.0.1:3000/mcp \
  -H "Authorization: Bearer $MCP_SERVER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}' -i
# Reuse the mcp-session-id response header on subsequent requests.

Client config (clients with native Streamable HTTP support):

{
  "mcpServers": {
    "google-maps": {
      "url": "https://mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer <MCP_SERVER_API_KEY>" }
    }
  }
}

Configuration

All variables are validated at boot; the process exits with a clear message on any invalid value. See .env.example for the commented reference.

| Variable | Default | Purpose | | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | ---------------------------------------------- | | GOOGLE_MAPS_API_KEY | — (required) | Google Maps Platform key | | MCP_TRANSPORT | stdio | stdio or http | | LOG_LEVEL | info | pino level, logs on stderr | | GOOGLE_MAPS_LANGUAGE / GOOGLE_MAPS_REGION | — | Default result language / region bias | | MCP_TOOLS_ENABLED / MCP_TOOLS_DISABLED | (all) | CSV allow/deny lists for the tool surface | | MCP_HTTP_HOST / MCP_HTTP_PORT | 127.0.0.1 / 3000 | HTTP bind address | | MCP_SERVER_API_KEY | — (required in http) | Bearer key(s), comma-separated, ≥ 16 chars | | MCP_ALLOWED_ORIGINS | (none) | Allowed browser Origins, CSV | | MCP_ALLOWED_HOSTS | 127.0.0.1:<port>,localhost:<port> | Allowed Host headers (DNS-rebinding defense) | | MCP_MAX_SESSIONS / MCP_SESSION_IDLE_TIMEOUT_MS | 50 / 1800000 | Session cap / idle eviction | | METRICS_ENABLED | false | Expose /metrics (http mode) | | RATE_LIMIT_INBOUND_RPS / RATE_LIMIT_INBOUND_BURST | 5 / 10 | Per-client inbound limit (0 disables) | | RATE_LIMIT_OUTBOUND_RPS / RATE_LIMIT_OUTBOUND_BURST | 10 / 20 | Global limit towards Google | | OUTBOUND_MAX_WAIT_MS | 4000 | Max wait for an outbound token | | MAX_CONCURRENT_UPSTREAM | 8 | Simultaneous upstream calls | | DAILY_UPSTREAM_BUDGET | 0 (off) | Hard cap on upstream calls per UTC day | | UPSTREAM_TIMEOUT_MS | 10000 | Hard per-attempt timeout | | RETRY_MAX_ATTEMPTS / RETRY_BASE_DELAY_MS / RETRY_MAX_DELAY_MS | 3 / 250 / 4000 | Retry policy (backoff + full jitter) | | CIRCUIT_BREAKER_FAILURE_THRESHOLD / CIRCUIT_BREAKER_COOLDOWN_MS | 5 / 30000 | Per-host circuit breaker | | CACHE_MAX_ENTRIES / IMAGE_CACHE_MAX_ENTRIES | 500 / 32 | LRU cache sizes (0 disables) | | CACHE_TTL_GEOCODE_S / CACHE_TTL_PLACES_S / CACHE_TTL_ROUTES_S / CACHE_TTL_STATIC_S / CACHE_TTL_ENVIRONMENT_S | 3600 / 300 / 60 / 86400 / 600 | Per-domain TTLs (0 disables) |

Rate limiting & resilience architecture

Every tool call traverses, in order:

agent call
   │
   ▼
[1] inbound token bucket (per client identity)   → immediate [rate_limited] + retry_after_ms
   │
   ▼
[2] TTL cache + in-flight coalescing             → identical concurrent reads = 1 upstream call
   │
   ▼
[3] concurrency semaphore (global)               → agent fan-out cannot open N connections
   │
   ▼
[4] circuit breaker (per Google host)            → fail fast while an upstream is down
   │
   ▼
[5] daily budget (optional) + outbound token bucket (global)
   │
   ▼
[6] fetch with hard timeout ──429/5xx/timeout──► backoff + jitter, Retry-After honored,
   │                                             tokens consumed per attempt (no retry storms)
   ▼
Google Maps Platform (12 APIs, per-host breakers)

A 1000-call flood is part of the test suite: the upstream never sees more than the configured budget, every rejected call gets a clean [rate_limited] result with retry_after_ms, and the server keeps serving. Composite tools fan out through the same pipeline — they cannot bypass any limit.

Google's default quotas are typically expressed per minute and per API. The defaults here (10 req/s sustained, 20 burst, globally) stay well under that; align RATE_LIMIT_OUTBOUND_RPS with the quotas of your project, and set DAILY_UPSTREAM_BUDGET for spend control.

Security model

| Threat | Mitigation | | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Google key leakage | Key read once at boot; never logged; URLs logged as host+path only; image URLs never returned to agents; AIza… patterns, key= params and bearer tokens scrubbed from every error and log line | | Agent retry loops / runaway automation | Inbound per-client buckets reject instantly with retry_after_ms; outbound bucket + semaphore + budget bound what ever reaches Google | | Upstream outage hammering | Per-host circuit breakers (fail-fast + single half-open probe), bounded retries, hard timeouts via AbortSignal | | Prompt injection via Maps content (reviews, names…) | Control characters stripped, lengths capped, reviews fetched only on explicit opt-in, server instructions tell agents to treat content strictly as data | | SSRF via tampered photo URIs | The photo CDN hop is credential-free and hard-allowlisted to *.googleusercontent.com over HTTPS | | Oversized image payloads | 6 MiB hard cap on binary responses; image dimensions capped by schema; dedicated small image cache | | Unauthorized remote access | HTTP refuses to start without a strong bearer key; constant-time (hashed timingSafeEqual) comparison; failed auths throttled per IP; WWW-Authenticate on 401 | | DNS rebinding / browser abuse | Host allowlist + Origin allowlist (default: no browser origins) | | Session hijacking / fixation | Session IDs are crypto.randomUUID(), bound to the creating key, idle-evicted, capped in number | | Oversized / malformed payloads | 1 MiB JSON body limit, Content-Type enforcement, strict Zod validation on every tool input — nothing unvalidated reaches Google | | Upstream response tampering | Responses parsed with tolerant-but-typed Zod schemas; unexpected shapes become clean upstream_errors, never crashes | | Supply chain | 3 runtime dependencies only (@modelcontextprotocol/sdk, zod, pino); raw node:http instead of a web framework |

Notes: tool arguments are never logged (addresses and coordinates are user data — privacy by design). The MCP logging capability is deliberately not exposed; operator logs stay on stderr.

Error taxonomy

Tool failures return isError: true with a single actionable line, e.g. [rate_limited] Too many requests from this client. Retry after 1200ms (retry_after_ms=1200). retryable=true. Auth errors name the exact Google API to enable.

| Code | Meaning | Agent action | | ------------------ | -------------------------------------------------------------------------- | --------------------------------------- | | validation_error | Arguments rejected (locally or by Google) | Fix arguments; do not retry as-is | | auth_error | Key invalid / named API not enabled / billing off / restriction mismatch | Stop and report; operator action needed | | rate_limited | Inbound, outbound, concurrency, budget or Google 429 | Wait retry_after_ms, then retry | | upstream_error | Google unavailable / timeout / circuit open / not found / changed contract | Retry only if retryable=true | | config_error | Invalid environment at boot | Fix env; process refuses to start |

Caching note

The in-memory cache is intentionally short-lived (and never persisted). Google Maps Platform terms restrict caching of most content — place IDs are the notable exception, and geocoding results have a bounded allowance. Review the current terms for your use case and set any CACHE_TTL_*_S=0 to disable a domain's cache entirely.

Docker

docker build -t google-maps-mcp .
docker run --rm -p 3000:3000 \
  -e GOOGLE_MAPS_API_KEY=AIza... \
  -e MCP_SERVER_API_KEY="$(openssl rand -hex 32)" \
  -e MCP_ALLOWED_HOSTS=mcp.example.com \
  google-maps-mcp

The image is multi-stage, slim, runs as the non-root node user and has a /healthz healthcheck. It defaults to MCP_TRANSPORT=http on 0.0.0.0:3000 — which is why the bearer key is mandatory.

Development

npm install
npm run dev        # stdio server via tsx
npm test           # vitest (109 tests: every tool, rate limiter, retry, breaker, cache, HTTP security, SSRF, 1000-call flood)
npm run verify     # typecheck + lint + test + build

Layered architecture: tools/ (thin handlers: validate → delegate → map) → services/ (per Google domain: geocoding, places, routes, environment, weather, airQuality, solar, imagery, insights) → lib/ (httpClient, rateLimiter, circuitBreaker, cache, errors, logger, redact, metrics, toolkit). Transports live in transports/; assembly in server.ts/index.ts; configuration in config.ts.

MCP SDK v2 migration note

This server pins @modelcontextprotocol/sdk to the stable 1.x branch (currently 1.29.x) with the registerTool/registerResource/registerPrompt high-level API, Zod v4 schemas and protocol version negotiation (nothing hardcoded). When SDK v2 stabilizes (package split into @modelcontextprotocol/server etc.), migration is contained: imports in server.ts, transports/* and lib/toolkit.ts, plus the package.json dependency. Tool/service/limiter code is SDK-agnostic by design.

License

MIT © Rog Studios