Running GroupDocs MCP servers on-premise: architecture and security model

Run document generation for AI agents fully on-premise: the GroupDocs.Assembly MCP server uses local stdio transport with no external endpoints, no inbound ports, and no telemetry, and reads templates and data from folders you choose, locally. This page is the one to send your security reviewer.

Note
The commands and config snippets on this page are for the .NET build of the server — the only platform available today. Installation and client setup: MCP server for .NET. Other platforms will expose the same tools with their own launch command; everything else on this page applies unchanged.

The architecture in one picture

+--------------+          +--------------------+         +------------------+
|  AI client   |  stdio   | MCP server process | reads / | local filesystem |
| (Claude, VS  | <----->  | (GroupDocs engine) | <-----> | storage / output |
| Code, agent) | JSON-RPC |   child process    |  writes |     folders      |
+--------------+          +--------------------+         +------------------+
  • Transport: the AI client starts the server as a child process and communicates over standard input/output. The server never listens on a network socket.
  • Data path: agent → local server → local filesystem. Templates and data files are read from the storage folder; generated documents are written there (or to GROUPDOCS_MCP_OUTPUT_PATH).
  • Network use: at install time (nuget.org, or ghcr.io/docker.io); at runtime only if metered licensing is enabled — or if a template asks for a URL (below).
  • Telemetry: none. The engine runs in-process.

Templates are trusted input

A template is closer to code than to a document. <<image [expr]>> and <<doc [expr]>> tags load whatever path or URL their expression returns — from the data, or written into the template. A template from an untrusted source could read a file the server can reach, or make a request to an internal address.

  • Run with one mounted folder. In Docker the container sees /data and nothing else; that is the boundary.
  • Review templates like code — they live in a repository, changes go through review.
  • Data is not code. JSON, XML and CSV values are printed, not executed; the risk is in the template’s expressions.

A tighter deployment

docker run --rm -i \
  -v /srv/templates:/data \
  -v /srv/licenses:/license:ro \
  -e GROUPDOCS_MCP_STORAGE_PATH=/data \
  -e GROUPDOCS_LICENSE_PATH=/license/GroupDocs.Assembly.lic \
  --network none \
  ghcr.io/groupdocs-assembly/assembly-net-mcp:26.11.0
  • Pin the tag (:26.11.0, not :latest).
  • --network none makes URL-loading tags fail instead of fetching — and rules out metered licensing, so use a license file.
  • License read-only; the image runs in UTC (dates in data are not shifted).

The part that still travels

The data usually starts in the conversation: the agent extracts it from your message, or reads it from a file and passes it as dataJson. With a cloud-hosted model, those values have reached your model provider; the generated document has not. To keep values off the conversation entirely, pass a data file (data: { "filePath": "orders.json" }) — the agent then handles only names.

License management

  • License file — read from local disk by the local process. Fully offline; the right answer for air-gapped deployments.
  • Metered (pay-per-use) — reports usage to GroupDocs servers, so it needs outbound egress. Document content is never part of that report.

Both: Licensing.

What this fits — honestly

A good fit: contracts, letters, certificates and reports generated from approved templates, inside a network that does not allow uploads; generation steps for coding agents in a repository.

Not what this is: a shared service or a sandbox for untrusted templates. One stdio server, one client, one machine; templates are trusted.

FAQ

Does any document content leave the machine? Not from the server. Values the agent writes into dataJson are in the conversation with your model provider.

Does it need internet at runtime? No — unless metered licensing is on, or a template loads a URL.

Can I run it air-gapped? Yes: pre-pull the image or pre-cache the package, use a license file, pin the version, --network none.

What ports does it open? None. stdio only.

How do I prove that? The verification script performs a real handshake and a real engine call so you can watch exactly what happens.

Read more