MCP dùng JSON-RPC 2.0, và transport là lớp mang các message đó giữa client với server. Hiện spec định nghĩa hai transport chuẩn: stdio cho server chạy cục bộ và Streamable HTTP cho server từ xa. Transport HTTP+SSE cũ (spec 2024-11-05) đã bị thay thế, chỉ còn giữ để tương thích ngược. Khái niệm chung về MCP xem MCP tổng quan.
So sánh transport
| stdio | Streamable HTTP | |
|---|---|---|
| Cách chạy | Client spawn server thành tiến trình con, trao đổi qua stdin/stdout | Server chạy độc lập, có một endpoint HTTP (vd /mcp) |
| Số client | 1 server phục vụ 1 client | Nhiều client, nhiều phiên |
| Message client → server | Một dòng JSON trên stdin | POST JSON-RPC lên endpoint |
| Message server → client | Một dòng JSON trên stdout | Body JSON, hoặc SSE stream trong response của POST / GET |
| Phiên | Gắn với vòng đời tiến trình | Header Mcp-Session-Id do server cấp lúc initialize |
| Xác thực | Không cần: credential lấy từ biến môi trường | OAuth 2.1 (Bearer token) hoặc header API key |
| Log | Chỉ ghi ra stderr vì stdout dành cho giao thức | Log thoải mái |
| Hợp với | Tool cá nhân, chạy trên jump host, dev | Server dùng chung cho team, chạy cạnh NetBox/ClickHouse |
Với stdio, chỉ một lệnh print() / console.log() ghi ra stdout là đủ làm hỏng luồng JSON-RPC, và client sẽ báo lỗi parse hoặc mất kết nối.
Luồng OAuth cho remote server
Theo spec (từ bản 2025-06-18), MCP server là resource server của OAuth: nó chỉ kiểm tra token. Token do một authorization server riêng cấp (Keycloak, Entra ID, Okta...).
sequenceDiagram participant C as MCP client participant S as MCP server participant A as Authorization server C->>S: POST /mcp (không có token) S-->>C: "401 + WWW-Authenticate (resource_metadata)" C->>S: "GET /.well-known/oauth-protected-resource" S-->>C: authorization_servers C->>A: Metadata, đăng ký client, login (PKCE) A-->>C: access token (audience = MCP server) C->>S: "POST /mcp + Authorization: Bearer" S-->>C: kết quả JSON-RPC
| Thành phần | Chuẩn | Ghi chú |
|---|---|---|
| Protected Resource Metadata | RFC 9728 | Server công bố authorization server nào cấp token cho nó |
| Authorization Server Metadata | RFC 8414 | Client tự tìm endpoint authorize/token |
| PKCE | OAuth 2.1 | Bắt buộc, kể cả với public client |
| Resource Indicators | RFC 8707 | Client gửi tham số resource để token gắn đúng audience |
| Dynamic Client Registration | RFC 7591 | Không bắt buộc. Nếu không có, cấu hình sẵn client id (Claude Code: --client-id) |
Phía server bắt buộc kiểm tra audience của token: chỉ chấp nhận token được cấp cho chính nó. Server không được chuyển tiếp token của client sang API phía sau (spec cấm "token passthrough"). Khi cần gọi NetBox hay router, server dùng credential riêng của nó.
Khai báo trong Claude Code
# stdio: secret qua env
claude mcp add netops -e NETBOX_TOKEN=$NETBOX_TOKEN -- uv run server.py
# HTTP có OAuth: thêm xong, vào phiên gõ /mcp để đăng nhập
claude mcp add --transport http netops https://mcp.example.net/mcp
# HTTP dùng API key tĩnh qua header
claude mcp add --transport http netops https://mcp.example.net/mcp \
--header "Authorization: Bearer $MCP_TOKEN"Rủi ro bảo mật
| Rủi ro | Mô tả | Cách chặn |
|---|---|---|
| DNS rebinding | Trang web độc hại gọi vào server HTTP đang chạy trên localhost | Kiểm tra header Origin, bind 127.0.0.1 thay vì 0.0.0.0, bật xác thực |
| Token passthrough | Server nhận token của client rồi dùng nó gọi API khác | Kiểm tra audience, dùng credential riêng của server |
| Confused deputy | Proxy MCP dùng một client id tĩnh cho mọi user, nên user A dùng được quyền đã cấp cho user B | Xin consent cho từng client, không bỏ qua bước xác nhận |
| Prompt injection qua dữ liệu | Description của interface, syslog hay ticket chứa câu kiểu "hãy chạy lệnh X" | Coi output tool là dữ liệu, tool ghi phải có người duyệt (an toàn agent mạng) |
| Tool poisoning | Server bên thứ ba cài chỉ dẫn độc hại vào description của tool | Chỉ cài server tin cậy, đọc tools/list, pin phiên bản |
| Session hijack | Đoán hoặc lộ Mcp-Session-Id |
Session id ngẫu nhiên, không dùng thay cho xác thực, mỗi request vẫn phải có token |
| Lộ secret | Token nằm trong .mcp.json đã commit |
Dùng ${VAR} / env, không ghi token thẳng vào file |
Khi nào dùng
- stdio: mặc định cho tool cá nhân và server chỉ đọc chạy ngay trên máy có quyền vào thiết bị. Đơn giản, không phải lo mở cổng.
- Streamable HTTP + OAuth: khi nhiều người hoặc nhiều agent cùng dùng một server, cần quản lý quyền theo user, cần audit tập trung, hoặc server phải nằm trong vùng mạng quản trị.
- HTTP + API key tĩnh: chỉ dùng trong mạng nội bộ, tạm thời. Phải xoay vòng key và đặt sau TLS.
- Server viết bằng TypeScript xem MCP server TypeScript, bằng Python xem FastMCP.