MCP File Tools: a IA sem corromper seu ISO-8859-1
Olá meus Unicórnios! 🦄✨
Se você mantém site antigo em PHP, provavelmente já viveu esta cena: pede uma alteração simples para a IA, ela responde que está tudo certo, você sobe o arquivo e a página aparece com "configuraçao", "Promoçao de Verao", ou aquele losango preto com uma interrogação dentro. 😱
A primeira reação é culpar o navegador. Depois o banco. Depois o servidor. E o culpado, na verdade, é bem mais chato que qualquer um dos três: a ferramenta de leitura da IA destruiu os acentos antes mesmo de ela começar a pensar no seu pedido.
Eu trabalho com uma família de sites que é inteirinha ISO-8859-1 (o velho latin1). São sistemas com mais de dez anos de estrada, banco em latin1, arquivos em latin1, tudo coerente entre si e funcionando muito bem. O que não funciona é abrir esses arquivos com uma ferramenta que presume UTF-8, e é exatamente isso que a IA faz por padrão.
Neste artigo eu mostro o estrago acontecendo byte a byte, e a solução que uso hoje: o MCP File Tools, um servidor MCP que ensina a IA a respeitar o encoding do seu arquivo. 🪄
💥 O estrago, byte a byte
Nada de teoria. Vamos criar um arquivo de verdade, em ISO-8859-1, e ver o que acontece com ele.
Este é o arquivo de teste, um config.php bem comum de site brasileiro:
<?php
// Configuração da loja
$titulo = "Promoção de Verão";
$aviso = "Endereço não confirmado. Ação necessária.";
Gravado em ISO-8859-1, ele tem 120 bytes. E o detalhe que interessa está nos bytes do comentário. Vou mostrar em hexadecimal, que é onde a verdade mora:
3c3f7068700d0a2f2f20436f6e666967757261e7e36f206461206c6f6a610d0a
Repare no miolo: 436f6e666967757261 é a palavra Configura, e logo depois vêm e7 e e3. Esses dois bytes são o ç e o ã. Um byte para cada letra, que é a definição do ISO-8859-1: cada caractere ocupa exatamente um byte.
Guarde esse e7e3 na memória. Ele é o personagem principal da história. 🎭
🔍 O que a IA vê quando abre esse arquivo
Agora eu abri esse mesmo arquivo com a ferramenta de leitura padrão da IA, a que vem ligada por natureza. Foi isto que apareceu na tela:
<?php
// Configura��o da loja
$titulo = "Promo��o de Ver�o";
$aviso = "Endere�o n�o confirmado. A��o necess�ria.";
Cada acento virou um losango com interrogação. Esse caractere tem nome: é o U+FFFD, o "caractere de substituição" do Unicode. Ele é o jeito que um leitor de UTF-8 tem de dizer "achei um byte aqui que não sei ler".
E é aqui que mora a crueldade da coisa: a leitura não deu erro. Nenhum aviso, nenhuma exceção, nenhum código de saída diferente de zero. Para a ferramenta, aquilo foi um sucesso. Ela leu o arquivo, entregou o conteúdo, e seguiu a vida.
💀 A gravação, que é quando o dano vira permanente
Ler errado ainda é reversível: é só ler de novo do jeito certo. O problema começa quando a IA grava o arquivo de volta.
Eu pedi uma alteração inocente naquele arquivo, daquelas que a gente faz sem pensar: trocar Verão por Inverno no título. A ferramenta padrão respondeu que o arquivo foi atualizado com sucesso. Fui conferir os bytes:
3c3f7068700d0a2f2f20436f6e666967757261efbfbdefbfbd6f206461206c6f6a610d0a
Compare com o de antes, na mesma altura da palavra Configura:
antes: Configura e7 e3 o da loja 2 bytes de acento
depois: Configura efbfbd efbfbd o da loja 6 bytes de lixo
Os dois bytes viraram seis. O efbfbd é o U+FFFD codificado em UTF-8, e ele aparece duas vezes: uma no lugar do ç, outra no lugar do ã. 😳
Lendo o arquivo do jeito que o site lê (como latin1), o resultado é este:
<?php
// Configura��o da loja
$titulo = "Promo��o de Inverno";
$aviso = "Endere�o n�o confirmado. A��o necess�ria.";
Olhe a última linha com carinho. Eu nunca pedi para mexer no $aviso. Pedi para trocar uma palavra no $titulo, e mesmo assim a linha do aviso foi destruída junto. É assim que funciona: a ferramenta lê o arquivo inteiro, aplica sua alteração no texto já estragado, e grava o arquivo inteiro de volta. Todo acento do arquivo entra na conta, não só os das linhas que você tocou.
⚰️ Por que não adianta "converter de volta"
A pergunta natural aqui é: se o arquivo virou UTF-8 estragado, por que não converter de UTF-8 para latin1 e pronto?
Porque não sobrou o que converter. Repare de novo nos seis bytes:
efbfbd efbfbd
| |
| +-- era um "ã"? um "ç"? um "é"? não dá para saber
+--------- idêntico ao vizinho, e a todos os outros do arquivo
Todos os U+FFFD do arquivo são exatamente iguais entre si. O que diferenciava um ç de um ã era justamente o byte que foi jogado fora. Uma conversão pega esse lixo e produz outro lixo, com o mesmo nada de informação dentro.
Não existe desfazer. Existe restaurar do Git, ou do backup, e torcer para ter um. É por isso que este assunto merece um artigo inteiro: a proteção precisa estar no lugar antes do primeiro pedido, não depois do primeiro susto. 🙏
🛠️ Instalando o MCP File Tools
O MCP File Tools é um servidor MCP (Model Context Protocol), que é o padrão pelo qual a IA ganha ferramentas novas. Ele oferece uma família própria de ferramentas de arquivo, todas conscientes de encoding, e é escrito em Go, então roda como um executável só, sem instalar runtime nenhum.
O jeito mais fácil é pelo marketplace de plugins. São dois comandos, digitados dentro da própria conversa com a IA:
/plugin marketplace add dimitar-grigorov/mcp-file-tools
/plugin install mcp-file-tools
O primeiro comando cadastra o repositório como fonte de plugins. O segundo instala. O binário certo para o seu sistema operacional é baixado sozinho na primeira execução.
Se preferir instalar na mão, o projeto publica os executáveis prontos na página de releases: mcp-file-tools_windows_amd64.exe para Windows, mcp-file-tools_linux_amd64 para Linux e mcp-file-tools_darwin_arm64 para Mac com chip Apple. Baixe o seu, guarde numa pasta fixa, e siga para a configuração abaixo.
📁 Os diretórios permitidos, que é onde todo mundo tropeça
Esta é a seção do artigo. Se você só for ler um pedaço, leia este. 🙏
O MCP File Tools não enxerga o seu disco inteiro, e isso é proposital: ele só alcança as pastas que você explicitamente liberou. A lista dessas pastas vai no campo args da configuração, e é a causa número um de erro em toda instalação nova.
A configuração fica num arquivo chamado .claude.json. Ele não aparece em pasta nenhuma de programa: mora na raiz da sua pasta de usuário, que é aquela com o seu nome. Onde exatamente, em cada sistema:
Windows C:\Users\SEU_USUARIO\.claude.json
Linux /home/seu_usuario/.claude.json
macOS /Users/seu_usuario/.claude.json
Repare no ponto na frente do nome. Ele não é enfeite: no Linux e no Mac, um arquivo que começa com ponto é oculto, e some do ls comum (para vê-lo, use ls -la). No Windows ele aparece normalmente, mas fica no meio de dezenas de outros arquivos escondidos da pasta de usuário.
O jeito mais rápido de abri-lo é não procurar à mão. No Windows, aperte Win + R, cole a linha abaixo e dê Enter: o %USERPROFILE% é uma variável que o próprio Windows troca pelo caminho da sua pasta, então você não precisa saber o seu nome de usuário. 😉
notepad %USERPROFILE%\.claude.json
No Linux ou no Mac, o mesmo pelo terminal (o ~ é o atalho para a sua pasta de usuário):
nano ~/.claude.json
Se o arquivo ainda não existir, é porque a IA nunca gravou configuração nenhuma: pode criá-lo vazio, com o conteúdo abaixo inteiro. E se ele já existir e estiver cheio de coisa, não apague o que está lá. Procure a chave mcpServers, que é onde ficam os servidores, e acrescente o bloco file-tools dentro dela, junto dos que já estiverem configurados. Se você instalou pelo marketplace, essa entrada já foi criada, e o que falta é só acrescentar as suas pastas:
{
"mcpServers": {
"file-tools": {
"command": "C:\\Users\\SEU_USUARIO\\AppData\\Local\\Programs\\mcp-file-tools\\mcp-file-tools.exe",
"args": [
"D:\\Sites\\meu-site",
"C:\\projetos\\loja"
]
}
}
}
O command é o caminho do executável. O args é a lista de pastas liberadas, uma por linha. Subpastas entram junto automaticamente: liberar D:\Sites\meu-site libera tudo que está dentro dele.
Se você errar essa parte, o sintoma é claro e o servidor avisa direitinho. Foi o que aconteceu comigo quando pedi um arquivo numa pasta que eu não tinha liberado:
access denied - path outside allowed directories:
C:\Users\meu-usuario\AppData\Local\Temp\config.php
Nada de mistério: a pasta não estava na lista. Acrescentei o caminho no args, reiniciei a IA, e funcionou. Quando nenhuma pasta é configurada, a mensagem que aparece é a irmã dessa, o famoso no allowed directories configured, e a cura é a mesma. 🔑
Para conferir quais pastas o servidor está enxergando agora, peça para a IA rodar a ferramenta list_allowed_directories. Ela responde a lista exata que está valendo, e resolve a dúvida em cinco segundos.
🧰 As ferramentas que passam a existir
Com o servidor de pé, a IA ganha uma família nova de ferramentas, todas com o prefixo mcp__file-tools__. São vinte no total, mas o dia a dia de quem cuida de site latin1 se resolve com cinco:
detect_encoding diz qual e o encoding do arquivo, com um grau de confianca
read_text_file le no encoding certo e devolve o texto em UTF-8
edit_file edita preservando o encoding original do arquivo
write_file cria arquivo novo no encoding que voce pedir
grep_text_files busca com regex, respeitando o encoding
A lógica de todas é a mesma: elas tratam o encoding como uma propriedade do arquivo, e não como uma suposição do programa. A conversão para UTF-8 acontece na memória, para a IA entender o texto, e a volta para o encoding original acontece na gravação.
São 24 encodings suportados. Para site em português interessam três: iso-8859-1 (que aceita os apelidos latin1 e iso88591), windows-1252 (apelido cp1252) e o utf-8. O resto da lista cobre cirílico, grego, turco, chinês, hebraico, árabe, báltico, vietnamita e tailandês.
✅ O mesmo arquivo, agora com as ferramentas certas
Voltei o config.php ao estado original, com os e7e3 intactos, e refiz tudo pelo caminho certo.
Primeiro o diagnóstico, com o detect_encoding:
{
"confidence": 73,
"encoding": "iso-8859-1",
"has_bom": false
}
Acertou o encoding, e informou de quebra que o arquivo não tem BOM. Segure o número 73, que ele volta a aparecer daqui a pouco com uma lição junto.
Depois a leitura, com o read_text_file:
{
"content": "<?php\r\n// Configuração da loja\r\n$titulo = \"Promoção de Verão\";\r\n$aviso = \"Endereço não confirmado. Ação necessária.\";\r\n",
"detectedEncoding": "iso-8859-1",
"encodingConfidence": 73,
"endLine": 5,
"fileSizeBytes": 120,
"startLine": 1,
"totalLines": 5
}
Os acentos chegaram inteiros. 🎉 Configuração, Promoção, Verão, Endereço, Ação, necessária. Todos eles, com a cedilha e o til no lugar. Agora sim a IA está raciocinando sobre o seu código de verdade, e não sobre uma versão mutilada dele.
Agora a mesma alteração de antes, trocar Verão por Inverno, mas com o edit_file. Ele devolve um diff do que mudou:
--- D:/Sites/meu-site/config.php
+++ D:/Sites/meu-site/config.php
@@ -1,5 +1,5 @@
<?php
// Configuração da loja
-$titulo = "Promoção de Verão";
+$titulo = "Promoção de Inverno";
$aviso = "Endereço não confirmado. Ação necessária.";
Repare no que o diff está dizendo: uma linha saiu, uma linha entrou, e as outras três estão intocadas, com os acentos vivos. Compare com o estrago da versão anterior, onde a linha do $aviso morreu sem ter sido pedida.
E o teste final, nos bytes, que é o único que não mente:
3c3f7068700d0a2f2f20436f6e666967757261e7e36f206461206c6f6a610d0a
O e7e3 está lá, exatamente como no começo do artigo. O arquivo continua ISO-8859-1 de verdade, com um byte por acento, e o site vai servi-lo sem soluço nenhum. 🦄
📄 Criando arquivo novo já no encoding certo
Editar arquivo que já existe é metade do problema. A outra metade é criar arquivo novo: se a IA cria em UTF-8 dentro de um projeto latin1, você ganha um arquivo que destoa de todos os outros, e o bug aparece depois, quando alguém abrir aquela página.
Para isso existe o write_file, que recebe o encoding de destino. Pedi este conteúdo:
<?php
// Página de manutenção
$mensagem = "Serviço em atualização. Não recarregue a página.";
E a resposta confirma o encoding usado na gravação, o que é ótimo para não ficar na dúvida:
{
"message": "Successfully wrote 94 bytes to D:/Sites/meu-site/novo.php (encoding: iso-8859-1)"
}
Conferindo os bytes do começo do arquivo:
3c3f7068700a2f2f2050e167696e61206465206d616e7574656ee7e36f0a
Ali no meio está 50 e1 67 69 6e 61, que é a palavra Página: o P, depois o byte e1 sozinho fazendo o á, e o resto. Um byte por acento, do jeito que o projeto espera. E o php -l confirmou que o arquivo é PHP válido, sem erro de sintaxe. ✨
⚠️ A confiança de 73, e por que ela importa
Lembra do número 73 que apareceu duas vezes lá em cima? Ele merece um parágrafo próprio, porque me custou tempo até eu entender.
Aquele arquivo era ISO-8859-1 puro, gravado por mim, sem sombra de dúvida. Ainda assim a detecção respondeu 73 de confiança, e não 100. O motivo é que detecção de encoding é estatística, não leitura de etiqueta: o arquivo não carrega em lugar nenhum um campo dizendo qual é o seu encoding. O detector olha a distribuição dos bytes e dá o palpite mais provável.
Em arquivo pequeno e cheio de acento, o palpite acerta fácil. Em arquivo grande com poucos acentos, que é o caso de um PHP de mil linhas com dois comentários em português, aquele punhado de bytes acentuados vira ruído estatístico. Aí o detector pode responder gbk, ou outro encoding parecido, com uma confiança igualmente morna.
📝 Ensinando a regra no CLAUDE.md
Agora a parte que quase todo mundo pula, e que é o que separa ter a ferramenta de usar a ferramenta.
Instalar o servidor não muda o comportamento da IA sozinho. Depois da instalação ela passa a ter duas famílias de ferramentas de arquivo à disposição: a padrão, que corrompe, e a do MCP File Tools, que preserva. E na dúvida ela vai de padrão, porque é a que ela usa em todo projeto do mundo.
Quem resolve isso é o CLAUDE.md, o arquivo de instruções que fica na raiz do projeto e que a IA lê antes de trabalhar. É lá que a ferramenta certa deixa de ser opção e vira regra. Este é o trecho que eu coloco nos meus projetos latin1, e que você pode copiar inteiro:
## ENCODING: ISO-8859-1, SEM EXCEÇÕES
**TODOS os arquivos deste projeto são ISO-8859-1 (Latin-1).**
Isso inclui PHP, HTML, JS, CSS, SQL e TXT: qualquer arquivo de texto.
### Regra absoluta: NUNCA use as ferramentas padrão Read / Edit / Write / Grep
Elas assumem UTF-8 e corrompem os acentos de forma irreversível.
Use sempre as ferramentas do MCP file-tools:
| Operação | Ferramenta obrigatória |
|----------------|---------------------------------------------------------|
| Ler arquivo | mcp__file-tools__read_text_file |
| Editar arquivo | mcp__file-tools__edit_file |
| Criar/gravar | mcp__file-tools__write_file com encoding "iso-8859-1" |
| Buscar texto | mcp__file-tools__grep_text_files |
| Diagnóstico | mcp__file-tools__detect_encoding |
### HTML entities: nunca usar, preferir os caracteres diretos
O arquivo é gravado em ISO-8859-1, que já suporta todos os caracteres
do português. Não escreva ó nem ç: escreva ó e ç.
ERRADO: Próxima Área
CORRETO: Próxima Área
### A detecção pode dar falso-positivo
Arquivo grande com poucos acentos pode ser detectado como "gbk" ou outro
encoding, com confiança baixa. Não confie cegamente: passe sempre
encoding "iso-8859-1" explícito. A detecção serve só como diagnóstico.
### Se aparecerem caracteres corrompidos (?, xEF, xBF)
Você gravou em UTF-8 por engano. Rode detect_encoding e regrave em
ISO-8859-1. Confira também os arquivos vizinhos que foram tocados junto.
Repare que esse trecho faz três coisas, e cada uma resolve um problema diferente:
A tabela é a mais importante. Ela não diz "prefira" nem "quando possível": ela nomeia a ferramenta exata para cada operação. Instrução vaga em CLAUDE.md é instrução que a IA contorna quando tem pressa.
A parte das HTML entities resolve um efeito colateral divertido. Quando a IA sabe que acento dá problema, a saída esperta que ela encontra é escrever ó no lugar do ó, porque isso é ASCII puro e nunca corrompe. Funciona, e polui seu HTML para sempre. Como o arquivo é latin1 e latin1 já tem todos os acentos do português, essa ginástica é desnecessária: melhor proibi-la de uma vez.
A parte do falso-positivo é o número 73 daquela seção virando regra escrita, para a IA não confiar demais na detecção num arquivo grande.
🧪 Conferindo que ficou tudo certo
Depois de configurar, dá para testar em trinta segundos, sem arriscar arquivo de verdade. Faça assim: crie uma pasta de teste, ponha lá dentro uma cópia de um arquivo do seu projeto que tenha acento, e peça para a IA ler com o read_text_file.
Se os acentos aparecerem inteiros na resposta, está funcionando. Se aparecerem losangos, uma das três coisas está errada: o servidor não subiu, a pasta não está no args, ou a IA usou a ferramenta padrão em vez da certa.
Para separar o terceiro caso dos outros dois, peça explicitamente: "use a ferramenta mcp__file-tools__read_text_file para ler este arquivo". Se aí funcionar, o servidor está ótimo e o que falta é a regra no CLAUDE.md. 🎯
E uma última conferência, que vale ouro em projeto latin1: depois de qualquer alteração, rode o php -l no arquivo. Ele não checa encoding, mas pega na hora o caso em que a corrupção acertou uma aspa ou uma chave e quebrou a sintaxe, antes de o arquivo chegar ao servidor.
Desde que essa combinação está no lugar, o servidor MCP mais a regra escrita no CLAUDE.md, eu não vi mais um acento morrer nesses projetos. E olha que a IA mexe neles todo dia. 💜
Por hoje é só, meus unicórnios! 🦄✨
Que a magia do arco-íris continue brilhando em suas vidas! Até mais! 🌈🌟
Perguntas frequentes
Por que a IA corrompe os acentos do meu site?
e7 do ç não é UTF-8 válido, então a leitura o substitui pelo caractere de substituição U+FFFD. Quando a IA grava o arquivo de volta, ela grava esse U+FFFD no lugar do acento original. O byte real nunca chega até ela, e nenhum erro é exibido.Dá para recuperar um arquivo já corrompido pela IA?
ç e ã ocupavam 2 bytes (e7 e3) e viraram 6 bytes de U+FFFD (efbfbd efbfbd), todos idênticos entre si. Não há como saber qual letra era qual. O jeito é voltar pelo Git ou pelo backup, e por isso a proteção tem de vir antes do estrago.O que significa o erro "no allowed directories configured"?
args da configuração, e essa é a causa número um de erro na instalação. O aviso também aparece como access denied - path outside allowed directories quando você pede um arquivo fora da lista.Posso confiar na confiança que o detect_encoding informa?
iso-8859-1 com confiança 73, não 100. Arquivo grande com poucos acentos pode ser detectado como gbk ou outro encoding parecido. Em projeto que você já sabe ser latin1, passe encoding: "iso-8859-1" explícito.Quais encodings o MCP File Tools suporta?
iso-8859-1 (apelidos latin1 e iso88591), windows-1252 (apelido cp1252) e utf-8. A lista completa inclui cirílico, grego, turco, chinês, hebraico, árabe, báltico, vietnamita e tailandês.Preciso configurar o CLAUDE.md ou basta instalar o servidor?
Leia também
API Mágica: notificações Web Push no navegador com PHP
Web Push com a API Mágica em PHP puro: o navegador se inscreve, o seu site envia a notificação e a API conta o clique. Sem Composer e sem biblioteca.
IndexNow: avisando o Bing e o Yandex mais rápido
Como criar a chave do IndexNow e avisar Bing e Yandex das suas páginas novas com Node.js e PHP: as requisições, os erros reais e as armadilhas.
llms.txt: por que importa e como gerar em PHP
Por que o llms.txt importa para quem quer ser citado pela IA, e como gerar o seu em PHP puro lendo o sitemap.xml que voce ja tem.