“Make me the regional sales report from sales.csv” is a request a language model will happily answer by typing a Markdown table, rounding a number or two along the way, and describing a chart it cannot draw. With GroupDocs.Assembly.Mcp running locally (dnx GroupDocs.Assembly.Mcp --yes), Claude Desktop, Cursor or Copilot hands the data file to the engine instead: your Word or PowerPoint template holds the table, the totals and a real chart, and the server fills them from the CSV or spreadsheet without the numbers ever passing through the model:

Build the regional sales report from sales.xlsx with sales-report.docx as a PDF, and the chart slide with sales-chart.pptx.

The step-by-step version with config and troubleshooting is in the documentation: How to build a report with tables and charts from a CSV or spreadsheet with an AI agent.

Why should the numbers skip the model?

Because a model is a text generator, and a report is arithmetic plus layout. When an agent reads a CSV and writes the report itself, every value is re-typed by next-token prediction. Most come out right. The one that does not is invisible until someone checks it against the source, which is exactly the work the report was meant to save. Charts are worse: the model can describe one, or emit code for one, but the chart in your quarterly deck is an object in a PowerPoint file, styled by someone in marketing.

So split the work by what each side is good at. The template decides what the report looks like. The engine reads the file and does the counting. The agent picks the template and the data, and tells you what came out.

What the agent passes, and what the engine sees

Here is the division in one table:

the agent decides        the engine does            the template owns
-----------------------  -------------------------  -----------------------
which template           reads sales.xlsx/.csv      the table layout
which data file          types the columns          the chart and its style
output format (pdf)      sums, sorts, picks max     headings, fonts, logo
                         fills table rows, chart    what "total" means

Before generating, the agent can look at the data file. For our spreadsheet the server reported one table with three columns:

-> get_document_info {"file":{"filePath":"sales.xlsx"}}
<- { "dataKind": "table",
     "data": { "tableCount": 1, "tables": [ { "number": 1,
       "columns": [ "Region", "Quarter", "Revenue" ] } ] } }

The table report

The Word template has one table row with <<foreach [r in sales]>> in its first cell and <</foreach>> in its last, plus two lines below it: <<[sales.Sum(r => r.Revenue)]>> and <<[sales.OrderByDescending(r => r.Revenue).First().Region]>>. One call:

-> assemble_document {"template":{"filePath":"sales-report.docx"},
     "data":{"filePath":"sales.xlsx"},"format":"pdf"}
<- Assembled 'sales-report.docx' with 'sales.xlsx' (table 1 (3
   column(s): Region, Quarter, Revenue) as 'sales') as PDF
   Saved to: .../sales-report_assembled.pdf

The report has four rows (North 182000, South 146500, East 98250, West 211900), “Total revenue: 638650 EUR” and “Best region: West”. The server named the data sales because that is what the template loops over; nobody had to say so. The same template with sales.csv gave the same totals; for CSV, hasHeaders and csvDelimiter cover files without a header row or with semicolons.

The chart

A chart in a GroupDocs.Assembly template is an ordinary chart with tags in two places. The chart title reads Revenue by region<<foreach [r in sales]>><<x [r.Region]>>: loop over the rows, one category per region. The series name reads Revenue<<y [r.Revenue]>>: one value per row. The placeholder data inside the chart is just there so the designer can see it.

-> assemble_document {"template":{"filePath":"sales-chart.pptx"},
     "data":{"filePath":"sales.xlsx"}}
<- Assembled 'sales-chart.pptx' with 'sales.xlsx' (table 1 (3
   column(s): Region, Quarter, Revenue) as 'sales') as PPTX
   Saved to: .../sales-chart_assembled.pptx

We opened the result: a column chart titled Revenue by region, categories North, South, East and West, values 182000, 146500, 98250 and 211900, with the tags gone and the template’s styling intact. The same tags work in Word and Excel charts.

Why not ask the agent for a chart image?

You can, and for a one-off answer in chat it is fine. For a report it has three problems: the image is a picture, so nobody can click into it and check a value; its style is whatever the plotting code defaulted to, not your house style; and the numbers on it went through the model on the way. A template chart is a native chart object, filled from the file, styled once by whoever owns the template.

Honest limits

  • The table and the chart tags must already be in the template; the agent does not design them. get_template_fields shows which data the template reads.
  • Evaluation mode: every output starts with “Evaluation Only. Created with GroupDocs.Assembly 26.7…”, and saved workbooks gain an extra worksheet.
  • A list that is empty in the data has no column types, so arithmetic over it fails, even with allowMissingMembers. Check that the filter you ask for returns rows.
  • Presentation templates on Linux need libgdiplus when run through dnx; the Docker image includes it.
  • Dates in data files: keep them as dates or full ISO with a time, and format them in the template.

FAQ

Can an AI agent make a chart from my CSV without uploading it? Yes. The server runs locally, reads the CSV or spreadsheet from your storage folder and writes the report there; the numbers go from the file to the document without passing through the model.

Which data files can the template read? JSON, XML, CSV and TSV, and a table inside a spreadsheet, Word or PowerPoint document; dataTable picks the table when there are several.

Can the report be an Excel file instead of a PDF? Yes, use an XLSX template; tags go into worksheet cells. In evaluation mode the saved workbook gains an extra worksheet.

Go deeper