Drey: o multiplexador LSP open source que compartilha language servers entre editores e agentes de IA para economizar RAM
Cansei de ver rust-analyzer duplicado em memória para cada cliente LSP. O Drey resolve isso com um proxy inteligente que compartilha um único…
O problema que todo desenvolvedor com múltiplos clientes LSP já sentiu na carteira
Quem trabalha com ambientes de desenvolvimento modernos sabe que o Language Server Protocol (LSP) revolucionou a forma como editores de código oferecem recursos como autocompletar, ir para definição, refatoração e diagnósticos em tempo real. O LSP desacoplou a inteligência da linguagem do editor em si, permitindo que ferramentas como rust-analyzer, gopls, tsserver, clangd, pyright e jdtls funcionem com qualquer editor compatível — VS Code, Neovim, Helix, Emacs, entre outros.
O problema é que essa arquitetura tem um custo silencioso e crescente: cada cliente LSP sobe sua própria instância do language server. Abriu dois terminais com Claude Code no mesmo workspace Rust? Parabéns, você tem dois processos rust-analyzer rodando em paralelo, cada um carregando um banco de dados Salsa completo na memória. A medição real documentada no projeto é impactante: uma sessão consumindo 3,2 GB e outra 4,2 GB — totalizando mais de 7 GB de RAM para indexar o mesmo codebase uma única vez. Adicione um editor ao lado dos agentes e o consumo escala linearmente, sem nenhum ganho real de funcionalidade.
Foi exatamente essa frustração que motivou o desenvolvedor Mario Đanić a criar o Drey, um multiplexador LSP open source escrito em Rust que resolve o problema de forma elegante: um único language server por workspace, compartilhado por todos os clientes que precisarem dele.
O que é o Drey e como ele funciona como proxy LSP
O nome vem do inglês drey, que significa o ninho de um esquilo — e a metáfora é perfeita: muitos esquilos (editores, agentes de IA) dividindo o mesmo ninho bem construído em vez de cada um cavar o seu próprio buraco na memória.
Tecnicamente, o Drey opera como um proxy transparente para o Language Server Protocol. Ele é um único binário que assume dois papéis distintos:
- Shim (cliente-lado): se apresenta para qualquer cliente LSP exatamente como um language server convencional via stdio. O editor ou agente não precisa saber que está falando com um intermediário.
- Daemon (servidor-lado): mantém um pool de language servers reais, um por workspace, e gerencia o ciclo de vida deles. Sobe automaticamente na primeira conexão e permanece ativo enquanto houver clientes conectados.
O fluxo de comunicação fica assim: Claude Code, Codex e Helix, por exemplo, cada um conecta ao seu próprio shim drey serve, que por sua vez encaminha tudo para um único daemon, que mantém apenas uma instância do rust-analyzer para aquele workspace. A economia de memória é imediata e proporcional ao número de clientes.
Critério de compartilhamento: contenção de raízes, não igualdade
Um detalhe de design importante é como o Drey decide se um novo cliente pode se anexar a um servidor existente. A condição não é que as raízes do workspace sejam idênticas, mas sim que estejam contidas nas raízes já indexadas. Isso significa que abrir um único crate dentro de um workspace Cargo maior não força a criação de um novo processo — o cliente simplesmente se anexa ao servidor que já tem tudo em memória. É um ganho real para projetos monorepo e workspaces grandes.
Por outro lado, git worktrees e branches diferentes são mantidos separados intencionalmente. Compartilhar contexto entre branches distintas produziria respostas incorretas — e o projeto deixa claro que usar mais memória é preferível a dar respostas erradas.
O problema mais difícil: divergência de documentos entre clientes
Compartilhar um único processo de language server entre múltiplos clientes parece simples até você considerar o caso em que dois editores têm edições não salvas diferentes no mesmo arquivo. Se o cliente A tem pub size: u32 e o cliente B tem pub size: u64 no mesmo arquivo, como um único processo pode responder corretamente para ambos?
Essa é exatamente a lacuna que o Drey endereça em relação ao projeto anterior lspmux (que o autor reconhece como pioneiro na mesma ideia). A solução implementada é um mecanismo de troca de estado por documento: cada documento mantém o texto em que todos concordam mais as edições do cliente que o servidor está segurando no momento. Antes de responder a um cliente, o daemon faz o swap do estado para aquele cliente específico.
O resultado verificado contra um rust-analyzer real é impressionante: com um cliente segurando u32 e outro u64, o mesmo processo reporta corretamente size = 4, align = 0x4 para o primeiro e size = 8, align = 0x8 para o segundo. Não é um cache de texto — é um recálculo real de layout de tipo por cliente.
O próprio autor é transparente sobre as limitações desta abordagem: ela assume que o servidor processa mensagens em ordem (obrigatório pela spec do LSP) e que ou faz snapshot por requisição (como o rust-analyzer) ou responde antes do próximo swap chegar. Um servidor que acumula trabalho entre mensagens poderia em princípio responder contra um estado que já foi trocado. Para esses casos, existe um parâmetro de threshold que força o cliente a ter seu próprio processo em vez de compartilhar.
Instalação e integração transparente com clientes existentes
A experiência de instalação foi claramente pensada para ser o menos invasiva possível. No macOS via Homebrew:
brew install mario/drey/drey
Ou em qualquer sistema com toolchain Rust:
cargo install drey
O comando drey install é onde a mágica de integração acontece: ele escreve wrappers com o nome de cada language server conhecido em ~/.drey/bin e adiciona esse diretório ao início do PATH. A partir daí, quando qualquer cliente chamar rust-analyzer, gopls ou tsserver, estará na verdade chamando o shim do Drey — sem nenhuma reconfiguração nos editores.
Para verificar que tudo está funcionando:
which rust-analyzer deve retornar ~/.drey/bin/rust-analyzer, e drey status mostra quais servidores estão rodando e quais clientes estão conectados a cada um.
Servidores suportados e estado atual do projeto
O Drey já tem suporte integrado para os language servers mais usados:
- rust-analyzer (Rust)
- gopls (Go)
- tsserver (TypeScript/JavaScript)
- clangd (C/C++)
- pyright (Python)
- jdtls (Java)
O projeto está em estágio inicial mas funcional. O autor é honesto: o sistema foi exercitado muito mais contra um mock server do que contra todos os servidores reais em produção. Contribuições com bug reports, patches e novos builtins de servidores são bem-vindas. O código está licenciado sob Apache License 2.0.
Por que isso importa para quem usa agentes de IA no desenvolvimento
O timing do Drey não é coincidência. O crescimento de ferramentas como Claude Code, GitHub Copilot, Codex e similares — que funcionam como clientes LSP autônomos rodando em paralelo com o editor do desenvolvedor — multiplicou o problema de instâncias duplicadas. Não é raro ter um ambiente de desenvolvimento com três ou quatro clientes LSP simultâneos no mesmo codebase: o editor principal, um agente de completar código, um agente de revisão e talvez um terminal com REPL assistido.
Nesse cenário, a economia proposta pelo Drey deixa de ser uma otimização de nicho e passa a ser uma necessidade prática para quem não quer comprar mais RAM para compensar ineficiência arquitetural. O projeto endereça um ponto cego real na ecossistema LSP atual, e a abordagem técnica — especialmente o mecanismo de state swapping para divergência de documentos — demonstra um nível de cuidado que vai além de um simples proxy de encaminhamento.