geolens-mcp
OfficialThis server is a read-only MCP interface to the GeoLens spatial data hub, letting you search and inspect datasets, read saved maps, and run sandboxed SQL queries.
Search datasets by free text (title, description, keywords) with paging, returning GeoJSON features with dataset IDs and trust metadata (origin, freshness).
Inspect dataset schema – get columns, geometry type, CRS, feature count, spatial extent, and source health/freshness status.
Fetch features from a dataset as GeoJSON, optionally filtered by bounding box, with paging (no support for raster datasets).
List and view saved maps – read-only metadata including layers, view state, basemap, and terrain configuration.
Run read-only SQL queries against accessible dataset tables through a hardened sandbox, with allowed functions (including PostGIS spatial functions), strict limits, and table scoping via
restrict_tables. Requires credentials with AI-chat permission.
GeoLens
English | Español | Français | Deutsch | 简体中文
Your team's self-hosted spatial data hub: searchable, mappable, and shareable in one place.
GeoLens is an open-source spatial data hub for GIS and data teams: one place to find and work with data on infrastructure you control, with no telemetry. GeoLens itself phones home to nothing, except the default basemap tiles, which load from tiles.openfreemap.org until an admin configures a different one. (Other features you opt into can make outbound calls: AI assist to your chosen OpenAI-compatible endpoint or Anthropic key, OAuth/OIDC sign-in, SMTP, remote/S3 data sources, and off-site backups.) Upload files, create datasets in the browser, register tables already in GeoLens's own PostGIS database without copying them, import one-shot copies from WFS, ArcGIS FeatureServer, or OGC API Features, or reference remote STAC assets live. GeoLens records each dataset's origin, indexes catalog metadata with pg_trgm for fuzzy search out of the box (pgvector adds semantic ranking once you configure an embedding provider and enable semantic search), and serves OGC/STAC APIs that QGIS, ArcGIS, and MapLibre clients connect to natively. Compose, style, and share multi-layer maps right in the browser. Built on FastAPI and React. Deployed with one command.
git clone https://github.com/geolens-io/geolens.git && cd geolens
bash scripts/install.sh # read it first: it writes .env, generates secrets, runs docker compose up -d
# Open http://localhost:8080, then log in with the credentials you choseOr the one-line form, which runs the same script and pulls the prebuilt images:
curl -fsSL https://getgeolens.com/install.sh | shImages are published for linux/amd64 and linux/arm64. A fresh install runs six containers at about 1.3 GB resident.
API stability. The standards surfaces (OGC API Features/Records, STAC, and the tile endpoints) track their specifications and are safe to build against. GeoLens's own REST API can still change between minor releases: contract changes are listed in the CHANGELOG, and breaking ones keep the old form working for at least one more minor release. Hit a rough edge? Open an issue.
Documentation
Full user, admin, and API documentation lives at docs.getgeolens.com. The Reference table below links each guide.
Related MCP server: FastAPI Database MCP Server
Published artifacts
GeoLens is published through the standard package registries:
pip install geolens # Python SDK
pip install geolens-cli # CLI; installs the `geolens` command
pip install geolens-mcp # MCP server for coding agents (read-only)
npm install @geolens/sdk # TypeScript/JavaScript SDKPrebuilt public API and frontend images are published to GitHub Container Registry:
docker pull ghcr.io/geolens-io/geolens-api:latest
docker pull ghcr.io/geolens-io/geolens-frontend:latestThe latest tag tracks the newest published stable release.
Why GeoLens?
Spatial data ends up scattered: shapefiles on shared drives, tables in database schemas, rasters in cloud buckets, metadata in spreadsheets. Finding the right dataset means asking Slack or grepping file servers. Sharing it means exporting, emailing, and hoping the CRS matches.
GeoLens replaces that workflow:
One data hub: upload files, create datasets, register tables already in GeoLens's database, import feature-service snapshots, or reference remote STAC assets — then search and preview them together
Source state, not guesswork: see how each dataset entered the catalog, when it was last refreshed or checked, how its last refresh compares with its declared cadence (fresh, due, overdue, or unknown), and whether a remote Service or STAC origin is still reachable
Works with your tools: OGC API Features/Records with server-side CQL2 filtering, STAC API 1.0, direct tile URLs for QGIS, ArcGIS, and MapLibre
No lock-in: your catalog and the copies GeoLens manages stay on infrastructure you control and leave through open formats. Vector datasets export to GeoPackage, GeoJSON, Shapefile, CSV, GeoParquet, FlatGeobuf, or PMTiles; rasters download as Cloud-Optimized GeoTIFF; and any OGC API client reads the catalog directly
Semantic and spatial search: pg_trgm fuzzy matching out of the box; add an embedding provider and enable semantic search to rank datasets by meaning (pgvector)
Built-in map builder: compose multi-layer maps, style them, and share via public link or embeddable iframe
AI-assisted (optional): chat with your maps, auto-generate descriptions, search by natural language. Bring an OpenAI-compatible endpoint or Anthropic key, or skip it entirely
See it in action
The examples below use a JWT bearer token. Mint one against the local stack (the login endpoint accepts an OAuth2 password form, so use -d with form fields, not JSON). Substitute your admin username and the password from .env (grep '^GEOLENS_ADMIN_PASSWORD=' .env):
TOKEN=$(curl -s -X POST http://localhost:8080/api/auth/login/ \
-d 'username=admin&password=<your-admin-password>' | jq -r '.access_token')Semantic search takes a one-time admin setup: an embedding provider and the AI + Semantic Search toggles in the admin AI settings, plus an embedding backfill for data ingested before setup (the search guide walks through it). Once that's on, search datasets by meaning instead of exact keyword matches:
# Semantic search ranks by meaning: "hydrology" surfaces the lake and river
# network datasets whose titles never mention the word
curl "http://localhost:8080/api/search/datasets/?q=hydrology&limit=3" \
-H "Authorization: Bearer $TOKEN" | jq '.features[].properties.title'One search-endpoint behavior to know when consuming it programmatically: the
first page augments the dataset results with up to five matching collections,
so numberReturned can exceed limit on page 0 only. That is deliberate, not
a bug — limit still bounds the number of datasets per page.
Every dataset is also a standard OGC API Features endpoint:
# Grab a public collection id from the catalog. Search anonymously (no token) so
# the id is one anyone can read, matching the unauthenticated items request below.
CID=$(curl -s "http://localhost:8080/api/search/datasets/?q=countries&limit=1" \
| jq -r '.features[0].id')
# GeoJSON features with a bbox filter, works in QGIS, ArcGIS, any OGC client
curl "http://localhost:8080/api/collections/$CID/items?bbox=-10,35,30,60&limit=5"PostGIS and pgvector share one database, so with semantic search enabled you can rank datasets by meaning inside a spatial window in a single query. See the search guide for how semantic and spatial search work together.
Connect directly from QGIS: Layer > Add WFS / OGC API Features and point at http://localhost:8080/api/.
The same endpoints from the tools you already use: geolens-examples holds single-file MapLibre, Leaflet, OpenLayers and ArcGIS JS pages, QGIS and DuckDB walkthroughs, both GeoLens SDKs, a semantic catalog search, a STAC browser, a saved-map embed, a Python/GeoPandas analysis, a catalog-as-code manifest for the CLI, and an MCP setup. The read-only ones run against the live demo, and CI replays them there on every push and once a week, so what you copy is code that worked this week. Browse the gallery.
Features
Each example above has a full guide in the docs. What GeoLens reads, writes, and exposes:
Data ingestion and export
Five source modes: Uploaded and Created data are managed locally; Register Table serves an existing table in GeoLens's own PostGIS database in place; Service imports are one-shot local copies; STAC datasets keep a live reference to the remote asset
Vector: Shapefile, GeoPackage, GeoJSON, GeoParquet, FlatGeobuf, KML/KMZ, zipped File Geodatabase, CSV, XLSX
Raster: GeoTIFF and Cloud-Optimized GeoTIFF (COG) with automatic conversion
Mosaics: VRT-based raster mosaics from multiple source files
Export: GeoJSON, Shapefile, GeoPackage, CSV, and FlatGeobuf with CRS reprojection; GeoParquet (always EPSG:4326); PMTiles as a self-contained tile archive for static hosts that support range requests
Source state: origin and last-refreshed/last-checked timestamps, cadence-based source freshness, and on-demand health checks for Service and STAC origins
Provenance tracking and metadata editing
Analysis
Buffer (metres, kilometres, feet, or miles), centroid, clip by a drawn area or by another polygon layer, and dissolve with an optional group-by column; spatial join and select by location match features on intersection, measure adds
area_sqmandlength_mcolumns, and intersect writes the pairwise overlay with attributes from both sidesAll operations preview on the map except dissolve, which is materialize-only; previews are capped at 500 features. Create dataset then runs any of the eight over every feature as a background job, within per-operation source limits (250k features for dissolve, 500k for buffer)
The output is an ordinary vector dataset — styleable, exportable, and served through the OGC API endpoints like any other
The chat assistant can run buffer, centroid, and layer-based clip previews on request
Standards and interop
OGC API - Features (with server-side CQL2 filtering and per-collection
/queryables) and OGC API - Records; STAC API 1.0 catalog endpoint; JSON-LD catalogs for DCAT 3, DCAT-US 3.0, and GeoDCAT-APDirect tile URLs and per-user API keys for QGIS, ArcGIS, MapLibre, and any OGC client
Vector tiles omit attribute columns below zoom 10 to keep low-zoom tiles small; add the
cols=<column>,<column>query parameter to a tile URL to opt specific columns in at every zoom (names are validated against the dataset's columns, unknown names are dropped)JWT + OAuth 2.0/OIDC, RBAC with per-dataset permissions
Interface in English, Spanish, French, German, and Simplified Chinese
JWT authentication with refresh tokens
API key management per user
OAuth 2.0 / OIDC support (Google, Microsoft, generic providers)
Role-based access control (RBAC) with per-dataset permissions
Self-serve registration is off by default; when enabled with SMTP verification, registration email delivery is uniform for new and colliding submissions
Audit logging for all administrative actions
Screenshots
Quick start
Prerequisites: Docker Engine 24+ and Docker Compose v2. The bundled stack
ships PostgreSQL 18. If you point GeoLens at an externally managed database, it
must be PostgreSQL 13+ (for gen_random_uuid()) with pgvector 0.5+ (for
HNSW semantic-search indexes), plus PostGIS, pg_trgm, and unaccent. The API and
worker run in containers (Python 3.14 bundled, no host Python needed). The
optional CLI runs on your host and requires Python 3.11+; the Python SDK and
seed scripts require Python 3.10+.
Clone the repo and run the installer from the checkout. You can read the script before running it; from a clone it builds the images locally:
git clone https://github.com/geolens-io/geolens.git
cd geolens
bash scripts/install.shThe one-line form runs the same script and pulls the prebuilt, version-pinned images instead of building them:
curl -fsSL https://getgeolens.com/install.sh | shEither way, scripts/install.sh copies .env.example to .env, generates a JWT signing
secret, sets up admin credentials, and runs docker compose up -d. The admin username
defaults to admin; the admin password is auto-generated as a strong random value
(written to .env, never printed to your terminal) unless you supply your own.
For unattended installs, set GEOLENS_ADMIN_USERNAME and GEOLENS_ADMIN_PASSWORD in the
environment before running and the prompts are skipped. Re-running the script is idempotent:
existing values in .env are preserved.
Wait about 60 seconds for services to start, then open http://localhost:8080.
Log in with your admin username and the generated password (retrieve it with
grep '^GEOLENS_ADMIN_PASSWORD=' geolens/.env — the one-line installer clones
into geolens/ under the directory you ran it from; inside a source checkout
it's just .env).
Verify all services are healthy:
docker compose psFirst-run notes: the one-line install pulls prebuilt images and is up in about
a minute (only the small PostGIS + pgvector database layer builds locally). Cloning
and running bash scripts/install.sh instead builds every image from source:
5-10 minutes on the first run (GDAL + Postgres extensions + the frontend bundle);
subsequent starts settle in ~60 seconds either way. If ports 5434/8001/8080 are
already taken, change DB_PORT, API_PORT,
or FRONTEND_PORT in .env. For port conflicts, stuck startups, out-of-memory,
and migration warnings, see the Troubleshooting guide.
For production deployment, see the Install Guide. A Kubernetes Helm chart lives in the separate geolens-deployments repo.
Verify the installer
Each GitHub Release attaches a SHA256SUMS
file generated by CI alongside install.sh. To confirm a downloaded installer was not tampered
with before running it, download both assets from the same release and place them in the same
directory, then run:
# Linux / Windows WSL
sha256sum -c SHA256SUMS
# macOS
shasum -a 256 -c SHA256SUMSA passing check prints install.sh: OK.
Upgrading
To upgrade a prebuilt install, run ./scripts/upgrade.sh from your install
directory. It backs up the database, pulls the new images, runs migrations
behind a health gate, and prints a rollback recipe if anything fails. See
UPGRADING.md for the prebuilt and source-build flows plus
rollback, or the online Upgrade Guide.
Add your first dataset
The repo ships a small city-parks.geojson. Upload and publish it in one command with the GeoLens CLI:
pip install geolens-cli # installs the `geolens` command
geolens login http://localhost:8080/api # use your admin username + password
geolens publish examples/manifests/first-catalog/city-parks.geojson --name "City Parks"geolens publish runs the upload → preview → commit ingest flow and prints the new dataset's URL. One command takes a local file to a published, mappable dataset.
For repeatable, multi-dataset catalogs, describe your sources in a manifest (geolens.yaml) and apply it with geolens apply. Manifest sources are referenced by HTTP(S) URL, S3 URI, or a path already staged on the server; the examples in examples/manifests/ are templates to adapt. Scaffold a fresh one with geolens init and edit it for your sources:
geolens init # writes geolens.yaml in the current directory
geolens validate geolens.yaml # local schema check, no API call
geolens apply geolens.yaml # validates + applies via /ingest/manifest/applySee the CLI guide for the full manifest schema, source kinds, and CI integration patterns.
Seed data
scripts/seed-showcase.py builds seven showcase maps from public open data: a global
tectonics story over real ocean-floor relief, the Manhattan 3D skyline colored by
construction era (the hero above), Atlantic hurricane tracks since 1950, clustered
meteorite falls, the Matterhorn in 2 m lidar 3D terrain, by-reference Sentinel-2
imagery of New York, and a hurricane-exposure map computed in place from the storm
tracks with buffer, intersect and dissolve:
pip install httpx
python scripts/seed-showcase.py --username admin --password "$(grep '^GEOLENS_ADMIN_PASSWORD=' .env | cut -d= -f2-)"Requires internet access to the upstream open-data sources. See
scripts/README.md for flags (--no-terrain, --prune, …).
Architecture
GeoLens is a small set of services around a single PostgreSQL/PostGIS database: the API serves the catalog, search, and OGC/STAC endpoints; a worker handles ingestion; and Titiler serves raster tiles from object storage.
flowchart TB
B["Browser: React + MapLibre app"]
OGC["QGIS · ArcGIS · OGC/STAC clients"]
NG["Nginx reverse proxy<br/>serves the React build, routes /api and tiles"]
subgraph Application
API["FastAPI<br/>catalog · semantic search · OGC/STAC · vector tiles"]
W["Worker<br/>GDAL/ogr2ogr ingestion"]
TT["Titiler<br/>COG raster tiles"]
end
subgraph store [Data and storage]
PG[("PostgreSQL 18<br/>PostGIS · pgvector · pg_trgm<br/>+ Procrastinate queue")]
OBJ[("Object storage<br/>local files or S3/MinIO")]
CACHE[("Valkey cache")]
end
B --> NG
OGC --> NG
NG --> API
NG --> TT
API <--> PG
API --> OBJ
API -. tile/query cache .-> CACHE
PG == job ==> W
W --> PG
W --> OBJ
TT --> OBJComponent | Technology |
Frontend | React 19, Vite, MapLibre GL v6, TanStack Query, Tailwind CSS |
Backend API | FastAPI (Python), GDAL/ogr2ogr, Procrastinate (task queue) |
Raster Tiles | Titiler (COG tile server) |
Object Storage | MinIO (S3-compatible, local dev) or any S3 provider |
Cache | Valkey (tile and query cache) |
Database | PostgreSQL 18 + PostGIS 3.6 + pgvector + pg_trgm (minimum: PostgreSQL 13, pgvector 0.5) |
Reverse Proxy | Nginx (production) / Vite dev proxy (development) |
Configuration
All configuration is managed through environment variables in .env. See the Configuration Reference for the full list of options with defaults and descriptions.
Connection pool budget
GeoLens ships tuned for a single PostgreSQL instance: the API, worker, and admin
pools fit within 70 of 80 max_connections out of the box (Postgres
max_connections is set to 80), sized by DB_POOL_SIZE (pool_size) and
DB_MAX_OVERFLOW (max_overflow, default 3). See
Connection Pool Tuning
for the per-process budget and how to raise the ceiling.
Backups
Automated, scheduled backups run by default. You do not need a --profile backup flag.
The backup service starts alongside api, worker, and db on every
docker compose up and runs pg_dump on a daily/weekly schedule alongside an
archive of the object-storage staging volume, so a restore reproduces a working
instance (DB + uploaded files).
Off-site (S3) upload is additionally gated on BACKUP_S3_ENABLED=true. The
built-in uploader signs requests with AWS Signature V4 (awscli), compatible
with Cloudflare R2, modern AWS S3, and MinIO. A failed upload surfaces a visible
ERROR in container logs (not a swallowed warning), so silent offsite backup
loss is detectable immediately.
For day-2 operations, restore procedures, and incident response, see RUNBOOK.md. For provider-specific configuration options, see Backups & Restore.
Monitoring
The API and worker export Prometheus metrics out of the box (HTTP rate/latency/
errors, job-queue depth, DB pool, tile-cache). Reference scrape config, alert
rules, and a Grafana dashboard ship in infra/monitoring/;
see RUNBOOK.md §4 for the setup steps.
Reference
Guide | Description |
Step-by-step deployment with Docker Compose | |
Upgrading between versions with rollback procedures | |
All environment variables and their defaults | |
User management, datasets, system health | |
Managed database, object storage, and cache deployment guides | |
Publish files and manage catalogs with the | |
Auto-generated reference at docs.getgeolens.com; development-mode stacks also serve Swagger UI at | |
Template | |
Runnable browser, QGIS, DuckDB, SDK, CLI, embed, Python, and MCP examples; the read-only ones are verified against the live demo in CI (gallery) |
Community
GitHub Discussions: questions, ideas, show and tell
Support: where to ask for help and how problems get routed
Contributing Guide: development setup, code style, and PR guidelines
Known limitations
Single PostgreSQL instance, with no built-in high availability or clustering.
GeoLens is designed for one organization per self-hosted deployment.
Terrain rendering assumes DEM units are in meters; datasets in other vertical units may render exaggerated.
GeoLens's own REST API may still change between minor releases (see the API stability note above).
License
GeoLens is licensed under the Apache License 2.0. The GeoLens name, logo, and brand assets are not covered by this license. See TRADEMARKS.md. Third-party sample-data attribution is in THIRD_PARTY_DATA.md.
Project policies: governance · maintainers · contributing · security · release process · egress & air-gap.
Available Tools
6 toolsget_dataset_schemaA
Get a dataset's schema and source trust metadata.
Returns columns (name/type/role), geometry type, CRS/SRID, feature count,
spatial extent, and the safe source_origin. source_health is healthy,
missing, inaccessible, or unknown: inaccessible means GeoLens could not
determine whether the source still exists, while unknown means it was never
probed or cannot be probed. source_health_detail is a fixed GeoLens reason
code, not provider text. last_checked_at records the latest probe attempt;
last_refreshed_at records only the latest successful committed refresh.
source_freshness is advisory; overdue means two declared update intervals
elapsed without a successful refresh. Raw provider URLs, origin pointers,
and credentials are excluded. Call this before writing spatial questions so
you know both the available columns and whether the source may be stale.
Args: dataset_id: Dataset id (e.g. from search_datasets).
| Name | Required | Description | Default |
|---|---|---|---|
| dataset_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It does an excellent job explaining the semantics of tricky fields like source_health (inaccessible vs unknown), source_health_detail (fixed reason code), and freshness (overdue meaning). It also discloses that raw provider URLs and credentials are excluded, which is important for agent expectations. It mentions the read-only nature implicitly by describing data retrieval, but does not explicitly state 'this is a read-only operation'. Given the lack of annotations, this is a strong effort, but could be a 5 if it explicitly stated it does not modify data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is thorough and well-structured. It starts with a clear summary sentence, then details the return fields and their semantics, and ends with usage guidance. It is moderately long but every sentence adds critical information about field meanings and exclusions. The main body is front-loaded with the tool's purpose. It could be slightly trimmed, but the richness justifies the length. A 5 would require even more efficiency, but this is clean.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's single parameter, no output schema, and no annotations, the description provides comprehensive information: what is returned (columns, geometry, etc.), the meaning of all status fields, and exclusions. It also gives usage context ('before writing spatial questions'), which is crucial for correct invocation. The agent has everything needed to call the tool correctly and interpret results. This is complete for its complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has 0% description coverage for the single parameter, dataset_id. The tool description provides context: 'Dataset id (e.g. from search_datasets).' This is minimal but adds value by indicating the source of the ID. However, it doesn't describe format or constraints, but the parameter is simple. Given the low schema coverage, the description partially compensates, but could be improved by explicitly saying 'A string identifier of the dataset as returned by search_datasets'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states the tool's purpose: retrieving a dataset's schema and source trust metadata. It enumerates the specific elements returned (columns, geometry, CRS, etc.), distinguishing it clearly from siblings like search_datasets (which finds datasets) and get_features (which retrieves data). The verb 'get' is precise and the resource is identifiable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage guidance: 'Call this before writing spatial questions so you know both the available columns and whether the source may be stale.' This tells the agent when to use it in a workflow. It does not explicitly mention when not to use it or name alternatives, but the context is strong enough to differentiate from search_datasets and get_features. It could be improved by explicitly stating 'use search_datasets to find datasets' but that's implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_featuresA
Get GeoJSON features for a dataset (bounded).
Returns an OGC-API FeatureCollection. Raster datasets have no features and
will error. Results are capped by limit; page with offset.
Args: dataset_id: Dataset id. limit: Max features (1-200, default 10). offset: Feature offset for paging. bbox: Optional bounding box filter "minx,miny,maxx,maxy" (WGS84).
| Name | Required | Description | Default |
|---|---|---|---|
| bbox | No | ||
| limit | No | ||
| offset | No | ||
| dataset_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of behavioral disclosure. It explicitly mentions the return format, the error on raster datasets, and the limit/offset paging behavior. It does not cover authentication, invalid dataset_id handling, or the meaning of 'bounded', but it discloses the key traits an agent needs to use it safely.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and logically ordered: a one-line summary, then key behavior, then parameter details. No filler or repetition. Every sentence contributes useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity and the absence of an output schema or annotations, the description covers everything essential: return type, error condition, pagination, and parameter semantics. It lacks only minor details like how to obtain a dataset_id or exact behavior for invalid IDs, but the provided information is sufficient to call the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description goes beyond the structured schema. The Args section adds meaning: limit constraints (1-200, default 10), offset purpose ('paging'), bbox format ('minx,miny,maxx,maxy' in WGS84), and dataset_id as the target. This fully compensates for the schema's lack of descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific action and resource: 'Get GeoJSON features for a dataset'. The return type (OGC-API FeatureCollection) and the error condition for raster datasets further clarify what the tool does. This clearly distinguishes it from siblings like get_dataset_schema (metadata) and search_datasets (dataset discovery).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides concrete usage context: it returns features only for vector datasets, errors on raster datasets, and supports pagination via limit/offset. It does not explicitly name alternatives or state when not to use it beyond the raster error, so it misses the 'use X instead' guidance, but the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_mapA
Get one saved map's full metadata, including its layers, view state, basemap, and terrain configuration.
Args: map_id: Map id (e.g. from list_maps).
| Name | Required | Description | Default |
|---|---|---|---|
| map_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It describes what the tool returns but does not explicitly state whether it is read-only, whether it has side effects, or if there are any limitations, errors, or permission requirements. The verb 'get' implies a read operation, but this is not made explicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded. The first sentence clearly states the tool's purpose, and the second sentence explains the parameter. No unnecessary words or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the lack of an output schema, the description provides a reasonable outline of the return value (full metadata with layers, view state, basemap, terrain). It does not cover error cases or edge conditions, but for a single-id fetch operation, the information is largely sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only the parameter name and type (string). The description adds useful context by giving an example source for the value ('e.g. from list_maps'), which helps an agent know how to obtain a valid map_id. It does not specify format or constraints, but the parameter is a simple identifier.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Get') and resource ('one saved map's full metadata') with a clear scope (layers, view state, basemap, terrain configuration). It is distinct from sibling tools like list_maps (which lists maps) and query (which queries features).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for retrieving details of a single map by ID, with an example source ('e.g. from list_maps'). However, it does not explicitly state when to use this tool over alternatives or when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_mapsA
List saved maps (read-only metadata: id, name, visibility, layer count).
Args: search: Optional text filter on map name/description. limit: Max results (1-200, default 50). offset: Result offset for paging.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| offset | No | ||
| search | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the transparency burden and explicitly notes the operation is read-only, which is valuable. It also discloses the result surface (metadata only) rather than implying full map contents are returned.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly organized with a front-loaded summary of the tool's behavior followed by a compact Args list. Every sentence adds direct value with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple listing operation, the description covers purpose, parameter behavior, pagination, and return scope (metadata fields). While no output schema exists and exact response shape isn't detailed, the metadata field list gives an agent enough to understand what to expect.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the description provides meaningful semantics for all three parameters: search as a text filter on name/description, limit with range and default, and offset as paging. This fully compensates for the schema's lack of descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb and object: 'List saved maps', and enumerates the metadata fields (id, name, visibility, layer count). It is distinguishable from sibling tools like get_map, though it doesn't explicitly call out the difference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies this is the tool for listing saved maps, which conveys a general usage context. However, it does not state when not to use it or which sibling tool to choose instead for map detail, dataset search, or feature queries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
queryA
Run one read-only SQL SELECT against accessible datasets.
Executes through the server's hardened SQL sandbox: a single SELECT over
data.* tables, an allowlisted function set (aggregates, math, string,
date, JSON, common PostGIS like ST_Area/ST_DWithin/ST_Intersects), and a
strict server-side budget (a few seconds of runtime, a repetition cap on
self-joins, and row_limit rows). Returns {columns, rows, row_count, truncated} with rows as lists in column order.
Requires credentials with AI-chat permission — anonymous configurations
cannot use this tool. Workflow: 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 the same table_name in
restrict_tables. Every table the query touches must be listed there;
the scope can only narrow what your credentials already see. Writes,
other schemas, and unlisted functions are rejected with a short reason.
Args:
sql: A single SELECT statement over data.* tables.
restrict_tables: Table names (no data. prefix) the query may touch.
Required and non-empty.
row_limit: Max rows to return (1-1000, default 100).
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | ||
| row_limit | No | ||
| restrict_tables | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so the description carries the full burden. It discloses the read-only nature, sandbox constraints (allowlisted functions, server-side budget), and explicitly states what is rejected (writes, other schemas, unlisted functions), as well as the return format.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-organized but somewhat repetitive, especially regarding the table reference format ('data.<table_name>') and the restriction on restrict_tables. It could be tightened without losing clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is comprehensive: it covers purpose, constraints, workflow, parameter details, return format, and credential requirements. It provides enough context for an agent to use the tool correctly, leaving no significant gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description explains each parameter beyond the schema: sql is a single SELECT, restrict_tables must list all accessed tables (with no 'data.' prefix) and is required, and row_limit has a default. It clarifies the scope restriction and adds meaning to the schema's bare types.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Run one read-only SQL SELECT against accessible datasets.' It specifies the verb, resource, and scope, and distinguishes it from sibling tools by being the primary query tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage context: it is for running SQL SELECT queries, and it even outlines a workflow (search_datasets, get_dataset_schema) and mentions required credentials. It implicitly indicates when not to use it (for non-SELECT operations) by emphasizing read-only.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_datasetsA
Search the GeoLens catalog for datasets by free text.
Matches title, description, and keywords (semantic ranking is used
automatically when the instance has it enabled). Returns a GeoJSON
FeatureCollection where each feature is a dataset record; use the feature
id as the dataset_id for the other tools. Each feature's properties include
source_origin (upload, postgis, service, stac, created, or null) and
source_freshness (fresh, due, overdue, or unknown). Freshness is advisory:
due means one declared update interval has elapsed, while overdue means
two have elapsed; neither proves that the content is wrong.
Search does not make a detail request per result. When the catalog summary
has no health/check/refresh value, source_health, source_health_detail,
last_checked_at, and last_refreshed_at are null. Call
get_dataset_schema for populated trust metadata. Raw provider URLs and
credentials are never included in these source-state fields.
Args: query: Search text. limit: Max results (1-200, default 10). offset: Result offset for paging.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | ||
| offset | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral disclosure burden, and it does so thoroughly. It explains the GeoJSON FeatureCollection return format, semantics of source_origin and source_freshness, null behavior for health fields, absence of per-result detail requests, and exclusion of credentials. This is far beyond a generic search-tool description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded in the first sentence, and subsequent paragraphs are organized by result format, trust metadata caveats, and parameter details. The description is longer than strictly necessary for three parameters, but the extra detail on freshness and null fields is substantive and earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with no annotations and no output schema, the description fully covers inputs, output shape, property enums, null semantics, and even the follow-up tool for richer trust metadata. An agent can invoke and interpret results correctly without guessing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the Args block is the sole parameter documentation. It provides search text semantics, a range and default for limit (1-200, default 10), and offset's basic purpose. The offset explanation is terse and could clarify paging interaction with limit, but the description does compensate for the absent schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb and resource: 'Search the GeoLens catalog for datasets by free text.' This unambiguously distinguishes it from sibling tools like get_features, list_maps, and get_map. The additional note about using the feature id as dataset_id for other tools reinforces its role in the catalog workflow.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use this tool: free-text search across catalog metadata, with semantic ranking mentioned. It also directs the agent to call get_dataset_schema when trust metadata is needed. However, it does not explicitly contrast search_datasets with alternatives like query or list_maps, so it lacks full when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.1.0- First observed
get_dataset_schema - First observed
get_features - First observed
get_map - First observed
list_maps - First observed
query - First observed
search_datasets
TDQS
Scored across 6 tools
Each tool has a clear, distinct target: dataset discovery, schema/trust metadata, feature retrieval, SQL querying, and map metadata. There is potential overlap between get_features and query for reading data, but the descriptions frame them as different access modes (GeoJSON features vs. read-only SQL) so an agent can choose intentionally.
Most tools follow a consistent verb_noun pattern (search_datasets, get_features, get_dataset_schema, list_maps, get_map). The bare `query` tool is the only deviation, but it is a recognizable verb and does not clash with the others.
Six tools is well-scoped for a geospatial catalog and map metadata server: three for dataset introspection/access, two for saved maps, and one for SQL. Each tool fills a distinct role without redundancy or sprawl.
The read-only dataset workflow is well covered: discover, inspect schema, fetch features, or run SQL. Minor gaps exist, such as no explicit list-all-datasets or per-feature lookup endpoint, but agents can work around them with search and query.
Maintenance
Related MCP Connectors
Generate and run high performance queries on open and private spatial data at-scale in the cloud
Read-only optical catalog search plus DiffBeam server, schema, and catalog revision metadata.
ArcGIS Hub — open government geospatial data (search + Feature Service query).
Read-only Texas oil & gas data: operator directory, county production, and dataset catalog.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceProvides read-only access to PostgreSQL databases with schema inspection, query execution in multiple formats (JSON, CSV, Markdown), and query history tracking with built-in security features.-
- FlicenseNot gradedqualityFmaintenanceProvides read-only SQL query access to Postgres and DuckDB databases via MCP tools, with extensive security hardening for public endpoints.1-
- AlicenseNot gradedqualityCmaintenanceProvides access to City of Salem GIS open geospatial data (ArcGIS), enabling search, query, and schema retrieval of datasets such as parcels, zoning, and public works.8MIT
- FlicenseBqualityBmaintenanceProvides read-only query tools over OpenStreetMap data in PostGIS, enabling natural language queries for features, categories, and spatial analysis.7-