Skip to content

feat(mcp): upgrade Python SDK to 2.0 - #1025

Open
edis-uipath wants to merge 10 commits into
mainfrom
chore/upgrade-mcp-sdk-latest
Open

feat(mcp): upgrade Python SDK to 2.0#1025
edis-uipath wants to merge 10 commits into
mainfrom
chore/upgrade-mcp-sdk-latest

Conversation

@edis-uipath

@edis-uipath edis-uipath commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • upgrade the MCP Python SDK from 1.26.0 to 2.0.0 and replace the copied SDK 1.x Streamable HTTP transport with a thin SDK 2 adapter
  • preserve UiPath's externally persisted SessionInfo, including restored-session protocol adoption, version headers, idempotent persistence, termination, and recovery from the SDK server's canonical Session not found response
  • make transport replacement atomic and failure-safe for concurrent callers, while retaining compatibility with the previously accepted httpx.Timeout API
  • replace the incompatible langchain-mcp-adapters sample dependency with a tested first-party active-session tool converter, and migrate local and remote samples to MCP 2 public APIs
  • declare a directly tested, bounded httpx2 dependency and release the public raw-model attribute changes as uipath-langchain 0.17.0

Compatibility

The low-level UiPath client remains compatible with servers negotiating 2025-03-26, 2025-06-18, or 2025-11-25; all three versions are covered by real-transport tests. Externally restored sessions are validated and adopted without another initialize request, then terminated on disposal when configured.

A server that also supports the legacy initialize flow connects in legacy mode. Strictly modern 2026-07-28 discovery-only support remains a separate, uncommitted follow-up layer and is not part of this PR.

Migration guide

McpClient intentionally continues to return the MCP SDK's raw result models. Their Python attributes changed from camelCase to snake_case in SDK 2, so this PR bumps the package to 0.17.0 and documents the exact mappings.

Validation

  • uv lock --check
  • uv run just lint
  • uv run ruff format --check .
  • uv run mypy --config-file pyproject.toml .
  • uv run pytest — 2,672 passed
  • uv run pytest -o addopts='' tests/agent/tools/test_mcp — 79 passed
  • real MCP 2 stdio initialization, discovery, and LangChain tool invocation smoke test
  • simple-local-mcp dependency resolution dry run without langchain-mcp-adapters
  • uv build — 0.17.0 sdist and wheel
  • local diff coverage against origin/main — 93%

Copilot AI review requested due to automatic review settings August 6, 2026 11:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Upgrades the MCP Python SDK integration in uipath_langchain.agent.tools.mcp to SDK 2.0.0, replacing the previously copied Streamable HTTP transport with a thin adapter and updating the client/tool layers (plus tests/docs) to the new SDK APIs while preserving UiPath’s externally persisted SessionInfo behavior.

Changes:

  • Bump dependency pin to mcp==2.0.0 (and remove direct langchain-mcp-adapters) with corresponding lockfile updates.
  • Replace the local forked Streamable HTTP transport with a session-aware adapter around the SDK 2 transport using httpx2 event hooks.
  • Rework session recovery logic (fresh transport + ClientSession replacement) and update tests/docs to exercise real SDK 2 transport behavior.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
uv.lock Updates resolved dependency graph for MCP SDK 2.0 (httpx2, mcp-types, etc.) and removes langchain-mcp-adapters.
pyproject.toml Pins mcp==2.0.0 and drops the direct langchain-mcp-adapters dependency.
src/uipath_langchain/agent/tools/mcp/streamable_http.py Replaces ~800-line fork with a small adapter that syncs mcp-session-id via httpx2 request/response hooks.
src/uipath_langchain/agent/tools/mcp/mcp_client.py Migrates to SDK 2 (MCPError, httpx2) and replaces recovery with “close connection + reopen transport/session” semantics.
src/uipath_langchain/agent/tools/mcp/mcp_tool.py Updates SDK 2 model field names (input_schema/output_schema) and MCP error mapping.
src/uipath_langchain/agent/tools/mcp/claude.md Updates implementation documentation for the new adapter/recovery model.
tests/agent/tools/test_mcp/test_mcp_client.py Replaces prior mocks with real SDK 2 transport over httpx2.MockTransport to validate negotiation/recovery/cache/disposal.
tests/agent/tools/test_mcp/test_mcp_tool.py Updates imports/types for SDK 2 and aligns schema assertions with snake_case fields.
tests/agent/tools/test_mcp/claude.md Updates test strategy documentation for the new endpoint + transport mocking pattern.
docs/mcp-sdk-2-upgrade.md Adds a focused upgrade review doc explaining SDK 2 changes, compatibility boundaries, and recovery behavior.

