چیت‌شیت

ساخت یک سرور MCP

Building an MCP Server

اعتبارسنجی، توزیع (dispatch)، کپسوله‌سازی و کش سرور بی‌وضعیت MCP نسخه 2026-07-28. ابزارها، منابع، پرامپت‌ها، خط‌مشی.

  • ۱۲ بخش
  • ۴ درس مرتبط
  • کد و دستورها عیناً از دوره‌ی اصلی
فهرست مطالب
  1. اجرای سرورهای درس
  2. حلقه‌ی توزیع (dispatch loop)
  3. اعتبارسنجی و کپسوله‌سازی تک‌تک درخواست‌ها
  4. ثبت یک ابزار و مجری آن
  5. ابزار، منبع یا پرامپت
  6. نکات
  7. راهنماهای کش در درس‌ها
  8. کدهای خطا
  9. قراردادهای ابزار
  10. پروژه‌ی سرور حافظه
  11. گیت خط‌مشی پروژه‌ی پایانی
  12. سؤالات متداول

اجرای سرورهای درس

کار و کد
وظیفهکد
سرویس‌دهی روی stdiopython3 main.py
اجرای دموی متناهیpython3 main.py --demo
اجرای پورت TypeScriptnpx tsx main.ts --demo
اجرای تست‌های درسpython3 -m unittest discover tests -v
ساخت یک درخواست به همراه _metamake_request(2, "tools/list")
اجرای مدل پروژه‌ی پایانی (capstone)python3 code/main.py
بررسی TypeScript پروژه‌ی پایانیnpm run typecheck

حلقه‌ی توزیع (dispatch loop)

گام‌به‌گام
  1. serve_stdioخواندن یک خط از stdin. ساختار JSON نامعتبر کد -32700 می‌دهد.
  2. dispatchنبود id یعنی اعلان است. سرور پاسخی ارسال نمی‌کند.
  3. validate_requestبررسی params._meta برای نسخه و قابلیت‌ها.
  4. HANDLERSمسیریابی بر اساس method. متد ناشناخته کد -32601 می‌دهد.
  5. completeاضافه کردن resultType و serverInfo به نتیجه‌ی کنترل‌کننده.
  6. serve_stdioنوشتن یک خط JSON روی stdout، فلاش کردن، و فراموشی متادیتا.

اعتبارسنجی و کپسوله‌سازی تک‌تک درخواست‌ها

کد
python
meta = params.get("_meta")              # کد -32602 در صورت مفقود بودن
requested = meta.get(VERSION_KEY)       # کلید protocolVersion
if requested not in SUPPORTED_VERSIONS:
    raise RpcProblem(-32022, "Unsupported protocol version",
        {"requested": requested, "supported": SUPPORTED_VERSIONS.copy()})
if not isinstance(meta.get(CAPABILITIES_KEY), dict):
    raise RpcProblem(-32602, f"{CAPABILITIES_KEY} is required")
result = {"resultType": "complete", **payload,
    "_meta": {SERVER_INFO_KEY: SERVER_INFO.copy()}}
if ttl_ms is not None:                  # لیست‌ها، خواندن‌ها، شناسایی
    result["ttlMs"] = ttl_ms
    result["cacheScope"] = cache_scope  # مقدار پیش‌فرض "private"

ثبت یک ابزار و مجری آن

کد
python
TOOL_EXECUTORS = {"notes_search": exec_notes_search}  # نام به تابع
def handle_tools_list(params):
    return complete({"tools": sorted(TOOLS, key=lambda tool: tool["name"])},
                    ttl_ms=60_000, cache_scope="public")
def handle_tools_call(params):
    executor = TOOL_EXECUTORS.get(name)
    if executor is None:                              # نتیجه، نه خطای RPC
        return complete({"content": [...], "isError": True})
    try:
        return complete({"content": executor(arguments), "isError": False})
    except (KeyError, TypeError, ValueError) as exc:
        return complete({"content": [...], "isError": True})

ابزار، منبع یا پرامپت

مقایسه
عاملابزارمنبعپرامپت
نیتانجام یک عملیاتخواندن محتوا در یک URIشروع یک جریان کاری پیام
انتخاب‌کنندهمدل یا برنامه کاربردیمیزبان، برنامه کاربردی یا کاربرکاربر از طریق واسط کاربری میزبان
نتیجهنتیجه‌ی ساختاریافته‌ی اکشنمحتوای متنی یا باینریپیام‌های پرامپت
متدهاtools/list, tools/callresources/list, resources/readprompts/list, prompts/get
مثالdelete_notenotes://note-1review_note

نکات

نکته‌ها
  • استفاده از cacheScope: "private" برای داده‌های کاربر. استفاده از ttlMs: 0 برای اسرار.
  • رد کردن توصیف‌گرهایی که مشابه password، secret، token یا authorization هستند.
  • محدود کردن بدنه درخواست‌ها. سرور حافظه برای بالای 50000 بایت کد 413 ارسال می‌کند.

راهنماهای کش در درس‌ها

پیش‌فرض‌ها
تنظیمپیش‌فرضیادداشت
`server/discover` ttl3_600_000میلی‌ثانیه، cacheScope عمومی (public)
`tools/list` ttl60_000عمومی (public). پروژه‌ی پایانی از خصوصی (private) استفاده می‌کند
`resources/list` ttl10_000درس 07، خصوصی (private)
`resources/read` ttl5_000درس 07، خصوصی (private)
`prompts/list` ttl600_000درس 10، عمومی (public)
cache_scopeprivateپیش‌فرض complete() در درس 07
`limit` of `notes_search`10عدد صحیح از 1 تا 50
MAX_TOOL_LIST_PAGES100سقف صفحه‌بندی کلاینت درس 28

