Ngày 28 tháng 7 năm 2026, Model Context Protocol công bố một bản sửa đổi mới của đặc tả. Changelog mở đầu bằng hai mục làm thay đổi cách mọi máy chủ từ xa hoạt động: session ở cấp giao thức không còn nữa, và bước bắt tay initialize cũng vậy. Giờ đây mỗi request mang theo mọi thứ máy chủ cần để trả lời nó.
Với nhiều máy chủ, đó là một cuộc chuyển đổi. Với một máy chủ đọc giấy tờ tùy thân, nó gần với một sự xác nhận hơn, vì loại máy chủ này vốn đã có lý do chính đáng để không giữ trạng thái. Bài viết này đi qua bản sửa đổi từ góc nhìn của một tool server: thay đổi nào có thể bỏ qua, thay đổi nào phải xây, và thay đổi nào đáng áp dụng sớm. Các ví dụ lấy từ một máy chủ chúng tôi đang vận hành, nên những thiếu sót được nêu dưới đây cũng là của chính chúng tôi.
Đây là blog của doc.cheap, một API OCR hộ chiếu và giấy tờ tùy thân có một máy chủ MCP đứng phía trước. Hãy đọc các phần nói về sản phẩm với ý thức đó.
Phiên bản ngắn gọn
| Thay đổi trong 2026-07-28 | Một tool server không trạng thái làm gì |
|---|---|
Không còn Mcp-Session-Id, không còn session |
Không cần làm gì, nếu nó chưa từng giữ session |
Không còn bắt tay initialize; phiên bản và capabilities nằm trong _meta của từng request |
Đọc chúng theo từng request, đối chiếu header MCP-Protocol-Version với chúng |
server/discover |
Triển khai nó: đây là MUST |
Bắt buộc có header Mcp-Method và Mcp-Name |
Kiểm tra rằng chúng khớp với body |
resultType trên mọi kết quả |
Gửi "complete" |
ttlMs và cacheScope trên kết quả list và read |
Chọn thời gian cache và phạm vi |
| Tool theo thứ tự xác định | Giữ thứ tự đăng ký ổn định |
| Bỏ luồng HTTP GET và khả năng nối lại (resumability) | Trả lời GET và DELETE bằng 405 |
| Roots, Sampling và Logging bị deprecated | Nhận tệp qua tham số của tool, ghi log ra stderr hoặc OpenTelemetry |
Phần còn lại của bài giải thích từng dòng và vì sao nó quan trọng hơn với một máy chủ xử lý giấy tờ.
Session không còn, và điều đó hợp với máy chủ đọc giấy tờ
Mục đầu tiên của changelog loại bỏ "session ở cấp giao thức và header Mcp-Session-Id khỏi transport Streamable HTTP". Các endpoint dạng list như tools/list "không còn thay đổi theo từng kết nối", và máy chủ cần giữ trạng thái giữa các lần gọi thì "dùng các handle tường minh do máy chủ tạo ra, truyền như tham số thông thường của tool".
Một máy chủ đọc giấy tờ hiếm khi cần session ngay từ đầu. Hãy nghĩ xem một session sẽ chứa gì: một API key, có thể là một ảnh đang chờ đọc, có thể là kết quả gần nhất. Tất cả đều là dữ liệu bạn không muốn giữ trong bộ nhớ của một tiến trình giữa các lần gọi, nơi một lỗi có thể trao nó cho người gọi tiếp theo.
Máy chủ được lưu trữ của chúng tôi tại https://mcp.doc.cheap/mcp đã được xây theo cách đó từ trước bản sửa đổi này. Mã nguồn ghi rằng nó "cố ý không giữ trạng thái": mỗi POST nhận một server mới và một transport mới, trả lời rồi bị bỏ đi, nên "không có session nào sống trong bộ nhớ, khởi động lại không mất gì, và key của một người gọi không bao giờ bị request của người gọi khác nhìn thấy". Transport được tạo mà không có bộ sinh session id, điều mà trong TypeScript SDK chính là thứ làm cho nó không trạng thái, và nó trả lời bằng JSON thuần thay vì một stream.
Mẫu handle mà bản sửa đổi mô tả cũng đã có sẵn. Khi một agent muốn lấy lại một kết quả đã lưu, nó không dựa vào session để nhớ. scan_document trả về một scan id, và các tool get_scan và delete_scan nhận id đó như một tham số scan_id thông thường. Id chính là handle. Bất kỳ instance nào của máy chủ cũng trả lời được, và một client kết nối lại vào ngày mai cũng dùng được.
Nếu máy chủ của bạn giữ thứ gì đó theo session, câu hỏi cần đặt ra vẫn là một: nó có thể trở thành một id mà máy chủ trả về và client gửi lại không? Với công việc xử lý giấy tờ, câu trả lời thường là có, và thường là một lợi ích về quyền riêng tư.
Không bắt tay: phiên bản đi cùng mỗi request
Mục thứ hai làm cho MCP không trạng thái ở cấp giao thức. Cuộc trao đổi initialize và notifications/initialized bị loại bỏ, và "mỗi request giờ mang phiên bản giao thức và capabilities của client trong _meta". Trên HTTP, phiên bản còn nằm trong một header. Trang Streamable HTTP nói rằng mỗi POST bắt buộc (MUST) phải có header MCP-Protocol-Version, và giá trị đó phải khớp với giá trị trong _meta; nếu không, máy chủ trả lời 400 với lỗi HeaderMismatch.
Đây là hình dạng mới của một lần gọi tool đọc giấy tờ. Các header và các khóa _meta theo đúng ví dụ của chính đặc tả; tool và tham số là của chúng tôi, với giá trị giữ chỗ:
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: scan_document
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "scan_document",
"arguments": { "image_base64": "<base64 image>", "retain_hours": 0 },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "<client>", "version": "<version>" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
Với máy chủ, điều này nghĩa là ba bước kiểm tra trên mỗi request thay cho một lần bắt tay:
- Phiên bản. Nếu máy chủ không hỗ trợ phiên bản được yêu cầu, nó trả lời 400 với
UnsupportedProtocolVersionErrorliệt kê các phiên bản nó hỗ trợ. - Header so với body.
Mcp-Methodbắt buộc trên mọi request, vàMcp-Nametrêntools/call,resources/readvàprompts/get. Chúng phản chiếu method và tên tool, và nếu không khớp thì là lỗiHeaderMismatch. - Capabilities. Những gì máy chủ trước đây nhớ từ
initialize, giờ nó đọc từ request đang xử lý.
Các header quan trọng với máy chủ đọc giấy tờ hơn vẻ ngoài của chúng. Một lần gọi scan_document mang cả một bức ảnh dưới dạng base64; máy chủ được lưu trữ của chúng tôi nhận body request đến 36 MiB. Với Mcp-Method và Mcp-Name trong header, một gateway có thể định tuyến, đếm hoặc giới hạn tốc độ riêng cho tool tốn kém, tách khỏi các tool nhẹ, mà không cần đọc body đó. Đặc tả thêm một cảnh báo cho các gateway như vậy: chúng nên (SHOULD) kiểm tra rằng header phiên bản chỉ đến một phiên bản yêu cầu kiểm tra header và body, và từ chối request nếu không, thay vì tin vào một header chưa ai kiểm tra.
server/discover là bắt buộc
Mục thứ ba thêm server/discover, và "máy chủ MUST triển khai RPC này để công bố các phiên bản giao thức được hỗ trợ, capabilities và danh tính của mình". Client có thể gọi nó trước tiên để chọn phiên bản, hoặc dùng nó như một phép thử tương thích trên stdio.
Đây là thay đổi duy nhất mà một máy chủ không trạng thái không thể bỏ qua. Máy chủ của chúng tôi vẫn cung cấp một tài liệu khám phá nhỏ tại GET / (tên, phiên bản, transport và endpoint), nhưng đó là một trang web thông thường, không phải method của giao thức. Một máy chủ muốn nói chuyện bằng 2026-07-28 phải tự trả lời server/discover.
Kết quả: resultType, cache và thứ tự
Ba mục nhỏ hơn thay đổi hình dạng của một kết quả.
resultType. Mọi kết quả giờ đều mang trường này:"complete"cho câu trả lời thông thường,"input_required"cho câu trả lời tạm thời của một request nhiều vòng. Client bắt buộc (MUST) coi kết quả từ máy chủ theo giao thức cũ không có trường này là"complete", nên máy chủ cũ vẫn chạy được, nhưng máy chủ mới thì gửi trường này.ttlMsvàcacheScope. Kết quả củatools/list,prompts/list,resources/list,resources/readvàresources/templates/listphải mang một gợi ý về độ mới tính bằng mili giây và một phạm vi,"public"hoặc"private". Với một máy chủ mà danh sách tool chỉ đổi khi deploy và resource là tài liệu đóng gói sẵn, mộtttlMsdài với"public"là lựa chọn tự nhiên. Một resource phụ thuộc vào key của người gọi sẽ là"private".- Thứ tự tool xác định. Máy chủ nên (SHOULD) trả về các tool của
tools/listtheo một thứ tự xác định, để giúp cache phía client và tăng tỷ lệ trúng prompt cache. Nếu tool được đăng ký trong mã theo một thứ tự cố định, hãy giữ nguyên như vậy; máy chủ của chúng tôi đăng ký sáu tool theo cùng một thứ tự mỗi lần.
Hỏi thêm người dùng: request nhiều vòng
Các bản sửa đổi cũ cho phép máy chủ gửi request của chính nó tới client ngay giữa một lần gọi: elicitation/create để hỏi người dùng điều gì đó, sampling/createMessage để mượn mô hình của client, roots/list để biết các thư mục được phép. Bản sửa đổi thay thế điều đó bằng mẫu Multi Round-Trip Requests. Máy chủ trả lời với resultType: "input_required" và một danh sách inputRequests, rồi client gửi lại request ban đầu kèm inputResponses.
Với một tool đọc giấy tờ, đây là chỗ gọn gàng cho một câu hỏi như "ảnh bị cắt ở mép trái, gửi ảnh khác nhé?". Vì lần gửi lại chính là cùng request đó, máy chủ không phải giữ gì giữa hai lần gọi. Một máy chủ không dùng request nào do máy chủ khởi tạo, như máy chủ của chúng tôi, thì không có gì phải chuyển đổi ở đây.
Những gì bị deprecated, và dùng gì thay thế
Roots, Sampling và Logging bị deprecated. Chúng vẫn hoạt động trong một khoảng thời gian chuyển tiếp ít nhất mười hai tháng, nhưng "các bản triển khai mới không nên thêm hỗ trợ cho chúng". Các phương án thay thế được đề xuất rất hợp với máy chủ đọc giấy tờ:
- Thay cho Roots, truyền tệp qua tham số của tool hoặc qua cấu hình. Máy chủ cục bộ của chúng tôi chỉ đọc đường dẫn tệp khi một thư mục ảnh được đặt trong cấu hình, và chỉ bên trong thư mục đó; đường dẫn nằm ngoài sẽ bị từ chối. Mô hình chọn tham số; người vận hành chọn hàng rào.
- Thay cho Logging, ghi log ra stderr hoặc OpenTelemetry. Bản sửa đổi cũng bỏ
logging/setLevel; mức log giờ đi theo từng request trong_meta, và máy chủ không được phát log cho những request không yêu cầu. Ngữ cảnh trace (traceparent) cũng có thể đi trong_meta.
Client cũ vẫn sẽ tiếp tục gọi
Không phải client nào cũng chuyển đổi cùng một ngày, nên trang transport mô tả việc một máy chủ chỉ hỗ trợ 2026-07-28 nên làm gì với lưu lượng cũ:
- GET hoặc DELETE tới endpoint MCP: 405 Method Not Allowed.
- Header
Mcp-Session-Id: bỏ qua, và không bao giờ tạo hay gửi lại một giá trị. - Header
Last-Event-ID: bỏ qua, vì stream không còn nối lại được nữa.
Máy chủ được lưu trữ của chúng tôi đã trả lời GET và DELETE trên endpoint của nó bằng 405 cùng header Allow: POST, OPTIONS, và không tạo session id nào. Một client vẫn nói theo bản sửa đổi cũ sẽ biết điều đó từ body của lỗi: đặc tả bảo nó chỉ quay về initialize khi body của lỗi 400 không phải là một lỗi hiện đại mà nó nhận ra.
Máy chủ của chúng tôi đang ở đâu
Nói cho chính xác: máy chủ được lưu trữ được xây trên phiên bản 1.30.0 của TypeScript SDK, và mã của chính chúng tôi chưa nhắc đến 2026-07-28 ở đâu cả. Những phần ở trên đã đúng với nó là những phần đến từ thiết kế của nó: không session, id làm handle, 405 cho GET và DELETE, tệp được rào bằng cấu hình. server/discover, các bước kiểm tra header, resultType và các trường cache là phần việc còn lại. Trang máy chủ MCP OCR hộ chiếu và hướng dẫn máy chủ MCP mô tả những gì client kết nối tới hôm nay.
Nếu bạn đang chọn một API đọc giấy tờ cho agent, trang so sánh API OCR hộ chiếu liệt kê các dịch vụ; phần MCP thì đáng hỏi riêng từng dịch vụ.
Danh sách kiểm tra cho tool server của bạn
- Không có trạng thái nào sống giữa các request; bất cứ thứ gì lần gọi sau cần đều là một id mà máy chủ trả về.
- Phiên bản của mỗi request được đối chiếu với header
MCP-Protocol-Version, vớiUnsupportedProtocolVersionErrorcho phiên bản bạn không hỗ trợ. Mcp-MethodvàMcp-Nameđược đối chiếu với body.server/discovertrả lời với phiên bản, capabilities và danh tính.- Kết quả mang
resultType; kết quả list và read mangttlMsvàcacheScope. tools/listtrả về cùng một thứ tự mỗi lần.- GET và DELETE nhận 405; header session và event-id bị bỏ qua.
- Tệp đi vào qua tham số, bên trong một hàng rào được cấu hình, không qua Roots.
- Log đi ra stderr hoặc OpenTelemetry, không gửi tới client.
Toàn văn nằm trên modelcontextprotocol.io, và bài công bố bản phát hành cung cấp bối cảnh. Thông tin thêm về máy chủ của chúng tôi có trên trang máy chủ MCP OCR hộ chiếu.
Một câu hỏi cho bạn: máy chủ MCP của bạn có giữ thứ gì trong session mà giờ bạn phải biến thành một handle không? Hãy cho chúng tôi biết trong phần bình luận bên dưới.