مجله ایرانیکاسرورآموزش سرور، هاست، وردپرس و شبکه
آموزشی

MCP چیست؟ آموزش ساخت MCP Server برای اتصال هوش مصنوعی به APIهای شخصی

فرض کنید یک دستیار هوش مصنوعی دارید و می‌خواهید بتواند وضعیت سرورهای شما را بررسی کند، اطلاعات یک سرویس را از API اختصاصی دریافت کند یا داده‌های مجاز یک نرم‌افزار را بخواند. آیا لازم…

MCP چیست؟ آموزش ساخت MCP Server برای اتصال هوش مصنوعی به APIهای شخصی
خلاصه سریع این آموزش

فرض کنید یک دستیار هوش مصنوعی دارید و می‌خواهید بتواند وضعیت سرورهای شما را بررسی کند، اطلاعات یک سرویس را از API اختصاصی دریافت کند یا داده‌های مجاز یک نرم‌افزار…

۴۰ دقیقه مطالعه ۰ دیدگاه ۱۳ آبان ۱۴۰۵ آخرین به‌روزرسانی
فرض کنید یک دستیار هوش مصنوعی دارید و می‌خواهید بتواند وضعیت سرورهای شما را بررسی کند، اطلاعات یک سرویس را از API اختصاصی دریافت کند یا داده‌های مجاز یک نرم‌افزار را بخواند. آیا لازم است برای هر مدل هوش مصنوعی یک اتصال اختصاصی برنامه‌نویسی کنید؟ خیر. با استفاده از Model Context Protocol یا MCP می‌توانید یک رابط استاندارد بین برنامه‌های هوش مصنوعی و APIهای شخصی خود بسازید.

به زبان ساده، MCP روشی استاندارد برای معرفی ابزارها، منابع اطلاعاتی و قابلیت‌های یک برنامه به سیستم‌های هوش مصنوعی است. به‌جای اینکه هر Agent مستقیماً روش اتصال به API شما را بداند، یک MCP Server می‌سازید که ابزارهای مشخصی را در اختیار برنامه‌های سازگار قرار می‌دهد.

برای مثال، یک MCP Server می‌تواند ابزاری به نام get_service_status داشته باشد. وقتی کاربر از دستیار می‌پرسد «وضعیت سرور ماینکرافت من چیست؟»، سیستم هوش مصنوعی می‌تواند در صورت مجاز بودن، ابزار را فراخوانی کند. MCP Server نیز درخواست را به API داخلی ارسال کرده و فقط نتیجه مجاز را برمی‌گرداند.

در این مقاله ابتدا توضیح می‌دهیم MCP چیست، چگونه کار می‌کند و چه تفاوتی با REST API و Function Calling دارد. سپس با Python MCP SDK نسخه ۲ یک MCP Server واقعی می‌سازیم، Tool Schema را تعریف می‌کنیم، اتصال به API شخصی را پیاده‌سازی می‌کنیم و روش استقرار آن روی Ubuntu VPS را توضیح می‌دهیم.

در ادامه، موضوعات مهمی مانند Streamable HTTP، احراز هویت OAuth، مدیریت API Key و Secrets، محدودیت نرخ درخواست، حفاظت از اطلاعات کاربران، Prompt Injection و Threat Model را نیز بررسی می‌کنیم.

MCP چیست و چرا ساخته شده است؟

MCP مخفف Model Context Protocol است؛ یک پروتکل باز که ارتباط برنامه‌های هوش مصنوعی با داده‌ها، ابزارها و سرویس‌های خارجی را استاندارد می‌کند.

تصور کنید چند برنامه هوش مصنوعی مختلف دارید و هر کدام باید به API مدیریت سرور، پایگاه داده، نرم‌افزار حسابداری یا سامانه پشتیبانی شما متصل شوند. بدون استاندارد مشترک، ممکن است برای هر برنامه اتصال جداگانه‌ای لازم باشد.

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

مثال ساده از کاربرد MCP

فرض کنید یک شرکت هاستینگ، API داخلی زیر را در اختیار دارد:

GET /v1/services/minecraft-1/status

این API وضعیت یک سرویس را در قالب JSON برمی‌گرداند؛ اما مدل هوش مصنوعی الزاماً با ساختار، کلید احراز هویت یا شیوه فراخوانی API آشنا نیست.

با MCP یک ابزار مانند get_service_status تعریف می‌کنیم. این ابزار پارامتر service_id را دریافت می‌کند، اعتبار آن را بررسی می‌کند، درخواست مجاز را به API داخلی ارسال می‌کند و نتیجه محدودشده را برمی‌گرداند.

به این ترتیب برنامه هوش مصنوعی با یک رابط مشخص و قابل کشف کار می‌کند و نیازی ندارد مستقیماً به رمزها یا جزئیات زیرساخت شما دسترسی داشته باشد.

آیا MCP خودش هوش مصنوعی است؟

خیر. MCP یک مدل زبانی نیست و به‌تنهایی پاسخ هوشمند تولید نمی‌کند. MCP روش ارتباط میان برنامه هوش مصنوعی و ابزارهای بیرونی را تعریف می‌کند.

برای استفاده از قابلیت‌های یک MCP Server معمولاً به یک MCP Client یا برنامه میزبان سازگار نیاز دارید. آن برنامه می‌تواند تصمیم بگیرد کدام ابزار را فراخوانی کند و از نتیجه در پاسخ نهایی استفاده کند.

معماری MCP؛ Host، Client و Server چه تفاوتی دارند؟

برای درک صحیح MCP باید سه بخش اصلی را از یکدیگر جدا کنید: Host، Client و Server.

بخش وظیفه نمونه
MCP Host برنامه‌ای که تجربه کاربر و Agent را مدیریت می‌کند محیط هوش مصنوعی یا IDE سازگار
MCP Client ارتباط پروتکلی با MCP Server را برقرار می‌کند کلاینت داخلی برنامه میزبان
MCP Server ابزارها و منابع مجاز را معرفی و اجرا می‌کند سرور اتصال به API اختصاصی
Backend API عملیات واقعی برنامه را انجام می‌دهد API مدیریت سرویس‌ها

مسیر یک درخواست واقعی

  1. کاربر از دستیار هوش مصنوعی وضعیت سرویس خود را می‌پرسد.
  2. برنامه میزبان قابلیت‌های MCP Server متصل‌شده را بررسی می‌کند.
  3. کلاینت با مجوز مناسب ابزار get_service_status را فراخوانی می‌کند.
  4. MCP Server ورودی و هویت درخواست‌کننده را بررسی می‌کند.
  5. سرور از طریق اعتبارنامه داخلی خودش به API مجاز درخواست می‌فرستد.
  6. API نتیجه را به MCP Server برمی‌گرداند.
  7. MCP Server اطلاعات لازم را به کلاینت تحویل می‌دهد.
  8. دستیار نتیجه را به زبان قابل فهم برای کاربر توضیح می‌دهد.

در این معماری، MCP Server مرز مهم امنیتی میان هوش مصنوعی و سرویس‌های داخلی شما است. اگر این مرز اشتباه طراحی شود، ابزار ظاهراً ساده می‌تواند دسترسی بیش از حد به اطلاعات یا امکانات مدیریتی ایجاد کند.

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

MCP قرار نیست REST API را حذف کند. در بیشتر پروژه‌ها، MCP Server در کنار API موجود قرار می‌گیرد و ابزارهای مشخصی از آن را برای برنامه‌های هوش مصنوعی قابل استفاده می‌کند.

فناوری نقش اصلی
REST API ارائه داده و عملیات برنامه از طریق HTTP
Function Calling تعریف و فراخوانی توابع در گردش کار مدل
MCP استاندارد اتصال، کشف و فراخوانی ابزارها و منابع خارجی
Agents SDK اجرای منطق Agent، گردش کار و ابزارهای آن

آیا MCP جایگزین OpenAI Agents SDK می‌شود؟

خیر. Agents SDK برای تعریف رفتار Agent، مدیریت اجرا، Toolها و Handoffها استفاده می‌شود. MCP برای ارائه ابزارها و منابع از طریق یک پروتکل مشترک طراحی شده است.

بنابراین یک Agent می‌تواند به‌جای داشتن تمام ابزارها در کد خودش، از MCP Serverهای مجاز استفاده کند.

آیا برای یک API کوچک حتماً MCP لازم است؟

خیر. اگر یک برنامه ساده فقط به یک API مشخص نیاز دارد و هیچ برنامه هوش مصنوعی دیگری از آن استفاده نمی‌کند، پیاده‌سازی مستقیم Function Calling ممکن است ساده‌تر باشد.

اما اگر می‌خواهید قابلیت‌های یک سیستم را به چند Host و Agent سازگار ارائه کنید، MCP می‌تواند ساختار اتصال و نگهداری را استانداردتر کند.

تفاوت Tools، Resources و Prompts در MCP

