راهنمای ساخت سرور MCP در ۲۰۲۶: اتصال LLM به ابزارها با Model Context Protocol

از معماری سه‌لایه‌ای MCP تا ساخت سرور با Python و TypeScript، انتخاب transport، OAuth 2.1 و الگوهای امنیتی تولید؛ همه با مثال عملی و تجربه میدانی.

راهنمای سرور MCP در ۲۰۲۶

به‌روزرسانی: ۳۰ مه ۲۰۲۶

پروتکل MCP (Model Context Protocol) یک استاندارد باز مبتنی بر JSON-RPC 2.0 است که به مدل‌های زبانی بزرگ اجازه می‌دهد بدون نوشتن یک‌پارچه‌سازی اختصاصی برای هر API، به ابزارها، منابع داده و سیستم‌های بیرونی متصل شوند. وقتی Anthropic در نوامبر ۲۰۲۴ این پروتکل را منتشر کرد، یک پروژه آزمایشی به‌نظر می‌رسید؛ اما تا میانه ۲۰۲۶، با پذیرش OpenAI، Google، Microsoft Copilot، Cursor و Windsurf و انتقال نگه‌داری آن به Linux Foundation، MCP عملاً به لایه زیربنایی اکوسیستم agentic تبدیل شد. در این راهنما، از معماری سه‌لایه‌ای MCP، تفاوت آن با Function Calling، ساخت سرور MCP در Python و TypeScript، تا الگوهای امنیتی تولید را پوشش می‌دهم.

  • MCP در سال ۲۰۲۶ بیش از ۹۷ میلیون دانلود ماهانه SDK و بالاتر از ۱۰٬۰۰۰ سرور عمومی فعال دارد و توسط Claude، ChatGPT، Cursor، Gemini و Copilot به‌صورت بومی پشتیبانی می‌شود.
  • هر سرور MCP سه نوع primitive ارائه می‌دهد: Tools (توابع قابل فراخوانی)، Resources (داده‌های فقط‌خواندنی) و Prompts (الگوهای قابل استفاده مجدد).
  • Streamable HTTP با OAuth 2.1 جایگزین SSE به‌عنوان حمل‌و‌نقل توصیه‌شده برای سرورهای راه‌دور تولید شده است.
  • OpenAI Assistants API در میانه ۲۰۲۶ منسوخ می‌شود و معماری‌های مبتنی بر MCP جایگزین آن هستند.
  • سقف عملی ابزارهای فعال در یک عامل حدود ۴۰ تا ۵۰ ابزار است؛ فراتر از این، دقت انتخاب ابزار افت می‌کند.
  • تهدید اصلی امنیتی، prompt injection از طریق محتوای بازگشتی سرور است — قبل از اعطای دسترسی نوشتن، رفتار سرور را در حالت فقط‌خواندنی پایش کنید.

MCP چیست و چرا در ۲۰۲۶ مهم است؟

به‌زبان ساده، MCP یک پروتکل JSON-RPC 2.0 است که قرارداد ارتباطی بین یک «میزبان» AI (مثل Claude Desktop، Cursor یا ChatGPT) و سرورهایی که ابزارها و داده‌ها را در معرض دید مدل قرار می‌دهند، استانداردسازی می‌کند. قبل از MCP، هر بار که می‌خواستید مدل را به Slack، Postgres یا GitHub وصل کنید، باید schemaهای Function Calling اختصاصی می‌نوشتید و آن‌ها را برای هر مدل دوباره مهندسی می‌کردید. MCP این لایه را یک‌بار برای همیشه حل می‌کند: یک سرور می‌نویسید، با هر کلاینت سازگار کار می‌کند.

چرا الان مهم‌تر از همیشه است؟ در دسامبر ۲۰۲۵، Anthropic مالکیت MCP را به Linux Foundation منتقل کرد و OpenAI، Block و Anthropic به‌عنوان بنیان‌گذاران Agentic AI Foundation اعلام شدند. در اوایل ۲۰۲۶، OpenAI رسماً منسوخ شدن Assistants API را برای نیمه دوم ۲۰۲۶ اعلام کرد و میلیون‌ها توسعه‌دهنده را به مهاجرت به MCP سوق داد. این فقط یک «پروتکل جذاب» نیست؛ این پلامبینگ جدید عامل‌های هوش مصنوعی است.

