# How to Set Up Per-Session MCP Routing for Concurrent Claude Code Sessions

> Master per-session MCP routing for concurrent Claude Code sessions. Configure ai-memory with auto_scope.mode per_session and the MCP bridge to isolate each Claude Code window using CLAUDE_CODE_SESSION_ID.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: how-to-guide
- Published: 2026-08-27

---

**Configure `ai-memory` with `auto_scope.mode = "per_session"` and install the session-aware MCP bridge to isolate each Claude Code window using the `CLAUDE_CODE_SESSION_ID` environment variable.**

When running multiple Claude Code windows against a single `ai-memory` server, the default single-slot routing causes every MCP read tool to fall back to the most recently active project. This breaks isolation by allowing requests from one window to see the project context of another. Setting up per-session MCP routing ensures each concurrent Claude Code session maintains its own active-project pointer without requiring explicit workspace arguments in every call.

## Why Concurrent Sessions Break Without Isolation

By default, `ai-memory` uses a global active-project pointer stored in [`crates/ai-memory-store/src/scope_resolver.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/scope_resolver.rs). When multiple Claude Code windows connect to the same server, they overwrite each other's context because the server cannot distinguish between sessions. According to the source code in [`docs/auto-scope.md`](https://github.com/akitaonrails/ai-memory/blob/main/docs/auto-scope.md), the system supports three isolation modes: `single`, `per_session`, and `per_actor`. Without per-session configuration, concurrent operations collide.

## Enabling Per-Session Mode on the Server

To key the active-project pointer by session identifier, modify your server configuration to enable per-session isolation. Create or edit your configuration file at `~/.config/ai-memory/config.toml`:

```toml
[auto_scope]
mode = "per_session"          # isolate each Claude session

session_ttl_secs = 3600      # optional – how long idle entries live

max_entries = 4096           # optional – hard cap on stored entries

```

The `mode = "per_session"` setting tells the server to respect the `X-Memory-Actor-Session-Id` header on incoming requests. As implemented in [`crates/ai-memory-store/src/scope_resolver.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/scope_resolver.rs), this scopes all memory operations to the specific session identifier provided by the client.

## Installing the Session-Aware MCP Bridge

Claude Code must propagate its session identifier through to the `ai-memory` server. Install the bridge using the CLI command defined in [`crates/ai-memory-cli/src/commands/install_mcp.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-cli/src/commands/install_mcp.rs):

```bash

# Install a session-aware MCP entry for Claude Code (runs once)

ai-memory install-mcp \
  --client claude-code \
  --session-aware \
  --apply

```

This command generates a bridge configuration that reads the `CLAUDE_CODE_SESSION_ID` environment variable. The bridge implementation in [`crates/ai-memory-cli/src/commands/mcp_bridge.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-cli/src/commands/mcp_bridge.rs) forwards this value as the `X-Memory-Actor-Session-Id` header on every MCP call.

## How Session Propagation Works

When Claude Code launches, it exports a unique `CLAUDE_CODE_SESSION_ID` into its subprocess environment. The session-aware bridge captures this identifier and attaches it to HTTP requests:

```rust
// Simplified excerpt from crates/ai-memory-cli/src/commands/mcp_bridge.rs
let session_id = std::env::var("CLAUDE_CODE_SESSION_ID")
    .expect("CLAUDE_CODE_SESSION_ID is missing; this bridge must be launched by Claude Code");
let client = SessionAwareBridge {
    server_url,
    token,
    session_id,
};
client.serve(stdin, stdout).await?;

```

The resulting HTTP headers look like this:

```http
POST /mcp HTTP/1.1
Host: localhost:49374
X-Memory-Actor-Session-Id: 7a9f3e1c-b2d4-4c8e-a1f5-d3e2b6c9f0a1
...

```

With per-session mode enabled, `ai-memory` stores the active-project pointer under that specific session id. A query like `memory_query "how did we split the API"` automatically resolves to the correct project for the current Claude window, regardless of other active sessions.

## Managing Session Lifecycle Across Restarts

**Restarting Claude Code** creates a fresh isolation boundary. After a `/clear` command or when launching a new window, Claude supplies a new `CLAUDE_CODE_SESSION_ID`. The bridge forwards this new id, and the server creates a fresh per-session entry in the scope resolver.

To maintain continuity across restarts, invoke Claude with the `--resume <session-id>` flag to reuse the previous identifier. This preserves the active-project context without requiring reconfiguration of the server.

## Summary

- **Per-session routing** prevents concurrent Claude Code windows from overwriting each other's active-project context by keying state with the `CLAUDE_CODE_SESSION_ID` environment variable.
- **Server configuration** requires setting `auto_scope.mode = "per_session"` in [`config.toml`](https://github.com/akitaonrails/ai-memory/blob/main/config.toml) to enable session-aware scope resolution in [`crates/ai-memory-store/src/scope_resolver.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/scope_resolver.rs).
- **Bridge installation** via `ai-memory install-mcp --session-aware` creates the stdio bridge that propagates `CLAUDE_CODE_SESSION_ID` as the `X-Memory-Actor-Session-Id` header.
- **Session persistence** can be controlled using Claude's `--resume` flag to maintain context across restarts, or allow new ids for fresh isolation.

## Frequently Asked Questions

### What happens if I don't enable per-session mode?

Without `mode = "per_session"`, the server uses global scope resolution. Concurrent Claude Code sessions overwrite each other's active-project pointers, causing queries in one window to return context from projects opened in other windows.

### Where does the session ID come from?

Claude Code generates and exports `CLAUDE_CODE_SESSION_ID` into the MCP subprocess environment automatically. The bridge in [`crates/ai-memory-cli/src/commands/mcp_bridge.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-cli/src/commands/mcp_bridge.rs) reads this variable and forwards it as the `X-Memory-Actor-Session-Id` HTTP header.

### Can I use per-session routing with multiple users?

Yes. If you have multi-user authentication enabled, use `mode = "per_actor"` instead of `per_session`. This combines both user identity and session identifiers for isolation, as documented in [`docs/auto-scope.md`](https://github.com/akitaonrails/ai-memory/blob/main/docs/auto-scope.md).

### How do I clear a session's memory without affecting others?

Each session maintains independent pointers in the scope resolver. Use the standard `memory_clear` tool from within the specific Claude Code window, or wait for the `session_ttl_secs` timeout to expire. The server automatically garbage collects idle sessions based on your [`config.toml`](https://github.com/akitaonrails/ai-memory/blob/main/config.toml) settings.