Skip to content

fix(agent): per-user MCP auth in shared deepsql-agent process - #127

Merged
venkateshsakamuri-lab merged 1 commit into
mainfrom
cursor/fix-agent-mcp-per-user-auth-a3b5
Oct 28, 2026
Merged

venkateshsakamuri-lab merged 1 commit into
mainfrom
cursor/fix-agent-mcp-per-user-auth-a3b5

Conversation

@venkateshsakamuri-lab

Copy link
Copy Markdown
Contributor

Root cause

Hermes runs one long-lived deepsql-phase1-server.js subprocess for the whole agent container. The provisioner wrote credentials to profiles/u-<user>/deepsql.token, but also mirrored the last provisioned user onto a shared root token file that the MCP env pointed at. After the first user opened the Agent tab, every other user's MCP calls used that token until it expired — then all users saw auth failures (often surfaced as "connection unavailable").

This was both an outage (one expired token broke everyone) and a security issue (MCP calls could run as the wrong user).

Design (why not one MCP process per user?)

Spawning one MCP stdio server per profile would fight Hermes' global MCP lifecycle and profile/switch (process_wide=False). The cleaner fit for this codebase is:

  1. Stamp the active Hermes profile before each deepsql tool call (agent/patches/stamp-deepsql-mcp-actor.py applied at image build).
  2. Resolve auth per tool call in mcp/deepsql-phase1-lib.js from DEEPSQL_PROFILES_HOME/<profile>/deepsql.token + .env (DEEPSQL_MCP_USER_ID), using AsyncLocalStorage so concurrent users on one MCP process cannot mix tokens.
  3. Never fall back to a shared root token or another user's env snapshot in agent multi-user mode.
  4. On 401/403, call provisioner POST /refresh (loopback), which mints a fresh token via new POST /api/agent/internal/mcp-token (secret-gated) and rewrites only that user's profile files.
  5. Map auth failures to: "Your DeepSQL session expired — please sign in again" (not generic connection errors).

Also removes the shared root token mirror from scripts/local-agent-provisioner.py.

Bonus

  • docker-compose.yml and agent/Dockerfile healthchecks now probe http://127.0.0.1:8787/health (agent API), not :8788.

How to verify on a running stack

  1. Start compose with two users provisioned (open Agent tab as user A, then user B).
  2. Confirm B's chat/execute_sql audit identity is B while A is still logged in.
  3. Expire or blank profiles/u-<A>/deepsql.token (or revoke A's MCP token in the DB). A's next Agent turn should show the re-login message; B should still succeed.
  4. Optional: watch MCP backend calls (proxy logs / security_events) — Authorization must match the requesting user per call.

Tests

  • node --test mcp/deepsql-phase1-lib.test.js — per-user Authorization headers, expired-user message, concurrent in-flight isolation.
  • python3 scripts/local-agent-provisioner.py --self-test — no cross-user token overwrite, no shared root token file.
Open in Web Open in Cursor 

Resolve each MCP backend call from the active Hermes profile's token file
instead of a shared root credential. Stamp the active profile before each
deepsql tool call, refresh expired tokens via the provisioner, map 401/403 to
a re-login message, and align compose/Docker healthchecks with :8787.

Co-authored-by: Venkat SF <venkatesh.sakamuri@stayflexi.com>
@venkateshsakamuri-lab
venkateshsakamuri-lab merged commit bd62d96 into main Oct 28, 2026
9 checks passed
@venkateshsakamuri-lab
venkateshsakamuri-lab deleted the cursor/fix-agent-mcp-per-user-auth-a3b5 branch October 28, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants