From e1af7ea9374c55de39374196ef9b0f3ab2d1cbb6 Mon Sep 17 00:00:00 2001 From: Peter Ibekwe Date: Wed, 3 Jun 2026 17:34:21 -0700 Subject: [PATCH] Fix pyupgrade and AGENTS.md reconnect description - pyupgrade: drop forward-reference string annotations in _mcp.py (Python 3.10+ resolves them natively now that MCPTaskOptions is defined before use). - AGENTS.md: align reconnect description with current behavior. Phase 1 (initial tools/call) does NOT retry on connection loss; raises 'connection lost; task state unknown' instead, so a server that accepted the request but lost the response cannot start the operation twice. Phase 2 (tasks/get / tasks/result) still reconnects once against the same task_id. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/packages/core/AGENTS.md | 2 +- python/packages/core/agent_framework/_mcp.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/packages/core/AGENTS.md b/python/packages/core/AGENTS.md index 81267a0815..fbbc171ae1 100644 --- a/python/packages/core/AGENTS.md +++ b/python/packages/core/AGENTS.md @@ -84,7 +84,7 @@ agent_framework/ - `default_ttl: timedelta | None` — forwarded to the server as `params.task.ttl` (milliseconds). When `None`, the server's default applies. - `cancel_remote_task_on_local_cancellation: bool = True` — on local `CancelledError`, spawn a best-effort `tasks/cancel` before re-raising. - **Permissive fallback**: servers that ignore the augmentation (return `CallToolResult` directly) or reject the unknown `task` field with `METHOD_NOT_FOUND` / `INVALID_PARAMS` fall back to the plain `session.call_tool(...)` path so legacy servers keep working. -- **Phase-aware reconnect**: a dropped connection before a `task_id` is known retries the augmented `tools/call`; once a `task_id` exists, only `tasks/get` / `tasks/result` are retried for the same id so reconnects never create duplicate remote tasks. +- **Phase-aware reconnect**: a dropped connection before a `task_id` is known raises `ToolExecutionException("connection lost; task state unknown")` without re-issuing the augmented `tools/call`, so a server that accepted the request but lost the response cannot be made to start the same operation twice; once a `task_id` exists, `tasks/get` / `tasks/result` reconnect once and retry against the same id, which is safe because the request references the existing operation. ### File Access Harness (`_harness/_file_access.py`) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index af9e2fcd3f..f56e7fc420 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -1344,7 +1344,7 @@ class MCPTool: inner_exception=ex, ) from ex - def _effective_task_options(self) -> "MCPTaskOptions": + def _effective_task_options(self) -> MCPTaskOptions: """Return the effective MCPTaskOptions, lazily constructing defaults on first use. Defers the implicit ``MCPTaskOptions()`` so the experimental warning only @@ -1358,12 +1358,12 @@ class MCPTool: return self._task_options_default @property - def task_options(self) -> "MCPTaskOptions": + def task_options(self) -> MCPTaskOptions: """The effective MCPTaskOptions for this tool (lazy defaults).""" return self._effective_task_options() @task_options.setter - def task_options(self, value: "MCPTaskOptions | None") -> None: + def task_options(self, value: MCPTaskOptions | None) -> None: self._task_options_explicit = value self._task_options_default = None @@ -1691,7 +1691,7 @@ class MCPTool: await asyncio.sleep(self._compute_poll_delay(snapshot.pollInterval).total_seconds()) @staticmethod - def _coerce_get_task_result(lenient: "types.Result", task_id: str) -> "types.GetTaskResult": + def _coerce_get_task_result(lenient: types.Result, task_id: str) -> types.GetTaskResult: """Coerce a lenient Result into GetTaskResult, defaulting ``ttl`` when absent.""" from mcp import types