Comment on lines +86 to +87
client.event_hooks["request"].remove(apply_session_id)
client.event_hooks["response"].remove(capture_session_id)
Comment on lines +304 to +307
if self._connection_stack is not None:
await self._connection_stack.aclose()
self._connection_stack = None
self._session = None
Comment on lines 316 to 320
else:
detail = (
f"MCP server '{server_slug}' returned an error for tool "
f"'{tool_name}': {error.error.message}"
f"'{tool_name}': {error.message}"
)
Comment thread pyproject.toml
"jsonpath-ng>=1.7.0",
"mcp==1.26.0",
"langchain-mcp-adapters==0.2.1",
"mcp==2.0.0",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restore compatibility with langchain-mcp-adapters before pinning MCP 2.0. The repository’s own simple-local-mcp integration resolves langchain-mcp-adapters==0.3.1 with this pin, then fails at import because the adapter imports RequestContext removed by MCP 2. I reproduced the same failure with uv run --with langchain-mcp-adapters==0.3.1 ..., and the alpha/cloud/staging integration jobs all fail on it. The adapter metadata only says mcp>=1.24.0, so dependency resolution cannot protect downstream users. Land a compatible adapter/migration for this supported path, or prevent the incompatible combination from resolving.

async with sdk_streamable_http_client(
url,
http_client=client,
terminate_on_close=terminate_on_close,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preserve the full restored-session lifecycle instead of only injecting its ID into requests. The SDK transport delegated to here keeps a separate in-memory session_id; when SessionInfo starts with a persisted ID and initialization is skipped, that SDK field remains None. I reproduced a successful tool call followed by disposal with terminate_on_close=True: no DELETE was sent (delete_count == 0) and the persisted ID remained set, whereas the replaced transport terminated through SessionInfo. The fresh ClientSession also has no adopted negotiation state, so the restored-session request carried no mcp-protocol-version. Explicitly hydrate/adopt the required session state or implement termination/version handling for restored IDs, and cover both the protocol header and DELETE path.

if self._session_info:
await self._session_info.set_session_id(None)
await self._initialize_session()
await self._open_connection()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep the client recoverable when opening the replacement connection fails. I reproduced _open_connection() raising MCPError(CONNECTION_CLOSED, ...) here: _open_connection clears _session, but _client_initialized remains True; every later operation then fails in _ensure_session() with RuntimeError("MCP client initialized without a session") instead of rebuilding the client. The handshake failure also escapes from inside the retry handler, so the configured retry loop does not repair this state. Reset the initialized state on reopen failure or make _ensure_session() rebuild when the session is absent, with a focused failed-recovery-then-next-call test.

Comment thread uv.lock
[[package]]
name = "mcp"
version = "1.26.0"
version = "2.0.0"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bump the uipath-langchain package version and regenerate the lockfile with that version. This PR changes runtime session/recovery behavior and takes a new major MCP dependency, but pyproject.toml and the local-package lock entry still publish 0.15.3. Every merge publishes immediately, so leaving the package version unchanged makes these changes ride another release and prevents an independent rollback.

@edis-uipath
edis-uipath force-pushed the chore/upgrade-mcp-sdk-latest branch from 43b7ac8 to ecbeb94 Compare August 12, 2026 11:38
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants