-
Notifications
You must be signed in to change notification settings - Fork 459
fix(mcp): single-session production /ws+/mcp (from #1169) #1180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -202,16 +202,23 @@ async def _connect_async(self) -> EnvClient: | |
| """ | ||
| Establish connection to the server. | ||
|
|
||
| In production mode (`use_production_mode=True`), open the WebSocket used | ||
| by `reset` / `step` / `state` and create a persistent HTTP MCP session | ||
| for `list_tools` / `call_tool`. Tool calls bypass `step()` over `/mcp`, | ||
| but the Gym lifecycle still requires `/ws` until production routing | ||
| covers those methods end-to-end. | ||
| In production mode (`use_production_mode=True`), create an HTTP MCP | ||
| session first and connect the WebSocket with that `session_id` so | ||
| Gym (`reset` / `step` / `state`) and tool (`list_tools` / `call_tool`) | ||
| traffic share one server-side environment session. | ||
| """ | ||
| if getattr(self, "use_production_mode", False): | ||
| try: | ||
| await super()._connect_async() | ||
| await self._ensure_production_session() | ||
| self._start_provider_if_needed() | ||
| session_id = await self._ensure_production_session() | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tier 2 (RFC 003) — non-blocking. Creating the HTTP MCP session here and then attaching |
||
| original_ws_url = self._ws_url | ||
| if self._ws_url and "session_id=" not in self._ws_url: | ||
| sep = "&" if "?" in self._ws_url else "?" | ||
| self._ws_url = f"{self._ws_url}{sep}session_id={session_id}" | ||
| try: | ||
| await super()._connect_async() | ||
| finally: | ||
| self._ws_url = original_ws_url | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Failed WS connect leaks MCP sessionHigh Severity Production connect now creates the HTTP MCP session first, then temporarily appends Additional Locations (2)Reviewed by Cursor Bugbot for commit 58b5500. Configure here. |
||
| except Exception: | ||
| await self.close() | ||
| raise | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tier 2 (agent-isolation invariant) — non-blocking.
/wsnow attaches to an existing session whenever?session_id=is supplied, so a caller that reaches/wsand knows a validsession_idcan drivereset/step/stateon the shared env (previously/wsalways minted a fresh, isolated session). Nothing binds a session to its creator. Under the intended topology this is fine — the orchestration client holds the UUID id and the trained policy only sees/mcptools — but per INVARIANTS.md (“the WebSocket interface for reset/step is for orchestration only”) please confirmsession_ids are never exposed to the agent and/wsstays unreachable by agent code. cc @Darktex. (See review summary.)