duckdb-analytics-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| WAREHOUSE_DB_PATH | No | Path to the DuckDB database file. If not set, an in-memory database is used. | |
| WAREHOUSE_DATA_DIR | No | Directory containing Parquet files to build the warehouse from. If not set, the bundled sample dataset is used. | |
| WAREHOUSE_MAX_ROWS | No | Maximum number of rows returned by a query. | 200 |
| WAREHOUSE_TIMEOUT_S | No | Query timeout in seconds. | 15 |
| WAREHOUSE_CATALOG_PATH | No | Path to the catalog YAML file (datasets.yaml). | |
| WAREHOUSE_MAX_CELL_CHARS | No | Maximum characters per cell in results. | 60 |
Capabilities
Features and capabilities supported by this server
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| list_datasetsA | List every table in the warehouse with its row count, grain, and caveats. Start here. The output is deliberately small -- one line per table plus the business rules that change query correctness -- so it is cheap to call before anything else. Returns: str: Markdown containing: - a table of (table, rows, grain, description) - a "Before you write SQL" section listing every caveat in the warehouse, each prefixed with its table name Examples: - Use when: "What data do I have access to?" - Use when: starting any analysis, before describe_table. - Don't use when: you already know the table and need its columns (use describe_table instead). |
| describe_tableA | Describe one table: columns, types, null rates, distinct counts, examples. Call this before writing a query against an unfamiliar table. It answers "what can I filter on, and what will be NULL" in a single round trip, which a raw information_schema dump does not. Args: table (str): Table name, case-insensitive (e.g. 'orders'). Returns: str: Markdown containing: - header with row count and grain - a table of (column, type, null%, distinct, range, examples) - column definitions from the semantic layer - declared join keys - caveats that affect correctness Examples: - Use when: "What columns does orders have?" - Use when: a query failed with an unknown-column error. - Don't use when: you need the distribution of one column (use profile_column instead). |
| profile_columnA | Profile one column: distribution, outliers, coverage gaps, dirty values. The profile adapts to the column type:
Args: table (str): Table name, case-insensitive. column (str): Column name, case-insensitive. Returns: str: Markdown profile, ending with the column's definition and any caveats that mention it. Examples: - Use when: "Is unit_price skewed? Should I use mean or median?" - Use when: "Are there missing days in order_date?" - Use when: a GROUP BY returned more groups than expected. - Don't use when: you want the whole schema (use describe_table). |
| explainA | Show the query plan and estimated row count WITHOUT executing the query. Use this when a query might be expensive, or after one has timed out. The estimate comes from DuckDB's optimizer, so it is an estimate, not a guarantee -- but it is enough to catch an accidental cross join before it runs for fifteen seconds. Args: sql (str): The SELECT to plan. It is not executed. Returns: str: Markdown containing the estimated cardinality, a verdict on whether the query is worth running, and the physical plan. Examples: - Use when: a previous query hit the timeout. - Use when: joining two large tables for the first time. - Don't use when: you just want the answer (call query directly). |
| queryA | Run one read-only SELECT against the warehouse and return the rows. Enforcement, in order: the statement is parsed and rejected unless it is a single SELECT; a LIMIT is injected if absent and lowered if it exceeds the server cap; execution is cancelled if it exceeds the time limit. When the result is truncated, the true total is counted and reported -- the output never implies it is complete when it is not. Prefer aggregating in SQL over selecting raw rows. Args: sql (str): One SELECT statement in DuckDB SQL. max_rows (Optional[int]): Per-call row cap, clamped to the server cap (default 200). response_format (ResponseFormat): 'markdown' (default) or 'json'. Returns: str: For 'markdown', an unpadded markdown table followed by a row-count footer that discloses truncation. For 'json', an object: { "columns": [str], "rows": [[Any]], "row_count": int, # rows returned "total_rows": int|null, # true total when truncated "truncated": bool, "elapsed_ms": float } Examples: - Use when: "What was revenue by month in 2025?" -> aggregate in SQL. - Use when: "Show me 10 example rows from orders." - Don't use when: you do not yet know the column names (call describe_table first -- it is cheaper than a failed query). Error Handling: - Non-SELECT statements, multiple statements, and filesystem functions are rejected before execution. - Queries exceeding the time limit are cancelled, not left running. - Unknown columns return DuckDB's message, which names the candidates. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/arthurxavier106/duckdb-analytics-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server