Skip to content

Python: migrate mcp connector to mcp 2.x - #14251

Open
ProgrammerPlus1998 wants to merge 2 commits into
microsoft:mainfrom
ProgrammerPlus1998:feat/mcp-2x-migration
Open

Python: migrate mcp connector to mcp 2.x#14251
ProgrammerPlus1998 wants to merge 2 commits into
microsoft:mainfrom
ProgrammerPlus1998:feat/mcp-2x-migration

Conversation

@ProgrammerPlus1998

Copy link
Copy Markdown

Motivation and Context

The mcp dependency is pinned to mcp>=1.26.0,<2.0, which blocks the MCP Python SDK 2.x line. 2.0 introduced several breaking changes that the connector relies on, so the integration and its tests need to be updated before the pin can be loosened.

Fixes #14246

This takes the "upgrade to 2.x" approach (require the 2.x SDK) rather than trying to support both 1.x and 2.x at once.

Description

Updates the MCP connector (python/semantic_kernel/connectors/mcp.py) to the 2.0 API and bumps the pin to mcp>=2.0.0,<3.0 (both the main dependency and the mcp extra). uv.lock regenerated.

Server side (create_mcp_server_from_kernel / create_mcp_server_from_functions):

  • The lowlevel @server.list_tools() / call_tool() / get_prompt() / list_prompts() / set_logging_level() decorators were removed. Handlers are now passed to the Server(...) constructor (on_list_tools, on_call_tool, on_list_prompts, on_get_prompt, on_set_logging_level), receive a (ServerRequestContext, params) pair, and return result models (ListToolsResult, CallToolResult, GetPromptResult, EmptyResult).
  • The per-request session used to emit log messages now comes from the handler's ServerRequestContext (the removed server.request_context no longer exists).

Client side (MCP*Plugin):

  • streamablehttp_clientstreamable_http_client; it now yields a 2-tuple and no longer accepts headers/timeout/sse_read_timeout, so MCPStreamableHttpPlugin builds an httpx2 client via create_mcp_http_client when those are set.
  • ClientSession(read_timeout_seconds=...) now takes float seconds instead of a timedelta.
  • Sampling callback context: mcp.shared.context.RequestContextmcp.client.session.ClientRequestContext.
  • message_handler now receives ServerNotification | Exception (no more RequestResponder); list-changed notifications are matched via .method directly.
  • Session re-init check uses session.initialize_result instead of the removed _request_id.
  • McpErrorMCPError (raised with code=/message=).
  • camelCase model fields → snake_case throughout: input_schema, mime_type, tool_use_id, model_preferences, max_tokens, system_prompt.

Removed in 2.0:

  • The websocket transport (mcp.client.websocket) no longer exists, so MCPWebsocketPlugin.get_mcp_client() now raises a clear KernelPluginInvalidConfigurationError at connect time instead of failing at import. (Happy to drop the class entirely if preferred.)

Tests / assets:

  • tests/assets/test_plugins/TestMCPPlugin/mcp_server.py: FastMCPMCPServer.
  • tests/unit/connectors/mcp/test_mcp.py: updated fixtures/mocks for the 2.0 shapes (input_schema, streamable_http_client 2-tuple, initialize_result), rewrote the server-handler assertions to the new get_request_handler(method) API, and replaced the websocket test with one asserting the removal error.

Contribution Checklist

  • The code builds clean without any errors or warnings (ruff check + ruff format clean, mypy clean on the connector)
  • The PR follows the SK Contribution Guidelines and the pre-submission formatting script raises no violations
  • All unit tests pass, and I have added new tests where possible — 23 passed (unit), 1 passed (integration, real stdio MCP server) under mcp 2.0.0
  • I didn't break anyone 😄

Bump the mcp dependency to >=2.0.0,<3.0 and update the connector for the
2.0 breaking changes:

- Server: the lowlevel @server.list_tools()/call_tool()/get_prompt()/
  set_logging_level() decorators were removed. Handlers are now passed to
  the Server(...) constructor as on_list_tools/on_call_tool/on_list_prompts/
  on_get_prompt/on_set_logging_level, take a (ServerRequestContext, params)
  pair, and return result models (ListToolsResult/CallToolResult/...). The
  per-request session for sending log messages is taken from the handler
  context instead of the removed server.request_context.
- Client: streamablehttp_client -> streamable_http_client (2-tuple); its
  headers/timeout/sse_read_timeout kwargs were removed, so the streamable
  http plugin builds an httpx2 client via create_mcp_http_client instead.
  ClientSession.read_timeout_seconds now takes float seconds.
- camelCase model fields are snake_case: input_schema, mime_type,
  tool_use_id, model_preferences, max_tokens, system_prompt.
- RequestContext -> ClientRequestContext for the sampling callback;
  message_handler now receives ServerNotification|Exception (no
  RequestResponder), and list-changed notifications are matched via
  .method directly.
- McpError -> MCPError (raised with code/message).
- The websocket transport was removed in mcp 2.0; MCPWebsocketPlugin now
  raises a clear configuration error at connect time instead of failing
  at import.
- Tests and the test MCP server asset updated; uv.lock regenerated.

Tests: 23 passed (unit), 1 passed (integration, real stdio server).
ruff and mypy clean.
Copilot AI review requested due to automatic review settings August 3, 2026 11:09
@ProgrammerPlus1998
ProgrammerPlus1998 requested a review from a team as a code owner August 3, 2026 11:09

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 5 | Confidence: 62% | Result: All clear

Reviewed: Correctness, Security Reliability, Test Coverage, Failure Modes, Design Approach


Automated review by ProgrammerPlus1998's agents

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Not ready to approve

There are correctness and reliability issues in the updated connector (notably streamable HTTP timeout semantics and server logging context handling under concurrency) that should be addressed before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR migrates the Python MCP connector integration in Semantic Kernel to the MCP Python SDK 2.x API, unblocking installation alongside mcp>=2.0.0 and updating server/client behaviors and tests to match the breaking changes.

Changes:

  • Update python/semantic_kernel/connectors/mcp.py for MCP 2.x (server handler registration via Server(...), client transport/notification changes, snake_case model fields, httpx2-based streamable HTTP).
  • Bump dependency pins to mcp>=2.0.0,<3.0 and regenerate uv.lock.
  • Update MCP test assets and unit tests to match new SDK shapes and removal of websocket transport.
File summaries
File Description
python/semantic_kernel/connectors/mcp.py Migrates MCP server/client connector implementation to the MCP 2.x API and transport expectations.
python/pyproject.toml Updates MCP dependency pins to require SDK 2.x.
python/tests/unit/connectors/mcp/test_mcp.py Adapts unit tests/mocks to MCP 2.x request/response shapes and transport changes.
python/tests/assets/test_plugins/TestMCPPlugin/mcp_server.py Updates test MCP server asset to the new server class.
python/uv.lock Regenerates lockfile reflecting MCP 2.x transitive dependency changes (e.g., httpx2).
Review details

Suppressed comments (1)

python/semantic_kernel/connectors/mcp.py:930

  • This docstring says extra kwargs are passed to the websocket client constructor, but get_mcp_client() unconditionally raises because websocket transport was removed in mcp 2.x, so those kwargs are never used. Updating the docstring avoids misleading callers.
        Any extra arguments passed to the constructor will be passed to the
        websocket client constructor.

  • Files reviewed: 4/5 changed files
  • Comments generated: 2
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment on lines +1081 to +1084
# In mcp 2.x there is no server-level request_context; handlers receive a per-request
# ServerRequestContext carrying the connection-scoped session. We track the most recent
# one so helper functions (e.g. _log) can reach the session to emit log messages.
current_ctx: dict[str, ServerRequestContext | None] = {"ctx": None}
Comment on lines +892 to +895
timeout = httpx2.Timeout(
self.timeout if self.timeout else 30.0,
read=self.sse_read_timeout if self.sse_read_timeout else 300.0,
)
@ProgrammerPlus1998

Copy link
Copy Markdown
Author

@ProgrammerPlus1998 please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.请阅读以下贡献者许可协议(CLA)。如果您同意该协议,请回复以下信息。

@microsoft-github-policy-service agree [company="{your company}"]

Options:  选项:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.(默认 - 未指定公司)我对其提交内容拥有完全的知识产权所有权,且并非在受雇工作期间进行提交。
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.(当提供公司时)我是在为雇主工作期间进行提交(或根据合同或适用法律,雇主对我的提交内容拥有知识产权)。我已获得雇主许可,代表雇主进行提交并签订本协议。通过下方签署,定义术语“您”包括我和我的雇主。
@microsoft-github-policy-service agree company="Microsoft"

Contributor License Agreement
贡献者许可协议

@ProgrammerPlus1998

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 5 | Confidence: 63% | Result: All clear

Reviewed: Correctness, Security Reliability, Test Coverage, Failure Modes, Design Approach


Automated review by ProgrammerPlus1998's agents

- Pass the per-request ServerRequestContext into _log explicitly instead of
  storing the most recent context in a shared dict, which was not safe under
  concurrent requests.
- In the streamable http plugin, fall back to the configured timeout for the
  read timeout when sse_read_timeout is unset, so a short timeout also
  bounds reads instead of defaulting reads to 300s.
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.

Python: support mcp 2.x (Python SDK): pin blocks mcp 2.0.0

2 participants