اگر سرور MCP را با پایتون می‌نویسید، نسخه‌ی 2.2.0 همین امروز روی PyPI نشسته و کد شما را می‌شکند. FastMCP به MCPServer تغییر نام داده، inputSchema به input_schema، و پورت دیگر در سازنده پذیرفته نمی‌شود. در این پست یک سرور واقعی با همین نسخه می‌سازیم، با کلاینت داخل همان پروسه صدایش می‌زنیم و خطاهای مهاجرت را با متن واقعی‌شان نشان می‌دهیم. همه‌ی عددها و خروجی‌ها روی همین نسخه و روی همین سرور خوانده شده‌اند.

این پست یک مهاجرت نیست و یک نظرسنجی هم نیست. یک کار انجام می‌دهد: از صفر تا اولین فراخوانی موفق، روی mcp==2.2.0. لحظه‌ی خواندن داده: ۲۷ سپتامبر ۲۰۲۶.

نصب و بسته‌هایی که واقعا کشیده می‌شوند

نصب یک دستور است، اما آنچه کشیده می‌شود یک عدد دارد که روی تصور شما اثر می‌گذارد: ۲۸ بسته. mcp-types دیگر بخشی از mcp نیست و یک توزیع جداگانه است که فقط به pydantic و typing-extensions نیاز دارد. کسی که فقط می‌خواهد شکل‌های پروتکل را مصرف کند، می‌تواند به‌تنهایی همان را نصب کند.

$ python3 -m venv .venv
$ .venv/bin/pip install mcp==2.2.0
...
Successfully installed annotated-types-0.8.0 anyio-4.15.1 ... mcp-2.2.0
  mcp-types-2.2.0 opentelemetry-api-1.45.0 ... uvicorn-0.54.0
$ .venv/bin/pip show mcp mcp-types
Name: mcp
Version: 2.2.0
Requires: anyio, httpx2, jsonschema, mcp-types, opentelemetry-api, pydantic,
  pyjwt, python-multipart, sse-starlette, starlette, typing-extensions,
  typing-inspection, uvicorn
Name: mcp-types
Version: 2.2.0
Requires: pydantic, typing-extensions
Required-by: mcp

کف پایتون 3.10 است و این عدد را خود بسته اعلام می‌کند. یک نکته‌ی عملی در همین نصب پنهان است: فرمان خط فرمان mcp بدون افزونه‌ی cli کار نمی‌کند و پیامش دقیقا همین است.

$ .venv/bin/mcp --help
Error: typer is required. Install with 'pip install mcp[cli]'

اگر فقط سرور می‌نویسید، لازم نیست typer را نصب کنید. برای mcp dev و mcp install اما باید باشد، چون هر دو فرمان را از همین بسته می‌آورند.

سرور و کلاینت، هر دو در یک فایل

سرور زیر دو ابزار می‌دهد که JSON پایتونی PyPI را می‌خوانند. MCPServer در نسخه‌ی 2 دیگر پورت و ترابری نمی‌گیرد؛ ترابری در run() است. ورودی هر ابزار مستقیم از همان امضای تابع می‌آید، پس اسکیمای ورودی را خودتان نمی‌نویسید.

import json
import urllib.request

from mcp.server import MCPServer

UA = "hoosh-mcp-demo/1.0"


def _pypi(package: str) -> dict:
    # پیش‌فرض urllib یک User-Agent بی‌نام می‌فرستد و PyPI خطای 403 می‌دهد
    req = urllib.request.Request(f"https://pypi.org/pypi/{package}/json",
                                 headers={"User-Agent": UA})
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.load(r)


mcp = MCPServer("pypi-inspector", instructions="Report the newest release.")


@mcp.tool()
def latest_version(package: str) -> str:
    """Newest released version of `package` on PyPI and when it was uploaded."""
    data = _pypi(package)
    version = data["info"]["version"]
    files = data["releases"].get(version) or []
    return f"{package} {version} uploaded {files[0]['upload_time']}"


if __name__ == "__main__":
    # ترابری و پورت اینجاست، نه در سازنده
    mcp.run("streamable-http")

کلاینت در نسخه‌ی 2 یک شیء است، نه سه لایه‌ی تودرتو. شما Client را با خودِ شیء سرور می‌دهید و هیچ پورتی، هیچ زیرپروسه‌ای و هیچ HTTPای در کار نیست. داخل بلوک، نسخه‌ی پروتکل همان لحظه مذاکره شده و روی خصوصیت‌ها نشسته است.

import anyio

from mcp import Client
from demo_server import mcp


