اگر سال‌ها وقت خود را صرف نگهداری از منطق تجاری (business logic) در برنامه‌های PHP کرده‌اید، تماشای آموزش‌های Model Context Protocol (MCP) می‌تواند مانند ایستادن پشت یک درِ قفل‌شده باشد. تقریباً هر راهنمایی فرض را بر TypeScript یا Python می‌گذارد. آن‌ها از SDKهای رسمی، نصب‌های npm و بسته‌های pip صحبت می‌کنند. این موضوع باعث می‌شود حجم عظیمی از داده‌های تجاری — سوابق مشتریان، تاریخچه سفارش‌ها، سیستم‌های موجودی — در کدهای PHP باقی بمانند که برای موج فعلی ابزارهای هوش مصنوعی، نامرئی به نظر می‌رسند.

خبر خوب این است که هیچ‌کدام از ویژگی‌های MCP نیازی به آن SDKها ندارند. MCP یک کتابخانه نیست؛ بلکه یک wire protocol است. اگر محیط اجرای شما (runtime) بتواند یک خط متن را از ورودی استاندارد (standard input) بخواند، JSON را تجزیه (parse) کند و دوباره JSON بنویسد، می‌تواند با این پروتکل صحبت کند. PHP از مدت‌ها پیش از ظهور LLMها، دقیقاً همین کار را انجام می‌داده است.

MCP در واقع چیست

MCP مخفف Model Context Protocol است. در هسته خود، یک استاندارد باز برای متصل کردن دستیارهای هوش مصنوعی به داده‌ها، ابزارها و APIهای خارجی است. به جای ساختن یک ادغام (integration) سفارشی برای هر دستیار یا مدل، شما یک رابط (interface) سازگار می‌سازید. هر کلاینتی که MCP را درک کند، می‌تواند بدون دانستن هیچ چیز درباره PHP، Laravel یا طرحواره (schema) پایگاه داده خاص شما، با سرور شما صحبت کند.

در لایه‌های زیرین، MCP از JSON-RPC 2.0 استفاده می‌کند. این بدان معناست که هر درخواست یک شیء JSON ساده شامل نام متد، پارامترها و یک ID است. سرور با شیء JSON دیگری که حاوی یک نتیجه یا یک خطا است، پاسخ می‌دهد.

یک سرور سه عنصر اولیه (primitives) را ارائه می‌دهد:

  • Tools: اقداماتی که مدل می‌تواند فراخوانی کند. یک ابزار ممکن است یک پایگاه داده را پرس‌وجو کند، وضعیتی را به‌روز کند یا یک API شخص ثالث را فراخوانی کند.
  • Resources: داده‌های ایستا یا نیمه‌ایستا که مدل می‌تواند از طریق یک URI به آن‌ها ارجاع دهد. فایل‌ها، اسناد پیکربندی یا مجموعه‌داده‌های مرجع را در نظر بگیرید.
  • Prompts: قالب‌های از پیش تعریف شده‌ای که به کاربر کمک می‌کنند با سیستم تعامل داشته باشد.

یک تمایز کنترلی مهم وجود دارد که باید به خاطر بسپارید. Tools توسط مدل کنترل می‌شوند؛ دستیار تصمیم می‌گیرد که چه زمانی یکی را فراخوانی کند. Resources توسط اپلیکیشن کنترل می‌شوند؛ سرور تصمیم می‌گیرد چه داده‌هایی در دسترس هستند و مدل صرفاً آنچه ارائه شده را می‌خواند. رعایت درست این موضوع، معماری شما را قابل پیش‌بینی نگه می‌دارد. شما نمی‌خواهید مدلی به دنبال منابعی بگردد که باید ابزار (tool) می‌بودند، یا برعکس.

نحوه عملکرد انتقال (Transport)

MCP دو روش انتقال را تعریف می‌کند و انتخاب شما تعیین می‌کند که بخش PHP را چگونه بنویسید.

روش stdio ساده‌ترین است. کلاینت MCP اسکریپت PHP شما را به عنوان یک زیرفرآیند (subprocess) اجرا می‌کند. کلاینت پیام‌های JSON-RPC را به ورودی استاندارد (standard input) اسکریپت شما می‌نویسد و اسکریپت شما پاسخ‌ها را به خروجی استاندارد (standard output) می‌نویسد. هیچ سوکتی برای مدیریت، هیچ پورتی برای باز کردن و هیچ هدر احراز هویتی برای تجزیه کردن وجود ندارد. اگر ابزار و کلاینت شما روی یک ماشین باشند، این معمولاً بهترین نقطه برای شروع است.

