Skip to main content
Monorepos with multiple Python projects, each with its own .venv, are fully supported. typemux-cc maintains a backend pool that routes LSP requests to the correct backend based on file location.

The Problem

With Claude Code’s official pyright plugin:
  1. Monorepo has 3 projects: project-a/, project-b/, project-c/
  2. Each project has its own .venv with different dependencies
  3. Opening project-a/main.py → pyright uses project-a/.venv
  4. Opening project-b/main.pypyright still uses project-a/.venv
  5. Type checking fails because project-b imports aren’t available
  6. Must restart Claude Code to switch to project-b/.venv
With typemux-cc, switching is automatic and instant.

Monorepo Structure Example

From the README:
Each project:
  • Has independent dependencies (different package versions)
  • Has its own .venv/pyvenv.cfg
  • Gets its own backend process in the pool

Backend Pool Routing

Pool Architecture

typemux-cc maintains a pool of backend processes, one per .venv:
Each backend instance tracks:

Routing Logic

When Claude Code sends an LSP request:
1

Extract document URI

2

Look up cached venv for document

3

Get backend from pool

4

Forward request to correct backend

If backend exists in pool, forward the request. If not, spawn a new backend (see “Operation Sequence” below).

Operation Sequence

From the README:

Detailed Flow

1

Session starts

typemux-cc searches for a fallback .venv at startup:
If found, pre-spawns a backend. Otherwise starts with empty pool.
2

Open project-a/src/main.py

Result: Backend spawned with VIRTUAL_ENV=project-a/.venv, session=1 added to pool.
3

Open project-b/src/main.py

Same process, but:
  • .venv search finds project-b/.venv
  • Not in pool → spawn new backend
  • Backend with VIRTUAL_ENV=project-b/.venv, session=2 added to pool
Pool state now:
4

Return to project-a/src/main.py

Result: Request routed to session 1 (already in pool), zero restart overhead.

What Actually Happens

From the README:
When Claude Code moves from project-a/main.py to project-b/main.py:
  1. Proxy detects different .venv (project-a/.venv → project-b/.venv)
  2. Checks the backend pool — project-b/.venv not found
  3. Spawns new backend with VIRTUAL_ENV=project-b/.venv (session 2)
  4. Session 1 (project-a) stays alive in the pool — no restart
  5. Restores open documents under project-b/ to session 2
  6. Clears diagnostics for documents outside project-b/
  7. All LSP requests for project-b files now use project-b dependencies
When Claude Code returns to project-a/main.py later, session 1 is still in the pool — zero restart overhead.

Document Restoration

When a new backend spawns, typemux-cc restores already-open documents:
Selective restoration: Only documents belonging to project-b/ are restored to the project-b/.venv backend. project-a/ documents are skipped.

Pool Management

Maximum Backends

Default: 8 concurrent backends
Configurable via:

LRU Eviction

When the pool is full and a new backend is needed:
Strategy:
  1. Prefer backends with no pending requests (safe to evict)
  2. Among those, pick the least recently used (oldest last_used timestamp)
  3. If all have pending requests, fall back to global LRU

TTL-Based Eviction

Default: 1800 seconds (30 minutes) of inactivity
Backends idle longer than TTL are automatically evicted:
Disable TTL eviction:

Switching Between Projects

Switching is instant and automatic:
What happens internally:
  1. Open project-a/src/main.py → routes to session 1
  2. Hover over pd.DataFrame → request forwarded to session 1
  3. Open project-b/src/main.py → routes to session 2
  4. Hover over np.array → request forwarded to session 2
  5. Return to project-a/src/main.py → routes to session 1 (still in pool)
No visible delay. From the user’s perspective: LSP just works.

Real Monorepo Example

Here’s a realistic monorepo structure:
Each project has:
  • Independent dependencies (e.g., api uses FastAPI, worker uses Celery)
  • Own .venv with different package versions
  • Own backend in the pool (up to 5 concurrent in this example)
Workflow:
1

Open api/src/main.py

Backend spawned: VIRTUAL_ENV=services/api/.venv (session 1)
2

Open worker/src/tasks.py

Backend spawned: VIRTUAL_ENV=services/worker/.venv (session 2)
3

Open libs/common/src/utils.py

Backend spawned: VIRTUAL_ENV=libs/common/.venv (session 3)
4

Return to api/src/main.py

Routes to session 1 (no spawn, instant)
Pool state:

Configuration for Monorepos

Increase max_backends for large monorepos

If you have more than 8 projects:

Disable TTL for active development

To keep all backends alive indefinitely:

Enable detailed logging

Monitor backend pool activity:

Troubleshooting

Wrong dependencies being used

Symptoms:
  • Import errors for packages that exist in the project’s venv
  • Type checking fails with “module not found”
Causes:
  1. Wrong venv cached: Document opened before correct .venv existed
  2. Shared venv: Multiple projects using the same .venv path
Fix:

Pool eviction too aggressive

Symptoms:
  • Backends being evicted while still needed
  • Frequent “Creating new backend” in logs
Causes:
  • TYPEMUX_CC_MAX_BACKENDS too low
  • TYPEMUX_CC_BACKEND_TTL too short
Fix:

Memory usage high with many backends

Each backend process uses ~200-500MB. With 16 backends: ~3-8GB total. Solutions:
  1. Reduce max_backends:
  2. Enable TTL to evict idle backends:
  3. Close unused projects in Claude Code to reduce active backends

Summary

Monorepo checklist

  1. ✅ Each project has its own .venv/pyvenv.cfg
  2. ✅ Configure TYPEMUX_CC_MAX_BACKENDS for project count
  3. ✅ Open files from different projects freely
  4. ✅ typemux-cc automatically routes to correct backends
  5. ✅ No restarts, no manual switching
Best practice: For large monorepos (>8 projects), increase TYPEMUX_CC_MAX_BACKENDS and monitor memory usage.