async def main() -> None:
    async with Client(mcp) as client:
        print("protocol_version :", client.protocol_version)
        listing = await client.list_tools()
        for t in listing.tools:
            print(f"tool {t.name:16} {t.description}")
        r = await client.call_tool("latest_version", {"package": "mcp"})
        print("latest_version   :", r.content[0].text)
        r = await client.call_tool("install_floor", {"package": "httpx"})
        print("install_floor    :", r.content[0].text)
        print("structured       :", r.structured_content, "| is_error:", r.is_error)


anyio.run(main)

این پنج خط خروجی، همان چیزی است که واقعا چاپ شد. خط اول یعنی دو طرف روی نسخه‌ی 2026-07-28 پروتکل توافق کرده‌اند. خط آخر نشان می‌دهد یک برگشتی، سه چیز با هم می‌دهد: content برای مدل، structured_content برای کد خودتان، و is_error برای اینکه بدانید به اولی اعتماد کنید یا نه.

protocol_version : 2026-07-28
tool latest_version   Newest released version of `package` on PyPI and when it was uploaded.
tool install_floor    The `requires_python` floor declared by the newest release.
latest_version   : mcp 2.2.0 uploaded 2026-09-07T16:06:19
install_floor    : httpx 0.28.1 requires_python=>=3.8
structured       : {'result': 'httpx 0.28.1 requires_python=>=3.8'} | is_error: False

اگر سرورتان روی HTTP واقعی است، تنها فرق این است که به‌جای شیء سرور، نشانی را می‌دهید: Client("http://127.0.0.1:8000/mcp"). برای سرور زیرپروسه‌ای هم StdioServerParameters را مستقیم به Client می‌دهید. دادن جفت خواندن و نوشتن به‌صورت دستی در نسخه‌ی 2 خطا می‌دهد.

سه تغییری که هیچ خطایی نمی‌دهند

بازارنگاشت‌ها با خودشان را می‌دهند و دقیقا به همین دلیل دیده نشده‌اند. این سه مورد را روی همین نصب اندازه گرفتیم.

نخست، تابع همگام روی نخ کارگر اجرا می‌شود. یک ابزار که با def تعریف شده دیگر حلقه‌ی رویداد را نمی‌بندد، و بهای آن این است که بدنه‌اش دیگر روی همان نخ اجرا نمی‌شود. کدی که به نخ وابسته است باید بازنویسی شود.

$ .venv/bin/python thread_probe.py
main thread     : MainThread
sync_tool ran on: AnyIO worker thread
async_tool ran on: loop_thread=MainThread

دوم، خطاها دو مسیر دارند و این مسیرها عوض شده‌اند. MCPError داخل ابزار یک خطای پروتکل است و به کلاینت می‌رسد؛ هر استثنای دیگری به یک نتیجه‌ی is_error=True تبدیل می‌شود و مدل فقط نام ابزار را می‌بیند. تنها پیام ToolError است که به مدل می‌رسد.

$ .venv/bin/python error_probe.py
--- fine ---
  is_error: False | text: ok
--- ordinary_error ---
  is_error: True | text: Error executing tool ordinary_error
--- protocol_error ---
  RAISED MCPError: this is a protocol error

سوم، کلاینت پاسخ سرور را اعتبارسنجی می‌کند. سروری که پاسخ نیم‌بند بدهد، جایی که نسخه‌ی 1 بی‌سروصدا می‌بلعید، حالا در همان کلاینت ValidationError می‌دهد. این تغییر آزاردهنده به نظر می‌رسد و دقیقا همان چیزی است که به درد شما می‌خورد وقتی به سرورهایی وصل می‌شوید که در اختیار شما نیستند.

خطاهای مهاجرت، با متن واقعی

سه خطایی که هر کدبیس نسخه‌ی 1 در اولین اجرا می‌بیند، با پیام کامل اینجا آمده‌اند. پیام اول عمدا طولانی است: خود کتابخانه مسیر مهاجرت را در آن می‌گذارد.

$ .venv/bin/python migration_probe.py
=== 1. the old v1 import path ===
ModuleNotFoundError: No module named 'mcp.server.fastmcp'. This is mcp 2.x, where
  FastMCP was renamed to MCPServer (from mcp.server.mcpserver import MCPServer)
  and other APIs changed; see the migration guide at ... or pin 'mcp<2' to keep
  running v1 code.

=== 2. transport kwargs in the constructor ===
TypeError: MCPServer.__init__() got an unexpected keyword argument 'port'