MCP فقط برای فراخوانی توابع نیست. این پروتکل سه قابلیت اصلی برای ارائه اطلاعات و عملکرد دارد.

Tools؛ ابزارهای اجرایی

Tool عملیاتی است که کلاینت می‌تواند آن را فراخوانی کند. مانند دریافت وضعیت سرور، جست‌وجوی اسناد یا ثبت یک درخواست مجاز.

برای مثال:

get_service_status(service_id="minecraft-1")

Resources؛ منابع اطلاعاتی

Resource معمولاً داده‌ای است که می‌توان آن را با یک URI مشخص خواند؛ مانند راهنمای یک سرویس یا یک سند ساختاریافته.

برای نمونه، منبعی با آدرس مفهومی زیر می‌تواند اطلاعات راهنمای عمومی یک سرویس را نمایش دهد:

service-guide://minecraft

Prompts؛ الگوهای آماده دستور

Prompt در MCP می‌تواند یک قالب آماده برای انجام کار مشخص باشد؛ برای مثال، الگویی که از دستیار می‌خواهد اطلاعات یک سرویس را بررسی و خلاصه کند.

در ساخت MCP Server لازم نیست هر سه قابلیت را پیاده‌سازی کنید. اگر فقط می‌خواهید Agent به API شخصی شما دسترسی محدود داشته باشد، ممکن است چند Tool مشخص کافی باشند.

نسخه فعلی MCP در سال ۲۰۲۶ چه تفاوتی با نسخه‌های قدیمی دارد؟

یکی از نکات مهم هنگام ساخت MCP Server، توجه به نسخه پروتکل و نسخه SDK است. بسیاری از مثال‌های اینترنتی مربوط به دوره‌ای هستند که MCP برای ارتباط HTTP از Session و مرحله initialize استفاده می‌کرد.

در نسخه رسمی 2026-07-28، هسته MCP به مدل بدون Session تغییر کرده است.

موضوع نسخه‌های قبلی نسخه 2026-07-28
شروع ارتباط Initialize Handshake بدون Handshake قدیمی
Session پروتکل HTTP در حالت‌های Stateful استفاده می‌شد از هسته جدید حذف شده
شناسه نسخه در مرحله شروع و برخی Headerها اطلاعات نسخه در هر درخواست
کشف قابلیت‌ها بخشی از Initialize با سازوکار جدید از جمله server/discover
مقیاس‌پذیری HTTP گاهی نیازمند Sticky Session در ارتباط مدرن Stateless ساده‌تر

نسخه جدید در کنار ویژگی‌هایی مانند Multi Round-Trip Requests و سازوکارهای بهبود‌یافته کشف قابلیت، برای استفاده در زیرساخت‌های معمول HTTP و مقیاس‌پذیری بهتر طراحی شده است.

البته پشتیبانی از Clientهای قدیمی همچنان به نسخه و تنظیمات SDK بستگی دارد. نباید فرض کنید تمام برنامه‌های هوش مصنوعی نصب‌شده، آخرین نسخه پروتکل را پشتیبانی می‌کنند.

برای بررسی تغییرات رسمی به اطلاعیه انتشار نسخه MCP 2026-07-28 مراجعه کنید.

Transport در MCP چیست؟ تفاوت stdio، Streamable HTTP و SSE

Transport روش انتقال پیام‌ها میان MCP Client و MCP Server است. انتخاب آن تعیین می‌کند سرور شما فقط برای یک برنامه محلی قابل استفاده باشد یا از طریق شبکه در اختیار Clientهای دیگر قرار بگیرد.

۱. stdio برای اجرای محلی

در روش stdio، برنامه میزبان، MCP Server را به‌صورت یک فرآیند فرزند اجرا می‌کند و ارتباط از طریق ورودی و خروجی استاندارد انجام می‌شود.

این روش برای اتصال محلی مناسب است و معمولاً نیازی به باز کردن پورت HTTP ندارد.

یک نکته مهم این است که در stdio، خروجی استاندارد برای پیام‌های پروتکل استفاده می‌شود؛ بنابراین نباید پیام‌های Debug و چاپ‌های دلخواه را روی stdout ارسال کنید. برای گزارش‌های اجرایی از stderr یا Logging مناسب استفاده کنید.

۲. Streamable HTTP برای سرور راه دور

برای MCP Serverهایی که روی VPS یا سرویس ابری اجرا می‌شوند، Streamable HTTP روش مناسب ارتباط است.

در نسخه فعلی، ارتباط اصلی از طریق درخواست‌های HTTP POST به Endpoint مشخصی مانند /mcp انجام می‌شود. سرور می‌تواند متناسب با نوع درخواست، پاسخ JSON یا جریان SSE مربوط به همان درخواست ارائه دهد.

توجه کنید که Streamable HTTP با روش قدیمی HTTP+SSE یکسان نیست. در نسخه مدرن، Endpoint قدیمی GET Stream و Session پروتکلی حذف شده‌اند.

۳. SSE قدیمی

روش قدیمی HTTP+SSE برای سازگاری با کلاینت‌هایی باقی مانده است که هنوز به پروتکل‌های پیشین وابسته‌اند. برای پروژه جدید توصیه می‌شود از Streamable HTTP استفاده کنید.

Transport محیط مناسب انتخاب پیشنهادی
stdio ابزار محلی و فرآیند فرزند بله، برای اجرای محلی
Streamable HTTP VPS و اتصال شبکه‌ای بله، برای سرور Remote
HTTP+SSE قدیمی سازگاری با Clientهای قدیمی خیر، برای توسعه جدید

امنیت Transport از چه زمانی اهمیت پیدا می‌کند؟

به محض اینکه MCP Server از طریق HTTP در دسترس قرار بگیرد، باید مانند یک API واقعی از آن محافظت کنید. ارتباط HTTPS، اعتبارسنجی Origin، محدودیت Hostهای مجاز و احراز هویت درخواست‌ها از مهم‌ترین موارد هستند.

اتصال stdio نیز به‌طور خودکار امن نیست؛ فرآیند میزبان همچنان باید دسترسی فایل‌ها و اطلاعات محرمانه را کنترل کند.

پیش‌نیازها و منابع موردنیاز VPS برای MCP Server

در این آموزش از Ubuntu 24.04 LTS و Python استفاده می‌کنیم. هدف اجرای یک MCP Server سبک است که درخواست‌ها را به API داخلی ارسال می‌کند.

منابع پیشنهادی

نوع استفاده RAM پیشنهادی CPU دیسک
آزمایش MCP ۱ گیگابایت ۱ vCPU ۵ گیگابایت
اتصال چند Tool به API شخصی ۲ گیگابایت ۱ تا ۲ vCPU ۱۰ گیگابایت
سرویس چندکاربره با پایش و لاگ ۲ تا ۴ گیگابایت ۲ vCPU یا بیشتر متناسب با داده‌ها

این اعداد پیشنهاد اولیه هستند، نه حداقل رسمی MCP. مصرف واقعی بیشتر به تعداد درخواست‌های هم‌زمان، عملیات Toolها، حجم پاسخ API و سرویس‌های جانبی بستگی دارد.

MCP Server در این سناریو مدل زبانی را روی VPS اجرا نمی‌کند؛ بنابراین برای خود سرویس اتصال به API معمولاً نیازی به GPU نیست.

مرحله اول: نصب Python MCP SDK روی Ubuntu VPS

ابتدا با SSH به VPS متصل شوید. برای امنیت بهتر، پروژه را تحت کاربر مستقل و غیر root اجرا می‌کنیم.

نصب پیش‌نیازها

sudo apt update
sudo apt install -y python3 python3-venv python3-pip nginx curl ca-certificates

بررسی نسخه Python

python3 --version

Ubuntu 24.04 معمولاً Python 3.12 دارد که برای نسخه جدید MCP SDK مناسب است.

ساخت کاربر و پوشه پروژه

sudo useradd --system --create-home \
--home-dir /opt/iranica-mcp \
--shell /usr/sbin/nologin mcpservice

sudo chown -R mcpservice:mcpservice /opt/iranica-mcp

این دستورات برای نصب جدید هستند. اگر کاربر یا مسیر موردنظر از قبل وجود دارد، آن‌ها را بدون بررسی تغییر ندهید.

ایجاد محیط مجازی

sudo -u mcpservice python3 -m venv /opt/iranica-mcp/.venv

نصب MCP Python SDK

در زمان تهیه این مقاله، نسخه پایدار بررسی‌شده پکیج mcp برابر 2.3.0 است.

sudo -u mcpservice /opt/iranica-mcp/.venv/bin/python -m pip install --upgrade pip

sudo -u mcpservice /opt/iranica-mcp/.venv/bin/python -m pip install \
"mcp[cli]==2.3.0" "httpx>=0.27,<1" "uvicorn>=0.30,<1"

