درس ۳ از ۳۳

MCP clients

عنوان اصلی: MCP clients

هدف یادگیری: شناسایی مسئولیت‌های یک MCP client و آشنایی با اکوسیستم client موجود.

مفاهیم کلیدی: client lifecycle، capability negotiation، request/response correlation، notification handling، host integration.

Client مولفه‌ای داخل host است که یک اتصال را مالک است. مسئولیت‌هایش: راه‌اندازی یا اتصال به serverش، اجرای handshake به نام initialize، اعلام capabilityهای host (roots، sampling، elicitation)، دریافت capabilityهای اعلام‌شده server (tools، resources، prompts، logging، completions)، صدا زدن tools/list/resources/list/prompts/list برای پر کردن UI host، route کردن تصمیم tool-call مدل به tools/call، گوش دادن به notifications/tools/list_changed (و معادل‌های resource و prompt) برای sync نگه داشتن state، و shutdown تمیز.

اکوسیستم client موجود شامل Claude Desktop، Claude Code، Cursor، VS Code (با extensionهای Claude Code یا Continue)، Zed، Cline و فهرست رو به رشدی از IDEهای agentic است. هر UI، primitiveها را متفاوت surface می‌کند: Claude Desktop ابزارها را به‌عنوان function قابل فراخوانی مدل و prompts را به‌عنوان slash command نمایش می‌دهد؛ Cursor، resources را به‌صورت @-mentionable context نشان می‌دهد.

مثال عملی — Python (SDK رسمی، minimal client)

import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from pydantic import AnyUrl

server_params = StdioServerParameters(
    command="uv",
    args=["run", "server", "fastmcp_quickstart", "stdio"],
)

async def run():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print(f"Available tools: {[t.name for t in tools.tools]}")
            result = await session.call_tool("add", arguments={"a": 5, "b": 3})
            print(f"Tool result: {result.content[0].text}")
            content = await session.read_resource(AnyUrl("greeting://World"))
            print(f"Resource: {content.contents[0].text}")

asyncio.run(run())

دیاگرام معماری (متنی): یک جعبه «Client» که داخلش به چهار خط تقسیم شده: «Lifecycle Manager»، «Request Router»، «Notification Listener»، «Capability Registry». فلش‌های خروجی به Server می‌روند؛ فلش‌های ورودی از Server به Notification Listener می‌خورند.

اشتباهات رایج

  • ارسال requestهایی غیر از ping قبل از اینکه server به initialize پاسخ بدهد.
  • فراموش کردن handle کردن notifications/tools/list_changed — لیست tool داخل client کهنه می‌شود.
  • رفتار با notification مثل request (notificationها id ندارند و هیچ‌وقت پاسخ نمی‌گیرند).