AI agents and LLM integration

GroupDocs.Assembly for Python via .NET is designed to work with AI agents, LLMs, and code generation tools. The library ships machine-readable documentation in several formats, including an AGENTS.md file inside the pip package itself, so that AI assistants can discover and use the API without manual guidance.

Built into the package

The groupdocs-assembly-net pip package includes an AGENTS.md file at groupdocs/assembly/AGENTS.md. AI coding assistants that scan installed packages (such as Claude Code, Cursor, and GitHub Copilot) can read it to learn the imports, the template-and-data workflow, the data sources, the template syntax, licensing, platform requirements, and troubleshooting tips. Its full text is reproduced below.

After installing the package, you can find it with:

pip show -f groupdocs-assembly-net | grep AGENTS

MCP server

GroupDocs provides an MCP (Model Context Protocol) server that lets LLMs query the documentation on demand instead of loading it all at once. This saves tokens and lets your AI assistant fetch only what it needs for the current task.

To connect your AI tool to the MCP server, add the GroupDocs endpoint to your MCP configuration:

// Claude Code:    ~/.claude/settings.json (or project .mcp.json)
// Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "groupdocs-docs": {
      "url": "https://docs.groupdocs.com/mcp"
    }
  }
}
// .vscode/mcp.json in your project root
{
  "servers": {
    "groupdocs-docs": {
      "url": "https://docs.groupdocs.com/mcp"
    }
  }
}
// .cursor/mcp.json in your project root
{
  "mcpServers": {
    "groupdocs-docs": {
      "url": "https://docs.groupdocs.com/mcp"
    }
  }
}
// Any MCP-compatible client
{
  "mcpServers": {
    "groupdocs-docs": {
      "url": "https://docs.groupdocs.com/mcp"
    }
  }
}

See https://docs.groupdocs.com/mcp for full setup instructions and the list of available tools.

Machine-readable documentation

Every documentation page is available as a plain Markdown file that AI tools can fetch and process directly:

ResourceURL
Full documentation (single file)https://docs.groupdocs.com/assembly/python-net/llms-full.txt
Full documentation (all products)https://docs.groupdocs.com/llms-full.txt
Individual page (any page)Append .md to the page URL
First reporthttps://docs.groupdocs.com/assembly/python-net/hello-world.md

How to use with AI tools

Point your AI assistant to the full documentation file for comprehensive context:

Fetch https://docs.groupdocs.com/assembly/python-net/llms-full.txt and use it
as a reference for GroupDocs.Assembly for Python via .NET API.

Why GroupDocs.Assembly is a good building block for AI pipelines

An agent’s answer is often structured data: a summary, a list of findings, a table of figures. People expect it as a document - a Word report, a spreadsheet, a PDF, an email. GroupDocs.Assembly turns that data into the document without asking the model to lay it out:

  • Keep the layout in a template: the branding, the tables and the styles are written once, in Word, Excel, PowerPoint or HTML, and never generated by the model.
  • Pass the model’s output as it is: a dict parsed from the model’s JSON is a data source; its keys are the template’s tags.
  • Any document, any format: tables, lists, charts and email messages, saved as DOCX, XLSX, PPTX, PDF, HTML or MSG.
  • Deterministic and local: the same data gives the same document, with no network calls.

A typical last step of an agent looks like this:

import json
from datetime import datetime

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

# The structured output of a model, as JSON (here a literal)
answer = json.loads("""{
  "Title": "Supplier review",
  "Customer": "Quayside Bistro",
  "Summary": "Deliveries were on time in 9 of 10 weeks; one cold-chain delay in March.",
  "Findings": [
    {"Topic": "Delivery", "Text": "One cold-chain delay, 5 March", "Priority": "High"},
    {"Topic": "Pricing", "Text": "Coffee price stable since January", "Priority": "Low"}
  ],
  "NextSteps": ["Confirm the March credit note", "Review the cheese order volume"]
}""")
answer["Date"] = datetime(2015, 4, 1)

# The template keeps the layout; the agent's data fills it, and the report is a PDF
DocumentAssembler().assemble_document("agent-findings.docx", "agent-findings.pdf", DataSourceInfo(answer, "report"))
print("Findings in the report:", len(answer["Findings"]))

The following sample file is used in this example: agent-findings.docx

For end-to-end examples, see the Developer Guide. Every code example has a runnable counterpart in the examples repository.

Note
Without a license, every report carries an evaluation mark and stops after about 200 paragraphs. Set the GROUPDOCS_LIC_PATH environment variable before an agent runs these steps. See Licensing and evaluation.

