Skip to content
getgeolens.com

MCP Server

The GeoLens MCP server (geolens-mcp) is a read-only Model Context Protocol server. Point a coding agent (Claude Code, Cursor, Codex, and any other MCP client) at a GeoLens instance and it can discover datasets, inspect schemas, and read features and maps from inside a dev session. It is Apache-2.0 licensed and built on the Python SDK, so every tool maps to a real endpoint in the API reference.

Terminal window
pip install geolens-mcp # or: uvx geolens-mcp

geolens-mcp needs Python 3.11 or newer, one minor version above the Python SDK it builds on, which supports 3.10.

uvx runs the server without a persistent install, which is what the client-registration examples below use.

geolens-mcp is released alongside GeoLens itself, so run the version that matches your instance: GET /api/health reports it. The examples repo publishes the pinned command and explains the trade-off of pinning.

The server reads its target instance and credentials from the environment — the same variable names the CLI uses:

VariableRequiredMeaning
GEOLENS_INSTANCEYesInstance URL, e.g. https://geolens.example.com. The /api suffix is appended automatically if you omit it, and left alone if you include it. The SDKs do not append it, so if one exported value feeds the SDK too, include /api.
GEOLENS_API_KEYRecommendedAPI key, sent as X-Api-Key. Omit for public-only access. See Authentication → API keys for how to obtain one.
GEOLENS_TOKENJWT bearer token, used only if GEOLENS_API_KEY is unset.

Every client needs the same inputs: the command uvx geolens-mcp and the GEOLENS_* variables above in the server’s environment. Most clients read them from an mcpServers block:

{
"mcpServers": {
"geolens": {
"command": "uvx",
"args": ["geolens-mcp"],
"env": {
"GEOLENS_INSTANCE": "https://geolens.example.com",
"GEOLENS_API_KEY": "your-api-key"
}
}
}
}

Where that block goes differs per client, and Codex reads TOML instead. Ready-to-paste files for each client, pointed at the public demo, live in the examples repo under mcp/clients/.

One command registers the server:

Terminal window
claude mcp add geolens \
-e GEOLENS_INSTANCE=https://geolens.example.com \
-e GEOLENS_API_KEY=... \
-- uvx geolens-mcp

That writes to the local scope (this project, this machine). Add -s user to make the server available in every project. For a config that travels with the repository, put the mcpServers block above in a .mcp.json at the project root and commit it. Keep the real key out of that file: reference a variable your shell already exports instead ("GEOLENS_API_KEY": "${GEOLENS_API_KEY}"), or register locally with claude mcp add.

ToolWhat it does
search_datasetsCatalog search by free text (semantic ranking where the instance enables it). Returns dataset records as GeoJSON features.
get_dataset_schemaA dataset’s columns, geometry type, CRS/SRID, feature count, and extent, plus the source-trust fields origin, source_health, and source_freshness.
get_featuresBounded GeoJSON features for a dataset (OGC API — Features), with optional bbox.
list_mapsSaved maps (id, name, visibility, layer count).
get_mapOne saved map’s full metadata, including layers and view state.
queryOne read-only SQL SELECT over data.* tables, through the server’s hardened sandbox. Returns {columns, rows, row_count, truncated}.

get_features caps results with limit (default 10) and pages with offset, and its bbox is minx,miny,maxx,maxy in WGS84 regardless of the dataset’s own SRID. Raster datasets have no features, so get_features against one errors rather than returning an empty collection; check record_type in get_dataset_schema first. A dataset or map id that is not a UUID is rejected by geolens-mcp itself (Invalid id (expected a UUID)) before any request reaches the instance, which is a different error from a 404.

query is the one tool that is not a GET; it POSTs to the sandbox endpoint and is still strictly read-only. A single SELECT is allowed, over an allowlisted function set (aggregates, math, string, date, JSON, and common PostGIS such as ST_Area, ST_DWithin, ST_Intersects), under a server-side budget: a few seconds of runtime, a repetition cap on self-joins, a row_limit between 1 and 1000 (default 100), at most 25 entries in restrict_tables, 20,000 characters of SQL, 100 output columns, an 8 MB response, and 30 queries a minute per user (60 per IP). The sandbox rejects writes, other schemas, and unlisted functions with a short reason.

This endpoint is tighter than AI chat on one axis: output-amplifying functions the shared allowlist otherwise admits are dropped here — concat, concat_ws, format, replace, regexp_replace, array_to_string, json_build_object, and jsonb_build_object — and with concat blocked the || concatenation operator is refused too. Assemble strings on the client instead.

It takes restrict_tables as a required, non-empty list. Every table the query touches must be listed there, and the scope can only narrow what the credential already sees. The usual workflow is search_datasets to find a dataset, then get_dataset_schema for its table_name and columns, then reference it as data.<table_name> in the SQL and list that same table_name in restrict_tables.

query requires credentials with AI-chat permission, so anonymous configurations cannot use it: without one it returns 401 Could not validate credentials. The other five tools work without a credential against public/published data.

  • Client SDKs: the Python SDK that geolens-mcp builds on, and the CLI-vs-SDK-vs-MCP-vs-API decision table
  • CLI & Manifests: the same credential/instance environment variables, for terminal and CI ingestion
  • API Authentication: JWT and API-key details for the credentials above
  • Search & Discovery: what search_datasets returns, including semantic ranking
  • Examples: MCP prompts: prompts to try against the public demo, each naming the tools it drives