گزینه [cli] ابزار خط فرمان MCP را نیز نصب می‌کند. برای محیط Production بهتر است پس از تست، نسخه دقیق تمام وابستگی‌ها را ثبت کنید.

بررسی نصب SDK

/opt/iranica-mcp/.venv/bin/pip show mcp

در خروجی باید مشخصات نسخه نصب‌شده نمایش داده شود.

مرحله دوم: ساخت اولین MCP Server ساده

قبل از اتصال به API شخصی، یک MCP Server محلی با یک ابزار ساده می‌سازیم تا ساختار اصلی آن را بشناسیم.

ایجاد فایل نمونه

sudo nano /opt/iranica-mcp/demo.py

محتوای زیر را وارد کنید:

from mcp.server import MCPServer

mcp = MCPServer("IranicaServer Demo")

@mcp.tool()
def get_server_message() -> str:
    """Return a simple demonstration message."""
    return "Iranica MCP Server is working."

if __name__ == "__main__":
    mcp.run(transport="stdio")

در این مثال، کلاس MCPServer یک سرور جدید می‌سازد و دکوراتور @mcp.tool() تابع Python را به یک ابزار MCP تبدیل می‌کند.

تست با MCP Inspector

برای تست توسعه می‌توانید از MCP Inspector استفاده کنید. این ابزار به Node.js و npx نیاز دارد. روی کامپیوتر توسعه یا محیط آزمایش، با داشتن پیش‌نیازهای لازم، از دستور زیر استفاده کنید:

/opt/iranica-mcp/.venv/bin/mcp dev /opt/iranica-mcp/demo.py

آدرس محلی نمایش‌داده‌شده را در محیط مجاز توسعه باز کنید، ابزار get_server_message را پیدا کنید و آن را اجرا کنید.

نکته امنیتی: Inspector برای توسعه و بررسی ابزارها است. رابط مدیریتی آن را بدون احراز هویت و کنترل شبکه روی اینترنت عمومی منتشر نکنید.

مرحله سوم: Tool Schema چیست و چگونه تعریف می‌شود؟

Tool Schema مشخص می‌کند یک ابزار چه پارامترهایی دریافت می‌کند، هر پارامتر چه نوع داده‌ای دارد و کدام مقدارها الزامی هستند.

این قرارداد معمولاً با JSON Schema بیان می‌شود. در Python MCP SDK نسخه ۲، با استفاده از Type Hint و Pydantic می‌توانید ساختار ورودی را به‌صورت خودکار تولید کنید.

نمونه ابزار با پارامتر محدود

from typing import Annotated
from pydantic import Field
from mcp.server import MCPServer

mcp = MCPServer("Schema Demo")

@mcp.tool()
def get_service_status(
    service_id: Annotated[
        str,
        Field(
            pattern=r"^[a-z0-9-]{3,32}$",
            description="Allowed service identifier"
        )
    ]
) -> str:
    """Read the status of an authorized service."""
    return f"Requested status for {service_id}"

در این مثال، ورودی باید رشته‌ای با طول و ساختار مشخص باشد. مقدارهایی که با الگوی تعریف‌شده سازگار نیستند پیش از اجرای تابع رد می‌شوند.

Schema تولیدشده چه شکلی است؟

بخش اصلی Schema ورودی به‌صورت مفهومی شبیه نمونه زیر خواهد بود:

{
  "type": "object",
  "properties": {
    "service_id": {
      "type": "string",
      "pattern": "^[a-z0-9-]{3,32}$",
      "description": "Allowed service identifier"
    }
  },
  "required": ["service_id"]
}

نام، ساختار و جزئیات Schema نهایی را باید از خروجی واقعی tools/list بررسی کنید؛ زیرا SDK ممکن است اطلاعات تکمیلی و ساختار خروجی را نیز اضافه کند.

آیا Schema امنیت Tool را تضمین می‌کند؟

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

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

Structured Output در MCP

برای خروجی‌های ساختاریافته می‌توانید به‌جای متن آزاد، یک مدل Pydantic برگردانید. SDK از نوع خروجی برای ساخت Output Schema و تولید پاسخ ساختاریافته استفاده می‌کند.

این روش برای نتیجه‌هایی مانند وضعیت سرویس، تعداد کاربران یا زمان بررسی مفید است؛ زیرا برنامه دریافت‌کننده مجبور نیست اطلاعات را از متن غیرساختاریافته استخراج کند.

مرحله چهارم: اتصال MCP Server به API شخصی

اکنون سراغ سناریوی اصلی می‌رویم. فرض کنید یک API داخلی برای مشاهده وضعیت سرویس‌های میزبانی دارید و می‌خواهید دستیار هوش مصنوعی فقط از طریق Toolهای محدود بتواند این اطلاعات را دریافت کند.

ساختار API داخلی

در این مثال، API خصوصی دو مسیر دارد:

GET /v1/services

GET /v1/services/{service_id}/status

API موردنظر برای احراز هویت داخلی از کلید اختصاصی سرویس استفاده می‌کند که فقط روی VPS نگهداری می‌شود.

برای اجرای نمونه آموزشی، یک Backend آزمایشی کوچک می‌سازیم. در پروژه واقعی باید آن را با API خودتان جایگزین کنید.

ایجاد Backend آزمایشی

برای Backend نمونه، کتابخانه FastAPI را نصب کنید:

sudo -u mcpservice /opt/iranica-mcp/.venv/bin/pip install fastapi

فایل mock_backend.py را ایجاد کنید:

import os
import secrets

from fastapi import FastAPI, Header, HTTPException

app = FastAPI(docs_url=None, redoc_url=None)

BACKEND_KEY = os.environ["BACKEND_API_KEY"]

SERVICES = {
    "minecraft-1": {
        "status": "online",
        "players": 8,
    },
    "cs2-1": {
        "status": "online",
        "players": 4,
    },
}


def authorize(key: str | None) -> None:
    if key is None or not secrets.compare_digest(
        key, BACKEND_KEY
    ):
        raise HTTPException(status_code=401)


@app.get("/v1/services")
def list_services(
    x_internal_key: str | None = Header(default=None),
):
    authorize(x_internal_key)
    return list(SERVICES.keys())


@app.get("/v1/services/{service_id}/status")
def service_status(
    service_id: str,
    x_internal_key: str | None = Header(default=None),
):
    authorize(x_internal_key)

    if service_id not in SERVICES:
        raise HTTPException(status_code=404)

    return {
        "service_id": service_id,
        **SERVICES[service_id],
    }

این Backend صرفاً داده‌های آزمایشی دارد و نباید به‌عنوان API مدیریت واقعی سرورها تلقی شود.

در تست محلی، برنامه باید با متغیر BACKEND_API_KEY و فقط روی 127.0.0.1 اجرا شود. در نسخه واقعی، API داخلی باید مجوزهای کاربر و دسترسی به هر سرویس را نیز در سمت Backend کنترل کند.

چرا نباید API Key را به مدل بدهیم؟

مدل برای انجام وظیفه نیازی به مشاهده رمز یا کلید API ندارد. Tool می‌تواند کلید را از محیط امن سرور بخواند، درخواست را ارسال کند و فقط نتیجه لازم را برگرداند.

به همین دلیل MCP Server باید مرز جداگانه‌ای میان اطلاعات محرمانه زیرساخت و داده قابل نمایش به هوش مصنوعی باشد.

مرحله پنجم: احراز هویت MCP Server با OAuth 2.1

برای یک MCP Server راه دور، فقط پنهان کردن URL کافی نیست. باید درخواست‌ها با هویت معتبر به سرور برسند و هر Tool فقط با مجوز مناسب اجرا شود.

طبق استاندارد MCP، سرور مبتنی بر HTTP در معماری OAuth نقش Resource Server دارد. یعنی توکن دسترسی را اعتبارسنجی می‌کند، اما الزاماً خودش صفحه ورود یا سرویس صدور Token نیست.

سه جزء احراز هویت OAuth

  • Authorization Server: سرویس احراز هویت که کاربر را شناسایی و Access Token صادر می‌کند.
  • MCP Client: کلاینتی که با روش OAuth، توکن مناسب را دریافت می‌کند.
  • MCP Resource Server: سرور ابزار که توکن را بررسی و دسترسی مجاز را اعمال می‌کند.

برای Authorization Server می‌توان از سرویس‌های استاندارد مدیریت هویت استفاده کرد. در این مقاله، فرض می‌کنیم چنین سرویسی از قبل وجود دارد و Endpoint استاندارد Token Introspection را در اختیار گذاشته است.

Access Token چگونه بررسی می‌شود؟

سرور باید مواردی مانند فعال بودن Token، زمان انقضا، دامنه دسترسی یا Scope و مخاطب Token یا Audience را بررسی کند.

برای مثال، توکنی که برای API دیگری صادر شده است نباید صرفاً به دلیل معتبر بودن امضایش برای MCP Server پذیرفته شود.