Pair with other GroupDocs products

For richer AI document pipelines, combine GroupDocs.Assembly with:

AGENTS.md reference

The full AGENTS.md shipped inside the package:

# GroupDocs.Assembly for Python via .NET -- AGENTS.md

> Instructions for AI agents working with this package.

Document automation and report generation -- fill Word, Excel, PowerPoint, OpenDocument, email, HTML, Markdown and text templates (documents with tags such as `<<[customer.Name]>>`) with data from Python dicts and lists, JSON, XML and CSV files, or the tables of other documents, and save the report in the template's format or another one, such as PDF. No MS Office or other software installed.

## Install

```bash
pip install groupdocs-assembly-net
```

Each release's wheels are also on https://releases.groupdocs.com/assembly/python-net/ for offline installs (`pip install <wheel file>`).

**Python**: 3.5 - 3.14 | **Platforms**: Windows x64, Linux x64 (glibc 2.27+), macOS 12+ (Intel and Apple Silicon)

## Resources

| Resource | URL |
|---|---|
| Documentation | https://docs.groupdocs.com/assembly/python-net/ |
| LLM-optimized docs | https://docs.groupdocs.com/assembly/python-net/llms-full.txt |
| API reference | https://reference.groupdocs.com/assembly/python-net/ |
| Code examples | https://github.com/groupdocs-assembly/GroupDocs.Assembly-for-Python-via-.NET |
| Release notes | https://releases.groupdocs.com/assembly/python-net/release-notes/ |
| PyPI | https://pypi.org/project/groupdocs-assembly-net/ |
| Free support forum | https://forum.groupdocs.com/c/assembly/ |
| Temporary license | https://purchase.groupdocs.com/temporary-license |

## MCP Server

If your environment has MCP configured, you can connect your AI tool to the GroupDocs documentation server for on-demand API lookups:

```json
{
  "mcpServers": {
    "groupdocs-docs": {
      "url": "https://docs.groupdocs.com/mcp"
    }
  }
}
```

Works with Claude Code (`~/.claude/settings.json`), Cursor (`.cursor/mcp.json`), VS Code Copilot (`.vscode/mcp.json`), and any MCP-compatible client. If MCP is unavailable, fall back to the LLM-optimized docs URL above and this file -- both are shipped inside the wheel.

## Imports

```python
from groupdocs.assembly import (
    DocumentAssembler, DataSourceInfo, DocumentAssemblyOptions,
    LoadSaveOptions, FileFormat, OoxmlCompliance,
    License, Metered, LicenseError, GroupDocsAssemblyException,
)
from groupdocs.assembly.data import (
    JsonDataSource, JsonDataLoadOptions, JsonSimpleValueParseMode,
    XmlDataSource, XmlDataLoadOptions,
    CsvDataSource, CsvDataLoadOptions,
    DocumentTable, DocumentTableOptions, DocumentTableSet,
)
```

## The core workflow

A template is a document whose tags name data sources. Pass each source in a `DataSourceInfo` with the name the template uses; `assemble_document` fills the template and writes the report in the format of its extension:

```python
from groupdocs.assembly import DataSourceInfo, DocumentAssembler

# hello-world.docx holds: Hello, <<[customer.Name]>>! Your order of <<[customer.Product]>> is on its way.
customer = {"Name": "Quayside Bistro", "Product": "Harbor Blend whole bean coffee"}

assembler = DocumentAssembler()
assembler.assemble_document("hello-world.docx", "hello-world-report.docx", DataSourceInfo(customer, "customer"))
assembler.assemble_document("hello-world.docx", "hello-world-report.pdf", DataSourceInfo(customer, "customer"))  # same template, PDF
```

- `assemble_document(template, report, sources)` -- `sources` is one `DataSourceInfo` or a list of them. Returns `True` (`False` only with `INLINE_ERROR_MESSAGES` and a template error).
- `assemble_document(template, report, LoadSaveOptions(...), sources)` -- the options go before the sources.
- Paths or binary streams: `open(path, "rb")` / `open(path, "wb")` / `io.BytesIO`, both sides the same kind (no path + stream mix).

## Data sources

