Lexware Office MCP Server

Servidor MCP para a API do Lexware Office — gerencie faturas, contatos, artigos, comprovantes e muito mais. 65 ferramentas em 20 domínios de recursos.

Documentação

lexware-mcp-server

Tests

Servidor MCP para a API do Lexware Office. Gerencie faturas, contatos, artigos, comprovantes e muito mais através do Model Context Protocol.

Não oficial — projeto da comunidade. Não é afiliado, endossado ou suportado pela Lexware GmbH ou Haufe Group. "Lexware" e "Lexware Office" são marcas registradas de seus respectivos proprietários; usadas aqui apenas para identificar a API que este cliente utiliza (uso justo nominativo).

66 ferramentas em 20 domínios de recursos, com 6 pontos de entrada para você escolher o servidor certo para o limite de ferramentas do seu cliente MCP.

Instalação

npm install -g @lazyants/lexware-mcp-server

Ou execute diretamente:

npx @lazyants/lexware-mcp-server

Configuração

O token da API é resolvido nesta ordem:

  1. Chaveiro do sistema operacional (recomendado — o token nunca é gravado em disco em texto puro)
  2. Variável de ambiente LEXWARE_API_TOKEN

Armazene o token no chaveiro do sistema operacional

Obtenha seu token nas configurações da API do Lexware Office e armazene-o com o gerenciador de credenciais nativo do seu sistema operacional.

[!IMPORTANT] Os comandos abaixo leem o token de um prompt interativo em vez de recebê-lo como argumento, para que ele nunca apareça no histórico do shell ou na lista de processos. Evite colar o token diretamente na linha de comando.

macOS

Omitir o valor após -w faz com que o security solicite o token (com confirmação):

security add-generic-password -s "lexware-mcp" -a "api-token" -w

Windows (PowerShell)

cmdkey só pode receber o token como argumento de linha de comando, o que o expõe na lista de processos. Em vez disso, leia-o de um prompt oculto e grave-o diretamente no Gerenciador de Credenciais do Windows via CredWrite, para que o token nunca chegue ao argv. O nome de destino da credencial é <account>.<service> — api-token.lexware-mcp para o serviço padrão — que é exatamente o que o servidor lê de volta:

