Problem Statement
Serena's current architecture uses a global active project model:
SerenaAgent._active_project is shared across all MCP clients
activate_project("/repo/A") from Agent-1 switches Agent-2's workspace
- Not compatible with multi-agent, multi-worktree workflows
Proposed Architecture
┌─────────────────────────────────────┐
│ SerenaMCPServer (HTTP) │
│ ┌───────────────────────────────┐ │
│ │ SessionRegistry │ │ ← Map session_id → SessionContext
│ │ {session_id: SessionCtx} │ │
│ └───────────────────────────────┘ │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ SessionContext │ ← Per-client state
│ - session_id │
│ - workspace_root (project path) │
│ - modes │
│ - lsp_workspace_folders[] │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ LSPProcessPool (SHARED) │ ← One process per language
│ - python_lsp (shared) │
│ - rust_lsp (shared) │
│ - typescript_lsp (shared) │
│ + workspace_folders tracking │
└─────────────────────────────────────┘
Key Insights
LSP Already Supports Multi-Workspace
LSP is inherently multi-client and multi-workspace:
- Each connection has its own
rootUri / workspaceFolders
- Documents are scoped by URI, not "project"
- Servers maintain per-workspace caches and indexes
HTTP Sessions Provide Isolation
MCP streamable-http transport provides Mcp-Session-Id header - this IS the session boundary.
Design Decisions
| Decision |
Rationale |
| One LSP process per language |
LSP natively supports workspaceFolders[] |
| Session = MCP session_id |
MCP streamable-http already provides session header |
| Lazy workspace registration |
workspace/didChangeWorkspaceFolders on project activation |
| Path validation per session |
Tools reject paths outside session's workspace_root |
Implementation Phases
Phase 1: Session Isolation (No LSP Changes)
SessionContext dataclass with session_id, workspace_root, modes
SessionRegistry mapping session_id → SessionContext
- Extract session_id from MCP request context
Phase 2: Session-Aware Tools
- Modify tool dispatch to use session context
- Validate paths are under session's workspace_root
- Route to correct LSP workspace
Phase 3: LSP Workspace Multiplexing
LSPPool with shared processes per language
- Dynamic
workspace/didChangeWorkspaceFolders calls
- Track workspace registrations per language
Open Questions
- Session creation trigger: First tool call? Explicit
activate_project? MCP initialize?
- Session cleanup: When does a session expire? LRU eviction? Explicit
shutdown?
- LSP workspace limits: Some LSPs may have limits on workspace folders
- Backward compatibility: Single-project mode should still work
Related
Problem Statement
Serena's current architecture uses a global active project model:
SerenaAgent._active_projectis shared across all MCP clientsactivate_project("/repo/A")from Agent-1 switches Agent-2's workspaceProposed Architecture
Key Insights
LSP Already Supports Multi-Workspace
LSP is inherently multi-client and multi-workspace:
rootUri/workspaceFoldersHTTP Sessions Provide Isolation
MCP streamable-http transport provides
Mcp-Session-Idheader - this IS the session boundary.Design Decisions
workspaceFolders[]workspace/didChangeWorkspaceFolderson project activationImplementation Phases
Phase 1: Session Isolation (No LSP Changes)
SessionContextdataclass with session_id, workspace_root, modesSessionRegistrymapping session_id → SessionContextPhase 2: Session-Aware Tools
Phase 3: LSP Workspace Multiplexing
LSPPoolwith shared processes per languageworkspace/didChangeWorkspaceFolderscallsOpen Questions
activate_project? MCPinitialize?shutdown?Related
feature/multi-project-support