Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(mcp): scope cedar-staff-debug sessions to the MCP connection

merged#2765CedarCopilot

CedarCopilot wants to merge 1 commit into staging from fix/mcp-acting-session-scope-by-connection

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Sep 12, 2026, 11:23 AM
  2. Sep 12, 2026, 11:30 AM
  3. Merged
    Sep 12, 2026, 11:36 AM
  4. Live on prod
    Sep 12, 2026, 11:36 AM
  5. Observed 3 days
    Sep 12, 2026, 11:36 AM
  6. Pipelines steady after this deploy
    Sep 12, 2026, 11:36 AM
  7. Unobserved

    Live on prod, no production signal yet

    Sep 15, 2026, 11:29 AM

Behaviors Libra is checking

The durable mcp_acting_session store is migrated from a caller-only key to the composite (caller_user_id, mcp_session_id) key while preserving existing rows in the empty-session sentinel bucket.Degradedlow confidence

Strict CloudWatch fallback found 6 prod failure log lines for [processBufferedSlackWebhookEvents] Parked non-recoverable row (No valid connection tokens found for user <id>) in the last 24h while checking mcp-schema, mcp_acting_session_scope_by_connection, acting-session.test.

prod, checked Sep 15, 2026, 11:29 AM
Concurrent Cedar-staff connections using cedar-staff-debug can impersonate different users without overwriting one another, and each connection continues resolving requests against its own target until switched or expired.Workingmedium confidence

Since the deploy, 9,208 prod OTEL spans matching POST /mcp JSON-RPC tools/call cedar-staff-debug and subsequent MCP tool calls ran with 63 error spans. In the 40h before the deploy the same surface ran 4,920 OTEL spans with 46 error spans. Confidence is medium because 63 error.

prod, checked Sep 14, 2026, 3:10 AM
Calling cedar-staff-debug with clear:true removes only the impersonation session belonging to the current MCP connection and leaves other connections for the same staff account active.Workingmedium confidence

Since the deploy, 9,317 prod OTEL spans matching POST /mcp JSON-RPC tools/call cedar-staff-debug({clear:true}) ran with 63 error spans. In the 41h before the deploy the same surface ran 4,970 OTEL spans with 46 error spans. Confidence is medium because 63 error spans remain at.

prod, checked Sep 14, 2026, 4:30 AM
Cedar-staff MCP callers receive a unique Streamable HTTP Mcp-Session-Id at initialize so each connection can retain its own debug-session scope.Workingmedium confidence

Since the deploy, 9,359 prod OTEL spans matching POST /mcp (initialize) ran with 63 error spans. In the 42h before the deploy the same surface ran 5,003 OTEL spans with 46 error spans. Confidence is medium because 63 error spans remain at 0.7% of traffic, under the 1% noise.

prod, checked Sep 14, 2026, 4:43 AM

Libra has verdicts on 3 of 4 tracked behaviors on prod; 1 is still being checked. Libra has 1 low-confidence degraded verdict it is still confirming. Libra checks hourly for 3 days after each deploy.

Summary

  • mcp_acting_session was keyed by caller_user_id alone, so two Claude Code windows on the same staff account calling cedar-staff-debug against two different users silently overwrote one row with the other , no error, and a guarded-SQL analysis in one window could run against the wrong tenant.
  • external-mcp.ts now mints a Streamable HTTP Mcp-Session-Id for every Cedar-staff caller regardless of the Armature-analytics flag, and acting-session.ts keys every read/write/clear on (caller_user_id, mcp_session_id) instead.
  • Updated cedar-staff-debug's own banner text (it used to claim "only one debug session per staff account can be active" , no longer true) and docs/design/mcp-impersonation.md.

DB migration status: apps/server/src/db/migrations/mcp_acting_session_scope_by_connection.sql has already been applied to the shared DB (additive: new mcp_session_id column defaulting to '', PK swapped to the composite). This PR is the matching code , until it deploys, the currently-live server's ON CONFLICT ("caller_user_id") won't match the new composite constraint, so cedar-staff-debug will fail on every call. Please merge/deploy this promptly to close that gap.

Test plan

  • pnpm --filter @zero/server exec vitest run src/mastra/mcp/external/__tests__/acting-session.test.ts src/mastra/mcp/external/__tests__/resolve-session.test.ts src/mastra/mcp/external/__tests__/server.test.ts src/mastra/mcp/external/__tests__/tool-call-observability.test.ts src/routes/__tests__/external-mcp.e2e.test.ts , 78 passed
  • pnpm --filter @zero/server run types , clean
  • Verified live DB schema post-migration (\d mcp_acting_session): composite PK in place, FKs intact, 0 rows

🤖 Generated with Claude Code

https://claude.ai/code/session_016hB3VjDQvdLHTDdQW76z23

Greptile Summary

This PR scopes Cedar-staff impersonation state by both the authenticated caller and the Streamable HTTP connection ID, updates the database key and tool messaging, and adds focused coverage for the new behavior.

  • Mints and propagates an Mcp-Session-Id for Cedar-staff initialization requests.
  • Changes acting-session reads, updates, inserts, and clears to use a composite connection-scoped key.
  • Migrates the table to a composite primary key and documents the revised lifecycle.
  • The missing-ID fallback still permits caller-wide session collisions, and disconnected connection rows lack eventual cleanup.

Confidence Score: 3/5

The PR is not yet safe to merge because headerless staff requests can still share an impersonation row and reproduce the wrong-tenant behavior being fixed.

The composite key works when clients consistently echo the issued connection ID, but the server accepts missing IDs and deliberately collapses those requests into a shared caller-wide key; connection-scoped rows also have no eventual cleanup after clients disconnect.

Files Needing Attention: apps/server/src/routes/external-mcp.ts, apps/server/src/mastra/mcp/external/cedar-staff-debug-tool.ts, apps/server/src/mastra/mcp/external/acting-session.ts

Security Review

A Cedar-staff request without an echoed Mcp-Session-Id is accepted and mapped to the shared empty-string key. Multiple headerless connections on the same staff account can consequently overwrite one another’s impersonation targets and execute tools against an unintended tenant.

Important Files Changed

FilenameOverview
apps/server/src/routes/external-mcp.tsResolves the staff connection ID before server construction, but does not reject later staff requests when that ID is absent.
apps/server/src/mastra/mcp/external/acting-session.tsCorrectly applies the composite key throughout, while retaining an unsafe shared fallback and request-driven-only expiry cleanup.
apps/server/src/mastra/mcp
Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/server/src/db/mcp-schema.tsno production surface mapped
  • apps/server/src/db/migrations/mcp_acting_session_scope_by_connection.sqlno production surface mapped
  • apps/server/src/mastra/mcp/external/__tests__/acting-session.test.tsno production surface mapped
  • apps/server/src/mastra/mcp/external/acting-session.tsno production surface mapped
  • apps/server/src/mastra/mcp/external/cedar-staff-debug-tool.tsno production surface mapped
  • apps/server/src/mastra/mcp/external/resolve-session.tsno production surface mapped
  • apps/server/src/routes/__tests__/external-mcp.e2e.test.tsno production surface mapped
  • apps/server/src/routes/external-mcp.tsno production surface mapped
  • docs/design/mcp-impersonation.mdno production surface mapped