| Data | Pass | The template sees |
|---|---|---|
| dict, list of dicts, nested | `DataSourceInfo(data, "name")` | typed objects: `customer.Name`, `o.Price`, `o.OrderDate.Month` |
| str, int, float, `Decimal`, bool, `datetime`, `date`, list of str | `DataSourceInfo(value, "name")` | the value |
| JSON file | `json.load` -> dict/list (dates: convert the strings with `datetime.strptime`), or `JsonDataSource("file.json")` | dict: Python's types; `JsonDataSource`: nullable values -- write `o.OrderDate.Value.Month` |
| XML file | `XmlDataSource("file.xml")` | nullable values, like `JsonDataSource` |
| CSV file | `CsvDataSource("file.csv", CsvDataLoadOptions(True))` (first line = column names) | rows |
| a table of a Word/Excel/PowerPoint file | `DocumentTable(path, index, DocumentTableOptions())` | rows; `table.columns["Price"].type = float` to make a column numeric |
| several related tables of one file | `DocumentTableSet(path)` + `tables.relations.add(parent_col, child_col)` | tables by name: `tables.tables["MANAGER"]` |
| database rows | turn them into dicts (`sqlite3.Row` -> `dict(row)`) | as dicts |

```python
import json
from datetime import datetime

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

def parse_dates(item):
    # JSON has no dates: convert the date strings the template reads as dates
    for key in ("OrderDate", "ShippingDate"):
        if key in item:
            item[key] = datetime.strptime(item[key], "%Y-%m-%dT%H:%M:%S")
    return item

with open("customers.json", encoding="utf-8") as f:
    customers = json.load(f, object_hook=parse_dates)

DocumentAssembler().assemble_document("orders-by-customer.docx", "orders-by-customer.pdf", DataSourceInfo(customers, "customers"))
```

## Template syntax (language-neutral; the same as in the .NET product)

| Tag | Does |
|---|---|
| `<<[customer.Name]>>` | writes a value; members by dot, dict keys as members |
| `<<[o.Price]:"0.00">>`, `<<[o.OrderDate]:"d MMMM yyyy">>`, `<<[name]:upper>>`, `:caps`, `:firstCap` | formats |
| `<<foreach [o in orders]>>...<</foreach>>` | repeats content; in a table, one row from the cell that opens it to the cell that closes it |
| `<<if [o.Price > 400]>>...<<else>>...<</if>>` | conditional content |
| `<<[orders.Sum(o => o.Price)]>>`, `Count()`, `Where(...)`, `OrderBy(...)`, `GroupBy(...)` | sequence operations |
| `<<var [total = 0]>>` | variables |
| `<<image [customer.Photo]>>` in a text box, `<<barcode [code] -qr>>` | images (path, bytes, Base64, URL), barcodes |
| `<<link [uri] [text]>>`, `<<bookmark [name]>>`, `<<doc [path]>>` | hyperlinks, bookmarks, inserted documents (`-build` fills their tags) |
| `<<x [...]>>`, `<<y [...]>>`, `<<size [...]>>` in a chart title or series | chart data |

## Operations

### A table report from a list of dicts

```python
from groupdocs.assembly import DataSourceInfo, DocumentAssembler

orders = [
    {"Number": 10001, "Product": "Harbor Blend whole bean coffee", "Quantity": 30, "Price": 552.0},
    {"Number": 10004, "Product": "Aged harbour cheddar", "Quantity": 8, "Price": 132.0},
]
DocumentAssembler().assemble_document("order-table.docx", "order-table-report.docx", [
    DataSourceInfo("Quayside Bistro", "customer"),
    DataSourceInfo(orders, "orders"),
])
```

### A spreadsheet with typed cells

```python
import json

from groupdocs.assembly import DataSourceInfo, DocumentAssembler, DocumentAssemblyOptions

with open("products.json", encoding="utf-8") as f:
    products = json.load(f)

assembler = DocumentAssembler()
assembler.options = DocumentAssemblyOptions.USE_SPREADSHEET_DATA_TYPES   # numbers as number cells, not text
assembler.assemble_document("stock-sheet.xlsx", "stock-sheet-report.xlsx", DataSourceInfo(products, "products"))
```

### An email message

```python
import json

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

with open("customers.json", encoding="utf-8") as f:
    customers = json.load(f)

DocumentAssembler().assemble_document("in-table-list.msg", "in-table-list-report.msg", [
    DataSourceInfo(customers, "customers"),
    DataSourceInfo("Harbor Lane Provisions <orders@harborlane.example>", "sender"),
    DataSourceInfo(["Quayside Bistro <orders@quaysidebistro.example>"], "recipients"),
    DataSourceInfo("amara.osei@harborlane.example", "cc"),
    DataSourceInfo("Orders by customer", "subject"),
])
```

### A document table as the data

