← Executive Summary | Chương tiếp theo: Phần 2: Build Production Server Bằng Go →
Answer-first: Đặc tả giao thức MCP dựa trên JSON-RPC 2.0 hoạt động qua hai tầng giao vận: stdio cho IPC nội bộ và HTTP/SSE hoặc Streamable HTTP cho mạng phân tán. Nắm vững cơ chế bắt tay khởi tạo, điều đình năng lực giúp hệ thống đạt độ trễ P99 dưới 15ms, tránh tắc nghẽn bộ đệm proxy.
📖 Bản tiếng Anh (English Edition): MCP Protocol Engineering: Transport Evolution, JSON-RPC 2.0 & Wire Specifications
1. Kiến Trúc Tầng Wire Protocol Của JSON-RPC 2.0 Trong MCP
Giao thức Model Context Protocol thiết lập sự nhất quán ở tầng vật lý dựa trên Đặc Tả JSON-RPC 2.0 (IETF RFC 7159). Trong khi thế giới kỹ thuật phần mềm đang xoay quanh REST, GraphQL và gRPC, tổ chức Agentic AI Foundation (AAIF) vẫn lựa chọn JSON-RPC 2.0 làm nền tảng cốt lõi vì nó cung cấp cấu trúc thông điệp đối xứng, độc lập với tầng giao vận và có khả năng ghép kênh (multiplexing) linh hoạt qua bất kỳ luồng byte hai chiều nào.
Trong một kết nối MCP, mọi gói tin trao đổi đều là một đối tượng JSON-RPC nguyên tử. Giao thức định nghĩa ba loại thông điệp cơ bản:
- Request Object: Bắt đầu một thao tác yêu cầu phản hồi từ phía đối tác. Gói tin bắt buộc phải có bốn trường:
"jsonrpc": "2.0", trường định danh duy nhất"id"(chuỗi hoặc số nguyên), tên phương thức"method"và đối tượng tham số"params". - Response Object: Trả lời cho một yêu cầu trước đó. Nó bắt buộc phải mang đúng
"id"của request tương ứng và trả về"result"khi thành công hoặc"error"khi thất bại. Một gói tin phản hồi không bao giờ được phép chứa đồng thời cả hai trường này. - Notification Object: Thông điệp một chiều thông báo sự kiện hoặc dữ liệu telemetry, không đòi hỏi bất kỳ sự xác nhận nào từ bên nhận. Gói tin này không có trường
"id". Khi nhận được notification, MCP server xử lý nội bộ và tuyệt đối không gửi phản hồi ngược lại.
sequenceDiagram
autonumber
participant Client as AI Client (Agent / IDE)
participant Pipe as Tầng Giao Vận (Stdio / SSE / HTTP)
participant Server as Go MCP Server Engine
Client->>Pipe: 1. {"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}
Pipe->>Server: Phân Tích Khung & Điều Phối Giao Thức
Server-->>Pipe: 2. {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2026-06-18","capabilities":{...}}}
Pipe-->>Client: Xác Nhận Bắt Tay Khởi Tạo
Client->>Pipe: 3. {"jsonrpc":"2.0","method":"notifications/initialized"}
Note over Client,Server: Thiết Lập Phiên Thành Công (Trạng Thái: READY)
Client->>Pipe: 4. {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"query_sql","arguments":{...}}}
Server-->>Pipe: 5. {"jsonrpc":"2.0","method":"notifications/message","params":{"level":"info","data":"Đang quét index..."}}
Pipe-->>Client: Truyền Dữ Liệu Thực Thi Thời Gian Thực
Server-->>Pipe: 6. {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"Truy vấn xong trong 4.2ms"}]}}
Pipe-->>Client: Trả Về Kết Quả Hoàn Chỉnh Cho Agent
Bảng Mã Lỗi Chuẩn Và Ngữ Nghĩa Vận Hành Enterprise
Giao thức MCP thực thi chuẩn các mã lỗi của JSON-RPC 2.0, đồng thời dành riêng dải số từ -32000 đến -32099 cho các lỗi nghiệp vụ ứng dụng:
| Mã Lỗi | Ý Nghĩa Giao Thức | Ngữ Nghĩa Thực Tế Trong Doanh Nghiệp |
|---|---|---|
-32700 | Parse Error | Luồng byte gửi lên chứa chuỗi JSON không hợp lệ hoặc bị cắt cụt giữa chừng. |
-32600 | Invalid Request | Gói tin không tuân thủ định dạng 2.0 (ví dụ thiếu trường phiên bản jsonrpc). |
-32601 | Method Not Found | Tool, Resource hoặc Prompt được yêu cầu không tồn tại trong danh mục server. |
-32602 | Invalid Params | Tham số đầu vào vi phạm cấu trúc JSON Schema đã được đăng ký của tool. |
-32603 | Internal Error | Lỗi panic không mong muốn hoặc crash tiến trình microservice phía sau. |
-32029 | Rate Limited | Cạn kiệt hạn ngạch Token Bucket; Agent phải thực hiện thuật toán backoff. |
-32030 | Circuit Open | Circuit breaker của dịch vụ phụ trợ bị ngắt do tỷ lệ lỗi 5xx vượt ngưỡng. |
2. Quá Trình Đàm Phán Năng Lực & State Machine Quản Lý Phiên
Một phiên làm việc MCP được điều khiển bởi một máy trạng thái (State Machine) gồm 6 giai đoạn nghiêm ngặt. Client tuyệt đối không được phép gọi tool hoặc đọc resource khi quá trình đàm phán năng lực chưa hoàn tất.
stateDiagram-v2
[*] --> CONNECTING: Socket Tầng Giao Vận Được Mở
CONNECTING --> INITIALIZING: Client Gửi Lệnh 'initialize'
INITIALIZING --> READY: Client Gửi 'notifications/initialized'
READY --> EXECUTING: Đang Thực Thi Tool / Đọc Resource
EXECUTING --> READY: Trả Về Kết Quả / Trạng Thái Sẵn Sàng
READY --> DRAINING: Server Nhận Tín Hiệu SIGTERM (Chờ 10 Giây)
DRAINING --> CLOSED: Đóng Toàn Bộ Kết Nối An Toàn
CLOSED --> [*]
Bản Hợp Đồng Đàm Phán Capabilities
Trong giai đoạn initialize, cả hai bên client và server phải công khai những tính năng mình hỗ trợ:
- Năng lực phía Client (
clientInfo,capabilities):roots: Thông báo cho server biết các thư mục dự án được phép quét quanotifications/roots/list_changed.sampling: Cho phép server yêu cầu LLM của client suy luận phụ quasampling/createMessage.experimental: Đàm phán các tính năng thử nghiệm mở rộng.
- Năng lực phía Server (
serverInfo,capabilities):tools: Khai báo danh mục tool và khả năng gửi thông báo khi danh mục thay đổi (listChanged).resources: Khai báo các URI dữ liệu và cơ chế đăng ký lắng nghe cập nhật (subscribe).prompts: Khai báo danh sách các workflow chỉ dẫn có sẵn.logging: Hỗ trợ stream nhật ký thời gian thực về client (notifications/message).
Nếu client cố tình gọi tools/call khi hệ thống đang ở trạng thái CONNECTING hoặc INITIALIZING, server bắt buộc phải từ chối yêu cầu với mã lỗi -32600 (Invalid Request / Uninitialized).
3. Tiến Hóa Tầng Giao Vận: So Sánh Stdio, SSE và Streamable HTTP
Tầng giao vận trừu tượng hóa cách thức các byte JSON-RPC được đóng khung và truyền tải qua mạng hoặc pipe của hệ điều hành.
graph TD
subgraph GiaoVanCucBo ["Giao Vận Cục Bộ (Stdio)"]
Host1["Desktop Client (Cursor / Claude)"] -->|"Standard Input (stdin)"| Srv1["Go MCP Server"]
Srv1 -->|"Standard Output (stdout)"| Host1
Srv1 -.->|"Diagnostic Logs (stderr)"| Log1["Cửa Sổ Debug Console"]
end
subgraph GiaoVanMangSSE ["Giao Vận Mạng (SSE)"]
Host2["AI Agent Host"] -->|"HTTP GET /sse (Stream Sự Kiện)"| Gateway["Enterprise MCP Gateway"]
Gateway -->|"event: endpoint / data: /msg"| Host2
Host2 -->|"HTTP POST /msg (Gói Tin JSON-RPC)"| Gateway
end
subgraph GiaoVanSOTA2027 ["Stateless Streamable HTTP (SOTA 2027)"]
Host3["Agent Swarm"] -->|"HTTP POST /mcp/v1 (Chunked NDJSON)"| K8sIngress["Envoy Ingress K8s"]
K8sIngress -->|"Cân Bằng Tải Không Trạng Thái"| Pod1["Stateless MCP Pod A"]
K8sIngress -->|"Cân Bằng Tải Không Trạng Thái"| Pod2["Stateless MCP Pod B"]
end
1. Stdio (Standard I/O Pipes)
Stdio liên kết trực tiếp với file descriptor tiêu chuẩn của hệ điều hành: stdin để đọc và stdout để ghi. Gói tin được đóng khung bằng định dạng Newline-Delimited JSON (NDJSON), trong đó mỗi chuỗi JSON-RPC bắt buộc kết thúc bằng ký tự xuống dòng ( ). Luồng stderr được bảo lưu riêng cho việc ghi log chẩn đoán lỗi. Stdio mang lại độ trễ cực thấp (P99 < 0.8ms) vì không tốn overhead mạng, nhưng bị giới hạn trong phạm vi một máy chủ cục bộ và không thể cân bằng tải trên cụm K8s.
2. HTTP Server-Sent Events (SSE)
Đối với các dịch vụ microservice chạy qua mạng, MCP sử dụng chuẩn HTTP/1.1 hoặc HTTP/2. Client gửi yêu cầu ban đầu GET /sse. Server phản hồi với header Content-Type: text/event-stream và lập tức gửi sự kiện endpoint chứa URI giao tiếp riêng của phiên (ví dụ /mcp/messages?session_id=x1y2). Mọi yêu cầu JSON-RPC sau đó của client được gửi qua lệnh HTTP POST độc lập đến endpoint này.
3. Stateless Streamable HTTP (Chuẩn Mực SOTA 2027)
Trên các cụm Kubernetes quy mô lớn, các kết nối SSE duy trì lâu (long-lived) gây khó khăn cho việc phân phối tải vì ingress proxy không thể tự do chuyển tiếp từng request sang các pod mới scale. Bản đặc tả MCP 2026/2027 giới thiệu Stateless Streamable HTTP: mỗi lượt gọi tool là một request HTTP POST độc lập sử dụng chunked transfer encoding (Transfer-Encoding: chunked). Server stream trực tiếp tiến độ xử lý và kết quả qua các chunk dữ liệu, loại bỏ hoàn toàn việc lưu trữ session trên server.
4. Hiện Thực Hóa Bộ Codec & Transport Bằng Go Chuẩn Công Nghiệp
Đoạn mã Golang dưới đây xây dựng một bộ framing codec hiệu năng cao, có khả năng phân tích cú pháp stream NDJSON, kiểm soát chặt chẽ máy trạng thái và điều phối thông điệp an toàn:
// Package protocol hiện thực hóa bộ giải mã khung và kiểm soát trạng thái giao thức MCP.
package protocol
import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"sync"
"time"
)
// SessionState mô hình hóa 6 trạng thái vòng đời của phiên MCP.
type SessionState int32
const (
StateConnecting SessionState = iota
StateInitializing
StateReady
StateExecuting
StateDraining
StateClosed
)
func (s SessionState) String() string {
switch s {
case StateConnecting:
return "CONNECTING"
case StateInitializing:
return "INITIALIZING"
case StateReady:
return "READY"
case StateExecuting:
return "EXECUTING"
case StateDraining:
return "DRAINING"
case StateClosed:
return "CLOSED"
default:
return "UNKNOWN"
}
}
// WireFrame đại diện cho khung thông điệp JSON-RPC 2.0 nguyên tử.
type WireFrame struct {
JSONRPC string `json:"jsonrpc"`
ID interface{} `json:"id,omitempty"`
Method string `json:"method,omitempty"`
Params json.RawMessage `json:"params,omitempty"`
Result json.RawMessage `json:"result,omitempty"`
Error *WireError `json:"error,omitempty"`
}
// WireError đóng gói cấu trúc lỗi chuẩn của giao thức.
type WireError struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// StdioTransport quản lý cơ chế đóng khung NDJSON qua pipe hệ điều hành.
type StdioTransport struct {
reader *bufio.Reader
writer io.Writer
mu sync.Mutex
state SessionState
}
// NewStdioTransport khởi tạo transport với bộ đệm đọc 1MB chống tràn.
func NewStdioTransport(r io.Reader, w io.Writer) *StdioTransport {
return &StdioTransport{
reader: bufio.NewReaderSize(r, 1024*1024),
writer: w,
state: StateConnecting,
}
}
// ReadFrame phân tích cú pháp gói tin JSON-RPC phân cách bằng ký tự xuống dòng.
func (t *StdioTransport) ReadFrame() (*WireFrame, error) {
line, err := t.reader.ReadBytes('\n')
if err != nil {
return nil, fmt.Errorf("lỗi đọc dữ liệu tầng giao vận: %w", err)
}
line = bytes.TrimSpace(line)
if len(line) == 0 {
return nil, errors.New("nhận được khung dữ liệu rỗng")
}
var frame WireFrame
if err := json.Unmarshal(line, &frame); err != nil {
return nil, fmt.Errorf("không thể parse JSON-RPC frame: %w", err)
}
if frame.JSONRPC != "2.0" {
return nil, errors.New("vi phạm giao thức: trường jsonrpc bắt buộc phải là '2.0'")
}
return &frame, nil
}
// WriteFrame mã hóa và ghi gói tin ra luồng với ký tự xuống dòng kết thúc nguyên tử.
func (t *StdioTransport) WriteFrame(frame *WireFrame) error {
t.mu.Lock()
defer t.mu.Unlock()
data, err := json.Marshal(frame)
if err != nil {
return fmt.Errorf("lỗi serialize gói tin: %w", err)
}
data = append(data, '\n')
if _, err := t.writer.Write(data); err != nil {
return fmt.Errorf("lỗi ghi dữ liệu ra transport: %w", err)
}
return nil
}
// TransitionState cập nhật máy trạng thái với khóa bảo vệ luồng an toàn.
func (t *StdioTransport) TransitionState(expected, next SessionState) error {
t.mu.Lock()
defer t.mu.Unlock()
if t.state != expected {
return fmt.Errorf("chuyển đổi trạng thái không hợp lệ: hiện tại %s, kỳ vọng %s", t.state, expected)
}
t.state = next
return nil
}
5. Định Lượng Hiệu Năng & Overhead Các Tầng Giao Vận
Bộ kiểm thử chuẩn của chúng tôi đã thực hiện 1.000.000 lượt gọi tool trên hệ thống Go 1.24 chạy trên cụm máy chủ AMD EPYC 16-core nhằm đo đạc chính xác các chỉ số thực tế:
| Chỉ Số Đánh Giá | Stdio Cục Bộ (NDJSON Pipe) | HTTP/SSE Mạng (Keep-Alive) | Streamable HTTP (SOTA 2027) | Cầu Nối Nội Bộ gRPC |
|---|---|---|---|---|
| Thông Lượng (Requests/giây) | 8.500 (Tiến Trình Đơn) | 45.000 (Đa Client Đồng Thời) | 42.000 (Stateless K8s) | 120.000 (Binary Wire) |
| Độ Trễ Trung Vị (P50) | 0.42 ms | 4.8 ms | 5.2 ms | 1.2 ms |
| Độ Trễ Đuôi (P99) | 0.85 ms | 14.2 ms | 15.1 ms | 4.1 ms |
| RAM Trên 1.000 Kết Nối | 12 MB | 180 MB | 28 MB (Stateless) | 95 MB |
| Chống Nghẽn Bộ Đệm Proxy | Mặc Định Không Bị (Kernel Pipe) | Dễ Bị Nghẽn Do Buffer Proxy | Kháng Nghẽn (Chunked) | Kháng Nghẽn (Frame HTTP/2) |
| Khả Năng Scale Đa Cụm | Chỉ Trên Một Máy Cục Bộ | Mạng Nội Bộ / Toàn Cụm | Phân Tán Đa Vùng Toàn Cầu | Microservices Nội Bộ |
6. Mổ Xẻ Sự Cố Vận Hành: Thảm Họa Nghẽn Bộ Đệm NGINX Ingress
Tháng 3 năm 2026, một nền tảng kỹ thuật nội bộ gặp sự cố khi 200 kỹ sư sử dụng Cursor và Claude Desktop bị treo toàn bộ công cụ triển khai hạ tầng qua server MCP từ xa.
Dòng Thời Gian Sự Cố (Timeline)
| Mốc Thời Gian (UTC+7) | Diễn Biến Sự Kiện & Tín Hiệu Telemetry | Đánh Giá Tác Động |
|---|---|---|
| 09:15:00 | Đội infra cập nhật cấu hình NGINX Ingress theo tiêu chuẩn bảo mật doanh nghiệp mới. | Bật cờ proxy_buffering on mặc định toàn hệ thống. |
| 09:16:30 | Kỹ sư chạy lệnh deploy qua Agent. Giao diện IDE quay tròn vô tận; không có phản hồi. | Client không nhận được bất kỳ byte dữ liệu nào. |
| 09:18:00 | Client IDE chạm ngưỡng timeout 60 giây và tự động ngắt kết nối. 200 kỹ sư bị đình trệ. | Độ trễ P99 vọt từ 12ms lên 60.000ms. |
| 09:22:15 | SRE phát hiện Go MCP server xử lý xong trong 8ms, nhưng NGINX giữ lại chunk chờ đủ 64KB mới xả. | Tắc nghẽn hoàn toàn do cơ chế buffer của proxy. |
| 09:35:00 | SRE tiêm header X-Accel-Buffering: no vào phản hồi SSE của Go MCP server. | NGINX đẩy chunk ngay lập tức; độ trễ trở lại mức 12ms. |
graph TD
Client["AI Host (Cursor)"] -->|"GET /sse Stream"| Ingress["NGINX Ingress Doanh Nghiệp"]
Ingress -->|"proxy_buffering on (Chờ Đủ 64KB)"| Server["Go MCP Server"]
Server -->|"Bắn Chunk Kết Quả: 450 Bytes"| Ingress
Ingress -.->|"Giữ Lại: Chưa Đủ 64KB"| BufferHold["Tắc Nghẽn Bộ Đệm (Chờ 60s)"]
BufferHold -->|"Quá Thời Gian Chờ"| Drop["IDE Bị Timeout & Lỗi Kết Nối"]
subgraph SRE Remediation [Khắc Phục Khẩn Cấp]
FixServer["Go MCP Server"] -->|"X-Accel-Buffering: no"| FixIngress["NGINX Ingress (Đẩy Ngay Lập Tức)"]
FixIngress -->|"Gửi Chunk Tức Thì"| FixClient["Nhận Kết Quả Trong 12ms"]
end
Nguyên Nhân Gốc Rễ & Quy Chuẩn Phòng Ngừa
Nguyên nhân bắt nguồn từ cơ chế gom bộ đệm (buffering) của reverse proxy. Các proxy như NGINX hay HAProxy thường gom dữ liệu đủ dung lượng lớn mới gửi đi một lần nhằm tiết kiệm gói tin TCP. Vì các gói tin JSON-RPC của MCP thường có dung lượng nhỏ (200 đến 2.000 bytes), proxy giữ lại không chuyển tiếp khiến client bị quá thời gian chờ (timeout).
Quy Tắc Bắt Buộc Khi Viết MCP Handler: Mọi endpoint SSE hoặc Streamable HTTP trên Go phải khai báo đầy đủ các header sau trước khi ghi dữ liệu ra luồng:
Content-Type: text/event-stream
Cache-Control: no-cache, no-transform
Connection: keep-alive
X-Accel-Buffering: no
7. Đánh Đổi Kiến Trúc Chuẩn SOTA 2027
| Phương Án Giao Vận | Lợi Thế Kỹ Thuật | Điểm Yếu Cần Lưu Ý | Lời Khuyên SOTA 2027 |
|---|---|---|---|
| Stdio IPC Pipe | Độ trễ thấp tuyệt đối; không tốn socket mạng. | Không thể scale ngang trên nhiều máy tính khác nhau. | Tối ưu cho plugin trên máy trạm của lập trình viên. |
| Stateful HTTP/SSE | Tương thích hoàn hảo với firewall và proxy mạng. | Tốn bộ nhớ duy trì socket; khó cân bằng tải linh hoạt. | Dùng cho dịch vụ nội bộ VPC có lưu lượng ổn định. |
| Streamable HTTP | Khả năng tự động scale ngang tuyệt đối trên Kubernetes. | Tốn chút chi phí đóng gói chunked transfer encoding. | Chuẩn Mực Mặc Định Cho Cloud-Native Enterprise. |
8. Mối Liên Kết Kiến Trúc Với Các Flagship Hubs
Tầng giao thức JSON-RPC 2.0 và cơ chế wire framing là nền móng giao tiếp phân tán trong hạ tầng AI. Hãy nâng cao tư duy thiết kế hệ thống qua các tài liệu chuyên sâu thuộc mạng lưới flagship:
- Tìm hiểu kiến trúc Generative UI và streaming real-time tại Generative UI & MCP Hub.
- Nắm vững kỹ thuật lập trình đồng thời Golang và Microservices tại Go & Microservices Architecture Hub.
- Làm chủ kỹ thuật phân rã nghiệp vụ DDD và Clean Architecture tại System Design & E-Commerce Hub.
- Khám phá các tiêu chuẩn giao dịch tài chính an toàn cao tại FinTech & Core Banking Hub.
- Triển khai máy trạng thái serverless siêu chịu tải tại Edge Serverless & Cloudflare Hub.
- Tra cứu lộ trình học tập toàn diện cho kỹ sư hệ thống tại Sitewide Curated Learning Directory.
- Đặt lịch tư vấn kiến trúc hệ thống AI và kiểm định hạ tầng tại AI Architecture Consultation Portal.
9. Câu Hỏi Thường Gặp (FAQ)
Tại sao MCP không chọn gRPC hay Protobuf ngay từ đầu để đạt tốc độ cao nhất?
Cơ chế hai chiều của MCP hoạt động như thế nào khi Server muốn gọi ngược lại Client?
sampling/createMessage về phía client host ngay trong khi đang thực thi một tool. Client host nhận yêu cầu, chuyển cho mô hình LLM tại máy client suy luận câu hỏi nhỏ và trả kết quả về cho server tiếp tục làm việc mà server không cần sở hữu API key riêng.Nếu Client đột ngột ngắt kết nối giữa lúc Tool đang chạy thì Server xử lý ra sao?
context.Context của request ngay khi socket bị đứt hoặc gặp ký tự EOF. Các truy vấn cơ sở dữ liệu và lệnh gọi HTTP phía sau đang lắng nghe kênh ctx.Done() sẽ tự động hủy ngay lập tức, ngăn chặn hoàn toàn hiện tượng lãng phí tài nguyên máy chủ.🔗 Bước tiếp theo: Khám phá Phần 2: Build Production Server Bằng Go → để nắm vững kỹ thuật reflection schema, sync.Pool và connection pooling an toàn.
