Nếu bạn đã dành nhiều năm duy trì logic nghiệp vụ bên trong các ứng dụng PHP, việc xem các hướng dẫn về Model Context Protocol có thể mang lại cảm giác như đang đứng ngoài một cánh cửa bị khóa. Hầu như mọi hướng dẫn đều giả định sử dụng TypeScript hoặc Python. Chúng đi sâu vào các SDK chính thức, cài đặt npm và các gói pip. Điều đó để lại một lượng lớn dữ liệu nghiệp vụ—hồ sơ khách hàng, lịch sử đơn hàng, hệ thống kho bãi—nằm trong các mã nguồn PHP vốn trông có vẻ "vô hình" trước làn sóng công cụ AI hiện nay.
Tin tốt là không có gì trong MCP yêu cầu các SDK đó cả. MCP không phải là một thư viện. Nó là một giao thức truyền tải (wire protocol). Nếu môi trường thực thi (runtime) của bạn có thể đọc một dòng văn bản từ đầu vào tiêu chuẩn (standard input), phân tích cú pháp JSON và ghi JSON ngược trở ra, nó có thể giao tiếp với giao thức này. PHP đã làm chính xác điều đó từ rất lâu trước khi các LLM tồn tại.
MCP Thực Sự Là Gì
MCP viết tắt của Model Context Protocol. Về cốt lõi, đây là một tiêu chuẩn mở để kết nối các trợ lý AI với dữ liệu, công cụ và các API bên ngoài. Thay vì xây dựng một tích hợp tùy chỉnh cho mọi trợ lý hoặc mô hình, bạn chỉ cần xây dựng một giao diện tuân thủ tiêu chuẩn. Bất kỳ client nào hiểu MCP đều có thể nói chuyện với server của bạn mà không cần biết bất cứ điều gì về PHP, Laravel hay lược đồ cơ sở dữ liệu (database schema) cụ thể của bạn.
Bên dưới, MCP sử dụng JSON-RPC 2.0. Điều đó có nghĩa là mỗi yêu cầu là một đối tượng JSON đơn giản chứa tên phương thức, các tham số và một ID. Server sẽ phản hồi bằng một đối tượng JSON khác mang theo kết quả hoặc lỗi.
Một server cung cấp ba thành phần cơ bản (primitives):
- Tools (Công cụ): Các hành động mà mô hình có thể gọi. Một công cụ có thể truy vấn cơ sở dữ liệu, cập nhật trạng thái hoặc gọi một API bên thứ ba.
- Resources (Tài nguyên): Dữ liệu tĩnh hoặc bán tĩnh mà mô hình có thể tham chiếu thông qua một URI. Hãy nghĩ về các tệp, tài liệu cấu hình hoặc các tập dữ liệu tham chiếu.
- Prompts (Lời nhắc): Các mẫu (template) được định nghĩa trước giúp người dùng tương tác với hệ thống.
Có một sự phân biệt quan trọng về quyền kiểm soát cần ghi nhớ. Tools được mô hình kiểm soát. Trợ lý sẽ quyết định khi nào cần gọi một công cụ. Resources được ứng dụng kiểm soát. Server quyết định dữ liệu nào có sẵn và mô hình chỉ đơn giản là đọc những gì được cung cấp. Việc thực hiện đúng điều này giúp kiến trúc của bạn có thể dự đoán được. Bạn sẽ không muốn một mô hình đi tìm kiếm các tài nguyên lẽ ra phải là công cụ, hoặc ngược lại.
Cách thức Hoạt động của Transport
MCP định nghĩa hai phương thức transport, và lựa chọn của bạn sẽ định hình cách bạn viết phần PHP.
stdio là cách đơn giản nhất. MCP client khởi chạy script PHP của bạn như một tiến trình con (subprocess). Client ghi các thông điệp JSON-RPC vào đầu vào tiêu chuẩn (standard input) của script và script của bạn ghi các phản hồi vào đầu ra tiêu chuẩn (standard output). Không có socket nào cần quản lý, không có cổng (port) nào cần mở và không có header xác thực nào cần phân tích. Nếu công cụ và client của bạn nằm trên cùng một máy, đây thường là nơi tốt nhất để bắt đầu.
Chạy qua stdio áp đặt hai quy tắc nghiêm ngặt lên tiến trình PHP của bạn. Thứ nhất, ứng dụng của bạn không bao giờ được ghi dữ liệu không thuộc giao thức vào stdout. Nếu bạn echo một câu lệnh debug hoặc để một thông báo (notice) của PHP lọt ra ngoài, bạn sẽ làm hỏng bộ phân tích cú pháp của client. Hãy chuyển tất cả nhật ký (logging) và chẩn đoán (diagnostics) sang stderr. Thứ hai, hãy tắt hoàn toàn việc đệm đầu ra (output buffering). PHP thích đệm stdout, đặc biệt là trong ngữ cảnh CGI hoặc web, nhưng ngay cả các script CLI cũng có thể giữ lại dữ liệu. Hãy flush mọi phản hồi ngay lập tức. Nếu bạn đang sử dụng stream, hãy đặt stream_set_write_buffer(STDOUT, 0) hoặc tắt việc đệm ngầm định để client nhận được ký tự xuống dòng ngay khi bạn gửi nó.
Streamable HTTP hoạt động theo cách khác. Ứng dụng PHP của bạn chạy như một endpoint HTTP duy trì, thường được truy cập thông qua các yêu cầu POST. Điều này hữu ích khi server nằm trên một host khác, hoặc khi bạn muốn một daemon chạy lâu dài mà nhiều client có thể tiếp cận. Trong PHP, điều này thường có nghĩa là chạy dưới sự quản lý của RoadRunner, FrankenPHP hoặc một trình quản lý tiến trình tương tự thay vì chu kỳ yêu cầu-phản hồi (request-response cycle) truyền thống vốn sẽ kết thúc sau mỗi lần gọi.
Xây dựng với PHP
Bạn không cần một framework để bắt đầu. Một MCP server tối giản trong PHP là một vòng lặp đọc từ STDIN, giải mã JSON, chuyển tiếp đến một trình xử lý (handler) và mã hóa kết quả.
while ($line = fgets(STDIN)) {
$request = json_decode($line, true);
// route to tool or resource handler
// write JSON-RPC response to STDOUT
}
Bên trong vòng lặp đó, công việc thực sự là xây dựng các giao diện có ý nghĩa đối với một mô hình.
Tạo schema cho công cụ từ mã nguồn. Một trong những cách nhanh nhất để gây ra rắc rối là tự viết tay các JSON Schema cho tham số công cụ và để chúng bị lệch pha so với logic xác thực thực tế của bạn. PHP có khả năng reflection mạnh mẽ. Hãy kiểm tra các chữ ký phương thức (method signatures), đọc các quy tắc xác thực hiện có từ form hoặc các đối tượng command, và tạo schema từ chính các ràng buộc đó. Nếu mã nguồn nội bộ của bạn yêu cầu định dạng email hợp lệ, schema MCP của bạn cũng phải quy định như vậy. Khi các quy tắc xác thực thay đổi, schema sẽ tự động cập nhật. Không còn tình trạng lệch pha, không còn các lỗi âm thầm.
Phân biệt lỗi giao thức và lỗi công cụ. JSON-RPC có không gian lỗi riêng. Hãy sử dụng nó cho các lỗi vi phạm giao thức: JSON sai định dạng, phương thức không xác định, hoặc thiếu ID yêu cầu. Khi một công cụ thực thi đúng nhưng gặp phải vấn đề về nghiệp vụ, hãy trả về một kết quả bình thường kèm theo một cờ báo lỗi (error flag) bên trong payload. Nếu một công cụ tra cứu khách hàng không tìm thấy bản ghi khớp nào, đó không phải là một lỗi sập giao thức. Việc trả về một kết quả có cấu trúc như {"found": false} cho phép mô hình hiểu chuyện gì đã xảy ra và chọn bước tiếp theo. Nó có thể thử tìm kiếm rộng hơn, hoặc yêu cầu người dùng làm rõ. Ngược lại, nếu bạn ném ra một lỗi JSON-RPC, mô hình thường sẽ bị mất ngữ cảnh.
Lập kế hoạch cho các tác vụ chạy lâu. PHP được xây dựng cho các yêu cầu ngắn. Một yêu cầu web có thể bị hết thời gian chờ (timeout) trong vòng 30 giây, và ngay cả các script CLI cũng có thể làm cạn kiệt bộ nhớ hoặc sự kiên nhẫn. Nếu một công cụ cần vài phút để hoàn thành—chẳng hạn như biên soạn một báo cáo lớn hoặc đồng bộ hóa dữ liệu giữa các hệ thống—đừng bắt mô hình phải chờ đợi. Hãy trả về một mã định danh công việc (job identifier) ngay lập tức. Sau đó, cung cấp một công cụ thứ hai để kiểm tra trạng thái bằng ID đó. Bạn có thể lưu trữ tiến trình trong Redis, một bảng cơ sở dữ liệu, hoặc thậm chí là một tệp phẳng (flat file) nếu khối lượng dữ liệu thấp. Mô hình nhận được ID, kiểm tra lại sau đó, và cuối cùng sẽ lấy được kết quả đã hoàn thành.
Bảo mật khi mô hình nắm giữ các khóa
Cấp quyền truy cập công cụ cho một mô hình AI không giống như cấp cho người dùng là con người. Một mô hình hoạt động cực kỳ nhanh chóng, theo đúng nghĩa đen, và nó có thể hiểu sai các mô tả. Hãy coi mọi công cụ được công khai là một rủi ro leo thang đặc quyền (privilege escalation).
Giới hạn phạm vi một cách quyết liệt. Đừng bao giờ công khai một công cụ run_sql chung chung. Hãy xây dựng các công cụ cụ thể và hẹp hơn như find_customer_by_email hoặc update_order_status. Mô hình chỉ nên có khả năng thực hiện chính xác những gì bạn đặt tên, với các tham số mà bạn đã định nghĩa.
Tách biệt các luồng đọc và ghi. Các công cụ chỉ đọc (read-only) mang lại rủi ro thấp hơn. Hãy đặt bất kỳ hành động mang tính phá hủy nào đằng sau một cơ chế xác nhận rõ ràng, hoặc hạn chế hoàn toàn nó ở một máy chủ thứ hai. Nếu ứng dụng khách (client) của bạn hỗ trợ, hãy yêu cầu một bước phê duyệt từ con người trước khi một công cụ ghi được thực thi.
Hãy viết mô tả công cụ như thể chúng là các chỉ dẫn bổ sung, vì thực tế đúng là như vậy. Hãy chính xác về việc khi nào mô hình nên gọi một công cụ. Nếu một công cụ dùng để tra cứu giá, hãy nói rõ. Nếu nó chỉ nên được sử dụng sau khi đã xác minh ID khách hàng, hãy nêu rõ điều đó. Các mô tả mơ hồ sẽ dẫn đến hành vi mơ hồ.
Lọc đầu ra của bạn. Đừng serialize (tuần tự hóa) toàn bộ một Eloquent model hoặc Doctrine entity rồi đổ hết vào kết quả. Chỉ trả về những trường mà mô hình thực sự cần. Các trường nội bộ—giá vốn, ghi chú của nhân viên, các ID cơ sở dữ liệu cần được giữ kín—không nên được truyền qua mạng. Hãy xác định rõ cấu trúc dữ liệu trả về (return shape).
Cuối cùng, hãy ghi log mọi thứ. Ghi lại tên công cụ, các đối số được truyền vào và kết quả. Nếu một mô hình bắt đầu lặp lại một truy vấn tốn kém hoặc thăm dò các công cụ theo một thứ tự không mong muốn, nhật ký (logs) là cách duy nhất để bạn nhận ra điều đó.
Bắt đầu từ đâu
Bạn không cần sự cho phép từ người duy trì SDK để kết nối ứng dụng PHP của mình với một trợ lý AI. Bạn chỉ cần JSON-RPC, một vòng lặp và sự kỷ luật trong việc sử dụng stdout.
Đừng vội vàng xây dựng lại toàn bộ API của bạn thành các công cụ MCP ngay từ ngày đầu tiên. Hãy chọn ra ba thao tác chỉ đọc mà ai đó trong tổ chức của bạn thực sự hỏi đi hỏi lại nhiều lần. Có thể là kiểm tra trạng thái đơn hàng, lấy tóm tắt khách hàng, hoặc liệt kê các hóa đơn gần đây. Hãy đóng gói chúng thành các công cụ, cung cấp chúng qua stdio và để một đồng nghiệp sử dụng thử. Hãy quan sát xem mô hình làm tốt điều gì và vấp ngã ở đâu. Bạn sẽ học được nhiều điều từ ba công cụ đó hơn là việc lên kế hoạch cho ba mươi công cụ.
MCP là một cây cầu, không phải là sự thay thế cho ứng dụng của bạn. Mã nguồn PHP của bạn đã hiểu rõ nghiệp vụ của bạn rồi. Giao thức này chỉ cho phép mô hình bước qua và đặt câu hỏi cho nó.