$secure = Read-Host -AsSecureString "Lexware API token"
Add-Type -Namespace LexwareKeyring -Name Native -MemberDefinition @'
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CREDENTIAL {
    public uint Flags;
    public uint Type;
    [MarshalAs(UnmanagedType.LPWStr)] public string TargetName;
    [MarshalAs(UnmanagedType.LPWStr)] public string Comment;
    public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
    public uint CredentialBlobSize;
    public IntPtr CredentialBlob;
    public uint Persist;
    public uint AttributeCount;
    public IntPtr Attributes;
    [MarshalAs(UnmanagedType.LPWStr)] public string TargetAlias;
    [MarshalAs(UnmanagedType.LPWStr)] public string UserName;
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredWriteW(ref CREDENTIAL credential, uint flags);
'@
$blob = [Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($secure)
try {
    $cred = New-Object LexwareKeyring.Native+CREDENTIAL
    $cred.Type = 1                              # CRED_TYPE_GENERIC
    $cred.Persist = 2                           # CRED_PERSIST_LOCAL_MACHINE
    $cred.TargetName = 'api-token.lexware-mcp'  # "<account>.<service>"
    $cred.UserName = 'api-token'
    $cred.CredentialBlob = $blob
    $cred.CredentialBlobSize = $secure.Length * 2   # UTF-16 bytes, no terminator
    if (-not [LexwareKeyring.Native]::CredWriteW([ref]$cred, 0)) {
        throw "CredWrite failed (Win32 error $([Runtime.InteropServices.Marshal]::GetLastWin32Error()))"
    }
    Write-Host 'Stored Lexware API token in Windows Credential Manager.'
} finally {
    [Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($blob)
    $secure.Dispose()
    Remove-Variable secure, blob
}

Usando um LEXWARE_KEYRING_SERVICE personalizado (ex.: acme)? Defina TargetName como api-token.acme para corresponder — o servidor procura o token sob <account>.<service>.

Linux

secret-tool store --label="Lexware Office API" service lexware-mcp username api-token
# (prompts for the token value)

Depois de armazenado, os arquivos de configuração do MCP não precisam de credenciais — o servidor lê o token do chaveiro na inicialização.

Use uma variável de ambiente em vez disso

Se preferir não usar o chaveiro, defina LEXWARE_API_TOKEN no seu shell ou na configuração do cliente MCP:

export LEXWARE_API_TOKEN=your-token-here

Variáveis de ambiente

VariávelPadrãoDescrição
LEXWARE_API_TOKEN—Token da API; usado quando o chaveiro não tem entrada para o serviço configurado
LEXWARE_KEYRING_SERVICElexware-mcpNome do serviço no chaveiro. Substitua ao conectar a várias contas Lexware simultaneamente — execute uma instância do servidor por conta, cada uma com seu próprio nome de serviço

Opcionalmente, substitua a chave pública de assinatura de webhook usada pelo lexware_verify_webhook_signature (por padrão, buscada da Lexware e armazenada em cache):

export LEXWARE_WEBHOOK_PUBLIC_KEY="$(cat lexware-webhook-public.pem)"

Pontos de Entrada

ComandoDomíniosFerramentas
lexware-mcp-serverTodos os 20 domínios66
lexware-mcp-salesFaturas, Notas de Crédito, Cotações, Confirmações de Pedido, Notas de Entrega, Faturas de Adiantamento, Cobranças, Lista de Comprovantes32
lexware-mcp-contactsContatos, Artigos10
lexware-mcp-bookkeepingComprovantes, Lista de Comprovantes, Pagamentos8
lexware-mcp-referencePaíses, Condições de Pagamento, Categorias de Lançamento, Perfil, Layouts de Impressão5
lexware-mcp-systemAssinaturas de Eventos, Arquivos, Modelos Recorrentes12

Use servidores divididos para reduzir o tamanho do contexto — escolha apenas as divisões que você precisa.

Claude Code

Adicione ao ~/.claude/settings.json. Se você armazenou o token no chaveiro do sistema operacional sob o nome de serviço padrão lexware-mcp (recomendado), nenhuma chave env é necessária:

{
  "mcpServers": {
    "lexware": {
      "command": "npx",
      "args": ["-y", "@lazyants/lexware-mcp-server"]
    }
  }
}

Se preferir a abordagem com variável de ambiente:

{
  "mcpServers": {
    "lexware": {
      "command": "npx",
      "args": ["-y", "@lazyants/lexware-mcp-server"],
      "env": { "LEXWARE_API_TOKEN": "your-token-here" }
    }
  }
}

Servidores divididos

Use servidores divididos para reduzir o tamanho do contexto — escolha apenas os pontos de entrada que você precisa. A flag -p @lazyants/lexware-mcp-server informa ao npx de qual pacote obter o comando; o argumento final (ex.: lexware-mcp-sales) é o binário específico do ponto de entrada definido nesse pacote (veja Pontos de Entrada):

{
  "mcpServers": {
    "lexware-sales": {
      "command": "npx",
      "args": ["-y", "-p", "@lazyants/lexware-mcp-server", "lexware-mcp-sales"]
    },
    "lexware-contacts": {
      "command": "npx",
      "args": ["-y", "-p", "@lazyants/lexware-mcp-server", "lexware-mcp-contacts"]
    }
  }
}

Exemplo com múltiplas contas (duas empresas Lexware, tokens armazenados sob nomes de serviço separados no chaveiro):

{
  "mcpServers": {
    "lexware-company-a": {
      "command": "npx",
      "args": ["-y", "@lazyants/lexware-mcp-server"],
      "env": { "LEXWARE_KEYRING_SERVICE": "lexware-company-a" }
    },
    "lexware-company-b": {
      "command": "npx",
      "args": ["-y", "@lazyants/lexware-mcp-server"],
      "env": { "LEXWARE_KEYRING_SERVICE": "lexware-company-b" }
    }
  }
}

Claude Desktop

Adicione ao claude_desktop_config.json. Com o chaveiro do sistema operacional (recomendado — assume que o token está armazenado sob o nome de serviço padrão lexware-mcp):

{
  "mcpServers": {
    "lexware": {
      "command": "npx",
      "args": ["-y", "@lazyants/lexware-mcp-server"]
    }
  }
}

Com uma variável de ambiente em vez disso:

{
  "mcpServers": {
    "lexware": {
      "command": "npx",
      "args": ["-y", "@lazyants/lexware-mcp-server"],
      "env": { "LEXWARE_API_TOKEN": "your-token-here" }
    }
  }
}

Ferramentas

Faturas (5 ferramentas) — vendas

lexware_create_invoice (suporta finalize=true na criação), lexware_get_invoice, lexware_download_invoice_file, lexware_pursue_invoice, lexware_deeplink_invoice

Notas de Crédito (5 ferramentas) — vendas

lexware_create_credit_note, lexware_get_credit_note, lexware_download_credit_note_file, lexware_pursue_credit_note, lexware_deeplink_credit_note

Cotações (4 ferramentas) — vendas

lexware_create_quotation, lexware_get_quotation, lexware_download_quotation_file, lexware_deeplink_quotation

Confirmações de Pedido (5 ferramentas) — vendas

lexware_create_order_confirmation, lexware_get_order_confirmation, lexware_download_order_confirmation_file, lexware_pursue_order_confirmation, lexware_deeplink_order_confirmation

Notas de Entrega (5 ferramentas) — vendas

lexware_create_delivery_note, lexware_get_delivery_note, lexware_download_delivery_note_file, lexware_pursue_delivery_note, lexware_deeplink_delivery_note

As ferramentas independentes de criação de fatura, nota de crédito, cotação, confirmação de pedido e nota de entrega aceitam finalize: true para finalizar na criação. Omita-o ou defina false para criar um rascunho; apenas true explícito envia o parâmetro de consulta ?finalize=true da API.

Faturas de Adiantamento (3 ferramentas) — vendas

lexware_get_down_payment_invoice, lexware_download_down_payment_invoice_file, lexware_deeplink_down_payment_invoice

Cobranças (4 ferramentas) — vendas

lexware_get_dunning, lexware_download_dunning_file, lexware_pursue_dunning, lexware_deeplink_dunning

Lista de Comprovantes (1 ferramenta) — vendas, contabilidade

lexware_list_voucherlist

Para sincronização incremental, updatedDateFrom e updatedDateTo filtram a data da última modificação (yyyy-MM-dd, dias completos em CET/CEST). sort aceita voucherDate, voucherNumber, createdDate ou updatedDate seguido por ASC ou DESC, por exemplo updatedDate,DESC. Os filtros de status incluem transferred e sepadebit; o status transitório overdue não pode ser combinado com outros status.

Por padrão, isso é uma passagem de página única da resposta da API. Duas adições são opcionais:

  • fetchAllPages: true segue a paginação até que todas as páginas sejam recuperadas, limitado a 100 solicitações. O resultado adiciona fetchedPages e truncated, sendo este último uma marcação de conjunto interrompido pelo limite — campos da API como totalElements são preservados.
  • contactName (curingas estilo SQL %/_, sem diferenciar maiúsculas de minúsculas) e hasOpenAmount filtram no lado do cliente após a busca, e cada um implica fetchAllPages. Eles são aplicados aqui em vez de em lexware_list_vouchers porque /voucherlist é a forma de resposta que carrega contactName e openAmount.

page não pode ser combinado com nenhum dos três — esses modos leem todas as páginas, então um deslocamento inicial é sem sentido. Use size para controlar o tamanho do lote em vez disso. A combinação é rejeitada em vez de ignorada silenciosamente, para que ninguém acredite que um deslocamento foi respeitado quando não foi.

Contatos (5 ferramentas) — contatos

lexware_list_contacts, lexware_get_contact, lexware_create_contact, lexware_update_contact, lexware_deeplink_contact

lexware_deeplink_contact retorna deeplink e o alias permanente url, ambos contendo a mesma URL de contato. Qualquer chave de saída é suportada.

Artigos (5 ferramentas) — contatos

lexware_list_articles, lexware_get_article, lexware_create_article, lexware_update_article, lexware_delete_article

Comprovantes (6 ferramentas) — contabilidade

lexware_list_vouchers, lexware_get_voucher, lexware_create_voucher, lexware_update_voucher, lexware_upload_voucher_file, lexware_deeplink_voucher

lexware_list_vouchers exige voucherNumber. GET /vouchers é um endpoint de consulta, não uma coleção navegável — a API responde 400 "voucherNumber parameter is required" sem ele. Para navegar ou filtrar comprovantes, use lexware_list_voucherlist, que é o endpoint de coleção e também carrega os campos de resumo (contactName, openAmount) que /vouchers não carrega.

lexware_get_voucher normaliza voucherStatus para minúsculas e tenta novamente um 404 três vezes (1 s / 2 s / 4 s) para cobrir o atraso de indexação após um upload; se o comprovante ainda estiver ausente, retorna { voucherId, status: "processing", message }. Outras falhas são relatadas como erros.

Pagamentos (1 ferramenta) — contabilidade

lexware_get_payments

Países (1 ferramenta) — referência

lexware_list_countries

Condições de Pagamento (1 ferramenta) — referência

lexware_list_payment_conditions

Categorias de Lançamento (1 ferramenta) — referência

lexware_list_posting_categories

Perfil (1 ferramenta) — referência

lexware_get_profile

Layouts de Impressão (1 ferramenta) — referência

lexware_list_print_layouts

Assinaturas de Eventos (5 ferramentas) — sistema

lexware_create_event_subscription, lexware_list_event_subscriptions, lexware_get_event_subscription, lexware_delete_event_subscription, lexware_verify_webhook_signature

Seu aplicativo hospeda o manipulador de callbacks; este servidor MCP gerencia assinaturas e verifica assinaturas, mas não hospeda um receptor. Confirme os callbacks prontamente com HTTP 200 ou 204 dentro do tempo limite de 5 segundos (5000 ms) da Lexware, coloque o payload recebido na fila e processe-o de forma assíncrona.

Use a fila para absorver rajadas de eventos e aplicar novas tentativas/backoff controlados de processamento independentemente das novas tentativas de entrega da Lexware. Limite as chamadas de API de saída para respeitar os limites de taxa da API. Veja as práticas recomendadas de assinatura de eventos da Lexware.

Arquivos (4 ferramentas) — sistema

lexware_upload_file, lexware_download_file, lexware_get_file_status, lexware_deeplink_file

lexware_download_file aceita format: "pdf" (padrão) ou format: "xml" para recuperar um original XML de e-invoice disponível. XML incorporado em um PDF não pode ser recuperado separadamente; a Lexware retorna 404 quando o XML solicitado não está disponível. Os downloads retornam os bytes como contentBase64.

lexware_get_file_status chama GET /files/{id}/status. O GET /files/{id} puro é a rota de download binário — com Accept: application/json ele ainda responde 200 com o corpo do arquivo codificado em base64, então nunca pode produzir metadados de status. A rota de status é limitada por escopo: chaves de API sem a permissão necessária recebem access_denied da Lexware em vez de um status.

Ambas as ferramentas de upload (lexware_upload_file e lexware_upload_voucher_file) recebem o arquivo como contentBase64 ou como filePath — um caminho absoluto legível pelo processo do servidor MCP. Prefira filePath para qualquer coisa grande: base64 infla o payload em cerca de um terço e precisa percorrer a janela de contexto do modelo. Com filePath, fileName assume o nome base do arquivo e contentType é detectado automaticamente para .png, .jpg/.jpeg, .tiff/.tif e .xml, com fallback para application/pdf. Forneça exatamente um dos dois — fornecer ambos, ou nenhum, é um erro de validação.

Os uploads são limitados a 5 MB. Para filePath, o tamanho é obtido do descritor aberto antes de o arquivo ser lido, então um arquivo superdimensionado custa um stat em vez de um carregamento completo na memória, e qualquer coisa que não seja um arquivo regular é recusada imediatamente (ler /dev/zero de outra forma nunca retornaria). A contagem de bytes decodificados é verificada novamente depois, o que também cobre contentBase64. Falhas carregam um erro file_too_large com os tamanhos real e máximo.

Modelos Recorrentes (3 ferramentas) — sistema

lexware_list_recurring_templates, lexware_get_recurring_template, lexware_deeplink_recurring_template

Segurança

  • Use o chaveiro do sistema operacional para manter seu token da API fora dos arquivos de configuração e do histórico do shell (veja Configuração)
  • Nunca envie seu token da API para o controle de versão
  • Use acesso somente leitura quando precisar apenas listar/obter recursos
  • As ferramentas de criar, atualizar e excluir modificam dados comerciais reais — faturas, contatos e registros contábeis na sua conta Lexware
  • A limitação de taxa é tratada automaticamente: as solicitações são repetidas com backoff exponencial em 429, incluindo uploads de arquivos — o corpo multipart é reconstruído do zero em cada tentativa, para que possa ser reproduzido com segurança

Lançamento

Releases são publicados por meio do evento GitHub Release. Fluxo do mantenedor:

  1. Atualize a versão em package.json, package-lock.json e server.json (npm version <x.y.z> --no-git-tag-version atualiza os dois primeiros juntos). npm run check-versions falha de forma rígida, a menos que package.json#/version, server.json#/packages[0].version e ambos os campos de versão package-lock.json (raiz e packages[""]) estejam de acordo. server.json#/version é verificado de forma mais flexível: ele deve estar presente, mas só é comparado com packages[0].version como uma verificação de regressão — ele pode legitimamente estar à frente (republicações apenas no registro atualizam apenas esse campo), então um valor deixado para trás na versão anterior passa com uma linha WARN: e sem falha. Para uma versão comum, ambos devem avançar juntos, então leia a saída do script em vez de confiar no código de saída. CHANGELOG.md não é verificado de forma alguma.

  2. Atualize CHANGELOG.md.

  3. Faça o commit e mescle o aumento de versão em main antes de criar o release. Em seguida, crie a tag você mesmo, em um SHA que você verificou, e só então crie o release a partir dela:

    V=X.Y.Z && PR=<release-pr-number> &&
      SHA="$(gh pr view "$PR" --json mergeCommit -q .mergeCommit.oid)" && test -n "$SHA" &&
      git fetch origin main && git merge-base --is-ancestor "$SHA" origin/main &&
      PKG="$(git show "$SHA:package.json")" &&
      test "$(printf '%s' "$PKG" | node -pe 'JSON.parse(require("fs").readFileSync(0,"utf8")).version')" = "$V" &&
      CL="$(git show "$SHA:CHANGELOG.md")" &&
      printf '%s\n' "$CL" | awk -v v="$V" 'index($0,"## ["v"]")==1{f=1;next} /^## \[/{f=0} /^\[[0-9]+\.[0-9]+\.[0-9]+\]:/{f=0} f' > "/tmp/notes-v$V.md" &&
      grep -q '[^[:space:]]' "/tmp/notes-v$V.md" &&
      git tag -a "v$V" "$SHA" -m "v$V" &&
      git push origin "v$V" &&
      gh release create "v$V" --verify-tag --notes-file "/tmp/notes-v$V.md"
    

    A falha que isso evita: sem uma tag existente, gh release create coloca uma na ponta do branch padrão, então executá-lo enquanto o aumento ainda está em um branch de release marca o commit da versão anterior. O workflow então publica qualquer versão que encontrar naquele commit package.json, e você obtém um GitHub Release vX.Y.Z que silenciosamente republica a versão antiga. Desde a 5.2.0, o próprio workflow de publicação se recusa a continuar quando GITHUB_REF_NAME não é v<package.json version> (#103), então um release com tag incorreta agora falha antes de npm publish em vez de republicar silenciosamente. Essa proteção só é acionada quando o workflow já está em execução — a sequência acima é o que impede que o commit errado seja marcado em primeiro lugar, então continue usando-a em vez de confiar no workflow para detectar o erro.

    Cada elemento é essencial:

    • gh pr view … .mergeCommit.oid nomeia o commit de squash do próprio PR do release. Não substitua por git rev-parse origin/main — isso é apenas o que estiver em main no momento em que você olhar, então um merge não relacionado que chegar no intervalo será marcado e publicado em seu lugar. gh sai com 0 e não imprime nada para um PR não mesclado, daí o test -n explícito.
    • A cadeia && para na primeira falha em vez de cair na etapa irreversível. Ambas as chamadas git show são atribuídas a uma variável em vez de serem canalizadas diretamente, para que seu status de saída seja realmente verificado — um pipeline relata apenas o status do seu último comando, a menos que pipefail esteja definido, o que não é assumido aqui.
    • git merge-base --is-ancestor prova que o commit está realmente acessível a partir de main. A mera existência não é suficiente — um commit pode estar presente localmente porque algum outro branch foi buscado, e se seus arquivos de versão coincidirem, ele passaria por todas as verificações restantes.
    • O teste de versão lê package.json do commit alvo, não da árvore de trabalho, que ainda mostraria a versão correta enquanto $SHA apontasse para outro lugar.
    • O awk extrai a seção dessa versão do CHANGELOG.md do commit para --notes-file. Sem ele, o corpo do release é o que --notes-from-tag encontrar na anotação — para este fluxo, a string literal vX.Y.Z, que é uma nota de release ruim para qualquer versão e ativamente enganosa para uma major com uma mudança que quebra compatibilidade. Ele para no próximo cabeçalho ## [ ou na primeira definição de referência de link, porque a entrada mais antiga do arquivo não tem cabeçalho depois dela e, de outra forma, engoliria todo o bloco de referências de link. grep -q em vez de test -s protege o resultado: uma seção que está vazia exceto por sua linha em branco ainda produz um arquivo de um byte, que test -s aceita.
    • --verify-tag faz gh abortar em vez de inventar uma tag se o push não chegou — a proteção contra gh cair no comportamento de ponta-do-branch-padrão descrito acima.

    Se gh release create falhar depois que a tag já foi enviada, não execute todo o bloco novamente — ele parará em git tag, o que está correto. Execute novamente apenas o comando final.

  4. O workflow Publish to npm + MCP Registry é executado automaticamente: ele npm publish com proveniência, consulta o registro até que o tarball esteja disponível e então envia o server.json correspondente para o MCP Registry via mcp-publisher.

O workflow ignora npm publish de forma limpa se a versão já estiver no npm (proteção de transição para releases que foram parcialmente publicados manualmente).

Autenticação de publicação — npm Trusted Publishing (sem token)

A publicação usa npm Trusted Publishing via OIDC — não há segredo NPM_TOKEN. A permissão id-token: write do workflow é trocada por um token de publicação de uso único e curta duração no momento da publicação, usando a vinculação de editor confiável configurada para @lazyants/lexware-mcp-server na interface web do npm. A única configuração necessária é essa vinculação de editor confiável no npm; nada precisa ser armazenado nos segredos do repositório.

Aviso legal

Este é um projeto comunitário independente e não oficial. Não é afiliado, endossado, patrocinado ou apoiado pela Lexware GmbH, Haufe Group ou qualquer uma de suas afiliadas. Para suporte oficial da Lexware, entre em contato diretamente com a Lexware — problemas com este servidor MCP devem ser relatados aqui, não à Lexware.

"Lexware" e "Lexware Office" são marcas registradas de seus respectivos proprietários e são usadas no nome e na documentação deste projeto sob uso justo nominativo, apenas para identificar a API de terceiros à qual este cliente se conecta.

Operações de criar, atualizar e excluir modificam dados comerciais reais na sua conta Lexware. Os autores fornecem este software "no estado em que se encontra" e não aceitam responsabilidade por alterações não intencionais, perda de dados ou quaisquer outros danos decorrentes do seu uso. Teste em uma conta sandbox ou não crítica antes de executar operações de escrita em dados de produção.

Licença

FSL-1.1-MIT — consulte LICENSE para os termos completos.