کدهای خطا

عیب‌یابی
  • خطای تجزیه -32700

    علت
    خط موردنظر JSON نیست
    راه‌حل
    ارسال دقیقاً یک شیء JSON در هر خط
  • -32600 درخواست نامعتبر

    علت
    نبود jsonrpc نسخه 2.0 یا نبود متد
    راه‌حل
    اصلاح ساختار پوششی (envelope)
  • -32602 پارامترهای نامعتبر

    علت
    مفقود بودن params._meta
    راه‌حل
    ارسال نسخه و قابلیت‌ها در هر نوبت
  • -32022

    علت
    رشته‌ی نسخه‌ی پشتیبانی‌نشده
    راه‌حل
    تلاش مجدد با نسخه‌ای از supported
  • -32601

    علت
    متد در HANDLERS نیست
    راه‌حل
    افزودن کنترل‌کننده یا اصلاح نام آن
  • -32602 روی resources/read

    علت
    URI ناشناخته یا نامعتبر
    راه‌حل
    هرگز یک خواندن خالی برنگردانید
  • HTTP 400 و -32020

    علت
    هدر Mcp-Param-* با بدنه همخوانی ندارد
    راه‌حل
    رد کردن قبل از توزیع (dispatch)
  • شکست در تجزیه کلاینت

    علت
    لاگ‌ها روی stdout
    راه‌حل
    نوشتن پیام‌های تشخیصی روی stderr

قراردادهای ابزار

مفهوم‌ها
inputSchema
اسکیمای JSON. در صورت نبود $schema، گویش پیش‌فرض 2020-12 است.
بدون آرگومان
{"type": "object", "additionalProperties": false}.
outputSchema
اختیاری. پس از انتشار، هر نتیجه شامل structuredContent منطبق خواهد بود.
structuredContent
هر مقدار معتبر JSON. همچنین ارسال JSON به عنوان یک بلوک متنی.
بلوک‌های محتوا
text, image, audio, resource_link, resource.
x-mcp-header
نگاشت یک آرگومان سطح بالا به Mcp-Param-{name}. فقط رشته، عدد صحیح یا بولین.
انکودینگ هدر
مقادیر غیر ASCII یا دارای فاصله‌گذاری از =?base64?{Base64UTF8}?= استفاده می‌کنند.
مکان‌نماها (Cursors)
مات و نامشخص (Opaque). مقدار خالی برای nextCursor معتبر است. توقف فقط با None.

پروژه‌ی سرور حافظه

دستورها
دستورعملکرد
project_test.py --init <workspace>میز کار شما را می‌سازد. یک کد اولیه‌ی دست‌نخورده شکست می‌خورد
project_test.py --all --path <workspace> --strictنمره‌دهی به هر چهار مرحله
cli.ts --data-dir <dir> --put <file>افزودن نسخه‌ی بازنگری 1 به لاگ JSONL
cli.ts --data-dir <dir> --query "<text>"امتیاز: 0.6 واژگانی به‌علاوه 0.4 کسینوسی
cli.ts --data-dir <dir> --history <id>تک‌تک نسخه‌های بازنگری به همراه منبع آن‌ها
--put <file> --revision 1افزودن نسخه‌ی بازنگری 2. نسخه‌ی 0 دارای تداخل است
cli.ts --data-dir <dir> --serve --port 8788سرویس‌های REST و MCP روی یک منبع ذخیره‌سازی مشترک. نیازمند MEMORY_TOKEN

گیت خط‌مشی پروژه‌ی پایانی

معماری
URL راه‌دورtools/callمجاز (allow)مجاز (allow)تأییدیه (approval)API رجیستریserver.jsonکلاینت MCPserver/discover, tools/listpolicy_decideissuer, audience, scopeرپلیکاهای فقط‌خواندنیبدون همگرایی (no affinity)سند تأییدیهarguments_digestرپلیکاهای تغییردهنده‌ی وضعیتابزارهای مخربمحل تجمیع آدیت (Audit sink)ورودی‌های پاک‌سازی‌شده

سؤالات متداول

پرسش و پاسخ
خطای ابزار یا خطای پروتکل؟
یک فراخوانی معتبرِ ناموفق isError: true برمی‌گرداند. یک ساختار پوششی نامعتبر، خطای JSON-RPC می‌دهد.
آیا شناسایی (discovery) قفل سرور را باز می‌کند؟
خیر. tools/list نیز متادیتای درخواست مشابهی دارد.
آیا سرور می‌تواند از کلاینت ورودی بخواهد؟
پاسخ input_required برمی‌گرداند. خودش هرگز مستقیماً درخواستی ارسال نمی‌کند.
کلاینت چطور متوجه تغییر یک منبع می‌شود؟
سابسکریپشن subscriptions/listen باز می‌کند. سپس دوباره resources/read را فراخوانی می‌کند.
آیا حاشیه‌نویسی‌ها (annotations) دسترسی را احراز می‌کنند؟
خیر. readOnlyHint و destructiveHint صرفاً راهنما هستند.