One serena language server per project, shared across all your agents — instead of one per agent.
A tiny, drop-in wrapper for Claude Code (and any MCP client that spawns serena over stdio). When you run many parallel agents — fan-out, worker pools, multiple panes — each one normally spawns its own serena process and its own language server (rust-analyzer, pyright, …), often several GB each. On the same repo that's pure duplication, and on a busy box it ends in a global OOM.
This routes every agent through one shared serena per project, and reaps idle/orphaned ones. Linux + systemd, ~2 bash scripts, no daemon to babysit.
serena's stdio transport is 1:1 — one server per client by design. With N parallel agents you get N serena processes and N language servers. This shows up across the serena tracker:
- #1235 — duplicate instances when the same project is open in multiple clients.
- #1367 / #1549 — orphaned serena processes persisting and growing to extreme RSS, crashing machines.
- #979 — launching from
~/makes--project-from-cwdactivate your entire home directory, triggering a recursive scan that blows the 30s MCP timeout. - #1496 — worktree teammates silently edit the primary checkout because serena stays rooted to it.
- Cross-client: openai/codex#12333 hits the same duplication.
serena already supports --transport sse (one server, many clients, one shared LSP). The missing piece is the glue: auto-routing each agent to the right per-project server and managing lifecycle. The maintainers consider that glue out of scope for serena itself ("a template layer … outside Serena itself") — so this is that layer.
serena-shared-bridge is registered as the serena MCP command. Per agent it:
- Resolves the project root (
git rev-parse --show-toplevel, else cwd). - Ensures one detached serena SSE server for that root — singleton via a lock + a
root→portregistry ($XDG_RUNTIME_DIR/serena-shared/). - Bridges the agent's stdio MCP ↔ that SSE server with
mcp-proxy.
So agents on the same repo share one server + one LSP. The server is detached, so it outlives individual agents and the next one reuses it.
serena-shared-reap (systemd user timer, every 10 min) reaps any server that's been idle (no client connected) for ≥30 min, or that's orphaned (running but not listening). That's the fix for the orphan-RSS issues above.
It deliberately runs serena-free (no LSP) for:
- linked git worktrees — fan-out/ephemeral workers that edit per a handoff and don't need symbol tools (and sidesteps the #1496 mis-rooting trap), and
$HOME— never a project; avoids the #979 home-dir scan.
Override either with SERENA_SHARED=force.
Requires Linux + systemd (user units), git, ss (iproute2), flock, and uvx (uv).
git clone https://github.com/Eyalm321/serena-shared-bridge ~/dev/serena-shared-bridge
~/dev/serena-shared-bridge/install.shThe installer copies the scripts to ~/.local/bin, installs + enables the reaper timer, and registers serena to route through the bridge in Claude Code's user scope (backing up your config first). It's idempotent.
| var | default | meaning |
|---|---|---|
SERENA_SHARED |
1 |
1 = shared bridge (skip worktrees/$HOME); force = always shared, even there; 0 = disable, fall back to per-agent stdio serena |
SERENA_REAP_GRACE |
1800 |
seconds a server must be idle before the reaper kills it |
- Linux + systemd only for now (uses
/proc,ss, systemd user timers). macOS/launchd port welcome. - One shared LSP serializes symbol queries — correct under heavy concurrency (validated to 32 simultaneous clients, 0 errors), but if many agents query at the exact same moment, latency grows (they queue, never fail). The trade for killing per-agent LSP RAM.
- Fail-open: any bridge failure falls back to the original per-agent stdio serena, so an agent never loses serena because of this.
- A worktree/
$HOMEskip makes the client show serena as "failed to connect" — cosmetic; those agents intentionally run serena-free.
MIT — see LICENSE.