cd /news/developer-tools/show-hn-drey-open-source-lsp-multipl… · home topics developer-tools article
[ARTICLE · art-68148] src=github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Show HN: Drey, open source LSP multiplexer so you have to buy less RAM

Drey, an open-source LSP multiplexer, lets multiple editors and coding agents share a single language server per workspace to reduce memory usage, as demonstrated by two Claude Code sessions on one Rust workspace consuming 7.4 GB of RAM with separate rust-analyzer processes. The tool, installable via Homebrew or Cargo, intercepts LSP client requests and routes them through a daemon that pools real servers, swapping document states per client to handle unsaved edits correctly. Developer Mario claims Drey is early but tested, with architecture details available in the repository.

read4 min views1 publishedJul 22, 2026
Show HN: Drey, open source LSP multiplexer so you have to buy less RAM
Image: source

One language server per workspace, shared by every client.

A drey is a squirrel's nest. This one is shared: many editors and coding agents crawl into a single indexed workspace instead of each digging their own.

Every LSP client starts its own language server. Two Claude Code sessions on one Rust workspace means two rust-analyzer processes, each holding a complete salsa database:

$ footprint -p 36924   # session A
phys_footprint: 3208 MB
$ footprint -p 28845   # session B
phys_footprint: 4225 MB

That is 7.4 GB to answer questions about one copy of one codebase. Add a third session, or an editor alongside the agents, and it scales linearly. The same applies to gopls, tsserver, clangd, pyright and jdtls; Rust is the example because it is the one with the measurement.

drey makes them share.

brew install mario/drey/drey     # macOS, brings its own Rust toolchain
cargo install drey               # anywhere with a Rust toolchain

Then put drey in front of every language server on this machine, for every client at once:

drey install                     # --dry-run first, if you want to see the plan

That writes a wrapper named after each language server into ~/.drey/bin

and prepends it to PATH

, so your existing clients pick drey up without being reconfigured. Open a new shell and check:

which rust-analyzer              # ~/.drey/bin/rust-analyzer
drey status                      # what is running, and who is attached

To point one client at drey by hand instead, name drey serve rust-analyzer

wherever that client currently names rust-analyzer

.

docs/install.md covers configuring servers, watching what is running, uninstalling, and what to do when something looks wrong.

drey is one binary in two roles. The shim looks exactly like a language server on stdio. The daemon owns a pool of real servers, one per workspace, and autostarts on first use.

  Claude Code ──▶ drey serve ─┐
  Codex       ──▶ drey serve ─┼──▶ daemon ──▶ one rust-analyzer
  Helix       ──▶ drey serve ─┘

A client attaches to a running server when the server name and initializationOptions

match and its workspace roots are contained in the roots already indexed. Containment rather than equality means opening a single crate inside a Cargo workspace costs nothing: it attaches to the workspace already in memory. Git worktrees and branches deliberately stay separate, because merging them would produce wrong answers, which is worse than using memory.

Two clients with different unsaved edits to the same file still share one process. Each document keeps the text everyone agrees on plus whichever client's edits the server is currently holding, and the daemon swaps that before answering. Verified against a real rust-analyzer: with one client holding pub size: u32

and another holding u64

, the same process reports size = 4, align = 0x4

to the first and size = 8, align = 0x8

to the second. That is a type layout recomputed per client, not an echo of the text they sent.

docs/architecture.md explains the three ideas the design rests on, and the parts that were harder than they look.

Early. It works and it is tested, but it has been exercised against a mock server far more than against every real one.

The state swapping rests on an assumption worth stating plainly. It needs the server to process messages in order, which every LSP server must, and to either snapshot per request (rust-analyzer does) or answer before the next swap arrives. A server that batches work across messages could in principle answer against a state that has since moved. If you hit that, lowering the thrash threshold makes such a client fork instead.

A swap also resends full document text rather than an incremental edit. That is cheap for source files and would not be for very large generated ones.

lspmux does the same job and got there first; lspmux-rust-analyzer packages it for this exact use case. drey exists because of the two things they document as out of scope: server-initiated requests are dropped there, and document divergence between clients is not reconciled.

Bug reports, patches and new server builtins are welcome. Read CONTRIBUTING.md first: it covers the checks CI runs on every pull request, and what a change to sharing policy needs before it lands.

Apache License 2.0. Copyright 2026 Mario Đanić. See LICENSE and NOTICE.

── more in #developer-tools 4 stories · sorted by recency
── more on @drey 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-drey-open-so…] indexed:0 read:4min 2026-07-22 ·