Nhìn từ bên ngoài, MCP (Model Context Protocol) dễ khiến người ta nghĩ đây là một lớp hạ tầng đặc biệt cho AI agent: có server riêng, cơ chế execution riêng và cách giao tiếp hoàn toàn khác REST API.
Nhưng bóc từng lớp ra thì nó không bí ẩn đến vậy. MCP cung cấp một cách chuẩn để agent hoặc harness biết server có những function nào, input của chúng có dạng gì và gọi chúng ra sao. Database, business logic, authentication, payment—những phần quyết định hệ thống làm gì—vẫn nằm ở application như trước.
Nói chung tìm hiểu cho vui thôi. Cái này không practical, cái chuẩn này sẽ sớm oẳng.
MCP nằm ở đâu trong hệ thống?
Một hệ thống thông thường có thể trông như thế này:
1 | Database |
Khi thêm MCP, ta có thêm một adapter dành cho agent:
1 | Database |
MCP không thay database, business logic, authentication hay HTTP. Nó thêm một interface có format chuẩn để các MCP client biết cách khám phá và sử dụng capability của server.
Ví dụ: Rails server bán quyền đọc blog
Giả sử một Rails application có các bài viết premium. Agent cần ba tool:
1 | search_articles |
Service layer phía Rails vẫn là code application bình thường:
1 | class ArticlesService |
Phần thanh toán cũng không liên quan đến MCP:
1 | class ArticlePurchaseService |
MCP chỉ đứng ở phía trước các service này và chuyển request vào đúng chỗ.
Có cần cài MCP gem không?
Không bắt buộc. Ruby SDK chủ yếu giúp viết sẵn các phần lặp lại của adapter:
- nhận MCP request;
- parse JSON;
- validate schema;
- route request;
- format response;
- xử lý lỗi protocol.
Nếu muốn tự kiểm soát mọi thứ, một Rails app có thể bắt đầu bằng một route:
1 | # routes.rb |
Controller tối giản:
1 | class McpController < ApplicationController |
SDK giúp giảm boilerplate và tránh tự xử lý sai protocol. Nhưng nó không làm business logic thay cho application.
/mcp vẫn chỉ là một endpoint
Một request có thể trông như sau:
1 | POST /mcp |
1 | { |
id là request ID của JSON-RPC. Nó không phải database ID và cũng không phải idempotency key. Client có thể dùng số tuần tự hoặc UUID; server chỉ cần trả lại đúng giá trị đó trong response.
Không cần tạo riêng các route kiểu:
1 | /mcp/tools/search |
Trong ví dụ này, method và params.name làm nhiệm vụ routing bên trong một endpoint.
tools/list là phần đáng giá nhất
Server mô tả các tool của mình cho client:
1 | { |
Client lấy metadata này rồi đưa tool definitions vào context của model. Khi đó model biết server có thể tìm bài, lấy bài hoặc mua quyền đọc bài. Nếu không có bước này, model không tự biết API nào đang tồn tại.
MCP client layer trong coding harness
Nếu coding harness như Pi chưa hỗ trợ MCP native, chỉ cần một extension mỏng làm bridge:
1 | MCP Server |
Extension khởi động bằng cách gọi tools/list, sau đó đăng ký các tool với Pi—mỗi MCP tool là một tool riêng, hoặc gói tất cả vào một generic tool như mcp(name, arguments).
Khi model yêu cầu gọi tool, extension chỉ convert request thành tools/call, gửi POST /mcp, parse response rồi trả result về cho Pi. Pi đưa result vào context và để model quyết định bước tiếp theo: trả lời, gọi tool khác hoặc kết thúc.
1 | Pi extension: call API → parse response → return result |
Client layer không cần intelligence. Nó chỉ là bridge giữa harness và MCP server—đồng thời là nơi dễ làm tool list phình vào context nếu đăng ký quá nhiều tool.
Model chọn tool, harness thực thi
Nếu user hỏi:
1 | Find me articles about Bitcoin monetary policy. |
Model có thể yêu cầu gọi search_articles. Model không tự gửi HTTP; harness nhận tool call và gửi request đến MCP server:
1 | { |
Rails nhận request rồi gọi service tương ứng:
1 | def call_tool |
Từ đây trở đi, việc tìm bài, kiểm tra quyền đọc hay charge tiền đều là việc của application.
Response chỉ là data có thêm envelope
Một response cho get_article có thể chứa dữ liệu như sau:
1 | { |
MCP chuẩn hóa envelope và một số field như jsonrpc, id, result, error, content hay structuredContent. Nó không thể chuẩn hóa domain của từng ứng dụng.
Blog có thể trả:
1 | { |
Polymarket có thể trả:
1 | { |
GitHub có thể trả:
1 | { |
Schema không khiến model khỏi phải suy luận. Nó cho model biết capability nhận input gì và dữ liệu trả về có hình dạng nào trước khi gọi.
MCP khác REST API ở điểm nào?
REST có thể expose những endpoint rất rõ ràng:
1 | GET /articles |
Vấn đề là agent harness không mặc nhiên biết các endpoint đó tồn tại. Ta phải viết plugin, dùng OpenAPI adapter, viết custom tool hoặc hard-code API definition vào harness.
MCP đưa capability discovery vào protocol thông qua tools/list. Client có thể hỏi server đang có tool gì, lấy schema của chúng rồi expose cho model. Sau đó tools/call cung cấp một convention chung để gọi tool.
Đó là giá trị chính của MCP: capability discovery và standardized tool calling. Nó không biến REST API thành thứ khác; nhiều MCP server thực chất vẫn chỉ là lớp chuyển đổi từ API hiện có:
1 | REST API → MCP tools |
Ví dụ một Polymarket MCP server không tạo ra Polymarket mới. Nó chỉ map các API như:
1 | GET /markets |
thành những tool mà Claude, Cursor, Pi hoặc agent khác có thể khám phá và gọi. Nếu Polymarket có official MCP server đủ tốt, adapter bên ngoài sẽ chỉ còn lý do tồn tại khi cung cấp thêm capability riêng.
Capability discovery cũng có cái giá của nó
tools/list nghe tiện, nhưng mỗi tool đi kèm name, description và schema. Tất cả đống metadata này thường được đưa vào context của model. Có vài tool thì không sao. Có vài chục hoặc vài trăm tool, context bắt đầu phình ra và model phải chọn trong một danh sách dài những thứ có thể chẳng liên quan đến request hiện tại.
Đó là chưa kể metadata bị gửi lại qua nhiều lượt gọi, description viết không nhất quán hoặc hai tool có tên gần giống nhau. Khi ấy MCP không còn giúp model “biết mình có gì” một cách gọn gàng nữa; nó biến context thành một catalog API khá dài.
Với một hệ thống chỉ có vài endpoint ổn định, cách đơn giản hơn có thể là đưa API document hoặc OpenAPI spec làm source of truth, rồi dùng một HTTP client/tool để gọi endpoint cần thiết. Không có capability discovery đẹp bằng MCP, nhưng dễ debug hơn, ít metadata hơn và không phải expose toàn bộ tool list trong mọi request.
MCP vẫn hợp lý khi cần interoperability giữa nhiều client, tool có schema rõ ràng hoặc capability thay đổi động. Nhưng đó không phải lý do để mặc định bọc mọi REST API bằng MCP.
Authentication và payment vẫn là việc của application
Không thể mở endpoint rồi cho bất kỳ agent nào mua bài bằng tài khoản của người khác. Remote MCP vẫn có thể dùng Bearer token như các API khác:
1 | POST /mcp |
Rails vẫn authenticate request rồi kiểm tra token, user, scope, permission, balance và purchase history. Chẳng hạn:
1 | articles:search |
MCP không thay authentication system. Nó chỉ tạo ra một convention để client và server interoperable.
Với purchase_article, flow có thể là:
1 | Check user |
Payment rail có thể là Stripe, credits, stablecoin, x402 hoặc internal wallet. MCP không cần biết.
Vì purchase_article có side effect, application vẫn nên tự xử lý idempotency. JSON-RPC id chỉ dùng để ghép request với response, không đảm bảo một operation chỉ chạy một lần.
Kết luận
Một MCP server tối giản cho Rails vẫn gồm những phần quen thuộc:
- Internal logic: database, CRUD và service layer.
- Public interface: HTTP endpoint.
- Authentication và authorization.
- Input/output: schema, validation và serialization.
- Routing: map tool name vào application logic.
MCP bổ sung cách chuẩn để:
- khám phá capability;
- mô tả input;
- gọi tool;
- trả kết quả về client.
Nhưng capability discovery không miễn phí. Tool list càng lớn thì context càng phình, và lợi ích của convention có thể không bù được chi phí token, độ nhiễu và công sức debug.
Với server bán bài viết, một endpoint và service layer hiện tại là đủ để thử nghiệm. Còn có nên thêm MCP hay không thì phải nhìn vào use case thật: cần nhiều client khác nhau và discovery động thì có thể đáng; chỉ cần nối một agent vào vài API nội bộ thì API document cùng một HTTP tool thường thực dụng hơn. Không phải project nào cũng cần thêm một adapter chỉ để chứng minh rằng nó có thể thêm adapter.
Sau khi tìm hiểu cho biết, tôi không thấy MCP là thứ cần phải nhét vào mọi hệ thống AI. Nó là một convention hữu ích trong một số trường hợp, nhưng cũng có thể nhanh chóng trở thành lớp bloat nằm giữa model và API. Nếu phần lớn use case chỉ là đưa một đống endpoint vào context rồi chờ model chọn, một API document tốt và cách gọi trực tiếp có khi đã đủ.
REST hoặc API vẫn làm công việc thực tế. MCP chỉ là một cách khác để agent biết API nào tồn tại—và đôi khi là một cách hơi đắt để làm việc đó.