Can Claude load a CSV into a database?
Only into a database you give it: Claude reads a CSV you upload but has no database of its own, and Claude Code can script a load into one you run. Connect the superagnt_ MCP server and Claude uploads the file and loads it into a hosted Postgres table, column types inferred.
Load next week's export into the same table and rows with a matching key update in place instead of piling up as copies. In the run below: 5 rows, then 4 more with 2 repeats, and the table held 7, not 9.
Connect superagnt_ to Claude
Copy the setup prompt.
Your agent does the rest.
Install superagnt for me. It's the superagnt plugin from the github.com/superagnt/plugins marketplace, plus the superagnt MCP server (on Claude it's a connector I add in my browser; elsewhere I sign in through my browser). The setup guide for each kind of agent is at https://www.superagnt.com/install» the base connection already serves your workspace database and files. Nothing to switch on.
What Claude does alone, and what changes once it's connected
| job | Claude on its own | with superagnt_ |
|---|---|---|
| Read the file | Upload it and Claude reads it in the chat. | Claude puts it in your workspace files with agnt_files_upload, or uses a file you uploaded. |
| Load it | No database of its own. Claude Code can write and run a load script against a database you already have. | agnt_db_write with load_csv streams the file into a table, 1,000 rows a batch, and creates the table when you ask, typing each column from the first 200 rows. |
| Load it again next week | Whatever your script does with repeats. | Name a key column (on_conflict) and rows that match update in place, so a re-import adds no copies. |
| Use the data | Run code on the uploaded file in that chat. | SQL over the table from any chat or client, and dashboards on it. |
The job, run on superagnt_
Real tool calls on superagnt_'s staging environment, the same calls Claude makes once the server is connected. Each result is what the client received, trimmed where it says so and otherwise unchanged, and each wall time was measured around the call.
“Load this week's signups CSV into a signups table, keyed on email. Next week: load the new export into the same table.”
- 01Upload the CSV to the workspace860 ms wall time→ call
agnt_files_upload{ "file_name": "signups-week-43.csv", "mime_type": "text/csv", "content": "name,email,company,title,signed_up\nAda Park,ada@northwind.example,Northwind,Head of Growth,2026-10-19\nBen Ito,ben@tailspin.example,Tailspin,Founder,2026-10-20\nCara Diaz,cara@fabrikam.example,Fabrikam,VP Sales,2026-10-20\nDev Shah,dev@contoso.example,Contoso,RevOps Lead,2026-10-21\nEli Moss,eli@litware.example,Litware,CEO,2026-10-22\n" }← result{ "file": { "id": "0cdab729-bba8-4256-af3b-6179b1db4372", "file_name": "signups-week-43.csv", "mime_type": "text/csv", "byte_size": 332, "status": "ready", … "created_at": "2026-10-26T12:41:55.720644+00:00" } }» Trimmed: internal ids on the file record.
- 02Load it into a new table1.4 s wall time→ call
agnt_db_write{ "action": "load_csv", "file_id": "0cdab729-bba8-4256-af3b-6179b1db4372", "table": "signups", "create_if_missing": true, "on_conflict": "email" }← result{ "file_id": "0cdab729-bba8-4256-af3b-6179b1db4372", "table": "signups", "table_created": true, "dry_run": false, "inferred_schema": [ { "source_column": "name", "target_column": "name", "type": "text", "nullable": true }, { "source_column": "email", "target_column": "email", "type": "text", "nullable": true }, { "source_column": "company", "target_column": "company", "type": "text", "nullable": true }, { "source_column": "title", "target_column": "title", "type": "text", "nullable": true }, { "source_column": "signed_up", "target_column": "signed_up", "type": "timestamptz", "nullable": true } ], "total_rows_processed": 5, "rows_upserted": 5, "rows_skipped": 0, "errors": [] } - 03Upload next week's export629 ms wall time→ call
agnt_files_upload{ "file_name": "signups-week-44.csv", "mime_type": "text/csv", "content": "name,email,company,title,signed_up\nCara Diaz,cara@fabrikam.example,Fabrikam,CRO,2026-10-20\nDev Shah,dev@contoso.example,Contoso,RevOps Lead,2026-10-21\nFay Lund,fay@adatum.example,Adatum,Marketing Director,2026-10-24\nGus Hale,gus@proseware.example,Proseware,COO,2026-10-25\n" }← result{ "file": { "id": "c7bcefca-578f-458e-b01e-e485ce57ce5a", "file_name": "signups-week-44.csv", "mime_type": "text/csv", "byte_size": 272, "status": "ready", … "created_at": "2026-10-26T12:42:08.671983+00:00" } }» Trimmed: internal ids on the file record.
- 04Load next week's export into the same table1.0 s wall time→ call
agnt_db_write{ "action": "load_csv", "file_id": "c7bcefca-578f-458e-b01e-e485ce57ce5a", "table": "signups", "on_conflict": "email" }← result{ "file_id": "c7bcefca-578f-458e-b01e-e485ce57ce5a", "table": "signups", "table_created": false, "dry_run": false, "total_rows_processed": 4, "rows_upserted": 4, "rows_skipped": 0, "errors": [] } - 05Read the table back364 ms wall time→ call
agnt_db_read{ "action": "select", "table": "signups", "columns": [ "name", "email", "title", "signed_up" ], "order": "signed_up.asc" }← result{ "rows": [ { "name": "Ada Park", "email": "ada@northwind.example", "title": "Head of Growth", "signed_up": "2026-10-19T00:00:00+00:00" }, … { "name": "Cara Diaz", "email": "cara@fabrikam.example", "title": "CRO", "signed_up": "2026-10-20T00:00:00+00:00" }, … ] }» Trimmed: five of the seven rows (Ben, Dev, Eli, Fay and Gus).
Five calls, 4.3 seconds. The first load created the table and typed signed_up as a timestamp; the second loaded 4 rows, of which 2 were people already in the table (Cara's new title replaced the old one) and 2 were new. The table ended with 7 rows, not 9.
Set it up in your client
Claude
- 01Open the prefilled Add custom connector dialog on claude.ai. One connector covers Claude on the web, Claude Desktop and Cowork.
- 02Click Add, then approve on the superagnt_ consent screen. It creates your superagnt_ account if you don't have one.
- 03Ask: “Load the CSV I just attached into a table called signups, keyed on email.”
Free plans can add one custom connector. On Team and Enterprise plans, an owner adds it under Organization settings, Connectors (Anthropic).
Claude Code
- 01In Claude Code, run
/plugin marketplace add superagnt/plugins, then/plugin install superagnt@superagntfor the superagnt_ skills. - 02Add superagnt_ to Claude as a connector with the prefilled dialog and click Add. Claude Code signed in with your Claude account picks it up.
- 03Start a new session.
- 04Ask: “Load the CSV I just attached into a table called signups, keyed on email.”
ChatGPT
- 01In ChatGPT on the web, go to Plugins, select the plus button, then Add custom MCP server.
- 02Name it superagnt, enter the server URL under Connection, and set Authentication to OAuth. The server URL is
https://mcp.superagnt.com/mcp. - 03Tick "I understand and want to continue", then select Create as a plugin. Sign in to superagnt_ when asked. No token needed.
- 04Install the plugin, then start a new chat, type @ and select superagnt.
- 05Ask: “Load the CSV I just attached into a table called signups, keyed on email.”
ChatGPT takes custom MCP servers on the web, not on mobile, and only Business and Enterprise/Edu workspaces get write actions. On Pro, connected servers are read and fetch only, so loading a CSV needs one of those plans in ChatGPT (OpenAI).
Codex
- 01In a terminal, run
codex mcp add superagnt --url https://mcp.superagnt.com/mcp, thencodex mcp login superagnt. The login opens a browser to approve. - 02Start a new Codex session.
- 03Ask: “Load the CSV I just attached into a table called signups, keyed on email.”
Repeats across loads update. Repeats inside one file don't load
Each batch of up to 1,000 rows goes in as one upsert, and Postgres refuses an upsert that touches the same row twice. In our run, a three-row file that listed one person twice loaded 0 rows, the valid one included, and reported ON CONFLICT DO UPDATE command cannot affect row a second time. So have Claude drop repeated keys from the file before it loads. Across loads there's no such catch: a key already in the table just updates its row.
Not sure what the file holds? "dry_run": true previews about 10 parsed rows without writing anything, and for a new table it shows the columns it would create.
Common questions
01Can Claude load a CSV into a database?
Only into a database you give it: Claude reads a CSV you upload but has no database of its own. With the superagnt_ MCP server connected, Claude uploads the file and loads it into a hosted Postgres table, column types inferred.
02How big a CSV can it load?
load_csv streams the file in batches of up to 1,000 rows, so the limit is your plan's row allowance across all tables: Starter 25,000, Growth 250,000 and Scale 2,500,000. A load can run past it; after 24 hours over, new rows are refused until usage is back under the allowance or the plan goes up.
03Does it detect column types?
When the table doesn't exist yet and you pass create_if_missing, it samples the first 200 or so rows and types each column as numeric, boolean, timestamptz or text. Otherwise it loads into the table you already have.
04Can I rename columns on the way in?
Yes. column_mapping maps CSV headers to column names, and headers left out of the map are dropped. Without a map, headers become column names as they are, in snake case.
05Which values load as empty?
By default an empty cell, NA, N/A, null, NULL and NaN load as SQL NULL. null_values changes the list.
What else Claude can do with superagnt_
- Can Claude search Twitter?
- Can Claude read Reddit threads?
- Can Claude watch YouTube videos?
- How do I get a YouTube transcript into Claude?
- Can Claude find email addresses?
- Can Claude save data between chats?
- Can Claude run tasks on a schedule?
- Can Claude make a dashboard?
Go deeper: Postgres database for AI agents · Workspace database docs · Data pipelines docs