Scope در MCP چیست؟

Scope مشخص می‌کند یک توکن برای چه دسته‌ای از عملیات مجاز است. در سناریوی این مقاله از Scope خواندنی زیر استفاده می‌کنیم:

mcp:services:read

این Scope یک نام قراردادی برای برنامه نمونه ما است، نه یک Scope ثابت و ازپیش‌تعریف‌شده در تمام سرورهای MCP.

اگر در آینده قابلیت تغییر تنظیمات یا حذف اطلاعات اضافه کنید، نباید از همین مجوز خواندنی برای عملیات مدیریتی استفاده کنید.

Protected Resource Metadata چیست؟

این سند به MCP Client توضیح می‌دهد سرور از کدام Authorization Server استفاده می‌کند و برای دریافت توکن باید چه اطلاعاتی را کشف کند.

SDK رسمی Python برای پیکربندی OAuth، امکانات مربوط به Protected Resource Metadata و پاسخ‌های HTTP مناسب احراز هویت را فراهم می‌کند.

چرا Token Passthrough ممنوع است؟

نباید Access Token دریافت‌شده از MCP Client را بدون بررسی و بدون تغییر مستقیماً به API دیگری ارسال کنید.

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

در مثال ما، درخواست MCP با OAuth اعتبارسنجی می‌شود؛ ولی اتصال به Backend با یک کلید داخلی جداگانه انجام می‌شود.

مرحله ششم: کد کامل MCP Server برای اتصال امن به API شخصی

اکنون یک MCP Server با Python SDK نسخه ۲ می‌سازیم که شامل موارد زیر است:

  • دو Tool خواندنی و مشخص.
  • اعتبارسنجی پارامترها با Pydantic.
  • استفاده از Structured Output.
  • احراز هویت OAuth با Token Introspection.
  • کنترل Audience و Scope توکن.
  • استفاده از API Key جداگانه برای Backend.
  • Allowlist شناسه سرویس‌ها.
  • Timeout هنگام فراخوانی API.
  • محدودسازی اطلاعات خروجی و خطاها.
  • حفاظت Host و Origin برای ارتباط HTTP.

این مثال برای محیطی طراحی شده که یک سازمان، سرویس‌های مشخصی را مالک است و Scope خواندنی به اپراتورهای مجاز برای مشاهده همان سرویس‌ها داده می‌شود. برای سامانه چندمشتری، باید علاوه بر این کنترل‌ها، مالکیت هر سرویس را در Backend با هویت واقعی کاربر بررسی کنید.

ساخت فایل server.py

sudo nano /opt/iranica-mcp/server.py

کد کامل زیر را وارد کنید:

import logging
import os
import time

from typing import Annotated, Literal
from urllib.parse import urlsplit

import httpx

from pydantic import AnyHttpUrl, BaseModel, Field

from mcp.server import MCPServer
from mcp.server.auth.provider import AccessToken, TokenVerifier
from mcp.server.auth.settings import AuthSettings
from mcp.server.auth.middleware.auth_context import get_access_token
from mcp.server.mcpserver.exceptions import ToolError
from mcp.server.transport_security import TransportSecuritySettings
from mcp.types import ToolAnnotations


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("iranica-mcp")

PUBLIC_URL = os.environ["MCP_PUBLIC_URL"]
ISSUER_URL = os.environ["MCP_ISSUER_URL"]
INTROSPECTION_URL = os.environ["MCP_INTROSPECTION_URL"]

OAUTH_CLIENT_ID = os.environ["MCP_OAUTH_CLIENT_ID"]
OAUTH_CLIENT_SECRET = os.environ["MCP_OAUTH_CLIENT_SECRET"]

BACKEND_URL = os.environ["BACKEND_API_URL"].rstrip("/")
BACKEND_KEY = os.environ["BACKEND_API_KEY"]

REQUIRED_SCOPE = "mcp:services:read"

ALLOWED_SERVICES = {
    item.strip()
    for item in os.environ["ALLOWED_SERVICES"].split(",")
    if item.strip()
}

host = urlsplit(PUBLIC_URL).hostname

if not host or urlsplit(PUBLIC_URL).scheme != "https":
    raise RuntimeError("MCP_PUBLIC_URL must be a HTTPS URL")

if not ALLOWED_SERVICES:
    raise RuntimeError("ALLOWED_SERVICES cannot be empty")


class IntrospectionVerifier(TokenVerifier):
    async def verify_token(
        self,
        token: str,
    ) -> AccessToken | None:
        try:
            async with httpx.AsyncClient(
                timeout=5.0,
                follow_redirects=False,
            ) as client:
                response = await client.post(
                    INTROSPECTION_URL,
                    data={
                        "token": token,
                        "token_type_hint": "access_token",
                    },
                    auth=(
                        OAUTH_CLIENT_ID,
                        OAUTH_CLIENT_SECRET,
                    ),
                )
                response.raise_for_status()
                payload = response.json()

        except (httpx.HTTPError, ValueError):
            logger.warning("Token introspection failed")
            return None

        if payload.get("active") is not True:
            return None

        expires = payload.get("exp")
        if type(expires) is not int or expires <= time.time():
            return None

        audience = payload.get("aud")
        audiences = (
            [audience] if isinstance(audience, str)
            else audience if isinstance(audience, list)
            else []
        )

        if PUBLIC_URL not in audiences:
            return None

        scopes = payload.get("scope", "").split()

        if REQUIRED_SCOPE not in scopes:
            return None

        subject = payload.get("sub")
        client_id = payload.get("client_id")

        if not isinstance(client_id, str) or not client_id:
            return None

        return AccessToken(
            token=token,
            client_id=client_id,
            subject=subject if isinstance(subject, str) else None,
            scopes=scopes,
            expires_at=expires,
            resource=PUBLIC_URL,
        )


class ServiceStatus(BaseModel):
    service_id: str
    status: Literal["online", "offline", "degraded", "unknown"]
    players: int = Field(ge=0)


mcp = MCPServer(
    "Iranica Private API",
    token_verifier=IntrospectionVerifier(),
    auth=AuthSettings(
        issuer_url=AnyHttpUrl(ISSUER_URL),
        resource_server_url=AnyHttpUrl(PUBLIC_URL),
        required_scopes=[REQUIRED_SCOPE],
        validate_token_resource=True,
    ),
)


def require_read_access() -> None:
    access = get_access_token()
    if access is None or REQUIRED_SCOPE not in access.scopes:
        raise ToolError("Access denied")


def ensure_allowed(service_id: str) -> None:
    if service_id not in ALLOWED_SERVICES:
        raise ToolError("Service is not accessible")


@mcp.tool(
    title="List authorized services",
    annotations=ToolAnnotations(
        read_only_hint=True,
        open_world_hint=False,
    ),
)
def list_services() -> list[str]:
    """List the service identifiers this MCP server permits."""
    require_read_access()
    return sorted(ALLOWED_SERVICES)


@mcp.tool(
    title="Read service status",
    annotations=ToolAnnotations(
        read_only_hint=True,
        open_world_hint=False,
    ),
)
async def get_service_status(
    service_id: Annotated[
        str,
        Field(
            pattern=r"^[a-z0-9-]{3,32}$",
            description="An authorized service ID",
        ),
    ],
) -> ServiceStatus:
    """Read only the current status of an authorized service."""
    require_read_access()
    ensure_allowed(service_id)

    try:
        async with httpx.AsyncClient(
            timeout=httpx.Timeout(5.0, connect=2.0),
            follow_redirects=False,
            trust_env=False,
        ) as client:
            response = await client.get(
                f"{BACKEND_URL}/v1/services/{service_id}/status",
                headers={
                    "X-Internal-Key": BACKEND_KEY,
                },
            )

            if response.status_code == 404:
                raise ToolError("Service not found")

            response.raise_for_status()
            payload = response.json()

    except ToolError:
        raise

    except (httpx.HTTPError, ValueError):
        logger.warning("Backend status request failed")
        raise ToolError("Backend temporarily unavailable")

    status = payload.get("status", "unknown")
    if status not in {"online", "offline", "degraded"}:
        status = "unknown"

    players = payload.get("players", 0)
    if type(players) is not int or players < 0:
        players = 0

    return ServiceStatus(
        service_id=service_id,
        status=status,
        players=players,
    )


security = TransportSecuritySettings(
    allowed_hosts=[host, f"{host}:*"],
    allowed_origins=["https://app.example.com"],
)

app = mcp.streamable_http_app(
    transport_security=security,
)

چرا در این مثال از MCPServer استفاده کردیم؟

در Python SDK نسخه ۲، MCPServer رابط اصلی ساخت سرور در مستندات جدید است و Toolها را با @mcp.tool() ثبت می‌کند.

همچنین streamable_http_app یک برنامه ASGI ایجاد می‌کند که می‌توان آن را با Uvicorn و Reverse Proxy اجرا کرد.

چرا AuthSettings و TokenVerifier جدا هستند؟

AuthSettings مشخصات عمومی Resource Server، آدرس صادرکننده Token و Scopeهای لازم را تعریف می‌کند.

TokenVerifier منطق اعتبارسنجی واقعی Token را بر عهده دارد. در این مثال، اعتبار Token از طریق RFC 7662 Token Introspection بررسی می‌شود.

این نمونه فرض می‌کند Authorization Server شما Endpoint استاندارد Introspection دارد و Scope و Audience و زمان انقضای Token را در پاسخ برمی‌گرداند. اگر سرویس هویت شما از JWT و JWKS یا قالب دیگری استفاده می‌کند، باید TokenVerifier را مطابق همان سرویس پیاده‌سازی کنید.

نکته امنیتی درباره اتصال به Backend

آدرس BACKEND_API_URL در پیکربندی توسط مدیر سیستم تعیین می‌شود و کاربر نمی‌تواند آدرس دلخواهی را برای Tool وارد کند. همچنین فقط شناسه‌های قرارگرفته در ALLOWED_SERVICES قابل درخواست هستند.

این محدودیت‌ها خطر دسترسی به مسیرهای ناخواسته و سوءاستفاده از پارامترها را کاهش می‌دهند، اما جایگزین Firewall خروجی، اعتبارسنجی DNS، کنترل مجوزهای Backend و محافظت در برابر SSRF در معماری‌های پیچیده نیستند.

آیا برای هر مشتری به Tool جداگانه نیاز داریم؟

خیر. در یک سامانه چندمشتری می‌توان یک Tool مشترک داشت؛ اما Backend باید براساس هویت احراز‌شده و Scope مجاز، مشخص کند کاربر به کدام سرویس‌ها دسترسی دارد.

فهرست ALLOWED_SERVICES در مثال بالا یک Allowlist سازمانی ثابت است. برای محصول چندمستاجری یا Multi-Tenant باید آن را با مجوزهای وابسته به کاربر و سازمان جایگزین کنید.

مرحله هفتم: تست Tool Schema و اتصال MCP

برای تست یک MCP Server بهتر است چند سطح جداگانه را بررسی کنید: اعتبارسنجی Python، Schema ابزارها، اتصال به Backend و ارتباط HTTP با احراز هویت.

تست Syntax برنامه

/opt/iranica-mcp/.venv/bin/python -m py_compile \
/opt/iranica-mcp/server.py

این دستور ساختار نحوی Python را بررسی می‌کند. توجه کنید که موفقیت آن به معنی معتبر بودن تنظیمات OAuth یا در دسترس بودن Backend نیست.

تست Tools با Client رسمی

Python MCP SDK یک Client نیز دارد که می‌توانید از آن برای بررسی Toolهای ثبت‌شده استفاده کنید.

پس از تنظیم متغیرهای محیطی پروژه، فایل test_tools.py را ایجاد کنید:

import anyio

from mcp import Client
from server import mcp


async def main():
    async with Client(mcp) as client:
        result = await client.list_tools()

        for tool in result.tools:
            print("Tool:", tool.name)
            print("Schema:", tool.input_schema)


if __name__ == "__main__":
    anyio.run(main)

این تست از اتصال In-Process استفاده می‌کند و برای مشاهده Schema مناسب است. اما در چنین تستی، لایه HTTP OAuth اجرا نمی‌شود؛ بنابراین موفقیت تست نباید به معنی تأیید امنیت احراز هویت Endpoint عمومی در نظر گرفته شود.

تست واقعی احراز هویت HTTP

برای تأیید OAuth باید از یک MCP Client واقعی با Access Token صادرشده برای آدرس MCP Server استفاده کنید.

درخواست بدون توکن معتبر باید با خطای HTTP 401 رد شود و درخواست دارای توکن معتبر و Scope مناسب باید بتواند مراحل ارتباط پروتکلی و فراخوانی Tool را طی کند.

در نسخه مدرن پروتکل، فرستادن یک درخواست HTTP معمولی بدون ساختار MCP جایگزین تست با کلاینت استاندارد نیست.

تست ورودی نامعتبر

مقداری مانند ../../etc/passwd نباید به‌عنوان service_id مجاز پذیرفته شود. الگوی Schema و Allowlist در این مثال چنین مقداری را رد می‌کنند.

همچنین باید بررسی کنید اگر یک شناسه از نظر Schema معتبر اما خارج از Allowlist باشد، ابزار بدون تماس با Backend درخواست را رد کند.

مرحله هشتم: اجرای دائمی MCP Server روی VPS با systemd

پس از تکمیل کد و اتصال به سرویس هویت و Backend، MCP Server را به‌عنوان یک سرویس مستقل اجرا می‌کنیم.

ساخت فایل متغیرهای محیطی

ابتدا یک پوشه محافظت‌شده برای تنظیمات ایجاد کنید:

sudo install -d -o root -g root -m 700 /etc/iranica-mcp
sudo nano /etc/iranica-mcp/server.env

مقادیر نمونه:

MCP_PUBLIC_URL=https://mcp.example.com/mcp

MCP_ISSUER_URL=https://auth.example.com
MCP_INTROSPECTION_URL=https://auth.example.com/oauth2/introspect
MCP_OAUTH_CLIENT_ID=YOUR_INTROSPECTION_CLIENT_ID
MCP_OAUTH_CLIENT_SECRET=YOUR_INTROSPECTION_CLIENT_SECRET

BACKEND_API_URL=http://127.0.0.1:9000
BACKEND_API_KEY=YOUR_PRIVATE_BACKEND_KEY

ALLOWED_SERVICES=minecraft-1,cs2-1

تمام آدرس‌ها و Secrets باید با مقادیر واقعی جایگزین شوند. این پیکربندی فرض می‌کند API Backend روی همان VPS و از طریق Loopback قابل دسترسی است.

اگر Backend روی سرور دیگری قرار دارد، از شبکه خصوصی محافظت‌شده یا ارتباط TLS معتبر استفاده کنید. ارسال کلید داخلی روی HTTP عمومی قابل قبول نیست.

همچنین آدرس MCP_PUBLIC_URL باید دقیقاً با Resource مورد انتظار Authorization Server هماهنگ باشد تا بررسی Audience موفق شود.

تنظیم سطح دسترسی

sudo chown root:root /etc/iranica-mcp/server.env
sudo chmod 600 /etc/iranica-mcp/server.env

ساخت سرویس systemd

sudo nano /etc/systemd/system/iranica-mcp.service

محتوای زیر را قرار دهید:

[Unit]
Description=IranicaServer MCP API Gateway
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=mcpservice
Group=mcpservice
WorkingDirectory=/opt/iranica-mcp
EnvironmentFile=/etc/iranica-mcp/server.env

ExecStart=/opt/iranica-mcp/.venv/bin/uvicorn server:app --host 127.0.0.1 --port 8000 --workers 1 --proxy-headers --forwarded-allow-ips=127.0.0.1

Restart=on-failure
RestartSec=10
TimeoutStopSec=30

NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
UMask=0077

MemoryAccounting=true
CPUAccounting=true

[Install]
WantedBy=multi-user.target

در این پیکربندی، MCP Server فقط روی 127.0.0.1 و پورت 8000 اجرا می‌شود. بنابراین پورت داخلی نباید مستقیماً برای اینترنت باز شود.

دستور –forwarded-allow-ips فقط هدرهای Proxy ارسال‌شده از مسیر مورد اعتماد Loopback را می‌پذیرد. اگر معماری Reverse Proxy شما متفاوت است، این مقدار باید متناسب با شبکه واقعی تنظیم شود.

فعال‌سازی سرویس

sudo systemctl daemon-reload
sudo systemctl enable --now iranica-mcp
sudo systemctl status iranica-mcp --no-pager

اگر سرویس اجرا نشد، ابتدا خطاهای Import، متغیرهای محیطی، دسترسی فایل‌ها و تنظیمات AuthSettings را بررسی کنید.

مرحله نهم: اتصال دامنه و HTTPS به MCP Server با Nginx

برای ارتباط از راه دور، MCP Server را پشت Nginx و گواهی TLS معتبر قرار می‌دهیم.

در مثال زیر فرض می‌کنیم دامنه mcp.example.com به IPv4 عمومی VPS اشاره می‌کند. این دامنه نمونه است و باید آن را با دامنه واقعی خود جایگزین کنید.

ساخت تنظیمات Rate Limit در Nginx

ابتدا یک Zone برای محدودسازی درخواست‌ها تعریف کنید:

sudo nano /etc/nginx/conf.d/iranica-mcp-limits.conf

محتوا:

limit_req_zone $binary_remote_addr zone=mcp_per_ip:10m rate=60r/m;