```python
from groupdocs.assembly import DataSourceInfo, DocumentAssembler
from groupdocs.assembly.data import DocumentTable, DocumentTableOptions

options = DocumentTableOptions()
options.first_row_contains_column_names = True
contracts = DocumentTable("contracts.xlsx", 0, options)
DocumentAssembler().assemble_document("table-row.docx", "table-row-report.docx", DataSourceInfo(contracts, "contracts"))
```

### Streams

```python
import io

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

customer = {"Name": "Quayside Bistro", "Product": "Harbor Blend whole bean coffee"}
with open("hello-world.docx", "rb") as template:
    report = io.BytesIO()
    DocumentAssembler().assemble_document(template, report, DataSourceInfo(customer, "customer"))
print("Report bytes:", len(report.getvalue()))
```

### Options and target formats

```python
from groupdocs.assembly import DataSourceInfo, DocumentAssembler, DocumentAssemblyOptions, FileFormat, LoadSaveOptions

customer = {"Name": "Quayside Bistro", "Product": "Harbor Blend whole bean coffee"}
assembler = DocumentAssembler()
# Flags combine with |: remove the paragraphs left empty, write template errors into the report instead of raising
assembler.options = DocumentAssemblyOptions.REMOVE_EMPTY_PARAGRAPHS | DocumentAssemblyOptions.INLINE_ERROR_MESSAGES
ok = assembler.assemble_document("hello-world.docx", "hello-world-report.pdf", LoadSaveOptions(FileFormat.PDF),
                                 DataSourceInfo(customer, "customer"))
print("Template parsed without errors:", ok)
```

Other `DocumentAssemblyOptions`: `UPDATE_FIELDS_AND_FORMULAS`, `ALLOW_MISSING_MEMBERS`, `USE_SPREADSHEET_DATA_TYPES`. `assembler.known_types.add("System.Math")` lets a template call a .NET type's static members (`<<[Math.Round(x, 1)]>>`).

## Licensing

```python
from groupdocs.assembly import License

License().set_license("path/to/license.lic")       # from a file
with open("path/to/license.lic", "rb") as f:        # or from a stream
    License().set_license(f)
```

Or auto-apply: `export GROUPDOCS_LIC_PATH="path/to/license.lic"` (a file or a URL). It is applied at the first call into the engine, and a licence that cannot be applied raises `LicenseError` from then on instead of falling back to evaluation mode (`GROUPDOCS_LIC_STRICT=0` makes it a warning). `License().is_licensed` tells whether a license is applied. `Metered().set_metered_key(public_key, private_key)` activates metered billing; `Metered.get_consumption_quantity()` and `Metered.get_consumption_credit()` report usage.

**Evaluation mode** (no licence): word-processing, spreadsheet, presentation and email reports carry an "Evaluation Only" mark (an XLSX also gets an "Evaluation Warning" sheet), and a word-processing report stops after about 200 paragraphs while `assemble_document` still returns `True`. A 30-day full license is free: https://purchase.groupdocs.com/temporary-license

**Windows:** applying a licence makes the first call take 15-30 seconds, once per process (the engine's licence check; the same in plain .NET). Reuse the process.

## API Reference

### DocumentAssembler

| Member | Description |
|---|---|
| `DocumentAssembler()` | Creates an assembler; one instance builds any number of reports. |
| `assemble_document(template, report, [load_save_options,] sources)` | Fills the template, writes the report. Paths or streams. `sources`: one `DataSourceInfo` or a list. |
| `options` | `DocumentAssemblyOptions` flags. |
| `known_types` | Types a template may call static members of: `.add("System.Math")` or `.add(float)`. |
| `barcode_settings` | `base_x_dimension`, `base_y_dimension`, `resolution`, `use_auto_correction` of barcode tags. |

### Data and options classes

| Type | Notes |
|---|---|
| `DataSourceInfo(data_source, name=None)` | A data source and the name the template uses (no name: the template reads its members directly). |
| `LoadSaveOptions(save_format)` | `save_format` (`FileFormat.PDF`, `FileFormat.DOCX`...), `resource_load_base_uri`, `resource_save_folder`, `ooxml_compliance`. |
| `JsonDataSource(path_or_stream[, JsonDataLoadOptions])` | `simple_value_parse_mode` (`JsonSimpleValueParseMode.STRICT` keeps "007" a string), `exact_date_time_parse_format`, `always_generate_root_object`. A `str` is a path: pass JSON text as `io.BytesIO`. |
| `XmlDataSource(path_or_stream[, schema][, XmlDataLoadOptions])` | XML data; `always_generate_root_object`. |
| `CsvDataSource(path_or_stream, CsvDataLoadOptions(has_headers))` | `delimiter`, `quote_char`, `comment_char`. |
| `DocumentTable(path, index, DocumentTableOptions)` | `first_row_contains_column_names`, `min/max_row_index`, `min/max_column_index`; `columns["Name"].type`. |
| `DocumentTableSet(path[, load_handler])` | All tables of a document; `tables["Name"]`, `relations.add(...)`. `load_handler`: a function `(args)` or an object with `handle(args)` setting `args.options` per table. |

## Key Patterns

- **Properties**: use `snake_case` -- auto-mapped to .NET `PascalCase`. A misspelled property raises `AttributeError` with a "Did you mean" hint instead of being stored
- **Dict keys are template members**: keep the key names the template reads (`{"CustomerName": ...}` for `<<[c.CustomerName]>>`); Python's types decide: a `str` stays a string ("007" stays "007"), a date must be a `datetime`/`date`
- **An empty list** with no non-empty list at the same key has no item type: a template reading its items fails unless `ALLOW_MISSING_MEMBERS` is set
- **One `DataSourceInfo` or a list**; `LoadSaveOptions` before the sources
- **Report format = report extension**, unless `LoadSaveOptions(FileFormat....)` says otherwise; a Word template cannot become XLSX or PPTX
- **Text and HTML templates**: save them with a UTF-8 byte order mark, or the report is written in Windows-1252
- **Enums**: UPPER_CASE members (`DocumentAssemblyOptions.REMOVE_EMPTY_PARAGRAPHS`, `FileFormat.PDF`); flags combine with `|`
- **Exceptions**: a template error or a missing member raises `RuntimeError` (message: the engine's, naming the tag); a missing file `FileNotFoundError`; other engine errors `GroupDocsAssemblyException`; `LicenseError` is a licence that could not be applied. `str(e)` adds the .NET type and stack trace
- **Classes you get, not create**: `BarcodeSettings`, `KnownTypeSet`, `DocumentTableColumn`, `DocumentTableLoadArgs` and the collections come from the assembler or the table; calling their constructor raises `TypeError`

## Platform Requirements

| Platform | Requirements |
|---|---|
| Windows | None |
| Linux | glibc 2.27+, `apt install libicu-dev libgdiplus` (ICU is required; libgdiplus for presentation templates) and the fonts your templates use |
| macOS | 12 or newer, `brew install mono-libgdiplus` (for presentation templates) |

## Troubleshooting

**"Evaluation Only" in the report, or a report that stops early** -- evaluation mode. Apply a license with `License().set_license(...)` or set `GROUPDOCS_LIC_PATH`; a free 30-day license is at https://purchase.groupdocs.com/temporary-license

**`An error has been encountered at the end of expression ... Can not get the value of member 'X' on type ...`** -- the data has no `X` where the tag reads it: check the key names and the data source name; for an empty list see Key Patterns

**`Can not get the value of member 'Month' on type 'System.Nullable...'`** -- a `JsonDataSource`/`XmlDataSource`/`CsvDataSource` value: write `.Value.Month`, or pass `json.load`ed dicts with `datetime` values

**`The document cannot be saved to the given format`** -- that template family cannot be saved to that format (a Word template to XLSX, for example)

**`The type initializer for 'Gdip' threw an exception`** -- a presentation template on Linux/macOS: install libgdiplus (`sudo apt install libgdiplus` / `brew install mono-libgdiplus`)

**`Value cannot be null. (Parameter 'trueTypeFont')`** -- a QR code on Linux: install the template's default font (a Word document's default, often Times New Roman; a workbook's Normal style, often Calibri) under that name; DejaVu, Liberation and Carlito do not stand in. Code 128, EAN-13 and Codabar need no particular font

**The first call takes 15-30 seconds** -- Windows, with a licence applied: the engine's licence check, once per process

**`DOTNET_SYSTEM_GLOBALIZATION_INVARIANT` errors, or the process aborts with `Couldn't find a valid ICU package`** -- do NOT set this variable. Install ICU: `sudo apt install libicu-dev`

**`is not a supported wheel on this platform`** -- pip is older than 20.3 (`python -m pip install --upgrade pip`), or the OS is older than glibc 2.27 / macOS 12

**Still stuck?** Post your question at https://forum.groupdocs.com/c/assembly/ -- the development team responds directly.
Close
Loading

Analyzing your prompt, please hold on...

An error occurred while retrieving the results. Please refresh the page and try again.