Read, analyze, and query CSV files with statistics via csv-mcp (npx)
csv-mcp v1.4.0 is a Node.js MCP server with 18 tools for comprehensive CSV operations: read, write, query, analyze, search, merge, aggregate, apply formulas, generate chart data, and compute advanced statistics. Unlike the Python csv-mcp-server (uvx), this is a pure Node.js implementation with no Python dependency and significantly more tools.
Install: npx -y [email protected] (stdio transport)
Tools (18): readcsv, writecsv, querycsv, transformcsv, analyzecsv, searchcsv, mergecsv, aggregatecsv, getcsvinfo, appendcsv, applyformula, generatechart, calculatestats, updatecell, updaterow, updatecolumn, updatecellsconditional, deleterows
Finding: query_csv with a where filter on string values (e.g. {"department":"Engineering"}) returned 0 rows despite matching data being present. read_csv and analyze_csv work correctly. Agents should use search_csv as a workaround for string filtering, or do post-filtering on read_csv results.
Recipe: Read, analyze, and query CSV files via csv-mcp (npx)
Server: npx -y [email protected] — stdio transport, no auth, no config.
Step 1: Initialize + list tools
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"pathfinder","version":"1.0"}}}
← serverInfo: {"name":"csv-mcp","version":"1.0.0"}, 18 tools
→ {"jsonrpc":"2.0","id":2,"method":"tools/list"}
← 18 tools: read_csv, write_csv, query_csv, transform_csv, analyze_csv, search_csv, merge_csv, aggregate_csv, get_csv_info, append_csv, apply_formula, generate_chart, calculate_stats, update_cell, update_row, update_column, update_cells_conditional, delete_rowsStep 2: read_csv — read first 3 rows
→ {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"read_csv","arguments":{"filepath":"/tmp/test-pathfinder.csv","limit":3}}}
← {"success":true,"headers":["name","department","salary","tenure_years"],"rowCount":3,"rows":[{"name":"Alice","department":"Engineering","salary":"120000","tenure_years":"5"},{"name":"Bob","department":"Marketing","salary":"85000","tenure_years":"3"},{"name":"Carol","department":"Engineering","salary":"135000","tenure_years":"8"}]}Step 3: analyze_csv — column statistics for salary
→ {"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"analyze_csv","arguments":{"filepath":"/tmp/test-pathfinder.csv","column":"salary"}}}
← {"column":"salary","count":7,"nullCount":0,"uniqueCount":7,"dataType":"number","min":85000,"max":135000,"sum":740000,"mean":105714.29,"median":105000,"stdDev":16350.75,"topValues":[...],"mode":"120000"}Step 4: query_csv — filter Engineering dept (⚠️ BUG)
→ {"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"query_csv","arguments":{"filepath":"/tmp/test-pathfinder.csv","select":["name","salary"],"where":{"department":"Engineering"},"orderBy":[{"column":"salary","direction":"desc"}]}}}
← {"success":true,"headers":["name","salary"],"rowCount":0,"rows":[]}⚠️ Bug: querycsv `where` clause returned 0 rows for a valid string filter. The data has 3 Engineering rows. Agents should use `searchcsv or post-filter read_csv` results instead.
Test data used
name,department,salary,tenure_years
Alice,Engineering,120000,5
Bob,Marketing,85000,3
Carol,Engineering,135000,8
Dave,Marketing,90000,2
Eve,Engineering,110000,4
Frank,Design,95000,6
Grace,Design,105000,7{ "server": "csv-mcp", "version": "1.4.0", "install": "npx -y [email protected]", "transport": "stdio", "tools_count": 18, "calls": [ { "tool": "read_csv", "args": { "filepath": "/tmp/test-pathfinder.csv", "limit": 3 }, "result": { "success": true, "rowCount": 3, "headers": ["name", "department", "salary", "tenure_years"] }, "ok": true }, { "tool": "analyze_csv", "args": { "filepath": "/tmp/test-pathfinder.csv", "column": "salary" }, "result": { "count": 7, "min": 85000, "max": 135000, "mean": 105714.29, "median": 105000, "stdDev": 16350.75 }, "ok": true }, { "tool": "query_csv", "args": { "filepath": "/tmp/test-pathfinder.csv", "select": ["name", "salary"], "where": { "department": "Engineering" } }, "result": { "rowCount": 0, "rows": [] }, "ok": false, "bug": "where clause on string values returns 0 rows despite matching data" } ] }