MCP server providing DuckDB SQL query execution with S3 parquet data access, H3 geospatial indexing, and STAC catalog discovery for geo-referenced datasets
This server demonstrates solid tool design with clear naming, comprehensive descriptions, and proper schema definitions. All 4 tools are properly registered with input schemas and detailed descriptions. The naming convention follows verb_noun patterns (query, browse_*, get_*) and is appropriately specific. Descriptions are detailed and contextual, ranging from 100-500+ characters. However, there are gaps in output schema documentation and error handling guidance. The `query` tool description is exceptionally detailed (620+ chars) with embedded SQL rules, but this violates the pattern of keeping descriptions concise and embedding constraints in parameters instead. Schemas are JSON-Schema compliant with type definitions and descriptions for all parameters. The server shows thoughtful design around S3/STAC dataset discovery patterns and SQL safety rules.
Discover all available STAC dataset IDs and titles from the configured STAC catalog. Returns a list of dataset metadata that can be used with get_stac_details to retrieve exact S3 paths and column schemas.
Retrieve detailed collection-level metadata for a STAC dataset, including licensing, links, and provenance information.
Retrieve exact S3 paths, column schemas, and metadata for a specific STAC dataset by ID. Always call this before querying to get the verbatim parquet path(s) to use in read_parquet()—never guess or modify paths.
Execute a SQL query against DuckDB with access to S3 parquet datasets via read_parquet(). The database is empty—use ONLY read_parquet('s3://...') for all data access. Discover dataset paths via browse_stac_catalog and get_stac_details before querying. Must follow critical rules: (1) use parquet paths from STAC, (2) never guess or modify S3 URLs, (3) mask before aggregate for differential privacy, (4) use h3_cell_to_parent() for hex rollups, (5) set memory_limit for large queries, (6) avoid count(DISTINCT col) and SELECT * on large tables.
Output schemas not documented in tool definitions. While input schemas are complete, tool descriptions do not specify the structure or fields of returned data. LLMs cannot plan downstream tool calls or extract specific fields without knowing the response format.
query tool description embeds 6 embedded SQL rules as prose (520+ chars of the 620-char description). Per pattern guidance, constraints should be validated at invocation and errors should guide recovery, not frontload all rules in the description. This bloats the description and wastes tokens on every invocation.
Error handling and recovery guidance is absent from all tool descriptions. If query fails (e.g., invalid S3 path, invalid SQL), there is no actionable error response pattern documented. Users and agents cannot determine if a failure is retryable or what corrective action to take.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | B | 74 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 35 | - | v1 |
No pagination or result limiting guidance documented. browse_stac_catalog and get_stac_collection could return large lists; descriptions do not specify if results are paginated, limited, or what happens if there are hundreds of datasets.
query tool sql_query parameter lacks format guidance. While the description says 'SQL query', it does not specify: expected SQL dialect (DuckDB), maximum query length, whether multi-statement queries are allowed, timeout behavior, or expected result format. This invites invalid input from LLMs.