PDF Letter MCP

Renderiza uma carta estruturada como um PDF pronto para impressão, formatado conforme a DIN 5008, de modo que o endereço fique na janela de um envelope alemão, totalmente offline, sem API externa.

Documentação

pdf-letter-mcp

Servidor MCP local (stdio) que transforma dados de carta estruturados em um PDF pronto para impressão. Um layout fixo, a carta alemã do dia a dia, com o endereço do destinatário posicionado conforme a DIN 5008, de modo que fique exatamente na janela de envelopes com janela DL e C6/C5. Sem serviço web, sem API externa, tudo roda offline na sua máquina.

Recursos

  • Um layout, sem variantes: linha de endereço de remetente, destinatário, "local, data" alinhado à direita, bloco de assunto em negrito com referências abaixo, saudação, corpo, fechamento, assinatura, anexos
  • Campo de endereço com 45 mm de altura, terminando a 105 mm da esquerda, com linha de endereço de remetente, zona de observação (Einschreiben, Nicht nachsenden) e zona de endereço
  • Endereço de remetente, observações e destinatário são equilibrados dentro do campo de endereço, de modo que o bloco fique uniforme na janela do envelope e linhas adicionais de endereço ainda caibam. addressLayout: "din" volta aos limites fixos da zona
  • Quebras de página mantêm fechamento, imagem de assinatura, nome e anexos juntos; páginas subsequentes trazem destinatário, data e número da página
  • Assunto 33 mm abaixo do campo de endereço, local e data três linhas acima
  • Marcas de dobra em 105 mm e 210 mm, marca de perfuração em 148,5 mm
  • Margem de escrita DIN 5008 de 25 mm à esquerda, e o campo de endereço a segue, de modo que endereço, assunto e corpo compartilham uma borda. O campo mantém a borda direita em 105 mm e permanece dentro da janela de 90 mm do envelope
  • Fechamento, uma linha em branco, nome impresso. signature.spaceMm reserva mais espaço para assinatura à mão
  • Assinatura como arquivo de imagem: PNG com canal alfa mantém a transparência; a imagem é sobreposta à linha em branco acima do nome impresso e nunca desloca o texto. Recorte opcional, remoção de fundo e recolorir da tinta para digitalizações
  • Multilíngue: de, en, fr, es, it, nl, pt, pl, tr, da, sv, cs, com formatos de data sensíveis ao idioma e regras de endereço por país
  • Hifenização sensível ao idioma, marcação em negrito/itálico, listas com marcadores e numeradas, quebras de página automáticas com cabeçalhos de continuação
  • Números de página e um modo de depuração de layout que desenha as zonas DIN 5008
  • Fontes Unicode incorporadas (DejaVu), qualquer fonte do sistema instalada ou um caminho .ttf

Instalação

npm install
npm run build

Registre o servidor no Claude Code:

claude mcp add pdf-letter -- node "/absolute/path/to/pdf-letter-mcp/dist/src/index.js"

Ou no ~/.claude.json / claude_desktop_config.json:

{
  "mcpServers": {
    "pdf-letter": {
      "command": "node",
      "args": ["/absolute/path/to/pdf-letter-mcp/dist/src/index.js"],
      "env": {
        "PDF_LETTER_OUTPUT_DIR": "~/Documents/Briefe"
      }
    }
  }
}

Ambiente

VariávelFinalidade
PDF_LETTER_OUTPUT_DIRDiretório padrão para os PDFs gerados. Recorre ao diretório temporário do sistema.
PDF_LETTER_FONT_DIRDiretório adicional que é pesquisado quando uma família de fontes é resolvida por nome.
PDF_LETTER_FONTFamília de fontes para cada carta, ex.: Arial. O padrão é a DejaVu Sans incorporada.
PDF_LETTER_FONT_SIZETamanho da fonte em pt para cada carta, padrão 11.
PDF_LETTER_PROFILESCaminho para os perfis de remetente, padrão ~/.config/pdf-letter-mcp/profiles.json.
PDF_LETTER_PROFILEPerfil usado quando uma carta não indica nenhum e o arquivo não possui defaultProfile.

Perfis de remetente

Endereços de remetentes e assinaturas ficam em um arquivo na máquina, nunca no prompt. Uma carta nomeia um perfil; o servidor preenche endereço, linha de endereço de remetente e imagem de assinatura literalmente.

{
  "defaultProfile": "erika",
  "profiles": {
    "erika": {
      "description": "Erika Musterfrau, privat",
      "sender": {
        "name": "Erika Musterfrau",
        "street": "Musterstraße 12",
        "postalCode": "12345",
        "city": "Musterstadt",
        "country": "DE"
      },
      "place": "Musterstadt",
      "locale": "de",
      "signature": { "path": "/pfad/zur/unterschrift.png" }
    }
  }
}

create_letter então precisa apenas de "profile": "erika" mais o conteúdo. O perfil é o dono da identidade: um remetente ou assinatura passado na chamada é substituído pelo perfil; place, locale e closing são padrões que uma carta pode sobrescrever. list_profiles mostra o que está disponível. Sem perfil e sem remetente, a carta é recusada em vez de ser inventada.

