---
summary: "Per-session isolation on ServerRegistry connection state and the CanvasBridge default canvas — concurrent HTTP callers under MCP_AUTH_MODE=none now operate in isolated workspaces by default."
breaking: false
---

# 0.5.3 — 2026-05-05

Adds a session sub-scope on top of tenant-keyed `ctx.state` for the two stateful surfaces. Under HTTP stateful/auto with `BRAPI_SESSION_ISOLATION=true` (the new default), each MCP session carves its own connection-alias namespace and its own default canvas — registered aliases, exchanged SGN/OAuth tokens, and `df_<uuid>` rows do not cross sessions. Stdio and stateless HTTP fall back to per-tenant keying. Set `BRAPI_SESSION_ISOLATION=false` to keep the pre-0.6 multi-agent collaboration model where every caller in a tenant shares one workspace.

## Added

- **`BRAPI_SESSION_ISOLATION` env var (default `true`).** New `sessionIsolation` field on `ServerConfigSchema`. When `true` and `ctx.sessionId` is present (HTTP stateful/auto), `ServerRegistry` keys connection state by `brapi/conn/sess/<sessionId>/<alias>` and `CanvasBridge` keys the default-canvas pointer by `brapi/canvas/sess/<sessionId>` — so concurrent HTTP callers under `MCP_AUTH_MODE=none` see their own aliases, tokens, and spilled rows. Falls back to the legacy tenant-keyed prefixes (`brapi/conn/<alias>`, `brapi/canvas/default`) for stdio, stateless HTTP, or when the flag is `false`. No effect on JWT/OAuth tenant keying — sessions add a sub-scope inside each tenant.
- **`ctx.sessionId` reference in `skills/api-context`.** New section covering when the field is defined (HTTP stateful/auto vs stdio/stateless), the `exposeStatelessSessionId` opt-in, the capability-token model, and recipes for strict (`if (!ctx.sessionId) throw invalidRequest(...)`) vs lax (fall back to a tenant-shared key) handling. Bumped to `1.3`.
- **Session-isolation tests.** New suites in `tests/services/server-registry.test.ts` and `tests/services/canvas-bridge.test.ts` covering: same-tenant sessions land on disjoint buckets, same-session requests share state, one session's alias / dataframe is unreachable from another, undefined `sessionId` falls back to tenant keying, `BRAPI_SESSION_ISOLATION=false` collapses everyone onto the shared workspace.

## Changed

- **`ServerRegistry` keying refactor.** `connKey(ctx, alias)` and the new `list()` prefix both route through a private `scopePrefix(ctx)` helper — single source of truth for the session-vs-tenant decision, no logic duplication. `register`, `get`, `getOptional`, `list`, and `unregister` all honor the keying scheme uniformly.
- **`CanvasBridge.getInstance` keying.** The cached default-canvas pointer now reads from `defaultCanvasKey(ctx)` instead of a single constant; same scoping rule (session-when-on, tenant-otherwise). Stale-id recovery (NotFound on acquire → clear cache and re-acquire) honors the keyed slot, so a stale token from one session doesn't leak across.
- **`Dockerfile` default session mode `stateless` → `stateful`.** Engages `mcp-session-id` lifecycle and hijack protection in the container image — precondition for `BRAPI_SESSION_ISOLATION=true` to actually scope per session. Operators that want the older stateless behavior can override with `-e MCP_SESSION_MODE=stateless` at run time.
- **README rewritten around three deployment shapes.** Replaces the previous shared-trust-vs-per-user table with three rows: **Per-session (default)** for multi-user shared-trust HTTP without SSO, **Per-user credentials** for institutional JWT/OAuth, **Shared workspace (legacy)** for one-researcher / parallel-agent setups under `BRAPI_SESSION_ISOLATION=false`. Multi-agent workflows section updated to reflect the new (tenant, session) bucket model. Inline references to `MCP_SESSION_MODE` clarify the `auto` default and the precondition for session-keyed isolation.

## Dependencies

- **`@cyanheads/mcp-ts-core` ^0.8.16 → ^0.8.17.** Picks up the upstream `ctx.sessionId` field on the `Context` interface and the framework's session-binding plumbing for HTTP stateful/auto transports.
