Command Palette

Search for a command to run...

UnylyUnyly
Browse all

LibreWriter

FreeNot checked

Local stdio MCP server to read and edit open LibreOffice Writer documents through PyUNO. Built for Claude Code and Codex.

GitHubEmbed

About

Local stdio MCP server to read and edit open LibreOffice Writer documents through PyUNO. Built for Claude Code and Codex.

README

Servidor MCP stdio local para ler e modificar documentos abertos no LibreOffice Writer através de PyUNO.

Requisitos validados

  • Python 3.12.3.
  • MCP SDK Python 2.3.0, com a API mcp.server.mcpserver.MCPServer inspecionada neste ambiente.
  • PyUNO compatível com o LibreOffice de sistema.
  • LibreOffice Writer 24.2.7.2, lançado por /usr/bin/soffice.
  • Sessão gráfica X11 para o launcher e os testes GUI. O Writer não é iniciado em modo headless.

Estas são as versões efetivamente validadas. Não mistures PyUNO com outra instalação; o wrapper soffice no PATH pode apontar para LibreOfficeDev.

Instalação

O projeto já contém .venv. Só executa a primeira linha se o ambiente ainda não existir; --system-site-packages é necessário para expor o PyUNO de sistema:

cd /home/yberudev/Documentos/MCP_Project/LibreOffice/Writer/MCPProj
python3.12 -m venv --system-site-packages .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python -m pip install -e .

Para executar também a integração GUI, instala a dependência Python de teste: .venv/bin/python -m pip install -e '.[test]'. O wmctrl é uma dependência de sistema (por exemplo, sudo apt install wmctrl em Debian/Ubuntu).

PyUNO deve corresponder ao LibreOffice instalado. Em Debian/Ubuntu costuma ser fornecido por python3-uno; a exposição ao venv varia por distribuição. Verifica:

/usr/bin/soffice --version
.venv/bin/python -c 'import uno; from mcp.server.mcpserver import MCPServer; print(uno.__file__, MCPServer)'

Iniciar Writer com listener local (Modo B)

O launcher usa /usr/bin/soffice, cria uma sessão gráfica com perfil isolado e escuta apenas em 127.0.0.1. Sem --profile, usa ~/.local/share/yberu-writer-mcp/profile-PORTA.

cd /home/yberudev/Documentos/MCP_Project/LibreOffice/Writer/MCPProj
.venv/bin/python scripts/launch_writer.py --port 2002 --profile "$HOME/.local/share/yberu-writer-mcp/profile" --wait 25 --json

O comando devolve PID, porta e perfil. O Writer continua aberto quando o MCP termina; fecha esta sessão pela interface gráfica. Para uma segunda sessão, configura outra porta e perfil. O launcher recusa porta ocupada. Não usa --headless e não força SAL_USE_VCLPLUGIN=gen.

Uma janela Writer normal não aceita automaticamente ligações UNO externas.

Ligar a uma sessão existente (Modo A)

Só funciona se a instância já tiver sido iniciada com listener UNO local. Configura YBERU_WRITER_PORT para a porta desse listener. Acrescentar --accept depois de uma sessão normal estar aberta pode apenas encaminhar o pedido para a instância sem criar o listener. O servidor não encerra sessões externas.

Iniciar o servidor MCP

O stdout é reservado às mensagens MCP; os logs técnicos vão para stderr.

cd /home/yberudev/Documentos/MCP_Project/LibreOffice/Writer/MCPProj
PYTHONPATH=/home/yberudev/Documentos/MCP_Project/LibreOffice/Writer/MCPProj/src \
YBERU_WRITER_HOST=127.0.0.1 YBERU_WRITER_PORT=2002 \
.venv/bin/python -m yberu_writer_mcp.server

Normalmente o Codex inicia este processo. Terminar o processo MCP remove listeners quando possível, mas não fecha o Writer.

Registar no Codex

Neste ambiente, yberu_writer foi registado em ~/.codex/config.toml, com o ambiente virtual, caminhos absolutos, timeouts e política default_tools_approval_mode = "writes". Esse registro global permite descobrir as ferramentas também quando o workspace aberto no Codex é a pasta pai Writer. O arquivo local .codex/config.toml é específico desta máquina e fica fora do Git. Para instalar noutro checkout, adapta docs/codex-mcp-config.example.toml. As ferramentas de leitura/prévia declaram-se read-only; as mutações declaram-se mutáveis/destrutivas para que o Codex peça aprovação segundo essa política.