=== 3. where the field names went ===
Tool fields     : ['annotations', 'description', 'execution', 'icons',
  'input_schema', 'meta', 'name', 'output_schema', 'title']
has input_schema: True | has inputSchema: False
CallToolResult  : ['content', 'is_error', 'meta', 'result_type', 'structured_content']

خط سوم از همه‌ی راهنماهای مهاجرت مهم‌تر است: روی سیم JSON هنوز camelCase است و فقط نام صفت‌های پایتون عوض شده. یعنی inputSchema به input_schema می‌رود و isError به is_error. هر کدی که صفت‌های camelCase را می‌خواند، در سرور می‌میرد و در کلاینت ساکت از کار می‌افتد.

پین کنیم یا مهاجرت کنیم

خط 1.x هنوز زنده است و در حالت نگهداری اصلاح امنیتی می‌گیرد. تاریخ‌های زیر از کلید انتشارهای PyPI خوانده شده‌اند و ساعت‌ها به وقت UTC است.

نسخهتاریخ آپلودجایگاه در تاریخ
0.9.1۲۰ نوامبر ۲۰۲۴نخستین انتشار بسته
1.29.1۲۴ اوت ۲۰۲۶آخرین نسخه پیش از 1.30.0
1.30.0۷ سپتامبر ۱۴:۳۴آخرین نسخه‌ی پایدار خط 1
2.0.0rc1۲۷ ژوئیه ۱۳:۳۵آخرین پیش‌انتشار پیش از 2.0.0
2.0.0۲۸ ژوئیه ۱۳:۴۵نخستین نسخه‌ی پایدار خط 2
2.2.0۷ سپتامبر ۱۶:۰۶آخرین نسخه‌ی خط 2

تفاضل دو ساعت آخر 1 ساعت و 32 دقیقه است، یعنی هر دو خط در یک روز زنده شدند و هیچ‌کدام دیگری را متوقف نکرد. از 64 نسخه‌ی پایداری که بسته تا امروز دارد، ۵ تا روی خط 2 است: 5 تقسیم بر 64 برابر 7.8 درصد. بقیه، یعنی 59 نسخه، روی خط 1 مانده‌اند و 8 کلید دیگر هم نسخه‌ی پیش‌انتشار است.

پس انتخاب را این‌طور ببندید: اگر کد شما خطای مهاجرت را نمی‌دهد، همان را پین کنید و نسخه را دست نزنید.

$ .venv/bin/pip install "mcp>=1.28,<2"
Collecting mcp<2,>=1.28
Would install ... mcp-1.30.0 ...

اما اگر کتابخانه‌ی خودتان به mcp وابسته است و هنوز مهاجرت نکرده‌اید، همین کران بالا را در pyproject.toml بگذارید تا یک به‌روزرسانی ناخواسته شما را به نسخه‌ی 2 نبرد. مهاجرت را جدا انجام دهید، روی یک شاخه، وقتی کار دیگری روی شاخه نیست.

اگر تازه سرور MCP می‌نویسید، نسخه‌ی 2 انتخاب پیش‌فرض است. اگر دارید کد موجود را مهاجرت می‌دهید، راهنمای رسمی مهاجرت هر شکست را با قبل و بعد می‌نویسد. برای سنجیدن یک سرور پیش از پذیرش هم پست ارزیابی سرور MCP چک‌لیست عملی دارد، و اگر می‌خواهید ببینید یک سرور واقعی در یک میزبان چطور بالا می‌آید، سرور MCP کروم همین مسیر را از نصب تا فراخوانی نشان می‌دهد.

منابع

  1. صفحه‌ی بسته mcp در PyPI: نسخه‌ها، کف پایتون و تاریخچه
  2. مستندات رسمی: چه چیزی در نسخه‌ی 2 عوض شده است
  3. راهنمای نصب و فهرست وابستگی‌ها و افزونه‌ها
  4. مستندات کلاینت: چهار شکل اتصال و محتوای بازگشتی
  5. راهنمای مهاجرت از نسخه‌ی 1، با کد پیش و پس از هر تغییر
  6. تفکیک خطای پروتکل و خطای ابزار
  7. نسخه‌های پروتکل و مذاکره در کلاینت
  8. بسته‌ی mcp-types و وابستگی‌های آن
  9. مشخصات پروتکل MCP، نسخه‌ی 2026-07-28
  10. مخزن رسمی python-sdk
  11. انتشارهای مخزن و یادداشت هر نسخه
  12. صفحه‌ی httpx در PyPI، نمونه‌ی دوم در خروجی بالا