What happens
After the server restarts, agents in already-running threads lose their preview_* tools. The agent reports that the tools do not exist; nothing is logged and no error surfaces in the UI.
Why
McpSessionRegistry keeps issued credentials only in memory:
// apps/server/src/mcp/McpSessionRegistry.ts:98
const state = yield* SynchronizedRef.make<RegistryState>({ records: new Map() });
The acquireRelease finalizer only clears the module-level activeMcpSessionRegistry pointer, so the map simply dies with the process. A resumed provider session keeps presenting the bearer token it was issued before the restart, the auth middleware no longer has a record for that token hash, and every tool call is rejected.
This is reachable through ordinary use — anything that restarts the backend (an update, the desktop app restarting its server) while a thread has an active provider session.
Reproduce
- Start a thread with a provider that gets the
t3-code MCP server attached.
- Restart the server.
- Ask the agent to use a
preview_* tool.
The agent reports the tool does not exist rather than reporting an auth failure.
One thing worth knowing before fixing it
issueActiveMcpCredential revokes the thread's existing credentials before issuing new ones:
activeMcpSessionRegistry
.revokeThread(request.threadId)
.pipe(Effect.andThen(activeMcpSessionRegistry.issue(request)))
That matters if the fix is "re-issue on resume": a resumed session that is still using its old credential can have it revoked out from under it while the new config is in flight. Persisting credentials and reloading them on boot sidesteps that, but it needs the liveness writes to coalesce or every turn writes a row.
Context
Found downstream in a fork (thedouglenz/trellis), where the same registry serves an additional toolkit and the symptom was more obvious. Happy to leave this as a report — I know large PRs are not what you are looking for.
What happens
After the server restarts, agents in already-running threads lose their
preview_*tools. The agent reports that the tools do not exist; nothing is logged and no error surfaces in the UI.Why
McpSessionRegistrykeeps issued credentials only in memory:The
acquireReleasefinalizer only clears the module-levelactiveMcpSessionRegistrypointer, so the map simply dies with the process. A resumed provider session keeps presenting the bearer token it was issued before the restart, the auth middleware no longer has a record for that token hash, and every tool call is rejected.This is reachable through ordinary use — anything that restarts the backend (an update, the desktop app restarting its server) while a thread has an active provider session.
Reproduce
t3-codeMCP server attached.preview_*tool.The agent reports the tool does not exist rather than reporting an auth failure.
One thing worth knowing before fixing it
issueActiveMcpCredentialrevokes the thread's existing credentials before issuing new ones:That matters if the fix is "re-issue on resume": a resumed session that is still using its old credential can have it revoked out from under it while the new config is in flight. Persisting credentials and reloading them on boot sidesteps that, but it needs the liveness writes to coalesce or every turn writes a row.
Context
Found downstream in a fork (thedouglenz/trellis), where the same registry serves an additional toolkit and the symptom was more obvious. Happy to leave this as a report — I know large PRs are not what you are looking for.