Bug Description
MCP server configurations that use ${workspaceFolder} or ${workspaceFolderBasename} can be bound to the backend default workspace instead of the workspace of the session making the tool call. In a gateway process serving multiple sessions, the same process-global MCP transport can then be reused across distinct session workspaces.
For filesystem-style MCP servers, this can make a tool read from or write to the wrong project without an obvious routing error.
Steps to Reproduce
- Configure a stdio MCP server whose fixed launch arguments include
${workspaceFolder} and ${workspaceFolderBasename}.
- Start one Hermes gateway process and record two active sessions with different working directories, such as
session-alpha and session-beta.
- Invoke the same MCP server from each session and have the server report its fixed launch root and process identity.
- Observe that, before the fix, both variables expand to the backend default workspace and the later session reuses the original process-global server.
Expected Behavior
Each MCP call should expand workspace context variables from the active session's authoritative working directory. Calls may reuse a transport only when they resolve to the same canonical workspace.
Actual Behavior
Workspace context variables are expanded before a tool call has session identity. The resulting fixed transport is stored under the global server name, so distinct sessions can share a server configured for the wrong workspace.
Impact
- Workspace-sensitive MCP tools can operate on the backend or another session's project instead of the caller's project.
- Filesystem-style servers can therefore return incorrect files or modify the wrong workspace.
- The reproduced scope is one process serving multiple session working directories; broader deployment frequency was not measured.
Affected Component
Tools (MCP client and generated MCP resource/prompt utilities)
Messaging Platform (if gateway-related)
Gateway sessions; platform-independent
Debug Report
N/A - reproduced locally with an isolated multi-session MCP probe. An accompanying PR includes automated coverage.
Operating System
Windows 10
Python Version
3.11.15
Hermes Version
Reproduced on main commit 372b3b7bba4b2b8f5880581984eedf685056dbdf.
Additional Logs / Traceback (optional)
session-alpha and session-beta had distinct recorded and runtime cwd values, but both workspace variables expanded to backend-workspace. Registering the same server name for session-beta reused the original fixed stdio launch root.
Root Cause Analysis (optional)
Trigger: tools/mcp_tool.py::_workspace_folder, _load_mcp_config, and the process-global server lookup used by MCP handlers.
Causal chain:
- Gateway startup loads MCP configuration before a tool call supplies its task/session identity.
- Workspace context variables are eagerly expanded from the backend/default root and the connected transport is cached by raw server name.
- A later session resolves the same public MCP tool to that transport and operates in the wrong workspace.
Why it is wrong: Workspace context is session-scoped, but interpolation and transport ownership were process-scoped. The public tool schema can be global; the fixed transport configuration cannot when it contains workspace variables.
Working sibling / contrast: File and terminal tools pass task/session identity into authoritative cwd resolution, so they select the session-specific root.
Ruled out: This is not stale session cwd persistence. The reproduction confirmed that both authoritative session roots were correct while only rootless MCP interpolation and transport reuse selected the backend workspace.
Verified on tip: The failure was reproduced against the stated main commit, and no open PR found by the duplicate search covers session-scoped MCP workspace interpolation and routing.
Proposed Fix (optional)
Preserve workspace-sensitive configuration templates, interpolate them with the active tool call's task/session identity, and route regular MCP tools plus generated resource/prompt utilities through transports keyed by canonical workspace. Keep one stable public tool schema while including scoped transports in availability, retry, probe, and shutdown lifecycle handling.
Are you willing to submit a PR for this?
Bug Description
MCP server configurations that use
${workspaceFolder}or${workspaceFolderBasename}can be bound to the backend default workspace instead of the workspace of the session making the tool call. In a gateway process serving multiple sessions, the same process-global MCP transport can then be reused across distinct session workspaces.For filesystem-style MCP servers, this can make a tool read from or write to the wrong project without an obvious routing error.
Steps to Reproduce
${workspaceFolder}and${workspaceFolderBasename}.session-alphaandsession-beta.Expected Behavior
Each MCP call should expand workspace context variables from the active session's authoritative working directory. Calls may reuse a transport only when they resolve to the same canonical workspace.
Actual Behavior
Workspace context variables are expanded before a tool call has session identity. The resulting fixed transport is stored under the global server name, so distinct sessions can share a server configured for the wrong workspace.
Impact
Affected Component
Tools (MCP client and generated MCP resource/prompt utilities)
Messaging Platform (if gateway-related)
Gateway sessions; platform-independent
Debug Report
Operating System
Windows 10
Python Version
3.11.15
Hermes Version
Reproduced on main commit
372b3b7bba4b2b8f5880581984eedf685056dbdf.Additional Logs / Traceback (optional)
session-alpha and session-beta had distinct recorded and runtime cwd values, but both workspace variables expanded to backend-workspace. Registering the same server name for session-beta reused the original fixed stdio launch root.Root Cause Analysis (optional)
Trigger:
tools/mcp_tool.py::_workspace_folder,_load_mcp_config, and the process-global server lookup used by MCP handlers.Causal chain:
Why it is wrong: Workspace context is session-scoped, but interpolation and transport ownership were process-scoped. The public tool schema can be global; the fixed transport configuration cannot when it contains workspace variables.
Working sibling / contrast: File and terminal tools pass task/session identity into authoritative cwd resolution, so they select the session-specific root.
Ruled out: This is not stale session cwd persistence. The reproduction confirmed that both authoritative session roots were correct while only rootless MCP interpolation and transport reuse selected the backend workspace.
Verified on tip: The failure was reproduced against the stated main commit, and no open PR found by the duplicate search covers session-scoped MCP workspace interpolation and routing.
Proposed Fix (optional)
Preserve workspace-sensitive configuration templates, interpolate them with the active tool call's task/session identity, and route regular MCP tools plus generated resource/prompt utilities through transports keyed by canonical workspace. Keep one stable public tool schema while including scoped transports in availability, retry, probe, and shutdown lifecycle handling.
Are you willing to submit a PR for this?