O Codex carrega configurações .codex/ locais somente para projetos confiáveis. Este repositório está marcado como confiável em ~/.codex/config.toml; a configuração global do servidor faz com que ele também seja carregado fora deste projeto. A documentação oficial do MCP no Codex descreve a configuração local e a condição de projeto confiável.

Confirma a descoberta com codex mcp list e, no Codex, chama writer_get_state, writer_list_documents e writer_read_document sobre um documento de teste. Para verificar escrita, lê writer_read_structure, gera uma prévia com writer_preview_change e confirma a operação com o token devolvido. O CLI já lista yberu_writer como habilitado neste ambiente.

Neste ambiente, o Codex usa pastas dedicadas: /home/yberudev/.local/share/yberu-writer-mcp/saves para gravação, /home/yberudev/.local/share/yberu-writer-mcp/images para imagens e /home/yberudev/.local/share/yberu-writer-mcp/templates para modelos. Para permitir gravação noutro ambiente, configura YBERU_WRITER_ALLOWED_SAVE_ROOTS com pastas locais existentes. writer_save_document grava um ficheiro existente; writer_save_as grava num destino novo, aceita ODT/DOCX/RTF/TXT e exige confirmação adicional para substituir um destino existente. Sem allowlist, ambas recusam a gravação.

Para inserir imagens, define YBERU_WRITER_ALLOWED_IMAGE_ROOTS com uma ou mais pastas locais autorizadas. Neste ambiente foi preparada /home/yberudev/.local/share/yberu-writer-mcp/images; cria a pasta e coloca nela a imagem que pretendes usar. O servidor aceita PNG, JPEG, GIF e BMP até YBERU_WRITER_MAX_IMAGE_BYTES. A imagem é incorporada no documento, não ligada ao caminho original.

Ferramentas disponíveis

Ferramenta Função
writer_get_state Ligação, versão LibreOffice, documento ativo, contagem e capacidades.
writer_list_documents IDs opacos, título, modificado, leitura/escrita e revisão.
writer_read_document Texto em fatias limitadas sem montar outra cópia integral; revisão, hash e referência de conteúdo.
writer_read_selection Seleção textual, múltiplos ranges, não textual ou nenhuma seleção.
writer_read_structure Parágrafos, títulos, níveis, estilos, tabelas e referências separadas para frames, bookmarks, secções, cabeçalhos/rodapés, notas e campos quando expostos pelo UNO.
writer_read_text_range Lê intervalo delimitado por âncoras de parágrafo e offsets Unicode.
writer_preview_change Valida uma escrita sem mutar e emite token de confirmação válido por 120 segundos.
writer_insert_text Insere no início/fim/offset de um parágrafo ancorado após confirmação da prévia.
writer_replace_range Substitui exatamente um parágrafo ou intervalo lido após confirmação da prévia.
writer_read_table Lê matriz e hashes das células de uma tabela ancorada.
writer_replace_table_cell Substitui uma célula identificada, verificando revisão e conteúdo esperado.
writer_apply_style Aplica estilo de parágrafo existente a um parágrafo ancorado.
writer_save_document Guarda ficheiro local existente, alterado e sob raiz autorizada.
writer_undo_last_change Desfaz a última ação MCP só se continuar no topo da pilha Writer.
writer_preview_create_document Valida e mostra a prévia de um documento novo sem ainda o criar.
writer_create_document Cria um documento Writer novo, inicialmente sem gravação em disco, com parágrafos opcionais.
writer_preview_compose_document / writer_compose_document Pré-visualiza e cria um documento a partir de blocos de títulos, parágrafos, tabelas e imagens.
writer_apply_formatting Aplica negrito, itálico, sublinhado, fonte, tamanho, cor e alinhamento a um parágrafo ancorado.
writer_insert_table Insere tabela nativa após um parágrafo ancorado, com dimensões e valores de células opcionais.
writer_insert_image Incorpora uma imagem local autorizada após um parágrafo, com texto alternativo e dimensões opcionais.
writer_edit_table_structure Insere/remove linhas ou colunas após mostrar os valores afetados.
writer_format_table_cell Formata uma célula específica sem alterar o texto ou as outras células.
writer_update_image Atualiza texto alternativo, dimensões ou alinhamento de uma imagem identificada.
writer_preview_create_from_template / writer_create_from_template Pré-visualiza e cria um documento novo a partir de um ODT/OTT autorizado.
writer_save_as Grava um documento aberto num novo destino local dentro da allowlist.

Eventos MCP

writer_list_documents inclui um event_uri por documento. Clientes que suportam subscriptions/listen podem subscrever esse URI e recebem uma notificação ResourceUpdated quando o Writer sinaliza alteração, seleção ou ciclo de vida. A notificação indica que o estado mudou; não contém texto antigo/novo. Depois da notificação, lê o recurso para obter a sequência, os tipos coalescidos e a revisão atual:

from mcp.client.subscriptions import listen

async with listen(session, resource_subscriptions=[document["event_uri"]]) as events:
    event = await anext(events)
    latest = await session.read_resource(event.uri)

O recurso yberu-writer://documents/{document_id}/events contém apenas metadados, sem o texto do documento. A fila coalesce eventos e não guarda histórico; após reconexão ou perda da subscrição, volta a ler a lista/estado. Clientes que não suportam subscriptions/listen podem continuar a ler as ferramentas normalmente, mas não recebem atualizações push.

Fluxo de edição

  1. Usa writer_list_documents para escolher o documento.
  2. Usa writer_read_structure para obter revision, anchor e texto.
  3. Chama writer_preview_change com a operação e argumentos exatos. A ferramenta não altera o documento; mostra o antes/depois e emite um token ligado ao payload, revisão e documento.
  4. Chama a ferramenta de escrita correspondente com esse confirmation_token, um operation_id UUID novo e os mesmos argumentos. Todos os endpoints de escrita recusam chamadas sem token válido.
  5. Para intervalos, obtém range_anchor por writer_read_text_range. Para tabelas, lê a estrutura/célula e passa a célula e conteúdo esperado à prévia.
  6. Relê a estrutura para confirmar e obter âncoras novas.

Para reverter uma ação MCP, chama writer_preview_change com change_type="undo_last_change" e a revisão atual; confirma com writer_undo_last_change, usando target_operation_id e token devolvidos. O undo recusa-se a agir se houver uma ação do utilizador no topo da pilha Writer. Tokens expiram após 120 segundos, são de uso único e ficam apenas em memória do processo. Uma repetição com o mesmo operation_id continua idempotente.

Âncoras de parágrafo, intervalo e tabela são temporárias e não podem ser reutilizadas após edição/revisão. Escritas obsoletas retornam CONFLICT; o servidor nunca procura texto aproximado. Intervalos que atravessam estruturas Writer não suportadas são recusados com RANGE_UNSUPPORTED.

Criar e compor um documento

  1. Chama writer_preview_create_document com title e, opcionalmente, initial_paragraphs.
  2. Chama writer_create_document com os mesmos valores, o confirmation_token da prévia e um operation_id UUID novo. A resposta contém o document_id, a revisão inicial e o hash do snapshot.
  3. Chama writer_read_structure para obter uma âncora de parágrafo e a revisão atual.
  4. Para uma tabela, chama writer_preview_change com change_type="insert_table", documento, âncora, revisão, número de linhas/colunas e, se desejado, cell_values; depois confirma com writer_insert_table usando os mesmos argumentos, token e UUID.
  5. Para uma imagem, chama writer_preview_change com change_type="insert_image", image_path dentro da allowlist e alt_text; depois chama writer_insert_image com o mesmo caminho, o image_hash devolvido pela prévia como expected_image_hash, além do token e UUID. width_mm e height_mm são opcionais, mas devem ser fornecidos juntos.
  6. Relê a estrutura/tabela para confirmar o resultado e obter âncoras e revisão atualizadas.

Tabelas aceitam de 1 a 100 linhas/colunas, limitadas também por YBERU_WRITER_MAX_TABLE_CELLS (padrão 500 células). A criação do documento não escolhe um caminho nem grava automaticamente; o novo documento permanece aberto e sem nome de ficheiro até ser guardado manualmente no Writer.

Para criar uma composição completa, envia blocks como lista ordenada. Cada bloco tem type e campos específicos: paragraph usa text; heading usa text e level (1–10); table usa uma matriz rows; image usa image_path, opcionalmente alt_text, width_mm e height_mm. A mesma prévia confirma o documento inteiro. Se uma composição falhar, o servidor tenta fechar o documento parcial que acabou de criar.

Formatação e elementos estruturais

