Xử lý lỗi MCP tool: isError, retry và kết quả rỗng
Xử lý lỗi MCP tool cho CCAR-F: cấu trúc isError, bốn loại lỗi, khi nào nên retry, và vì sao tra cứu thất bại không được trông giống kết quả rỗng hợp lệ.
Agent hỗ trợ khách hàng gọi lookup_order, dịch vụ đơn hàng bị timeout, và MCP tool trả về đúng một câu "Operation failed." Agent gọi lại y hệt thêm bốn lần, rồi báo khách là đơn hàng không tồn tại. Xử lý lỗi MCP tool đúng cách ngăn được cả hai sai lầm này. Bài viết nói về các loại lỗi mà đề CCAR-F yêu cầu nắm, cấu trúc kết quả có isError, cách phân biệt tra cứu thất bại với kết quả rỗng, và chỗ nào nên tự phục hồi trong hệ thống multi-agent.
Nội dung bám theo task statement 2.2 (structured error response cho MCP tool) thuộc Domain 2, và task statement 5.3 (lan truyền lỗi trong hệ thống multi-agent) thuộc Domain 5. Chủ đề này hay xuất hiện ở Scenario 1, agent hỗ trợ khách hàng truy cập backend qua MCP tool, và Scenario 3, hệ thống research multi-agent.
isError trong kết quả MCP tool nghĩa là gì?
isError: true báo cho client biết tool đã chạy nhưng thao tác bên trong thất bại. Đặc tả MCP gọi đây là tool execution error, gồm lỗi API, lỗi validate input và lỗi business logic. Chúng nằm trong một result bình thường, và đặc tả nói client SHOULD chuyển chúng cho model để model tự sửa.
Đặc tả tách loại này khỏi protocol error: tool không tồn tại, request sai cấu trúc, lỗi server. Protocol error trả về dưới dạng JSON-RPC error chuẩn có code, và model ít khi tự sửa được.
Đây là một tool execution error khi tra cứu đơn hàng bị timeout. MCP quy định content và isError. Phần JSON bên trong text block là quy ước của riêng bạn:
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"content": [
{
"type": "text",
"text": "{\"errorCategory\": \"transient\", \"isRetryable\": true, \"description\": \"Order service timed out after 5s. A retry in a few seconds may succeed.\", \"attempted\": {\"order_id\": \"ORD-99172\"}}"
}
],
"isError": true
}
}
⚠ Bản đặc tả MCP mới nhất (2026-07-28) thêm trường "resultType": "complete" vào kết quả. Exam guide không nhắc tới trường này, và cách dùng isError ở hai phiên bản là như nhau.
Khi ứng dụng chuyển kết quả này sang Claude Messages API, cờ đổi tên thành is_error trên block tool_result. Nhớ giữ cờ này, nếu không Claude sẽ đọc lỗi như output bình thường.
Xử lý lỗi MCP tool: transient, validation, business, permission
Exam guide liệt kê bốn loại lỗi, mỗi loại cần một bước xử lý khác nhau. Timeout có thể hết khi thử lại; lỗi quyền thì thử bao nhiêu lần cũng không hết.
| Loại lỗi | Ví dụ | Gọi lại y hệt? | Agent làm gì |
|---|---|---|---|
| Transient | Dịch vụ đơn hàng timeout, 503 | Có, số lần giới hạn | Retry tại chỗ, rồi báo lên kèm những gì đã thử |
| Validation | date: "last Tuesday-ish" | Không gọi lại nguyên xi | Sửa input hoặc hỏi người dùng, rồi gọi lại |
| Business rule | Yêu cầu hoàn tiền sau 45 ngày, chính sách cho 30 ngày | Không | Giải thích quy định cho khách và đưa phương án được phép |
| Permission | Tài khoản không được đọc dữ liệu billing | Không | Dừng gọi; escalate hoặc báo thiếu quyền |
| Kết quả rỗng hợp lệ | Tìm kiếm chạy xong, không đơn nào khớp | Không phải lỗi | Báo "không có kết quả" như một phát hiện |
Một chi tiết nhỏ: phần kỹ năng trong guide liệt kê giá trị errorCategory là transient, validation và permission, còn lỗi nghiệp vụ thì xử lý bằng cờ retriable: false kèm lời giải thích thân thiện cho khách. Thêm category business vẫn ổn. Đề kiểm tra hành vi, không kiểm tra enum.
Vì sao "Operation failed" là cái bẫy
Thông báo chung chung khiến timeout và ngày sai định dạng trông giống hệt nhau, nên agent hoặc retry cả hai, hoặc bỏ cả hai. Exam guide nói error response đồng nhất khiến agent không ra được quyết định phục hồi phù hợp. Giữ cấu trúc response thống nhất, còn nội dung phải cụ thể.
Cách trả structured error response từ tool
Trả về errorCategory, boolean isRetryable và mô tả dễ đọc cho người, đúng ba trường exam guide nêu tên. Với lỗi nghiệp vụ, thêm câu giải thích để agent nói lại với khách. Một hàm helper nhỏ giúp mọi tool trả lỗi giống nhau:
import json
def tool_error(category, retryable, description, **details):
payload = {"errorCategory": category, "isRetryable": retryable,
"description": description, **details}
return {"content": [{"type": "text", "text": json.dumps(payload)}],
"isError": True}
refund_refused = tool_error(
"business", False,
"Refund window closed: order delivered 2026-09-01, policy allows 30 days.",
customer_message="This order is outside our 30-day refund window, "
"but it is still eligible for store credit.",
)
isRetryable: false nghĩa là "đừng lặp lại đúng lời gọi này". Gọi mới sau khi sửa input thì vẫn được. Bản thân cờ này không chặn được gì; prompt hoặc code của bạn phải tuân theo nó.
Tra cứu thất bại và kết quả rỗng: vì sao phải tách riêng
Truy vấn đã chạy và không khớp gì là một câu trả lời thành công: "khách này không có đơn nào trong tháng Ba". Truy vấn chưa tới được database thì không cho biết điều gì. Nếu cả hai cùng trả về danh sách rỗng với trạng thái thành công, agent sẽ tự tin khẳng định một thứ không tồn tại trong khi chưa ai kiểm tra.
Exam guide nhắc sự khác biệt này hai lần, ở 2.2 và ở 5.3. Kết quả rỗng hợp lệ thì trả về thành công với danh sách rỗng. Lỗi truy cập thì trả isError: true kèm quyết định retry. Đừng bao giờ bắt timeout rồi trả [] như thành công. Task 5.3 gọi đó là giấu lỗi (silent suppression), một anti-pattern.
Subagent tự phục hồi hay đẩy lỗi lên coordinator
Subagent nên tự retry lỗi transient trong giới hạn cho phép, và chỉ đẩy lên những lỗi nó không tự xử lý được. Khi đẩy lên, nó kèm loại lỗi, những gì đã thử, kết quả một phần và phương án thay thế, để coordinator quyết định bước tiếp theo.
flowchart TD
A[Tool returns isError true] --> B{errorCategory}
B -- transient --> C{Retries left?}
C -- yes --> D[Retry locally]
C -- no --> E[Propagate: type, attempts, partial results, alternatives]
B -- validation --> F[Fix input or ask user]
B -- business --> G[Explain rule to customer]
B -- permission --> E
E --> H[Coordinator: re-delegate, use another source, or annotate the gap]
Trong Agent SDK, coordinator chỉ thấy báo cáo cuối của subagent, không thấy các lời gọi tool trung gian. Một trạng thái trống trơn như "search unavailable" sẽ che mất context mà coordinator cần.
Sai lầm ngược lại là dừng cả workflow chỉ vì một subagent lỗi, và đó cũng là anti-pattern trong 5.3. Giữ lại phần đã thành công, và cho bản tổng hợp ghi rõ mảng nào còn thiếu vì nguồn không truy cập được.
Thử sức
Một coordinator research giao cho bốn subagent tìm ở bốn kho dữ liệu khác nhau. Nguồn của subagent tìm bằng sáng chế trả HTTP 503 cả ba lần thử. Ba subagent còn lại hoàn tất. Subagent bằng sáng chế nên trả về gì?
A) Danh sách kết quả rỗng với trạng thái thành công, để coordinator hoàn thành báo cáo.
B) Structured error với loại lỗi transient, truy vấn đã chạy, ba lần đã thử, kết quả một phần nếu có và một nguồn thay thế nên thử.
C) Thông báo "patent search unavailable" để coordinator biết mà bỏ qua.
D) Tín hiệu yêu cầu huỷ lượt chạy, vì báo cáo sẽ không đầy đủ.
Đáp án: B. Subagent đã thử phục hồi tại chỗ. Giờ nó đưa cho coordinator đủ thông tin để chọn bước tiếp.
- A là giấu lỗi. Báo cáo sẽ khẳng định không có bằng sáng chế liên quan trong khi kho đó chưa hề được tìm.
- C là trạng thái chung chung. Coordinator không phân biệt được sự cố ngắn với API key bị thu hồi, cũng không biết đã thử những gì.
- D vứt bỏ ba lượt tìm thành công vì một lỗi, đúng anti-pattern còn lại mà task 5.3 nêu.
Ghi nhớ
isError: trueđánh dấu tool execution error bên trong một result bình thường; protocol error dùng JSON-RPCerror.- Phân loại mọi lỗi thành transient, validation, business hoặc permission, và trả về
errorCategory,isRetryablecùng mô tả dễ đọc. - Từ chối theo nghiệp vụ là không retry được, và nên kèm lời giải thích để agent nói lại với khách.
- Kết quả rỗng từ truy vấn thành công là một phát hiện. Truy vấn thất bại là điều chưa biết. Đừng để hai thứ trông giống nhau.
- Subagent tự retry lỗi transient, phần còn lại đẩy lên kèm số lần thử, kết quả một phần và phương án thay thế.
Câu hỏi thường gặp
JSON-RPC error là protocol error, ví dụ tool không tồn tại hoặc request sai cấu trúc. isError: true nằm trong một result thành công, nghĩa là tool đã chạy nhưng thao tác thất bại. Đặc tả khuyên client chuyển tool execution error cho model để nó tự sửa.
Không. MCP chỉ định nghĩa isError và các block content. Các trường như errorCategory và isRetryable là quy ước bạn đặt trong payload, và đó là tên mà exam guide dùng.
Chỉ với lỗi transient, và chỉ trong số lần giới hạn. Lỗi validation cần sửa input, lỗi nghiệp vụ cần giải thích cho người dùng, còn lỗi permission cần được cấp quyền, retry không giải quyết được.
Đọc thêm: cách viết tool description Claude dùng được, MCP tools, resources và prompts khác nhau thế nào và khi nào agent nên chuyển cho người thật. Sau đó luyện câu hỏi Domain 2 về thiết kế tool cho đến khi bạn quyết định retry hay không thật nhanh.