Skip to content

Architecture: Multi-Project Session Isolation #6

Description

@ketema

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

  1. Session creation trigger: First tool call? Explicit activate_project? MCP initialize?
  2. Session cleanup: When does a session expire? LRU eviction? Explicit shutdown?
  3. LSP workspace limits: Some LSPs may have limits on workspace folders
  4. Backward compatibility: Single-project mode should still work

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions