Skip to content

Commit 9dfeb8d

Browse files
committed
refact: remove verbose session deletion code
1 parent 2c07433 commit 9dfeb8d

File tree

4 files changed

+31
-70
lines changed

4 files changed

+31
-70
lines changed

src/uipath_mcp/_cli/_runtime/_runtime.py

Lines changed: 25 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -133,37 +133,29 @@ def get_sessions(self) -> dict[str, SessionHealthInfo]:
133133
}
134134

135135
async def remove_session(self, session_id: str, reason: str) -> None:
136-
"""Remove and stop a session by ID (SessionProvider protocol)."""
136+
"""Pop, stop, and clean up a single session (SessionProvider protocol)."""
137137
session_server = self._session_servers.pop(session_id, None)
138-
if session_server is not None:
139-
logger.warning(
140-
f"Removing session {session_id}: {reason}"
141-
)
142-
try:
143-
await session_server.stop()
144-
except Exception:
145-
logger.error(
146-
f"Error stopping session {session_id} during watchdog removal",
147-
exc_info=True,
148-
)
149-
await self._close_session_on_server(session_id)
138+
if session_server is None:
139+
return
140+
141+
logger.warning(f"Removing session {session_id}: {reason}")
150142

151-
async def _close_session_on_server(self, session_id: str) -> None:
152-
"""Notify the UiPath server to remove a session so it stops sending messages."""
153143
try:
154-
await self._uipath.api_client.request_async(
155-
"DELETE",
156-
f"agenthub_/mcp/{self._folder_key}/{self.slug}",
157-
headers={"mcp-session-id": session_id},
144+
await session_server.stop()
145+
except Exception:
146+
logger.error(
147+
f"Error stopping session {session_id}",
148+
exc_info=True,
158149
)
159-
logger.info(f"Notified server of session closure: {session_id}")
160-
except HTTPStatusError as e:
161-
if e.response.status_code == 404:
162-
logger.info(f"Session {session_id} already removed server-side")
150+
151+
if session_server.output:
152+
if self.sandboxed:
153+
self._session_output = session_server.output
163154
else:
164-
logger.error(f"Error closing session {session_id} on server: {e}")
165-
except Exception as e:
166-
logger.error(f"Error closing session {session_id} on server: {e}")
155+
logger.info(f"Session {session_id} output: {session_server.output}")
156+
157+
if self.sandboxed:
158+
self._cancel_event.set()
167159

168160
async def get_schema(self) -> UiPathRuntimeSchema:
169161
"""Get schema for this MCP runtime.
@@ -366,11 +358,8 @@ async def _cleanup(self) -> None:
366358
await self._watchdog.stop()
367359
self._watchdog = None
368360

369-
for session_id, session_server in list(self._session_servers.items()):
370-
try:
371-
await session_server.stop()
372-
except Exception as e:
373-
logger.error(f"Error cleaning up session server {session_id}: {str(e)}")
361+
for session_id in list(self._session_servers.keys()):
362+
await self.remove_session(session_id, reason="runtime shutdown")
374363

375364
# Stop the shared HTTP server process (streamable-http only)
376365
await self._stop_http_server_process()
@@ -396,26 +385,8 @@ async def _handle_signalr_session_closed(self, args: list[str]) -> None:
396385
return
397386

398387
session_id = args[0]
399-
400388
logger.info(f"Received closed signal for session {session_id}")
401-
402-
try:
403-
session_server = self._session_servers.pop(session_id, None)
404-
if session_server:
405-
await session_server.stop()
406-
if session_server.output:
407-
if self.sandboxed:
408-
self._session_output = session_server.output
409-
else:
410-
logger.info(
411-
f"Session {session_id} output: {session_server.output}"
412-
)
413-
# If this is a sandboxed runtime for a specific session, cancel the execution
414-
if self.sandboxed:
415-
self._cancel_event.set()
416-
417-
except Exception as e:
418-
logger.error(f"Error terminating session {session_id}: {str(e)}")
389+
await self.remove_session(session_id, reason="server closed")
419390

420391
async def _handle_signalr_message(self, args: list[str]) -> None:
421392
"""
@@ -619,13 +590,9 @@ async def _monitor_http_server_process(self) -> None:
619590
# Stop all HTTP sessions, they will fail on next request anyway
620591
for session_id, session_server in list(self._session_servers.items()):
621592
if isinstance(session_server, StreamableHttpSessionServer):
622-
try:
623-
await session_server.stop()
624-
except Exception as e:
625-
logger.error(
626-
f"Error stopping session {session_id} after process crash: {e}"
627-
)
628-
self._session_servers.pop(session_id, None)
593+
await self.remove_session(
594+
session_id, reason="http process crash"
595+
)
629596
except asyncio.CancelledError:
630597
pass
631598

@@ -833,7 +800,7 @@ async def on_keep_alive_response(
833800
health = s.get_health_info()
834801
runtime_sessions[sid] = {
835802
"task_done": health.task_done,
836-
"active_requests": health.active_request_count,
803+
"active_requests": len(s._active_requests),
837804
}
838805
logger.info(f"Runtime active sessions: {runtime_sessions}")
839806
# If there are no active sessions and this is a sandbox environment
@@ -849,7 +816,6 @@ async def on_keep_alive_response(
849816
)
850817
self._cancel_event.set()
851818

852-
853819
if self._signalr_client:
854820
logger.info("Sending keep-alive ping...")
855821
await self._signalr_client.send(

src/uipath_mcp/_cli/_runtime/_session.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class SessionHealthInfo:
4141
task_exception: BaseException | None
4242
last_activity_time: float
4343
queue_size: int
44-
active_request_count: int
4544

4645

4746
class BaseSessionServer(ABC):
@@ -118,7 +117,6 @@ def get_health_info(self) -> SessionHealthInfo:
118117
task_exception=task_exception,
119118
last_activity_time=self._last_activity_time,
120119
queue_size=self._message_queue.qsize(),
121-
active_request_count=len(self._active_requests),
122120
)
123121

124122
async def on_message_received(self, request_id: str) -> None:
@@ -182,9 +180,7 @@ async def _relay_messages(self) -> None:
182180
if self._last_request_id is not None:
183181
await self._send_message(message, self._last_request_id)
184182
except EndOfStream:
185-
logger.warning(
186-
f"Read stream closed for session {self._session_id}"
187-
)
183+
logger.warning(f"Read stream closed for session {self._session_id}")
188184
break
189185
except Exception as e:
190186
if session_message:

src/uipath_mcp/_cli/_runtime/_watchdog.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ async def _check_sessions(self) -> None:
104104
f"with exception: {health.task_exception}"
105105
)
106106
else:
107-
logger.warning(
107+
logger.info(
108108
f"Watchdog: {transport} session {session_id} task "
109-
f"completed unexpectedly"
109+
f"completed, cleaning up"
110110
)
111111
await self._provider.remove_session(
112112
session_id, reason="dead task"
@@ -135,4 +135,3 @@ async def _check_sessions(self) -> None:
135135
logger.info(
136136
f"Watchdog check: {len(sessions)} session(s), {removed_count} removed"
137137
)
138-

tests/test_watchdog.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,16 @@ def _make_health(
2929
task_exception: BaseException | None = None,
3030
last_activity_time: float | None = None,
3131
queue_size: int = 0,
32-
active_request_count: int = 0,
3332
) -> SessionHealthInfo:
3433
return SessionHealthInfo(
3534
session_id=session_id,
3635
transport_type=transport_type,
3736
task_done=task_done,
3837
task_exception=task_exception,
39-
last_activity_time=last_activity_time if last_activity_time is not None else time.monotonic(),
38+
last_activity_time=last_activity_time
39+
if last_activity_time is not None
40+
else time.monotonic(),
4041
queue_size=queue_size,
41-
active_request_count=active_request_count,
4242
)
4343

4444

0 commit comments

Comments
 (0)