Para formatar um parágrafo ou célula, obtém texto, âncora e revisão atuais, gera writer_preview_change com change_type="apply_formatting" ou "format_table_cell" e confirma com a ferramenta correspondente. As propriedades aceites são bold, italic, underline, font_name, font_size_pt (6–96), font_color (#RRGGBB) e alignment (left, center, right, justify).

Para ajustar tabela, usa change_type="edit_table_structure", table_operation (insert_rows, delete_rows, insert_columns, delete_columns), index zero-based e count. A prévia mostra dimensões e conteúdo removido; a confirmação inclui o hash da tabela. A operação mantém pelo menos uma linha e uma coluna.

Para atualizar uma imagem, usa o object_ref e a revisão devolvidos por writer_read_structure. A prévia change_type="update_image" devolve image_record_hash; passa-o como expected_image_hash para confirmar a atualização. Imagens continuam incorporadas no documento.

Criar a partir de modelos

Configura YBERU_WRITER_ALLOWED_TEMPLATE_ROOTS com as pastas de modelos autorizadas. writer_preview_create_from_template valida o pacote, tamanho e hash; writer_create_from_template abre uma cópia editável sem gravar sobre o modelo. São aceites .odt e .ott, com execução de macros desativada durante o carregamento. O título pode ser definido na prévia.

Configuração do servidor

Variável Padrão Descrição
YBERU_WRITER_HOST 127.0.0.1 Loopback apenas (127.0.0.1, localhost, ::1).
YBERU_WRITER_PORT 2002 Porta do listener UNO.
YBERU_WRITER_SOFFICE /usr/bin/soffice Executável usado pelo launcher.
YBERU_WRITER_ALLOWED_SAVE_ROOTS vazio Raízes permitidas para guardar documentos existentes.
YBERU_WRITER_ALLOWED_TEMPLATE_ROOTS vazio Raízes permitidas para modelos ODT/OTT.
YBERU_WRITER_ALLOWED_IMAGE_ROOTS vazio Raízes locais permitidas para inserir imagens.
YBERU_WRITER_MAX_IMAGE_BYTES 10000000 Tamanho máximo de uma imagem de entrada.
YBERU_WRITER_MAX_TEMPLATE_BYTES 50000000 Tamanho máximo compactado do modelo.
YBERU_WRITER_MAX_TABLE_CELLS 500 Número máximo de células por tabela inserida.
YBERU_WRITER_MAX_READ_CHARS 20000 Máximo por fatia de leitura.
YBERU_WRITER_MAX_SNAPSHOT_CHARS 5000000 Limite de snapshot para revisão/escrita segura.
YBERU_WRITER_MAX_STRUCTURE_ITEMS 2000 Máximo de parágrafos na estrutura.
YBERU_WRITER_MAX_TEXT_CHARS 100000 Máximo para texto de entrada.
YBERU_WRITER_LOG_LEVEL INFO Nível dos logs stderr.

Segurança e erros

As respostas de erro têm código, mensagem, retryability e detalhes quando aplicável. Incluem NOT_CONNECTED, DOCUMENT_NOT_FOUND, INVALID_ANCHOR, CONFLICT, PERMISSION_DENIED, CONFIRMATION_REQUIRED, UNDO_NOT_AVAILABLE, UNKNOWN_OUTCOME, INVALID_ARGUMENT, SIZE_LIMIT e RANGE_UNSUPPORTED.

Um worker serializa UNO. Callbacks UNO só enfileiram notificações; eventos coalescidos invalidam snapshots e atualizam a revisão local. XModifyListener não representa deltas. IDs ficam inválidos após reconexão. Escritas exigem uma prévia correspondente, token de confirmação de uso único e UUID de idempotência; resultado incerto não é repetido automaticamente.

Não há ferramentas de macros, shell, dispatch arbitrário, código Python ou métodos UNO genéricos. Gravação aceita apenas caminhos locais dentro de raízes autorizadas; destinos remotos não são suportados.

Testes

Testes unitários:

cd /home/yberudev/Documentos/MCP_Project/LibreOffice/Writer/MCPProj
PYTHONPATH=src .venv/bin/python -m unittest discover -s tests/unit -v

Integração com Writer gráfico isolado, cliente MCP stdio real, entrada de teclado X11 e reinício da sessão criada pelo teste:

cd /home/yberudev/Documentos/MCP_Project/LibreOffice/Writer/MCPProj
YBERU_RUN_LIVE_TESTS=1 PYTHONPATH=src .venv/bin/python -m unittest discover -s tests/integration -v

O teste GUI requer DISPLAY, wmctrl e Python Xlib. Usa documentos temporários e termina apenas o grupo de processos que ele iniciou.

Limitações conhecidas

  • Edição de intervalo cobre texto do corpo delimitado por âncoras de parágrafo. Intervalos que cruzam estruturas internas Writer não suportadas são recusados. As operações de tabela cobrem células nomeadas de tabelas de primeiro nível; tabelas aninhadas e cabeçalhos/rodapés/frames ainda não são alvo de edição.
  • Tabelas são representadas por referências e células; não são achatadas em texto corrido. Frames, bookmarks, secções, cabeçalhos/rodapés, notas e campos são descritos na estrutura, mas ainda não são alvos de edição.
  • Revisões são fingerprints locais, não transações nativas. Snapshots são reutilizados até um evento de modificação invalidá-los; cada escrita força uma nova leitura completa imediatamente antes da mutação. Uma alteração externa seguida de restauração textual idêntica entre verificações pode não ser detetada. Não é prometida atomicidade ACID.
  • Snapshots acima de YBERU_WRITER_MAX_SNAPSHOT_CHARS continuam disponíveis em leitura paginada, mas revisão completa e escritas seguras ficam desativadas para esse documento.
  • Eventos MCP são notificações coalescidas de recurso, sem histórico e sem deltas de texto. Clientes sem subscriptions/listen dependem de polling.
  • writer_save_document grava ficheiros existentes; writer_save_as aceita apenas ODT, DOCX, RTF e TXT em raízes autorizadas. URLs remotas e gravação fora da allowlist não são suportados. A gravação não promete atomicidade ACID.
  • writer_create_document e writer_compose_document deixam o documento aberto e não guardado. Tabelas são de primeiro nível e limitadas a 100 linhas/colunas e ao limite total de células. Imagens locais devem estar numa raiz autorizada, ter até ao tamanho configurado e estar num formato PNG, JPEG, GIF ou BMP reconhecido.
  • Formatação cobre parágrafos inteiros e células de tabelas de primeiro nível; formatação parcial de texto dentro do mesmo parágrafo ainda não é exposta. Atualização de imagem cobre texto alternativo, dimensões e alinhamento do parágrafo; legendas dedicadas e edição de recorte ainda não estão disponíveis.
  • Modelos aceites são pacotes .odt/.ott válidos dentro de YBERU_WRITER_ALLOWED_TEMPLATE_ROOTS. A allowlist fica vazia por padrão.
  • Undo cobre só a última mutação MCP verificada e apenas enquanto ela continuar no topo da pilha nativa do Writer. Guardar ficheiro não é reversível pelo undo do Writer.
  • Tokens de prévia expiram em 120 segundos e não sobrevivem ao reinício do MCP.
  • Modo A depende de listener ativo; Writer aberto normalmente não basta.

Resolução de problemas

  • connected=false / NOT_CONNECTED: confirma listener e que host/porta coincidem.
  • Porta ocupada: seleciona outra porta no launcher e no Codex.
  • Erro ao importar uno: alinha PyUNO com /usr/bin/soffice 24.2; não uses o wrapper LibreOfficeDev.
  • Ferramentas não aparecem: verifica caminhos absolutos, PYTHONPATH, recarrega Codex e executa codex mcp list.
  • CONFLICT ou INVALID_ANCHOR: relê writer_read_structure e usa revisão/âncora atuais.
  • Gravação recusada: verifica URL local existente e YBERU_WRITER_ALLOWED_SAVE_ROOTS.
  • Falha de teste GUI: verifica X11, wmctrl, DISPLAY e criação da janela com perfil de teste.

Relatório de implementação

Resultados, matriz de testes e limitações pendentes: docs/IMPLEMENTACAO_MVP.md. Os relatórios originais continuam em docs/audit/.

from github.com/yberudev/LibreWriter-MCP

Installing LibreWriter

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/yberudev/LibreWriter-MCP

FAQ

Is LibreWriter MCP free?

Yes, LibreWriter MCP is free — one-click install via Unyly at no cost.

Does LibreWriter need an API key?

No, LibreWriter runs without API keys or environment variables.

Is LibreWriter hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install LibreWriter in Claude Desktop, Claude Code or Cursor?

Open LibreWriter on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare LibreWriter with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All development MCPs