اجرا از طریق stdio دو قانون سختگیرانه را بر فرآیند PHP شما تحمیل می‌کند. اول اینکه، اپلیکیشن شما هرگز نباید داده‌های غیرپروتکلی را در stdout بنویسد. اگر یک دستور echo برای دیباگ بنویسید یا اجازه دهید یک PHP notice منتشر شود، تجزیه‌کننده (parser) کلاینت را از کار می‌اندازید. تمام لاگ‌ها و تشخیص‌ها (diagnostics) را به stderr هدایت کنید. دوم، بافرینگ خروجی (output buffering) را کاملاً غیرفعال کنید. PHP تمایل دارد stdout را بافر کند، به‌ویژه در محیط‌های CGI یا وب، اما حتی اسکریپت‌های CLI نیز می‌توانند داده‌ها را نگه دارند. هر پاسخ را بلافاصله تخلیه (flush) کنید. اگر از استریم‌ها استفاده می‌کنید، stream_set_write_buffer(STDOUT, 0) را تنظیم کنید یا بافرینگ ضمنی را خاموش کنید تا کلاینت بلافاصله پس از ارسال، کاراکتر خط جدید (newline) را دریافت کند.

روش Streamable HTTP متفاوت عمل می‌کند. اپلیکیشن PHP شما به عنوان یک نقطه پایانی (endpoint) HTTP پایدار اجرا می‌شود که معمولاً از طریق درخواست‌های POST قابل دسترسی است. این روش زمانی مفید است که سرور روی میزبان (host) دیگری قرار دارد، یا زمانی که یک دیمون (daemon) با طول عمر بالا می‌خواهید که چندین کلاینت بتوانند به آن دسترسی داشته باشند. در PHP، این معمولاً به معنای اجرا تحت RoadRunner، FrankenPHP یا یک مدیریت‌کننده فرآیند مشابه است، به جای چرخه سنتی درخواست-پاسخ که پس از هر فراخوانی خاتمه می‌یابد.

ساخت آن در PHP

برای شروع نیازی به فریم‌ورک ندارید. یک سرور MCP حداقلی در PHP شامل یک حلقه است که از STDIN می‌خواند، JSON را رمزگشایی (decode) می‌کند، به یک هندلر (handler) ارجاع می‌دهد و نتیجه را رمزگذاری (encode) می‌کند.

while ($line = fgets(STDIN)) {
    $request = json_decode($line, true);
    // route to tool or resource handler
    // write JSON-RPC response to STDOUT
}

در داخل آن حلقه، کار اصلی ساخت رابط‌هایی است که برای یک مدل معنا داشته باشند.

طرحواره‌های (schemas) ابزار را از روی کد تولید کنید. یکی از سریع‌ترین راه‌ها برای ایجاد مشکل، نوشتن دستی JSON Schemas برای پارامترهای ابزارتان و اجازه دادن به اینکه با منطق اعتبارسنجی واقعی شما ناهماهنگ شوند، است. PHP قابلیت‌های Reflection غنی‌ای دارد. امضاهای متد خود را بررسی کنید، قوانین اعتبارسنجی موجود را از فرم‌ها یا اشیاء دستور (command objects) خود بخوانید و طرحواره را از روی آن محدودیت‌ها تولید کنید. اگر کد داخلی شما به فرمت ایمیل معتبر نیاز دارد، طرحواره MCP شما نیز باید همین را بگوید. وقتی قوانین اعتبارسنجی تغییر می‌کنند، طرحواره به‌طور خودکار به‌روز می‌شود. بدون ناهماهنگی، بدون شکست‌های بی‌صدا.

خطاهای پروتکل را از خطاهای ابزار جدا کنید. JSON-RPC فضای خطای مخصوص به خود را دارد. از آن برای پروتکل‌های شکسته استفاده کنید: JSON نامعتبر، متدهای ناشناخته، یا نبودِ ID درخواست. زمانی که یک ابزار به‌درستی اجرا می‌شود اما با یک مشکل تجاری (business problem) مواجه می‌گردد، یک نتیجه عادی همراه با یک پرچم خطا (error flag) در داخل Payload برگردانید. اگر یک ابزار جستجوی مشتری، رکورد مطابقی پیدا نکند، این یک فروپاشی پروتکل نیست. بازگرداندن یک نتیجه ساختاریافته مانند {"found": false} به مدل اجازه می‌دهد بفهمد چه اتفاقی افتاده و مرحله بعدی را انتخاب کند. ممکن است جستجوی گسترده‌تری را امتحان کند، یا از کاربر بخواهد موضوع را شفاف‌سازی کند. اگر در عوض یک خطای JSON-RPC پرتاب کنید، مدل اغلب بافت (context) را از دست می‌دهد.

برای کارهای طولانی‌مدت برنامه‌ریزی کنید. PHP برای درخواست‌های کوتاه ساخته شده است. یک درخواست وب ممکن است در سی ثانیه منقضی شود (time out)، و حتی اسکریپت‌های CLI نیز می‌توانند حافظه یا صبر را تمام کنند. اگر یک ابزار برای اتمام کار به چند دقیقه نیاز دارد — مثلاً یک گزارش بزرگ را تدوین می‌کند یا داده‌ها را بین سیستم‌ها همگام‌سازی می‌کند — مدل را منتظر نگذارید. بلافاصله یک شناسه کار (job identifier) برگردانید. سپس ابزار دومی را برای بررسی وضعیت از طریق آن ID ارائه دهید. می‌توانید پیشرفت کار را در Redis، یک جدول پایگاه داده، یا حتی یک فایل ساده (flat file) در صورت کم بودن حجم داده‌ها ذخیره کنید. مدل شناسه را دریافت می‌کند، بعداً دوباره بررسی می‌کند و در نهایت نتیجه تکمیل‌شده را دریافت می‌کند.

امنیت زمانی که مدل به کلیدها دسترسی دارد

دادن دسترسی به یک ابزار به یک مدل هوش مصنوعی، مانند دادن آن به یک کاربر انسانی نیست. یک مدل، به معنای واقعی کلمه، سریع عمل می‌کند و ممکن است توضیحات را اشتباه تفسیر کند. با هر ابزارِ در دسترس، مانند یک ریسک افزایش سطح دسترسی (privilege escalation) برخورد کنید.

محدوده (scope) را به‌شدت محدود کنید. هرگز یک ابزار عمومی مانند run_sql را در دسترس قرار ندهید. ابزارهای خاص و محدود مانند find_customer_by_email یا update_order_status بسازید. مدل باید فقط بتواند دقیقاً همان کاری را انجام دهد که نام‌گذاری کرده‌اید، آن هم با پارامترهایی که خودتان تعریف کرده‌اید.

مسیرهای خواندن و نوشتن را جدا کنید. ابزارهای فقط-خواندنی (read-only) ریسک کمتری دارند. هر اقدام مخربی را پشت یک مکانیسم تأیید صریح قرار دهید، یا آن را کاملاً به سرور دوم محدود کنید. اگر کلاینت شما از آن پشتیبانی می‌کند، قبل از اجرای یک ابزار نوشتن (write tool)، مرحله تأیید انسانی را الزامی کنید.

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

خروجی خود را فیلتر کنید. یک مدل کامل Eloquent یا یک موجودیت Doctrine را سریال‌سازی (serialize) نکرده و در نتیجه رها نکنید. فقط فیلدهایی را برگردانید که مدل واقعاً به آن‌ها نیاز دارد. فیلدهای داخلی — قیمت‌های تمام‌شده، یادداشت‌های کارکنان، شناسه‌های پایگاه داده که باید داخلی بمانند — نباید از شبکه عبور کنند. در مورد ساختار خروجی (return shape) خود صریح باشید.

در نهایت، همه چیز را ثبت (log) کنید. نام ابزار، آرگومان‌های ارسال شده و نتیجه را ثبت کنید. اگر مدلی شروع به تکرار یک پرس‌وجوی (query) سنگین کرد یا ابزارها را با ترتیبی غیرمنتظره بررسی کرد، لاگ‌های شما تنها راهی برای مشاهده این اتفاق هستند.

از کجا شروع کنیم

برای متصل کردن اپلیکیشن PHP خود به یک دستیار هوش مصنوعی، نیازی به اجازه از نگهدارنده SDK ندارید. شما به JSON-RPC، یک حلقه (loop) و کمی نظم در مورد stdout نیاز دارید.

در روز اول، در برابر وسوسه بازسازی کل API خود به عنوان ابزارهای MCP مقاومت کنید. سه عملیات فقط-خواندنی را انتخاب کنید که کسی در سازمان شما واقعاً مکرراً درباره آن‌ها سوال می‌پرسد. شاید بررسی وضعیت سفارش، دریافت خلاصه اطلاعات مشتری یا لیست کردن فاکتورهای اخیر باشد. آن‌ها را در قالب ابزار بسته‌بندی کنید، از طریق stdio ارائه دهید و اجازه دهید یک همکار از آن‌ها استفاده کند. مشاهده کنید که مدل در چه کارهایی خوب عمل می‌کند و کجا دچار لغزش می‌شود. شما از آن سه ابزار بیشتر از برنامه‌ریزی برای سی ابزار یاد خواهید گرفت.

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