O arquivo fica fora do repositório; profiles.json está em .gitignore.

O layout é fixo

As ferramentas MCP aceitam apenas conteúdo: endereços, data, assunto, referências, texto, assinatura, anexos. Não há parâmetros para margens, espaçamento, fonte, tamanho ou posição de imagem, e campos desconhecidos enviados por um cliente são descartados. A tipografia é uma configuração de instalação (PDF_LETTER_FONT, PDF_LETTER_FONT_SIZE), de modo que toda carta de uma instalação parece idêntica. Os blocos de construção para um cabeçalho de empresa, um bloco de informações e um rodapé ainda estão na biblioteca, mas não são acessíveis por meio do MCP.

Ferramentas

FerramentaFinalidade
create_letterRenderiza a carta e grava o PDF. Retorna caminho, número de páginas, métricas de layout e avisos.
preview_letterMesma renderização sem gravar arquivo, para verificar o layout.
prepare_signatureLimpa uma assinatura digitalizada (recorte, fundo transparente, cor da tinta) e grava um PNG.
get_din5008_specRetorna a geometria em milímetros de um formulário.
list_localesLista os idiomas suportados e seus textos fixos.
list_fontsLista as famílias incorporadas e resolve um nome de fonte contra as fontes do sistema instaladas.

create_letter

O remetente aparece apenas na pequena linha de endereço de remetente acima do destinatário. Local e data ficam alinhados à direita acima do bloco de assunto em negrito; as referências vão diretamente abaixo do assunto.

{
  "locale": "de",
  "sender": {
    "name": "Erika Musterfrau",
    "street": "Musterstraße 12",
    "postalCode": "12345",
    "city": "Musterstadt",
    "country": "DE"
  },
  "recipient": {
    "company": "Stadtwerke Musterstadt",
    "street": "Industriestraße 8",
    "postalCode": "12345",
    "city": "Musterstadt"
  },
  "place": "Musterstadt",
  "date": "2026-07-24",
  "dateStyle": "long",
  "subject": "Widerspruch gegen die Jahresabrechnung vom 01.07.2026",
  "subjectLines": ["Zeichen: SW-2026-0815", "Kunden-Nummer: 000000000"],
  "body": "hiermit widerspreche ich der Jahresabrechnung.\n\n- korrigierte Abrechnung\n- Eingangsbestätigung",
  "signature": {
    "path": "/pfad/zur/unterschrift.png",
    "widthMm": 45,
    "removeBackground": true,
    "trim": true,
    "name": "Erika Musterfrau"
  },
  "enclosures": ["Kopie der Abrechnung"]
}

Tudo, exceto sender, recipient e body, é opcional. A saudação é derivada do destinatário (Frau mais Dr. Erika Mustermann vira Sehr geehrte Frau Dr. Mustermann,), a data tem como padrão hoje, e o fechamento, o padrão do idioma.

Marcação do corpo

  • Linha em branco: novo parágrafo
  • Nova linha simples: quebra de linha (defina bodyMode como markdown para refluir)
  • - item ou 1. item: lista com marcadores ou numerada
  • **bold**, *italic*
  • \pagebreak em linha própria: quebra de página forçada

Assinatura

Duas formas, ambas resultam em uma imagem real no PDF:

  1. Passe o arquivo diretamente: signature.path mais trim e removeBackground. A digitalização é limpa a cada renderização.
  2. Limpe uma vez com prepare_signature e reutilize o PNG resultante. Mais rápido e permite verificar o resultado antes de ir para a carta.

trim, removeBackground e inkColor precisam da dependência opcional sharp, que é instalada por padrão. Um PNG que já tem fundo transparente funciona sem ela.

A imagem é uma sobreposição: fechamento, uma linha em branco e nome impresso permanecem exatamente onde estão, e a assinatura fica por cima dessa linha em branco e atravessando o fechamento, do mesmo modo que uma assinatura é escrita sobre "Mit freundlichen Grüßen" no papel. Essa sobreposição é o resultado pretendido.

Tamanho e posição são decididos pelo renderizador, não pelo chamador. A assinatura é dimensionada para aproximadamente a largura da linha de fechamento, cerca de 50 mm, limitada proporcionalmente a 1,2 vezes essa largura e 28 mm de altura. Não há parâmetro para largura, altura, deslocamento ou espaçamento, então nada pode deslocar o layout carta a carta.

Verificação

npm test          # geometry, address rules, typography, rendering
npm run examples  # writes sample letters to examples/

examples/ contém a carta alemã, a mesma carta em inglês e uma variante de depuração que desenha as zonas DIN 5008, para que a posição do campo de endereço possa ser verificada contra um envelope com janela. preview_letter relata as mesmas medidas como JSON, além de avisos quando o endereço é muito longo ou não cabe no campo de endereço.

Licença

MIT, veja LICENSE. As fontes DejaVu incorporadas são cobertas pelas licenças Bitstream Vera e Arev.