Ở bài trước, chúng ta đã hiểu client và server trao đổi JSON-RPC message với nhau theo một luồng request/response cụ thể. Nhưng những message đó thực sự "đi" qua kênh vật lý nào? Đây chính là vai trò của transport layer trong MCP. Bài viết này sẽ mổ xẻ ba lựa chọn transport chính — stdio, SSE (đã bị deprecated nhưng vẫn còn gặp trong thực tế), và Streamable HTTP (chuẩn hiện tại) — kèm theo hướng dẫn cấu hình cụ thể và tiêu chí lựa chọn cho từng tình huống triển khai.
Transport stdio: Giao Tiếp Process Nội Bộ Cho MCP
stdio (standard input/output) là transport đơn giản nhất và cũng là lựa chọn mặc định cho phần lớn MCP server chạy local. Cơ chế hoạt động: host khởi chạy MCP server như một process con (child process) trên cùng máy, sau đó giao tiếp bằng cách ghi JSON-RPC message (mỗi message một dòng, ngăn cách bằng newline) vào stdin của process con, và đọc phản hồi từ stdout của nó. Log/debug output của server thường được quy ước ghi ra stderr để không lẫn với luồng giao thức chính trên stdout.
Cách cấu hình thực tế
Đây là ví dụ cấu hình một MCP server filesystem chạy qua stdio trong file cấu hình dạng mcp.json (định dạng này được Claude Desktop, Claude Code, và nhiều host khác dùng chung với tên field tương tự):
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/home/user/projects/my-app"
]
},
"postgres": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"mcp/postgres",
"postgresql://readonly_user:pass@localhost:5432/mydb"
]
}
}
}
Host đọc file cấu hình này, tự động chạy lệnh command kèm args tương ứng như một subprocess mỗi khi khởi động, và giữ subprocess đó sống suốt phiên làm việc.
Ưu điểm
- Độ trễ cực thấp: giao tiếp qua pipe nội bộ hệ điều hành, không có network overhead, thường chỉ vài millisecond mỗi round-trip.
- Không cần xử lý authentication phức tạp: vì server chạy cùng máy, dưới cùng quyền hệ điều hành với người dùng, không cần OAuth hay API gateway.
- Đơn giản để debug: có thể chạy thử server trực tiếp trong terminal, gõ tay JSON-RPC message để kiểm tra phản hồi.
Nhược điểm
- Chỉ chạy được local: không thể chia sẻ một server stdio cho nhiều người dùng từ xa — mỗi người dùng cần tự cài và tự chạy server trên máy của họ.
- Phụ thuộc vào runtime của máy host: cần Node.js/Python/Docker cài sẵn tùy theo server yêu cầu, gây khó khăn khi triển khai đồng loạt cho cả team.
- Không có cơ chế reconnect chuẩn: nếu process con crash, phần lớn implement phải khởi động lại toàn bộ session từ đầu.
Mẹo: Khi viết MCP server chạy qua stdio, tuyệt đối không được
console.log()hayprint()bất kỳ debug message nào rastdout— vì đây là kênh giao thức chính, chỉ một dòng log lạc chỗ cũng đủ làm client parse JSON lỗi và toàn bộ session sập. Luôn dùngconsole.error()/process.stderrhoặc một logging framework ghi ra file riêng cho việc debug.
Server-Sent Events (SSE): Giao Tiếp Streaming Qua HTTP Cho MCP Remote
SSE là transport được giới thiệu trong spec MCP ban đầu (2024-11-05) để giải quyết bài toán mà stdio không làm được: kết nối tới một MCP server chạy remote, không nằm trên cùng máy với host. Cơ chế: client mở một kết nối HTTP GET dài hạn (long-lived) tới một endpoint (thường là /sse) để nhận message từ server dưới dạng luồng sự kiện (event stream), còn khi client cần gửi message tới server, nó thực hiện một HTTP POST riêng tới một endpoint khác (thường là /messages).
Vì sao SSE (kiểu cũ) đã bị deprecated
Tính đến spec 2025-06-18, cách tiếp cận HTTP+SSE nguyên bản đã chính thức bị loại bỏ, thay bằng Streamable HTTP. Lý do chính:
- Cần duy trì hai endpoint riêng biệt (một cho GET stream, một cho POST message), làm phức tạp hóa việc triển khai load balancer, proxy, và cấu hình CORS.
- Khó khăn khi scale hàng ngang (horizontal scaling): vì kết nối SSE là long-lived, một load balancer cần đảm bảo các request tiếp theo của cùng một session quay lại đúng server instance đã mở kết nối GET ban đầu (sticky session), gây phức tạp cho hạ tầng cloud-native hiện đại.
- Không hỗ trợ tốt việc resume session khi kết nối bị ngắt giữa chừng (ví dụ do mất mạng tạm thời) — client thường phải khởi tạo lại toàn bộ handshake.
Dù vậy, tính đến thời điểm bài viết này, một số MCP server và client cũ (viết theo spec 2024-11-05) vẫn còn dùng SSE trong thực tế, nên hiểu về nó vẫn cần thiết khi bạn phải tích hợp hoặc maintain hệ thống legacy.
Mẹo: Nếu bạn đang gặp một MCP server third-party chỉ hỗ trợ SSE (kiểm tra qua việc endpoint của nó có
/sseriêng biệt với endpoint POST), đừng vội viết code tương thích ngược phức tạp — hãy kiểm tra trước xem server đó có bản cập nhật hỗ trợ Streamable HTTP chưa, vì phần lớn SDK MCP chính thức đã ngừng ưu tiên phát triển tính năng mới cho SSE.
Streamable HTTP: Chuẩn Mới Cho Kết Nối MCP Remote
Streamable HTTP được giới thiệu trong spec 2025-03-26 và trở thành transport chính thức duy nhất cho remote server kể từ spec 2025-06-18. Đây là thiết kế gộp cả gửi lẫn nhận qua một endpoint HTTP duy nhất (thường gọi là /mcp), giải quyết trực tiếp các hạn chế của SSE kiểu cũ.
Cơ chế hoạt động
Client gửi JSON-RPC request bằng HTTP POST tới endpoint /mcp. Điểm khác biệt quan trọng: server có thể chọn trả về theo hai cách tùy tình huống:
- Response JSON thông thường — nếu tác vụ xử lý nhanh, server trả về ngay một JSON response đơn giản, giống REST API thông thường.
- Response dạng stream (SSE trong lòng HTTP response) — nếu tác vụ cần thời gian xử lý dài hoặc cần gửi nhiều message/notification trong lúc xử lý (ví dụ báo tiến độ), server giữ kết nối mở và stream nhiều event về, kết thúc bằng kết quả cuối cùng.
Client xác định phía server chọn cách nào dựa vào header Content-Type của response (application/json cho response thường, text/event-stream cho response dạng stream).
Một điểm cải tiến quan trọng khác: Streamable HTTP hỗ trợ header Mcp-Session-Id để định danh session xuyên suốt nhiều HTTP request riêng biệt, giúp việc scale hàng ngang dễ dàng hơn nhiều — session state có thể được lưu ở một tầng lưu trữ chung (Redis, database) thay vì buộc phải dính (sticky) vào đúng một server instance.
Ví dụ cấu hình remote server qua Streamable HTTP
{
"mcpServers": {
"internal-jira": {
"url": "https://mcp.company.internal/jira/mcp",
"transport": "http",
"headers": {
"Authorization": "Bearer ${JIRA_MCP_TOKEN}"
}
}
}
}
Ví dụ gọi trực tiếp bằng curl để kiểm tra một server Streamable HTTP còn sống và phản hồi đúng handshake (hữu ích khi debug thủ công trước khi tích hợp vào host):
curl -N -X POST https://mcp.company.internal/jira/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer $JIRA_MCP_TOKEN" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "manual-test", "version": "1.0.0" }
}
}'
Bảo mật với OAuth 2.1
Vì Streamable HTTP dùng cho remote server (thường expose ra internet hoặc mạng nội bộ công ty), spec MCP từ 2025-03-26 yêu cầu hỗ trợ OAuth 2.1 cho authorization, thay vì chỉ dựa vào API key tĩnh. Luồng OAuth chuẩn: client redirect người dùng tới authorization server, người dùng đăng nhập và cấp quyền, client nhận access token (thường có thời hạn ngắn) kèm refresh token, rồi dùng access token đó trong header Authorization: Bearer <token> cho mọi request tools/call tiếp theo.
Mẹo: Khi triển khai một MCP server nội bộ qua Streamable HTTP cho cả team dùng, đừng dùng chung một static API key cho tất cả mọi người — hãy đầu tư implement OAuth 2.1 ngay từ đầu, kể cả khi chỉ có vài chục người dùng nội bộ. Việc này giúp tránh rủi ro "confused deputy" (đã nói ở bài kiến trúc) và giúp bạn dễ dàng revoke quyền truy cập của một người dùng cụ thể mà không ảnh hưởng cả team.
Chọn Giao Thức Transport Phù Hợp Cho MCP Server Của Bạn
Việc chọn transport không phải là quyết định "cái nào tốt hơn" một cách tuyệt đối, mà phụ thuộc hoàn toàn vào ngữ cảnh triển khai. Dưới đây là bảng so sánh tổng hợp để bạn tra cứu nhanh:
| Tiêu chí | stdio | SSE (deprecated) | Streamable HTTP |
|---|---|---|---|
| Phạm vi | Chỉ local | Remote | Remote |
| Trạng thái trong spec hiện tại | Chính thức, ổn định | Đã loại bỏ khỏi spec mới | Chính thức, chuẩn hiện hành |
| Latency điển hình | ~1-5ms | ~50-300ms (network) | ~50-300ms (network) |
| Cần authentication phức tạp? | Không | Có (thường tự chế) | Có (chuẩn hóa OAuth 2.1) |
| Dễ scale hàng ngang | Không áp dụng (1 process/user) | Khó (cần sticky session) | Dễ hơn (session ID + shared store) |
| Hỗ trợ resume khi mất kết nối | Không | Hạn chế | Tốt hơn (session ID) |
| Độ phức tạp triển khai hạ tầng | Rất thấp | Trung bình-cao | Trung bình |
| Trường hợp dùng điển hình | Dev tool cá nhân, IDE plugin, file/DB local | Legacy server chưa migrate | SaaS MCP server, tích hợp nội bộ doanh nghiệp |
Khung quyết định thực dụng
Trong thực tế triển khai, câu hỏi đầu tiên nên tự hỏi là: "Server này có cần được nhiều người dùng/máy khác nhau truy cập cùng lúc không?"
- Nếu không (ví dụ tool đọc file hệ thống, tool truy vấn database chỉ dùng bởi chính máy đang chạy IDE) → chọn stdio. Đơn giản, nhanh, không cần lo bảo mật network.
- Nếu có (ví dụ một MCP server công ty expose dữ liệu CRM cho toàn bộ đội sales dùng qua nhiều máy khác nhau, hoặc bạn đang xây MCP server dạng SaaS bán cho khách hàng bên ngoài) → chọn Streamable HTTP, đầu tư làm đúng OAuth 2.1 ngay từ đầu.
- Nếu bạn đang maintain một server cũ viết theo spec 2024-11-05 dùng SSE và chưa có ngân sách để migrate ngay → tạm thời giữ nguyên, nhưng nên lên kế hoạch migrate sang Streamable HTTP trong lộ trình gần, vì hệ sinh thái client mới sẽ dần ngừng hỗ trợ đầy đủ SSE cũ.
Mẹo: Nếu chưa chắc chắn agent của bạn sẽ chạy ở đâu (chỉ local hay cần mở rộng thành dịch vụ chia sẻ trong tương lai), hãy bắt đầu với stdio để phát triển nhanh, nhưng thiết kế lớp logic bên trong server (xử lý tool, truy cập dữ liệu) tách biệt hoàn toàn khỏi lớp transport ngay từ đầu — hầu hết SDK MCP chính thức cho phép "cắm" transport khác nhau (stdio hoặc HTTP) vào cùng một server logic mà không cần viết lại code xử lý nghiệp vụ.
Việc lựa chọn đúng transport ảnh hưởng trực tiếp tới trải nghiệm người dùng cuối, chi phí vận hành, và độ phức tạp bảo mật của toàn bộ hệ thống MCP bạn xây dựng. Ở bài cuối của module này, chúng ta sẽ mở rộng góc nhìn ra toàn bộ hệ sinh thái — nơi có hàng trăm MCP server community đã được xây dựng sẵn, và cách đánh giá, lựa chọn, hay đóng góp vào hệ sinh thái đó.