Cartas de boas‑vindas para quarenta novos clientes, um certificado para cada participante, uma fatura por pedido: o mesmo documento, um registro diferente a cada vez. Com o GroupDocs.Assembly.Mcp em execução localmente (dnx GroupDocs.Assembly.Mcp --yes), Claude Desktop, Cursor ou Copilot fazem isso com uma única chamada de ferramenta, assemble_per_record, que preenche seu modelo Word uma vez por elemento de um array JSON, salva cada resultado como um PDF próprio nomeado por um campo que você escolher e relata os registros que falharam em vez de parar no primeiro:

Crie um PDF de carta de boas‑vindas para cada cliente em customers.json usando welcome-letter.docx, e nomeie cada arquivo com o nome do cliente.

A versão passo a passo com configuração e solução de problemas está na documentação: How to generate one document per record (mail merge) with an AI agent.

Por que não deixar o agente fazer o loop?

Ele pode, e para cinco registros isso é suficiente. Mas um loop de quarenta chamadas de ferramenta são quarenta chances de o agente perder a conta, pular um registro quando o contexto fica longo ou “ajudando” reformular uma carta. Uma ferramenta por registro move o loop para o servidor: uma chamada, um modelo, uma passagem sobre o array e um relatório que diz exatamente quantos arquivos foram gravados e quais registros não foram processados. O trabalho do agente é escolher o modelo, o arquivo de dados e o campo de nomeação e, em seguida, ler o relatório de volta para você.

O que o agente verifica primeiro?

Duas coisas: como os dados se apresentam e o que o modelo lê. No nosso teste, cinco clientes em customers.json e uma carta de boas‑vindas de uma página:

-> get_document_info {"file":{"filePath":"customers.json"}}
<- { "dataKind": "json",
     "data": { "root": "array", "length": 5, "keys": null } }

-> get_template_fields {"template":{"filePath":"welcome-letter.docx"}}
<- { "dataSourceNames": [ "customer" ],
     "fields": [ "customer.Contact", "customer.Plan", "customer.Name",
                 "customer.Manager", "customer.FirstInvoice" ] }

O modelo chama um registro de customer; o servidor descobre isso por conta própria, portanto o agente não precisa passar dataSourceName.

Uma chamada, cinco registros, uma falha

-> assemble_per_record {"template":{"filePath":"welcome-letter.docx"},
     "data":{"filePath":"customers.json"},"fileNameField":"Name",
     "format":"pdf"}
<- Assembled 4 of 5 document(s) from 'welcome-letter.docx' with
   'customers.json', one per record (each record as 'customer'), as PDF.
   Saved to:
   .../Contoso Pharma.pdf
   .../Fabrikam Logistics.pdf
   .../Tailspin Toys.pdf
   .../Litware Labs.pdf
   1 record(s) failed:
   record 4: InvalidOperationException: ... Can not get the value of
   member 'FirstInvoice' on type 'System.Data.DataRow'. The template
   reads 'FirstInvoice', which the data does not have. ...

O registro 4, Wingtip Travel, não tinha data de primeira fatura nos dados. As outras quatro cartas foram escritas, cada uma nomeada com o cliente, e a falha retornou com o número do registro e o campo que estava ausente. Um agente relata isso como “quatro cartas concluídas; Wingtip Travel não tem data de primeira fatura, você quer que eu deixe a linha vazia ou aguarde a data?”. Essa é a conversa que você deseja, em vez de quarenta arquivos sem saber qual está errado.

Como funciona a distribuição

customers.json  [ {Name:"Contoso Pharma",...}, {Name:"Fabrikam ..."},
                  ... 5 records ]
        |
assemble_per_record {"template":{"filePath":"welcome-letter.docx"},
                     "data":{"filePath":"customers.json"},
                     "fileNameField":"Name","format":"pdf"}
        |
        +--> Contoso Pharma.pdf
        +--> Fabrikam Logistics.pdf     ... 2 more
        '--> "4 of 5 ... 1 record(s) failed: record 4 ..."

Três parâmetros fazem o trabalho. fileNameField nomeia cada saída por um campo do registro, customer.Name ou um número de fatura; sem ele, os arquivos recebem um número sequencial. recordsPath aponta para o array quando o JSON é um objeto que contém um, como "customers" ou "batch.items". E maxDocuments é o freio.

O que impede um lote descontrolado?

maxDocuments, que tem padrão 100 e pode chegar a 1000. Antes de gravar qualquer coisa, o servidor conta os registros; se houver mais que o limite, ele recusa. Definimos para 3 contra nossos cinco clientes:

-> assemble_per_record {..., "maxDocuments":3}
<- Per-record assembly failed for 'welcome-letter.docx': there are 5
   records, more than maxDocuments=3. Raise 'maxDocuments' (up to 1000)
   or pass fewer records.

Nada foi gravado. Para uma campanha que vai a pessoas reais, esse é o padrão correto: um prompt que aponta acidentalmente para a exportação completa do CRM gera uma pergunta de volta, não dez mil PDFs.

Meus dados são uma planilha, não JSON

A geração por registro lê apenas JSON: um array de objetos ou um objeto que contém um. Quando a lista está em um CSV ou em uma planilha, o agente converte as linhas para JSON primeiro, o que os agentes fazem bem, e grava o arquivo na pasta de armazenamento. Se o que você quer é um documento que liste cada linha, uma lista de preços ou um registro, isso é assemble_document com um <<foreach>> no modelo, e ele lê CSV e planilhas diretamente.

Limites honestos

  • A montagem por registro lê apenas JSON; dados CSV e de planilha precisam ser convertidos primeiro.
  • Modo de avaliação: todo arquivo gerado começa com “Evaluation Only. Created with GroupDocs.Assembly 26.7…”, e os modelos de e‑mail mantêm apenas o corpo em texto simples. Licencie o servidor antes de uma campanha real.
  • Um campo ausente falha esse registro. allowMissingMembers: true grava com a linha vazia; decida o que você prefere.
  • Datas: escreva-as como 2026-12-01T00:00:00 e formate-as no modelo, como nossa carta faz com <<[customer.FirstInvoice]:"d MMMM yyyy">>. Uma string de data apenas foi deslocada um dia em uma máquina UTC+2 no nosso teste.
  • O servidor grava arquivos; ele não os envia. A entrega é uma etapa separada.

Perguntas Frequentes

Posso fazer mesclagem de correspondência com Claude sem fazer upload da lista de clientes?
Sim. O servidor roda localmente e lê customers.json da sua pasta de armazenamento; os PDFs gerados são gravados lá também.

Como os arquivos de saída são nomeados?
Pelo campo de registro que você passa como fileNameField, aqui o nome do cliente, então Contoso Pharma.pdf. Sem ele, um número sequencial.

Cada documento pode ser um e‑mail em vez de um PDF?
Sim: use um modelo EML ou MSG, ou passe format: "eml". No modo de avaliação o corpo HTML de um e‑mail é substituído pela linha de avaliação, então teste a saída de e‑mail com uma licença.

Aprofunde-se