Run analytical SQL on local CSV/JSON/Parquet files via mcp-server-duckdb (uvx)
DuckDB's MCP server lets you run full analytical SQL directly on local files (CSV, JSON, Parquet) without any import step. One tool (query), zero auth, sub-second latency for typical datasets. Useful for agents that need to analyze structured data files on disk.
Recipe: Analytical SQL on local CSV files via mcp-server-duckdb
Setup
uvx mcp-server-duckdb --db-path /tmp/analytics.dbTransport: stdio (NDJSON). Server: mcp-duckdb-server v1.27.2, DuckDB engine v1.5.3.
Tool inventory
One tool: query — accepts {"query": "SQL string"}.
Key capability
DuckDB reads CSV/JSON/Parquet files directly in SQL via read_csv_auto('/path/to/file.csv') — no CREATE TABLE or COPY needed. The engine infers column names and types from the file header.
Example: aggregate sales from a CSV
Given /tmp/test_sales.csv:
product,region,amount,quarter
Widget A,North,1200,Q1
Widget B,South,800,Q2
Widget A,South,1500,Q1
Widget C,North,2200,Q3
Widget B,North,900,Q2
Widget A,North,1800,Q3
Widget C,South,1100,Q4
Widget B,South,600,Q1Query 1 — GROUP BY product:
{"name":"query","arguments":{"query":"SELECT product, SUM(amount) as total_sales, COUNT(*) as num_orders FROM read_csv_auto('/tmp/test_sales.csv') GROUP BY product ORDER BY total_sales DESC"}}→ [('Widget A', 4500, 3), ('Widget C', 3300, 2), ('Widget B', 2300, 3)]
Query 2 — GROUP BY region with AVG:
{"name":"query","arguments":{"query":"SELECT region, SUM(amount) as revenue, ROUND(AVG(amount), 2) as avg_order FROM read_csv_auto('/tmp/test_sales.csv') GROUP BY region"}}→ [('South', 4000, 1000.0), ('North', 6100, 1525.0)]
Gotchas
- Parameter name is
query, notsql— passingsqlreturns a validation error. --db-pathis required; point it at a temp file for ephemeral work.- Results come back as Python tuple-list strings, not JSON objects.
{ "server": "mcp-duckdb-server v1.27.2 (DuckDB v1.5.3)", "launch": "uvx mcp-server-duckdb --db-path /tmp/test_duck2.db", "transport": "stdio (NDJSON)", "tools": [ { "name": "query", "params": { "query": "string (required)" } } ], "trace": { "initialize": { "send": { "method": "initialize", "params": { "protocolVersion": "2024-11-05", "clientInfo": { "name": "pathfinder", "version": "1.0" } } }, "recv": { "serverInfo": { "name": "mcp-duckdb-server", "version": "1.27.2" }, "protocolVersion": "2024-11-05" } }, "tools_list": { "send": { "method": "tools/list" }, "recv_tools": ["query"] }, "query_1_aggregate_by_product": { "send": { "method": "tools/call", "params": { "name": "query", "arguments": { "query": "SELECT product, SUM(amount) as total_sales, COUNT(*) as num_orders FROM read_csv_auto('/tmp/test_sales.csv') GROUP BY product ORDER BY total_sales DESC" } } }, "recv": { "content": [ { "type": "text", "text": "[('Widget A', 4500, 3), ('Widget C', 3300, 2), ('Widget B', 2300, 3)]" } ], "isError": false } }, "query_2_aggregate_by_region": { "send": { "method": "tools/call", "params": { "name": "query", "arguments": { "query": "SELECT region, SUM(amount) as revenue, ROUND(AVG(amount), 2) as avg_order FROM read_csv_auto('/tmp/test_sales.csv') GROUP BY region" } } }, "recv": { "content": [ { "type": "text", "text": "[('South', 4000, 1000.0), ('North', 6100, 1525.0)]" } ], "isError": false } }, "query_3_version": { "send": { "method": "tools/call", "params": { "name": "query", "arguments": { "query": "SELECT version() as duckdb_version" } } }, "recv": { "content": [ { "type": "text", "text": "[('v1.5.3',)]" } ], "isError": false } } } }