از منظر من که چندین سال است سیستم‌های مبتنی بر LLM در تولید می‌سازم، تحول واقعی این است: قبلاً وقت زیادی صرف نوشتن آداپتورهای ابزار می‌شد، حالا آن وقت صرف ارزیابی، حاکمیت و تنظیم سیاست دسترسی می‌شود، یعنی همان کارهایی که واقعاً به کیفیت سیستم نهایی کمک می‌کند. اگر دنبال زمینه پایه‌ای در فراخوانی ابزار هستید، توصیه می‌کنم ابتدا راهنمای Function Calling و Tool Use در LLM را بخوانید، چون MCP در عمل یک لایه استانداردسازی روی همان مفهوم است.

معماری سه‌لایه‌ای MCP: Host، Client، Server

برای پیاده‌سازی درست MCP باید سه نقش را از هم جدا کنید: Host برنامه میزبانی است که با کاربر تعامل دارد (مثل Claude Desktop یا Cursor)، Client رابط داخل میزبان است که با یک سرور خاص گفت‌و‌گو می‌کند، و Server فرآیندی است که ابزارها، منابع و prompt‌ها را در معرض دید قرار می‌دهد. این تفکیک به‌خصوص وقتی به مرحله امنیت و حاکمیت می‌رسید بسیار حیاتی است، چون نقطه‌های اعمال سیاست (policy enforcement) دقیقاً همین مرزها هستند.

هر سرور MCP می‌تواند سه دسته primitive ارائه دهد:

  • Tools: توابعی که LLM می‌تواند با تأیید کاربر فراخوانی کند (مثل create_issue در GitHub).
  • Resources: داده‌های فقط‌خواندنی شبیه فایل که می‌توان به context تزریق کرد (مثل محتوای README یا نتیجه یک query).
  • Prompts: قالب‌های آماده که کاربر را در سناریوهای پرتکرار راهنمایی می‌کنند.

چرخه عمر استاندارد به این شکل است: کلاینت سرور را اجرا می‌کند، یک handshake نسخه‌بندی انجام می‌شود، کلاینت لیست tool/resource/prompt را discover می‌کند، و سپس بسته به نیاز کاربر، فراخوانی‌ها انجام می‌شوند. تمام پیام‌ها در قالب JSON-RPC 2.0 رد و بدل می‌شوند که این یعنی debug کردن آن با ابزارهایی مثل MCP Inspector تقریباً بدون درد است.

تفاوت MCP با Function Calling چیست؟

اگر فقط یک مدل و یک سرویس دارید، Function Calling خام کافی است. اما لحظه‌ای که می‌خواهید یک ابزار را روی Claude، GPT و Gemini همزمان کار کند یا چندین تیم در سازمان به یک سرویس متصل شوند، MCP عملاً اجباری می‌شود. جدول زیر تفاوت‌های عملی را خلاصه می‌کند:

ویژگی Function Calling خام MCP
قابلیت حمل بین مدل‌ها نه (schema هر مدل متفاوت است) بله، یک سرور برای همه کلاینت‌ها
discovery پویای ابزار دستی، در زمان طراحی خودکار، در زمان اجرا
پشتیبانی از منابع فقط‌خواندنی خیر بله (Resources)
احراز هویت استاندارد اختصاصی OAuth 2.1 رسمی
اکوسیستم سرور آماده هیچ ۱۰٬۰۰۰+ سرور عمومی
منحنی یادگیری پایین متوسط
مناسب برای ۱ مدل + ۱ ابزار چند مدل، چند ابزار، چند تیم

ساخت سرور MCP با Python: گام به گام

بیایید یک سرور MCP واقعی بسازیم که آب و هوای یک شهر را بازمی‌گرداند. این مثال با mcp[cli] نسخه ۱.۱۰+ تست شده و در Python 3.11 یا بالاتر اجرا می‌شود. اول پکیج‌ها را نصب کنید:

uv init weather-mcp
cd weather-mcp
uv add "mcp[cli]" httpx

سپس فایل server.py را بسازید:

from mcp.server.fastmcp import FastMCP
import httpx

# Initialize the MCP server with a human-readable name.
mcp = FastMCP("weather")

NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-mcp/1.0"


async def fetch_json(url: str) -> dict | None:
    headers = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
    async with httpx.AsyncClient(timeout=30.0) as client:
        try:
            r = await client.get(url, headers=headers)
            r.raise_for_status()
            return r.json()
        except Exception:
            return None


@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Return a short 5-period forecast for the given coordinates."""
    points = await fetch_json(f"{NWS_API_BASE}/points/{latitude},{longitude}")
    if not points:
        return "Forecast unavailable for these coordinates."

    forecast_url = points["properties"]["forecast"]
    forecast = await fetch_json(forecast_url)
    if not forecast:
        return "Forecast service returned no data."

    periods = forecast["properties"]["periods"][:5]
    lines = [f"{p['name']}: {p['shortForecast']}, {p['temperature']}°{p['temperatureUnit']}"
             for p in periods]
    return "\n".join(lines)


if __name__ == "__main__":
    # stdio transport: perfect for local Claude Desktop integration.
    mcp.run(transport="stdio")

برای اتصال به Claude Desktop، فایل claude_desktop_config.json را ویرایش کنید:

{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/weather-mcp", "run", "server.py"]
    }
  }
}

پیاده‌سازی سرور MCP با TypeScript

اگر اکوسیستم شما Node.js است، SDK رسمی TypeScript تجربه‌ای تقریباً برابر می‌دهد. این مثال با @modelcontextprotocol/[email protected] روی Node.js 22+ تست شده است.

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "tickets", version: "1.0.0" });

// A single write-capable tool. The schema doubles as runtime validation.
server.tool(
  "create_ticket",
  {
    title: z.string().min(3).max(120),
    severity: z.enum(["low", "medium", "high", "critical"]),
    assignee: z.string().email().optional(),
  },
  async ({ title, severity, assignee }) => {
    // In real life: POST to your ticketing system here.
    const id = `TCK-${Math.floor(Math.random() * 1e6)}`;
    return {
      content: [
        {
          type: "text",
          text: `Created ${id} (${severity}) for ${assignee ?? "unassigned"}: ${title}`,
        },
      ],
    };
  }
);

// A read-only resource. Surfaces structured context without tool overhead.
server.resource(
  "open-tickets",
  "tickets://open",
  async () => ({
    contents: [
      {
        uri: "tickets://open",
        mimeType: "application/json",
        text: JSON.stringify({ count: 12, oldestDays: 4 }, null, 2),
      },
    ],
  })
);

await server.connect(new StdioServerTransport());

نکته‌ای که در عمل زیاد دیده‌ام: schema را با Zod محکم تعریف کنید و فیلدهای enum را به‌جای رشته آزاد استفاده کنید. این کار دقت انتخاب ابزار توسط LLM را به‌طور قابل‌توجهی بالا می‌برد، چون مدل از معنای دقیق فیلد مطمئن‌تر می‌شود. این رویکرد ارزیابی‌محور با چیزی که در راهنمای ارزیابی و مشاهده‌پذیری LLM در تولید پوشش داده‌ایم هم‌خوانی کامل دارد.

انتخاب حمل‌و‌نقل: stdio، Streamable HTTP و OAuth 2.1

MCP دو حمل‌و‌نقل اصلی دارد و انتخاب بین آن‌ها بیشتر از یک تصمیم فنی، یک تصمیم معماری است. stdio برای سناریوهای محلی (desktop، CLI، توسعه) ایده‌آل است: راه‌اندازی صفر، latency حداقل، و امنیت ذاتی چون فقط در ماشین کاربر اجرا می‌شود. اما برای استقرار ابری، اشتراک سرور بین تیم‌ها، یا اتصال به یک سرویس remote، باید به Streamable HTTP مهاجرت کنید.

SSE که در نسخه‌های اولیه استفاده می‌شد، هنوز در SDKها پشتیبانی می‌شود اما طبق specification رسمی MCP در حال منسوخ شدن است. Streamable HTTP استاندارد جدید برای سرورهای راه‌دور است و OAuth 2.1 به‌عنوان مکانیزم احراز هویت رسمی توصیه می‌شود.

یک نکته که در پروژه‌های متعدد دیدم تیم‌ها بر سرش لیز می‌خورند: شروع با stdio و سپس مهاجرت به HTTP بدون بازنویسی منطق. اگر منطق Toolهایتان از همان ابتدا مستقل از حمل‌و‌نقل نوشته شده باشد (که SDKها این را تشویق می‌کنند)، این مهاجرت در حد چند خط setup transport است. بنابراین حتی برای پروژه‌های محلی، رابط Toolهایتان را طوری بنویسید که قابلیت remote شدن داشته باشد.

بهترین شیوه‌های امنیتی برای MCP در تولید

این بخش جایی است که اکثر تیم‌ها در ۲۰۲۶ زمین می‌خورند. وقتی یک سرور MCP می‌سازید، در عمل به یک LLM اجازه می‌دهید با ابزارهای واقعی شما تعامل کند. این یعنی هر اشتباه طراحی می‌تواند به دلایلی فراتر از prompt اشتباه (مثل prompt injection از طریق محتوای بازگشتی یک سرور دیگر) منجر به خسارت واقعی شود.

اصول عملی که در محیط‌های تولید همیشه رعایت می‌کنم:

  1. اصل حداقل دسترسی: برای هر سرور MCP یک credential اختصاصی با کمترین scope ممکن بسازید. هرگز credential تولید را بازاستفاده نکنید.
  2. شروع با read-only: قبل از اعطای دسترسی نوشتن، حداقل دو هفته رفتار عامل را در حالت فقط‌خواندنی پایش کنید.
  3. مراقب prompt injection باشید: هر سروری که محتوای وب یا داده کاربری بازمی‌گرداند، یک بردار حمله است. این محتوا را sanitize کنید یا حداقل وقتی به فراخوانی tool منجر می‌شود، تأیید کاربر بگیرید.
  4. فقط سرورهای رسمی: ترجیحاً از پیاده‌سازی‌های خود ارائه‌دهنده سرویس استفاده کنید (مثل سرور رسمی GitHub) نه fork جامعه.
  5. secret به‌صورت environment variable: کلیدهای API هیچ‌وقت نباید در config فایل وارد version control شوند.
  6. سرور rate limiting: سمت سرور MCP خودتان حتماً rate limit بگذارید؛ LLM می‌تواند در حالت اشتباه ده‌ها فراخوانی پشت سر هم بزند.
  7. لاگ کامل audit: تمام فراخوانی‌های tool را با اطلاعات کاربر، پارامتر و نتیجه لاگ کنید. این برای debug و compliance حیاتی است.

ارزیابی، debugging و MCP Inspector

هیچ سرور MCP بدون ارزیابی نباید به تولید برسد. شخصاً بدبین به پاسخ «فقط prompt کن» هستم. وقتی به ابزارهای واقعی متصل می‌شوید، تنها چیزی که جلوی فاجعه را می‌گیرد ارزیابی منظم است. ابزارهای کلیدی:

  • MCP Inspector: ابزار رسمی web-based برای debug کردن سرورها. قبل از اتصال به Claude Desktop، حتماً سرور را اینجا تست کنید. روی npx @modelcontextprotocol/inspector اجرا می‌شود.
  • تست‌های end-to-end: یک مجموعه از سناریوهای کاربر واقعی بنویسید و خروجی tool call را با مقدار مورد انتظار مقایسه کنید. این کار را قبل از هر deploy باید اجرا کنید.
  • regression suite برای schema: هر تغییر در schema ابزار را با مدل‌های هدف تست کنید تا مطمئن شوید LLM همچنان درست انتخاب می‌کند.
  • tracing و observability: ابزارهایی مثل OpenTelemetry را به سرور MCP خود متصل کنید تا latency، نرخ خطا و الگوهای استفاده را رصد کنید.

برای الگوهای پیشرفته‌تر طراحی عامل که این ارزیابی‌ها را عمیق‌تر می‌برد، توصیه می‌کنم نگاهی به راهنمای سیستم‌های چندعامله با LangGraph و CrewAI بیندازید. بسیاری از الگوهای orchestration که در آنجا توضیح داده شده، مستقیماً روی معماری مبتنی بر MCP قابل پیاده‌سازی هستند.

چه تعداد سرور MCP می‌توانم متصل کنم؟

سؤال متداول، با پاسخ غیر شهودی: کمتر، بهتر است. Cursor یک سقف نرم حدود ۴۰ ابزار فعال دارد که اگر از آن رد شوید، عامل بی‌سر و صدا به برخی ابزارها دسترسی پیدا نمی‌کند. حتی روی Claude Code، فراتر از ۵۰ ابزار قابل مشاهده، مدل شروع به انتخاب اشتباه می‌کند، چون توضیحات همه ابزارها در context window قرار می‌گیرند و مسئله انتخاب نویزی می‌شود.

تجربه عملی من: ۵ تا ۷ سرور هم‌زمان سقف عملی است. فراتر از این، نه‌تنها دقت انتخاب ابزار افت می‌کند، بلکه هزینه token هم به‌طور قابل‌توجهی بالا می‌رود چون هر سرور توضیحاتش را در هر turn به context تزریق می‌کند. سرورهایی که داشتن آن‌ها معمولاً ارزش دارد:

  • Context7 برای injection مستندات نسخه‌محور کتابخانه‌ها در زمان query.
  • Filesystem یا یک سرور Git محلی برای دسترسی به کد.
  • یک سرور دیتابیس (مثل Supabase MCP یا یک سرور Postgres سفارشی).
  • یک سرور جستجوی وب (Brave Search یا Fetch) برای grounding.
  • سرور سیستم issue تیم (Linear، GitHub، Jira) بسته به جریان کاری.

اگر در حال ساخت یک pipeline RAG هستید که از MCP استفاده می‌کند، الگوهای retrieval در راهنمای جامع Agentic RAG و انتخاب پایگاه داده برداری مناسب در مقایسه پایگاه‌های داده برداری ۲۰۲۶ به‌خوبی مکمل این مقاله هستند.

پرسش‌های متداول

آیا OpenAI از MCP پشتیبانی می‌کند؟

بله. OpenAI در اواخر ۲۰۲۵ پشتیبانی رسمی MCP را در ChatGPT و API اعلام کرد و در اوایل ۲۰۲۶ منسوخ شدن Assistants API را در نیمه دوم ۲۰۲۶ اعلام کرد. سرورهای MCP که برای Claude نوشته‌اید، روی OpenAI نیز کار می‌کنند.

آیا برای ساخت سرور MCP باید Python یا TypeScript بدانم؟

نه لزوماً. SDK رسمی برای Python و TypeScript کامل‌ترین است، اما SDKهای C#، Java، Go و Rust نیز توسط Microsoft، Spring AI و جامعه نگه‌داری می‌شوند. پروتکل JSON-RPC 2.0 است و می‌توانید حتی بدون SDK سرور بسازید.

آیا سرور MCP می‌تواند چندین کاربر را همزمان سرویس دهد؟

برای stdio، خیر؛ هر کاربر یک فرآیند جداگانه دارد. برای Streamable HTTP، بله، اما باید session management و OAuth 2.1 را به‌درستی پیاده‌سازی کنید. توصیه می‌شود برای سرورهای چندکاربره حتماً از یک gateway استفاده کنید.

چطور می‌توانم سرور MCP موجود را به Cursor متصل کنم؟

در Cursor به Settings → MCP بروید و JSON تنظیمات سرور (شامل command و args یا URL برای سرورهای HTTP) را اضافه کنید. پس از restart، ابزارهای آن سرور در لیست tools عامل ظاهر می‌شوند.

آیا MCP برای production-grade RAG مناسب است؟

بله، اما با احتیاط. MCP لایه ابزار را استانداردسازی می‌کند ولی پایگاه داده برداری، embedding pipeline و reranking همچنان مسئولیت شما هستند. الگوی متداول این است که retrieval را در یک سرور MCP اختصاصی encapsulate کنید و عامل از آن استفاده کند.

Nikhil Verma
درباره نویسنده Nikhil Verma

AI automation engineer chaining LLMs into workflows that actually work. Bullish on tool use; bearish on prompt theatre.