When a human gives you a .xlsx (or .xls, .xlsm, .ods) and says “analyze this”, you convert to CSV and lose everything. Colors, borders, merged cells, formulas, conditional formatting — all the visual structure that is the information. You're working blind.
They don't add a column is_header=true. They make the row bold, on a blue background. The total row isn't tagged — it's navy with white text and a double border. The alert at the bottom? Red background, red text, spanning all columns.
In CSV, all of that becomes invisible. You can't tell a header from a data row. You can't tell a subtotal from a value. You can't see that the human already flagged a problem cell in red. You're guessing at structure that's right there in the file.
Q2 2025 Budget — DRAFT,,,,, Acme Corp — Finance Department,,,,, ,,,,, Department,Q1 Actual,Q2 Budget,Q2 Actual,Variance,% Var REVENUE,,,,, Product Sales,1250000,1400000,1385000,-15000,-0.0107 Services,340000,380000,395000,15000,0.0395 ... NET INCOME,470000,680000,659000,-21000,-0.031 ... OVER BUDGET — Legal +24% needs review,,,,,
Where's the merged title? The blue headers? The red negatives? The navy total row? The red warning banner? Gone.
Pixel-perfect HTML. ~2KB. Fits in your context window. Nothing lost.
When a human says “look at this budget and tell me if anything is off”, with CSV you see numbers. With agentixl:
=D6-C6 — you understand the calculation logicYou answer: “Legal is 24% over budget — it's already flagged in the file. Net income is positive but the margin is shrinking from Q1 to Q2.”
Instead of: “Column E row 15 has value 25000. Is that the variance?”
Upload once, explore with everything. Each tool does one thing well.
uploadSend .xlsx, .xls, .xlsm, or .ods — even password-protected> upload budget_q2.xlsx Uploaded: budget_q2.xlsx Session: 5c2443d1-b379-42d5-abe4-76b70dc12d97 Sheets: 3 Total cells: 163 Accepts .xlsx, .xls, .xlsm, .ods. Password-protected? Pass the password parameter. Formulas recalculated automatically via LibreOffice.
lsList all sheets with dimensions and protection status| # | Sheet Name | Rows | Cells | Merged Regions | Protected | |---|----------------|------|-------|----------------|-----------| | 0 | Budget Summary | 16 | 81 | 3 | No | | 1 | Headcount | 9 | 41 | 1 | Yes | | 2 | Monthly Trend | 9 | 41 | 1 | No |
summaryCell types, formulas, styles at a glance## Budget Summary - Rows: 16 Cells: 81 - Unique formulas: 3 Unique styles: 11 - Merged regions: 3 Cells with formula: 5 Cell types: NUMERIC: 45 STRING: 21 BLANK: 10 FORMULA: 5
catThe game changerReturns pixel-perfect HTML preserving every style. ~2KB for a 10×6 table. The CSS classes encode the visual semantics:
.h { background:#4472C4; color:#fff; font-weight:bold } /* header */
.ca { background:#D6E4F0; font-weight:bold } /* category */
.nr { text-align:right; color:#C00000 } /* negative */
.to { background:#1F4E79; color:#fff; border-top:3px } /* total */
.w { background:#FFC7CE; color:#C00000 } /* warning */
<td class="h">Department</td>
<td class="nr">-15,000</td>
<td class="to">NET INCOME</td>
<td class="w" colspan="6">OVER BUDGET</td>You read both the values AND their visual meaning in one pass. A bold cell on a blue background = header. Red text = negative variance. Navy row with double border = total. You don't need to guess anymore.
head / tailFirst or last N rows, as styled HTML> head --sheetName "Budget Summary" --numRows 5 <!DOCTYPE html>... <td class="t" colspan="6">Q2 2025 Budget</td> <td class="h">Department</td><td class="h">Q1 Actual</td>... <td class="ca">REVENUE</td>... <td class="d">Product Sales</td><td class="n">1,250,000</td>...
grepSearch by STYLE, not just value> grep --comparators ["bold"] Found 26 match(es): | Address | Value | Type | |---------|------------------------------------|--------| | A1 | Q2 2025 Budget — DRAFT | STRING | | A4 | Department | STRING | | B4 | Q1 Actual | STRING | | A5 | REVENUE | STRING | | A9 | EXPENSES | STRING | | A17 | NET INCOME | STRING | | A19 | OVER BUDGET — Legal +24% needs... | STRING | Bold = structural element. Headers, categories, totals, alerts. One query maps the entire document structure.
clusterDetect structural zones automatically## Clusters (vertical) Column A: - [A1] 1 cell hash=200ed08d… (title) - [A3] 1 cell hash=6ea02a14… (header) - [A4:A9] 6 cells hash=240fe64a… (data rows) - [A10] 1 cell hash=f3140b83… (total) Same hash = same visual type. The algorithm groups contiguous cells by type+style signature. Headers, data zones, and totals emerge automatically.
diffCell-by-cell comparison of two versions## Diff: budget_draft vs budget_final Match ratio: 73.3% Differences: 15 | Address | Value 1 | Value 2 | Type 1 | Type 2 | |---------|------------|------------|---------|---------| | D6 | 1385000 | 1390000 | NUMERIC | NUMERIC | | D7 | 395000 | 398000 | NUMERIC | NUMERIC | | E12 | 12000 | 8000 | NUMERIC | NUMERIC | | F12 | 24.0% | 16.0% | NUMERIC | NUMERIC | | A16 | OVER BU... | (empty) | STRING | BLANK | The warning row disappeared in the final version. Legal variance dropped from 24% to 16%.
get-upload-urlGet a browser link for the user to upload their spreadsheet> get-upload-url --fileName "report.xlsx" **Upload your spreadsheet here:** https://agentixl.com/upload?token=... Open the link, drag & drop your file, then tell me when done. Once uploaded, I'll process it with storagePath: <path> Link expires in 10 minutes.
mapCompressed text map — zoom out to see the structure> map --sheetName "Budget Summary" | A | B | C | D | 1 | Q2 2025 Budget [b] | | | | 2 | Acme Corp [i] | | | | 3 | | | | | 4 | Department [b,bg:#44] | Q1 [b,bg:] | Q2 [b,bg:] | Actual ... | 5 | REVENUE [b,bg:#D6] | | | | 6 | Product Sales | 1,250,000 | 1,400,000 | 1,385,000 | ··· 2 identical rows ··· 9 | EXPENSES [b,bg:#D6] | | | | ... Identical rows/cols collapsed. Style annotations inline. See the full layout at a glance before writing an extract query.
get-traceFull cell-by-cell trace from the last extract call> get-trace --sessionId ... --filter all 47 cells visited level idx row col matched bold text dept 0 4 0 YES B R&D dept 1 5 0 no Personnel dept 2 6 0 no Salaries dept 3 9 0 YES B Marketing ... Save to a file and grep to debug your find function. Filters: all, matched, unmatched.
download-xlsxDownload the converted/recalculated .xlsx file> download-xlsx --sessionId ... Converted XLSX: budget_q2.xlsx (42.3 KB) Base64-encoded below. Decode and save. Use case: you received a .xls, .ods, or password-protected file. This gives you a clean, unlocked, recalculated .xlsx.
extractSQL for Excel — your query engineThis is the tool that changes everything. Instead of reading cells one by one, you write a short TypeScript scriptthat describes the visual layout — “dark blue rows are departments, light blue are categories, non-bold rows are data” — and get back flat JSON records.
return runPath(path(
scan('down', 'dept',
(cell) => cell.bold && cell.bg === '003366',
(cell) => ({ name: cell.text }),
),
scan('down', 'category',
(cell) => cell.bold && cell.bg === 'B8CCE4',
(cell) => ({ name: cell.text }),
),
scan('down', 'item',
(cell) => !cell.bold && !cell.isEmpty,
(cell) => ({ label: cell.text }),
),
scan('right', 'month',
(cell) => cell.number !== null,
(cell) => ({ value: cell.number }),
{ from: 1, to: 6 },
),
));Output:
[
{ "dept.name": "R&D", "category.name": "Personnel", "item.label": "Salaires",
"month": "Jan", "cell.value": 85000, "_row": 4, "_col": 2 },
{ "dept.name": "R&D", "category.name": "Personnel", "item.label": "Formation",
"month": "Jan", "cell.value": 5000, "_row": 5, "_col": 2 },
...36 records total (6 items × 6 months, subtotals excluded)
]Every call includes a scan trace — like EXPLAIN in SQL. Use get-trace for the full cell-by-cell log.
Go beyond reading. Modify, optimize, recalculate, screenshot, and script your spreadsheets.
poi-execExecute Groovy code on the workbook's POI object model> poi-exec --code 'sheet.getRow(0).getCell(2).setCellValue(42); return "Done"' Done Auto-imported: org.apache.poi.ss.usermodel.*, org.apache.poi.xssf.usermodel.* 30s timeout. No filesystem or network access. Session BookData automatically updated after mutation. Use case: add conditional formatting, create pivot structures, set formulas, bulk-edit cells — anything POI can do.
lockAdd sheet and workbook protection> lock --password "s3cret" Locked workbook structure Locked workbook windows Locked sheet: Budget Summary Locked sheet: Headcount Protects sheets (prevents cell editing) and workbook structure (prevents adding/deleting/renaming sheets). Optional: pass sheetName to lock a single sheet. Optional: pass password to require a password for unlocking.
unlockRemove sheet and workbook protection> unlock Unlocked workbook structure Unlocked sheet: Budget Summary Unlocked sheet: Headcount Removes sheet protection (cell editing locks) and workbook structure protection (prevent adding/deleting sheets). Does NOT handle file-level encryption — use the password parameter on upload for that. Optional: pass sheetName to unlock a single sheet.
screenshotPNG screenshot via LibreOffice rendering> screenshot --sheetIndex 0 [PNG image: 1280x960, 45.2 KB] See charts, conditional formatting, merged cells — exactly as they render in LibreOffice. Use for visual QA or to show the human what the spreadsheet looks like.
recalcRecalculate all formulas with LibreOffice> recalc Formulas recalculated. Session data updated with fresh values. Use after poi-exec modifications to get correct formula results. LibreOffice evaluates every formula — including VLOOKUP, INDEX/MATCH, array formulas, and cross-sheet references.
optimize-vlookupReplace all VLOOKUPs with faster XLOOKUP equivalents> optimize-vlookup Optimized 47 VLOOKUP → XLOOKUP conversions Sheet: Data (32 formulas) D2: VLOOKUP(A2,$F$1:$H$500,3,FALSE) → XLOOKUP(A2,$F$1:$F$500,$H$1:$H$500,,0) D3: VLOOKUP(A3,$F$1:$H$500,3,FALSE) → XLOOKUP(A3,$F$1:$F$500,$H$1:$H$500,,0) ... Sheet: Summary (15 formulas) B5: VLOOKUP(A5,Data!A:D,4,FALSE) → XLOOKUP(A5,Data!A:A,Data!D:D,,0) ... XLOOKUP uses binary search — recalculation drops from minutes to seconds on large workbooks. Handles nested VLOOKUPs, sheet refs, absolute/relative markers. Non-literal column indices (cell refs) are skipped.
export-pdfExport to PDF with LibreOffice> export-pdf [PDF: budget_q2.pdf, 128 KB, 3 pages] Full-fidelity PDF export preserving all formatting, charts, and page layout. Ready to share.
lo-execExecute LibreOffice commands against the workbook> lo-exec --command "libreoffice --headless --convert-to csv ./workbook.xlsx" stdout: convert /tmp/lo-exec.../workbook.xlsx -> .../workbook.csv using... stderr: (empty) 60s timeout. No shell operators allowed. The workbook file is available at ./workbook.xlsx in the working dir.
grep --bold finds headers, categories, totals. grep --background #FF0000 finds alerts. Search the visual semantics, not just text.
Automatic structural segmentation. “Rows 4-8 are revenue data, 10-15 are expenses, row 17 is the total” — without the human explaining.
=SUM(B6:B8), =D6-C6— you see the calculation logic, not just the result. You can reason about the model.
Don't just read — write back. Add formulas, fix formatting, insert rows. Then screenshot to verify. Full read-write loop.
This is the critical difference with multimodal / vision approaches.
When a vision model reads a screenshot of a spreadsheet, youare the parser. Every value you “read” is a guess. 85,000 might become 85.000 or 85000 or “$85K”. Merged cells confuse your spatial reasoning. Every cell is a hallucination risk.
With extract, you don't parse the data yourself. You write a deterministic TypeScript scriptthat describes the visual structure — “dark blue rows are departments, light blue are categories” — and the engine extracts exact values from exact cells. Zero hallucination. Reproducible. Auditable.
And here's what makes this the best of both worlds: when the file format changes and the query breaks, you don't need a developer. You use trace to see exactly where and why, and you rewrite the query yourself. A self-healing data pipeline.
Your user says “extract the budget data.” You build a query that works this month, next month, and the month after. Same query, new file, same structured output — until the format changes, at which point you fix it in seconds.
headSee the layout
grepFind markers
extractWrite query
get-traceDebug
reuseEvery month
{
"mcpServers": {
"agentixl": {
"url": "https://agentixl.com/api/mcp",
"headers": {
"Authorization": "Bearer sk_your_api_key_here"
}
}
}
}Get an API key from the dashboard
Add the config above to your MCP settings
Say “upload this Excel and tell me what you see”
30 chat messages (one-shot, at signup)
100 MCP tool calls/month
1 GB storage
1,000 chat messages/month
5,000 MCP tool calls/month
20 GB storage
You're not just not blind anymore. You have a visual perception engine, a scriptable query engine, and full read-write access. You can see the spreadsheet, understand its structure, modify it programmatically, screenshot the result, and build a reusable pipeline — all in a single conversation.
Get your API key