این نمونه، درخواست‌ها را براساس IP محدود می‌کند. مقدار ۶۰ درخواست در دقیقه صرفاً یک نقطه شروع است و باید با تعداد کاربران و رفتار واقعی Clientها هماهنگ شود.

ساخت Virtual Host

sudo nano /etc/nginx/sites-available/iranica-mcp

تنظیمات اولیه HTTP:

server {
    listen 80;
    server_name mcp.example.com;

    client_max_body_size 64k;

    location / {
        limit_req zone=mcp_per_ip burst=10 nodelay;
        limit_req_status 429;

        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Authorization $http_authorization;

        proxy_buffering off;
        proxy_connect_timeout 10s;
        proxy_read_timeout 180s;
    }
}

خاموش کردن Proxy Buffering در این مثال به عبور پاسخ‌های Stream شده کمک می‌کند. با این حال، اگر از قابلیت‌های خاص Multi Round-Trip یا ارتباط‌های طولانی استفاده می‌کنید، باید Timeoutها و رفتار Proxy را متناسب با آن‌ها آزمایش کنید.

فعال‌سازی تنظیمات Nginx

sudo ln -s /etc/nginx/sites-available/iranica-mcp \
/etc/nginx/sites-enabled/iranica-mcp

sudo nginx -t
sudo systemctl reload nginx

اگر Nginx از قبل سایت‌هایی دارد، پیش از جایگزینی یا اضافه کردن فایل، تعارض دامنه و تنظیمات موجود را بررسی کنید.

دریافت SSL با Certbot

sudo apt install -y certbot python3-certbot-nginx

sudo certbot --nginx -d mcp.example.com

برای صدور گواهی، تنظیم DNS و دسترسی شبکه موردنیاز باید از قبل برقرار باشند.

پس از فعال‌سازی HTTPS و هدایت HTTP به HTTPS، آدرس عمومی MCP Server به شکل زیر خواهد بود:

https://mcp.example.com/mcp

چرا خطای 421 Invalid Host دریافت می‌کنیم؟

در Python MCP SDK نسخه ۲، هنگام استفاده از Streamable HTTP، حفاظت در برابر DNS Rebinding می‌تواند درخواست‌هایی با Host ناشناس را رد کند.

اگر دامنه عمومی در allowed_hosts تعریف نشده باشد، حتی درخواست‌های معتبر نیز ممکن است با 421 Misdirected Request مواجه شوند.

برای همین در کد برنامه، TransportSecuritySettings را با Hostهای مجاز تعریف کرده‌ایم. غیرفعال کردن کامل حفاظت Host بدون داشتن کنترل مناسب در Reverse Proxy توصیه نمی‌شود.

مرحله دهم: Rate Limit برای جلوگیری از سوءاستفاده از MCP Server

هر درخواست به Tool ممکن است باعث فراخوانی API داخلی، مصرف منابع، اجرای Query یا پردازش داده شود. بدون محدودسازی درخواست، یک Client خراب یا مهاجم می‌تواند بار زیادی به Backend تحمیل کند.

Rate Limit در Nginx

در مثال قبلی با limit_req، محدودیت اولیه‌ای براساس IP تعریف کردیم. این روش برای کاهش فشار عمومی مفید است؛ اما به‌تنهایی برای کنترل مصرف کاربران کافی نیست.

چند کاربر ممکن است از یک IP مشترک استفاده کنند و یک کاربر نیز ممکن است از چند IP مختلف درخواست ارسال کند.

Rate Limit براساس هویت

در محیط Production بهتر است سهمیه درخواست براساس هویت اعتبارسنجی‌شده، Client ID یا سازمان تعیین شود.

برای مثال، می‌توانید تعداد فراخوانی get_service_status را برای هر حساب در یک بازه زمانی محدود کنید. در معماری چند Worker باید از شمارنده مرکزی مانند Redis یا یک سرویس Rate Limiter استفاده شود.

محدودیت هم‌زمانی

علاوه بر تعداد درخواست‌ها، تعداد فراخوانی‌های هم‌زمان Tool را نیز کنترل کنید. اگر Backend توان پاسخ‌گویی هم‌زمان به تعداد زیادی درخواست را ندارد، Semaphore یا ظرفیت محدود در Gateway می‌تواند فشار را کنترل کند.

محدودیت Timeout

در کد نمونه از Timeout هنگام فراخوانی Backend استفاده شده است. این کار مانع معطل ماندن نامحدود یک Tool در انتظار API داخلی می‌شود.

برای عملیات حساس باید Deadline درخواست، تعداد Retryها و وضعیت درخواست‌های نیمه‌تمام نیز مشخص باشد.

آیا Rate Limit جلوی تمام حملات را می‌گیرد؟

خیر. محدودیت نرخ درخواست فقط یکی از کنترل‌های دفاعی است. برای سرویس عمومی همچنان به احراز هویت، محدودیت دسترسی ابزارها، کنترل مصرف Backend، پایش و محافظت شبکه نیاز دارید.

مرحله یازدهم: مدیریت Secrets و API Keys در MCP Server

یکی از خطرناک‌ترین اشتباهات هنگام ساخت MCP Server، قرار دادن رمزها در Tool Description، خروجی Tool یا فایل‌های قابل دسترسی عمومی است.

چه اطلاعاتی Secret محسوب می‌شود؟

  • API Key سرویس‌های داخلی.
  • OAuth Client Secret.
  • Access Token و Refresh Token.
  • رمز پایگاه داده.
  • کلید خصوصی SSH.
  • رمز RCON یا پنل مدیریتی.
  • کلیدهای امضا و رمزنگاری برنامه.

چرا نباید Secrets را به‌عنوان آرگومان Tool بگیریم؟

اگر ابزاری مانند get_service_status پارامتری به نام api_key داشته باشد، ممکن است آن مقدار در درخواست‌های مدل، Logها، Traceها یا تاریخچه گفتگو قرار بگیرد.

برای ابزارهای سازمانی، بهتر است Credential معتبر از محیط امن MCP Server تأمین شود و مدل فقط پارامترهای عملیاتی مجاز را مشخص کند.

استفاده از EnvironmentFile

در این مقاله از فایل محافظت‌شده /etc/iranica-mcp/server.env استفاده کرده‌ایم. این روش برای یک سرویس کوچک قابل استفاده است، اما برای سامانه‌های بزرگ‌تر می‌توانید از Secret Manager اختصاصی، سازوکار مدیریت Credential سرویس یا سرویس مدیریت کلید سازمانی بهره ببرید.

چرخش کلید یا Secret Rotation

اگر یک Secret افشا شد، باید امکان لغو و جایگزینی آن را داشته باشید. بهتر است Backend امکان صدور کلید جدید با حداقل Scope و غیرفعال کردن کلید قدیمی را فراهم کند.

پس از تغییر Secret در EnvironmentFile، سرویس مربوطه باید به‌شکل کنترل‌شده راه‌اندازی مجدد شود تا مقدار جدید را دریافت کند.

جلوگیری از افشای اطلاعات در Log

از ثبت هدر Authorization، کلیدهای داخلی، درخواست کامل حاوی اطلاعات حساس و پاسخ‌های غیرضروری Backend خودداری کنید.

برای تحلیل امنیتی، اطلاعاتی مانند نام Tool، شناسه غیرحساس درخواست، زمان اجرا، کد وضعیت و نتیجه مجوز معمولاً از ذخیره کامل محتوای محرمانه مناسب‌تر هستند.

مرحله دوازدهم: Threat Model یا مدل تهدید MCP Server

ساخت MCP Server فقط یک مسئله برنامه‌نویسی نیست. وقتی یک مدل هوش مصنوعی می‌تواند براساس متن کاربر Tool فراخوانی کند، سطح حمله جدیدی ایجاد می‌شود که باید آن را پیش از انتشار بررسی کنید.

Threat Model مشخص می‌کند چه دارایی‌هایی ارزش حفاظت دارند، مهاجم از چه مسیرهایی ممکن است وارد شود و چه کنترل‌هایی برای کاهش ریسک لازم است.

دارایی‌های حساس در MCP Server

  • داده‌های کاربران و سرویس‌ها.
  • توکن‌های احراز هویت.
  • API Keyهای داخلی.
  • عملیات مدیریتی Backend.
  • فایل‌های VPS.
  • اعتبار و ظرفیت سرویس‌های API.
  • لاگ‌ها و تاریخچه درخواست‌ها.

تهدیدهای اصلی و راهکارهای مقابله

تهدید سناریو کنترل پیشنهادی
Prompt Injection متن ورودی یا نتیجه ابزار سعی می‌کند رفتار Agent را تغییر دهد تفکیک داده از دستور و کنترل مجوز مستقل از مدل
Tool Poisoning توضیحات Tool حاوی دستور مخرب هستند بازبینی، نسخه‌بندی و بررسی تغییرات Tool
Confused Deputy سرور با مجوز گسترده به درخواست کاربر کم‌مجوز عمل می‌کند کنترل هویت و Scope در هر عملیات
Token Passthrough توکن MCP به API نامرتبط ارسال می‌شود اعتبارسنجی Audience و Credential جداگانه
SSRF ابزار به آدرس داخلی یا مقصد کنترل‌شده توسط مهاجم متصل می‌شود Allowlist مقصد و کنترل شبکه خروجی
Excessive Permissions Tool دسترسی بیش از نیاز دارد اصل کمترین سطح دسترسی
Replay و عملیات تکراری درخواست نوشتنی دوباره اجرا می‌شود Idempotency Key و کنترل تکرار
Secret Leakage کلید API در Log یا خروجی Tool دیده می‌شود Secret Management و Redaction
Supply Chain پکیج یا MCP Server آلوده نصب می‌شود استفاده از منابع معتبر و بررسی وابستگی‌ها
DoS و مصرف بیش از حد فراخوانی زیاد Tool باعث فشار روی API می‌شود Rate Limit، Timeout و محدودیت هم‌زمانی

Prompt Injection در MCP چگونه اتفاق می‌افتد؟

فرض کنید Tool شما اطلاعات یک تیکت را از Backend دریافت می‌کند. متن تیکت ممکن است شامل جمله‌ای باشد که تلاش می‌کند Agent را به ارسال اطلاعات محرمانه یا اجرای ابزار دیگری تشویق کند.

این متن باید به‌عنوان داده غیرقابل اعتماد در نظر گرفته شود، نه دستورالعمل معتبر برای Agent.

به همین دلیل بهتر است Toolها فقط داده لازم را برگردانند و عملیات مهم صرفاً بر پایه تصمیم مدل انجام نشوند.

Tool Poisoning چیست؟

در این حمله، توضیحات یک ابزار، Schema یا خروجی آن برای هدایت نادرست مدل استفاده می‌شود. برای مثال، یک Tool ظاهراً خواندنی ممکن است در Description خود دستورهایی خارج از وظیفه اصلی داشته باشد.

بهتر است تغییرات تعریف Toolها مانند تغییرات کد برنامه بازبینی شوند و برای MCP Serverهای شخص ثالث، منبع و سطح دسترسی بررسی شود.

Confused Deputy چیست؟

فرض کنید MCP Server با یک کلید مدیریتی به تمام سرویس‌های مشتریان دسترسی دارد. اگر تنها براساس service_id ارسالی مدل عملیات انجام دهد، یک درخواست‌کننده ممکن است بتواند اطلاعات سرویس دیگری را بخواند.

در این وضعیت، MCP Server به نمایندگی از کاربری با دسترسی کمتر از مجوز گسترده خودش سوءاستفاده کرده است.

راهکار، بررسی هویت و مجوز در لحظه اجرای Tool و جلوگیری از استفاده بی‌قیدوشرط از دسترسی‌های مدیریتی است.

چرا تأیید انسانی برای Toolهای حساس مهم است؟

ابزار خواندن وضعیت سرویس با ابزار حذف VPS یا انجام پرداخت یکسان نیست. برای عملیات مخرب یا مالی باید تأیید معتبر، مجوز مشخص، ثبت رویداد و قابلیت جلوگیری از اجرای تکراری وجود داشته باشد.

همچنین تأییدیه‌ای که فقط در متن مدل ظاهر می‌شود جایگزین کنترل سمت Backend نیست.

مرحله سیزدهم: مانیتورینگ و رفع خطای MCP Server روی VPS

پس از استقرار، وضعیت فعال بودن فرآیند به‌تنهایی کافی نیست. باید بتوانید خطاهای پروتکل، احراز هویت، ارتباط Backend و فشار درخواست‌ها را تشخیص دهید.

بررسی سرویس systemd

sudo systemctl status iranica-mcp --no-pager

مشاهده لاگ‌های MCP Server

sudo journalctl -u iranica-mcp -n 100 --no-pager

مشاهده زنده لاگ‌ها

sudo journalctl -u iranica-mcp -f

بررسی پورت محلی

sudo ss -lntp | grep 8000

در پیکربندی صحیح این آموزش، برنامه فقط باید روی 127.0.0.1:8000 گوش دهد.

خطای 401 Unauthorized

این خطا معمولاً نشان می‌دهد درخواست بدون توکن معتبر ارسال شده، توکن منقضی شده یا اعتبارسنجی آن موفق نبوده است.

پاسخ Token Introspection، Scope، Audience و پیکربندی Authorization Server را بررسی کنید.

خطای 403 Forbidden

این خطا می‌تواند ناشی از نداشتن Scope کافی یا رد شدن Origin در تنظیمات امنیتی Transport باشد. متن و منبع خطا را در لاگ بررسی کنید.

خطای 421 Invalid Host

دامنه عمومی یا Host Header درخواست با Allowed Hosts تعریف‌شده مطابقت ندارد. مقدار MCP_PUBLIC_URL، تنظیمات Nginx و TransportSecuritySettings را بررسی کنید.

خطای 429 Too Many Requests

ممکن است Rate Limit تعریف‌شده در Nginx یا لایه برنامه فعال شده باشد. ابتدا مشخص کنید محدودیت مربوط به IP، هویت کاربر یا ظرفیت Backend است.

خطای 502 Bad Gateway

ممکن است Nginx نتواند به Uvicorn متصل شود یا فرآیند MCP Server متوقف شده باشد.

وضعیت سرویس، پورت محلی و لاگ‌های Nginx را بررسی کنید.

Tool در فهرست نمایش داده نمی‌شود

ابتدا مطمئن شوید Tool با @mcp.tool() ثبت شده و نسخه Client و Server با یکدیگر سازگار هستند.

همچنین ممکن است Client اطلاعات ابزارها را Cache کرده باشد. در صورت نیاز، اتصال را دوباره برقرار کرده و فهرست ابزارها را به‌روزرسانی کنید.

خطای Timeout هنگام اجرای Tool

این خطا می‌تواند ناشی از کندی Backend، اختلال شبکه یا Timeout کوتاه باشد. بررسی کنید آیا خود API در شرایط مشابه بدون MCP نیز پاسخ مناسب می‌دهد.

افزایش بی‌حد Timeout راهکار مناسبی نیست. بهتر است عملیات طولانی را به ساختار اجرای مستقل و قابل پایش منتقل کنید.

تفاوت خطای Tool و خطای Transport

اگر اتصال HTTP یا OAuth برقرار نشود، خطا در سطح Transport یا احراز هویت اتفاق افتاده است. اما اگر اتصال MCP صحیح باشد و خود Tool با ورودی نامعتبر یا پاسخ ناموفق Backend مواجه شود، خطا در سطح اجرای ابزار رخ می‌دهد.

این تفکیک به شما کمک می‌کند مشکل را در لایه درست عیب‌یابی کنید.

چک‌لیست نهایی ساخت MCP Server امن برای Production

پیش از آنکه MCP Server را به برنامه‌های واقعی متصل کنید، موارد زیر را بررسی کنید:

  • نسخه پروتکل و SDK بررسی و ثبت شده‌اند.
  • Toolها فقط عملیات موردنیاز را ارائه می‌کنند.
  • Tool Schema دارای نوع داده و محدودیت ورودی مناسب است.
  • هویت و Scope درخواست‌کننده در سمت سرور اعتبارسنجی می‌شود.
  • توکن مخصوص MCP از توکن یا کلید Backend جدا است.
  • Audience توکن بررسی می‌شود.
  • آدرس Backend از ورودی آزاد کاربر دریافت نمی‌شود.
  • مجوز دسترسی به داده‌ها در Backend نیز اعمال می‌شود.
  • HTTPS فعال است و پورت داخلی MCP عمومی نیست.
  • Host و Origin براساس دامنه‌های واقعی Allowlist شده‌اند.
  • Rate Limit براساس IP و در صورت نیاز هویت کاربر اعمال شده است.
  • برای فراخوانی API، Timeout و محدودیت Retry وجود دارد.
  • اطلاعات محرمانه در خروجی Tool و Logها منتشر نمی‌شوند.
  • Secret Rotation و روش ابطال توکن مشخص است.
  • برای عملیات نوشتنی، Idempotency و تأیید مناسب طراحی شده است.
  • تست ورودی مخرب و دسترسی غیرمجاز انجام شده است.
  • سناریوهای قطع Backend و خطای OAuth آزمایش شده‌اند.
  • مانیتورینگ، Alert و برنامه بازیابی سرویس وجود دارد.

سوالات متداول درباره MCP و ساخت MCP Server

MCP چیست؟

MCP یا Model Context Protocol استانداردی برای اتصال برنامه‌های هوش مصنوعی به ابزارها، منابع اطلاعاتی و سرویس‌های خارجی است.

MCP Server چیست؟

برنامه‌ای است که قابلیت‌های مشخصی مانند Tools، Resources و Prompts را از طریق پروتکل MCP در اختیار Clientهای سازگار قرار می‌دهد.

آیا MCP به OpenAI محدود است؟

خیر. MCP یک پروتکل باز است و می‌تواند توسط برنامه‌ها و مدل‌های مختلف دارای Client سازگار استفاده شود.

آخرین نسخه پروتکل MCP چیست؟

تا ۳ نوامبر ۲۰۲۶، آخرین نسخه رسمی پروتکل MCP برابر 2026-07-28 است.

برای ساخت MCP Server از چه زبانی استفاده کنیم؟

Python و TypeScript از گزینه‌های متداول هستند. در این آموزش از Python MCP SDK نسخه ۲ استفاده شده است.

آیا برای MCP به API Key مدل هوش مصنوعی نیاز داریم؟

برای اجرای خود MCP Server الزاماً به API Key مدل زبانی نیاز ندارید. اما برنامه میزبان یا Agent ممکن است برای استفاده از مدل هوش مصنوعی به اعتبار API جداگانه نیاز داشته باشد.

تفاوت stdio و Streamable HTTP چیست؟

stdio برای ارتباط محلی از طریق ورودی و خروجی فرآیند مناسب است، درحالی‌که Streamable HTTP برای ارائه MCP Server از طریق شبکه و VPS کاربرد دارد.

آیا SSE در MCP منسوخ شده است؟

روش قدیمی HTTP+SSE با Streamable HTTP جایگزین شده و بیشتر برای سازگاری با Clientهای قدیمی باقی مانده است.

Tool Schema چیست؟

ساختاری مبتنی بر JSON Schema است که نام پارامترها، نوع داده‌ها و محدودیت ورودی ابزار را مشخص می‌کند.

آیا MCP جایگزین REST API است؟

خیر. MCP می‌تواند در کنار REST API قرار بگیرد و عملیات مشخص آن را به شکل استاندارد برای برنامه‌های هوش مصنوعی ارائه کند.

آیا می‌توان MCP Server را روی VPS نصب کرد؟

بله. با Python، Uvicorn، systemd و Nginx می‌توانید آن را روی یک VPS لینوکسی اجرا کنید.

آیا MCP Server به GPU نیاز دارد؟

برای سروری که فقط ابزارها و APIها را ارائه می‌کند معمولاً خیر. نیاز به GPU به پردازش‌های محلی مستقل از خود MCP بستگی دارد.

چگونه MCP Server را امن کنیم؟

با HTTPS، OAuth، کنترل Scope و Audience، محدودسازی Toolها، مدیریت Secrets، اعتبارسنجی ورودی، Rate Limit و حفاظت از Backend.

آیا می‌توان از API Key ثابت برای احراز هویت استفاده کرد؟

برای یک یکپارچه‌سازی داخلی و کنترل‌شده ممکن است از کلید ثابت در یک Gateway استفاده شود، اما برای MCP Server راه دور و اتصال استاندارد با Clientهای مختلف، OAuth و سازوکار کشف مجوز انتخاب مناسب‌تری است.

چرا باید Token Audience را بررسی کنیم؟

برای اطمینان از اینکه توکن واقعاً برای MCP Server موردنظر صادر شده و از توکن سرویس‌های دیگر سوءاستفاده نمی‌شود.

آیا می‌توان توکن MCP را مستقیماً به Backend فرستاد؟

خیر. Token Passthrough بدون تفویض مجوز استاندارد و کنترل مناسب، خلاف الزامات امنیتی MCP است. برای Backend باید از اعتبارنامه مستقل یا جریان مجاز مناسب استفاده شود.

آیا Rate Limit در Nginx کافی است؟

خیر. محدودیت براساس IP مفید است، اما در سرویس چندکاربره بهتر است سهمیه بر اساس هویت و ظرفیت Backend نیز کنترل شود.

Prompt Injection در MCP چیست؟

تلاش برای تغییر رفتار Agent از طریق داده یا متن غیرقابل اعتماد، مانند محتوای Tool یا سند خارجی است.

Tool Poisoning چیست؟

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

آیا می‌توان چند MCP Server داشت؟

بله. یک برنامه میزبان می‌تواند متناسب با قابلیت‌های خود به چند MCP Server متصل شود. بهتر است مجوز و سطح اعتماد هر سرور جداگانه مدیریت شود.

آیا می‌توان MCP Server را به سیستم حسابداری یا CRM وصل کرد؟

بله. می‌توانید Toolهای مشخصی برای خواندن یا انجام عملیات مجاز در API آن سیستم‌ها تعریف کنید. دسترسی به اطلاعات شخصی و عملیات حساس نیازمند کنترل مجوز دقیق است.

آیا تغییر Tool Schema باعث مشکل سازگاری می‌شود؟

ممکن است. تغییر نام Tool، پارامترها یا ساختار خروجی می‌تواند Clientهای موجود را تحت تأثیر قرار دهد. بهتر است تغییرات ناسازگار را نسخه‌بندی و آزمایش کنید.

چگونه بفهمیم MCP Server درست کار می‌کند؟

علاوه بر وضعیت فرآیند، باید Schema ابزارها، OAuth، فراخوانی Tool، پاسخ Backend و رفتار خطاها را با MCP Client سازگار آزمایش کنید.

جمع‌بندی

ساخت MCP Server روشی استاندارد برای اتصال برنامه‌های هوش مصنوعی به APIهای شخصی و ابزارهای اختصاصی است. با استفاده از MCP می‌توانید قابلیت‌های مشخصی را در اختیار Clientهای سازگار قرار دهید، بدون اینکه لازم باشد برای هر برنامه هوش مصنوعی یک رابط متفاوت پیاده‌سازی کنید.

در نسخه فعلی پروتکل MCP، ارتباط‌های جدید HTTP با ساختار Stateless و Streamable HTTP طراحی شده‌اند. برای ساخت سرور در Python نیز می‌توانید از MCP SDK نسخه ۲ و کلاس MCPServer استفاده کنید.

در این آموزش، نحوه تعریف Tool Schema، اتصال به API داخلی، اعتبارسنجی OAuth، مدیریت Secrets، اجرای سرویس با systemd و انتشار از طریق Nginx و HTTPS بررسی شد.

مهم‌ترین نکته این است که MCP Server باید یک مرز امنیتی واقعی میان Agent و سرویس‌های داخلی باشد. هر Tool باید فقط عملیات ضروری را انجام دهد، هویت و مجوز کاربران بررسی شود و هیچ کلید محرمانه‌ای مستقیماً در اختیار مدل قرار نگیرد.

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

منابع و مستندات رسمی

برای بررسی نسخه‌های پروتکل، Transportها، APIهای SDK و الزامات امنیتی از منابع رسمی MCP و مستندات معتبر امنیت استفاده شده است.

۱. Model Context Protocol — Specification 2026-07-28

مرجع رسمی نسخه جدید MCP، معماری پیام‌ها و تغییرات پروتکل.

مشاهده استاندارد رسمی MCP

۲. MCP — Specification Release

توضیحات رسمی تغییرات نسخه 2026-07-28 شامل Stateless HTTP و سازوکارهای جدید.

مطالعه اطلاعیه انتشار MCP

۳. MCP Python SDK v2

مستندات رسمی نصب، ساخت MCPServer و کار با SDK پایتون.

مشاهده مستندات MCP Python SDK

۴. MCP Python SDK — Tools

راهنمای ساخت ابزارها، تعریف Type Hint و تولید Schema.

مستندات Tools

۵. MCP Python SDK — Authorization

راهنمای TokenVerifier، OAuth Resource Server و Protected Resource Metadata.

مستندات احراز هویت MCP

۶. MCP Python SDK — Deploy & Scale

راهنمای Host Allowlist، حفاظت DNS Rebinding، Reverse Proxy و مقیاس‌پذیری.

مستندات استقرار MCP Server

۷. MCP — Authorization Security Considerations

الزامات امنیتی مربوط به Token Audience، تفویض مجوز و منع Token Passthrough.

مشاهده ملاحظات امنیتی رسمی

۸. OWASP — MCP Security Cheat Sheet

راهنمای امنیتی درباره Prompt Injection، Tool Poisoning، Confused Deputy و حفاظت از داده‌ها.

مطالعه راهنمای امنیت MCP در OWASP

۹. MCP Python Package — PyPI

مرجع رسمی مشاهده نسخه منتشرشده و نصب پکیج mcp.

مشاهده نسخه MCP در PyPI

۱۰. Nginx — Limiting Access

مستندات رسمی Nginx برای محدودسازی نرخ درخواست و مدیریت دسترسی.

مشاهده راهنمای کنترل دسترسی Nginx

اشتراک‌گذاری مقاله
گفت‌وگو درباره آموزش

دیدگاه‌ها (۰)

هنوز دیدگاهی ثبت نشده است.

دیدگاه یا سؤال خود را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *