AI agent cần gì mà REST API không cho được
Trang này có một REST API, và một lập trình viên tích hợp nó sẽ không gặp vấn đề gì. Một agent thì không: nó không thể mở tài liệu tham khảo, chọn xem muốn dùng endpoint nào trong ba endpoint, rồi tự viết tay một yêu cầu với đúng chuỗi truy vấn. Nó hỏi một máy chủ xem máy chủ làm được gì, nhận về các schema mà máy có thể đọc được, và gọi một trong số đó.
Vì vậy, mọi thứ dịch vụ này làm đều được phơi bày thêm một lần nữa, dưới dạng công cụ. Sáu công cụ tất cả, và không có trạng thái nào cần quản lý giữa các lượt gọi:
| Công cụ | Dùng để làm gì |
|---|---|
create_inbox | Nghĩ ra một địa chỉ mới mà agent có thể đưa ra ngay lập tức. Không có gì được giữ trước ở phía máy chủ, nên việc này không thể thất bại. Nhận một prefix tùy chọn, dễ đọc; một hậu tố ngẫu nhiên giữ cho nó luôn duy nhất. |
list_domains | Các tên miền công khai mà ai cũng có thể dùng — hữu ích khi một biểu mẫu vừa từ chối một trong số đó. |
list_messages | Mọi thứ đang chờ ở một địa chỉ, mới nhất trước. Trả về ngay lập tức, kể cả khi không có gì cả. |
read_message | Một thư đầy đủ: người gửi, tiêu đề, văn bản thuần, HTML, tệp đính kèm. Đây là nơi có mã xác nhận hoặc liên kết đăng nhập. |
wait_for_message | Chờ cho đến khi có gì đó đến, rồi trả về đầy đủ. Là công cụ cần gọi ngay khi một biểu mẫu vừa được gửi đi. |
delete_message | Xóa một thư ngay bây giờ thay vì chờ 5 ngày để nó tự hết hạn. Có tính idempotent, nên một agent gọi lại cũng không tốn gì thêm. |
Máy chủ cũng trả lời initialize bằng một đoạn hướng dẫn ngắn, mà phần lớn client đưa thẳng cho mô hình. Nhờ vậy, một agent đã biết sẵn dịch vụ này dùng để làm gì và điều duy nhất cần lưu ý thực sự là gì, mà không ai phải viết điều đó vào một prompt.
Kết nối một client MCP chỉ trong một dòng
Endpoint chỉ là một URL, và không có gì cần đăng ký trên các tên miền công khai cả. Mọi client MCP đều nhận cùng một kiểu cấu hình — Claude Desktop, Claude Code, Cursor, Continue, OpenAI Agents SDK và bất kỳ thứ gì khác nói được giao thức này:
{"mcpServers":{"grabmail":{"url":"https://grabmail.io/mcp"}}}Giao vận là Streamable HTTP: một POST mang theo JSON-RPC 2.0, một phản hồi JSON, không giữ mở luồng nào cả. Vì vậy bạn có thể kiểm tra toàn bộ việc này từ một terminal trước khi có bất kỳ agent nào tham gia:
curl -sX POST https://grabmail.io/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Những client tự tìm máy chủ trước khi hỏi con người sẽ thấy /.well-known/mcp.json trên tên miền, tệp này nêu rõ cùng một endpoint và giao vận của nó.
Toàn bộ quy trình đăng ký, trong bốn lượt gọi công cụ
Đây là trình tự mà hầu như agent nào cũng cần, và chỉ có vậy thôi:
- Gọi
create_inbox. Kết quả trả về là một địa chỉ, một bí danh, tên miền, và một ghi chú cho agent biết nên đưa ra cái nào trong hai cái đó. Chưa có gì được tạo ra cả — hộp thư chỉ bắt đầu tồn tại khi thư đầu tiên đến. - Điền bí danh vào biểu mẫu. Dịch vụ đang được đăng ký nhận được một địa chỉ hoạt động được, chuyển thư đến hộp thư nhưng không thể dùng để đọc hộp thư đó.
- Gọi
wait_for_messagevới địa chỉ đó. Ngay sau khi gửi biểu mẫu, không phải theo một hẹn giờ. Nó chờ; nó không thăm dò trong một vòng lặp mà agent phải tự viết. - Đọc mã từ trong thư. Toàn bộ nội dung đã được trả về cùng với lượt chờ đó, nên thường không cần một lượt gọi thứ hai nào nữa —
read_messagechỉ cần khi có thứ gì đó đã đến từ trước.
Vì sao wait_for_message trả về trước khi thư kịp đến
Đây là công cụ khiến một agent thực sự dùng được, và cũng là công cụ có hành vi khiến người ta bất ngờ nhất, nên đáng dành một phút để nói rõ. Một lượt gọi trông như thế này:
curl -sX POST https://grabmail.io/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":
{"name":"wait_for_message",
"arguments":{"address":"demo.5kuqarzuch@grabmail.io",
"subject_contains":"code"}}}'Nó chờ tối đa 25 giây. Nếu đến lúc đó vẫn chưa có gì đến, nó không báo lỗi — nó trả về một câu trả lời rõ ràng và yêu cầu được gọi lại:
{
"timed_out": true,
"waited_seconds": 25,
"message": null,
"note": "Nothing arrived yet. Call wait_for_message again ..."
}- Vì sao phải giới hạn thời gian chờ
- Mỗi giây chờ là một worker trên máy chủ không làm gì ngoài việc ngủ chờ, và số lượng worker đó là cố định. Một lượt chờ có thể kéo dài năm phút sẽ là một agent giữ mất một chỗ mà cả trăm agent khác đang cần. 25 giây cũng nằm gọn trong thời gian timeout mặc định của mọi client, nên lượt gọi sẽ tự trả về thay vì bị client bỏ cuộc giữa chừng.
- Chỉ 8 lượt chờ cùng lúc
- Vượt quá mức đó, công cụ sẽ trả lời ngay lập tức bằng
timed_outkèm một ghi chú giải thích. Được báo quay lại sau vẫn tốt hơn là bị xếp hàng chờ sau bảy agent khác mà không có cách nào biết được điều đó. - Bộ lọc, để thư không liên quan không kết thúc lượt chờ
from_containsvàsubject_containskhiến lượt chờ bỏ qua mọi thứ khác đến trong lúc đó.since_idlà tham số cần truyền khi hộp thư đã có sẵn thứ gì đó từ trước: đưa cho nó id mới nhất đã thấy, và chỉ thư thực sự mới mới thỏa mãn được lượt gọi.
Đưa ra bí danh, thăm dò địa chỉ
Mỗi hộp thư ở đây có một địa chỉ thứ hai, dài mười hai ký tự, chuyển thư vào cùng hộp thư đó nhưng không thể dùng để đọc nó. Sự khác biệt này quan trọng với một agent hơn hẳn so với một con người, vì một agent sẽ vô tư dán bất cứ thứ gì nó được đưa vào bất cứ ô nào nó tìm thấy.
Vì vậy create_inbox không đơn giản trả về một địa chỉ rồi phó mặc. Nó trả về cả hai, cùng với một next_step nói rõ cái nào là cái nào — agent tự đọc kết quả công cụ của chính mình, nên chỉ dẫn đến đúng nơi cần đến, thay vì nằm trong một trang tài liệu mà không ai trong vòng lặp đó đọc được.
Nên viết gì trong phần chỉ dẫn của agent
Bản thân các công cụ đã tự mô tả đủ rõ để một mô hình đủ năng lực làm đúng việc này mà không cần nhắc thêm. Năm dòng sau đây biến điều đó thành chắc chắn, thay vì chỉ là khả năng cao:
- Một địa chỉ cho mỗi lượt đăng ký. Không dùng lại một địa chỉ ở khắp mọi nơi: một hộp thư chứa thư của sáu dịch vụ là sáu mã xác nhận mà agent phải phân biệt, và chỉ cần một lần rò rỉ là lộ hết cả sáu.
- Đưa ra bí danh, không bao giờ đưa địa chỉ. Đáng nói rõ ra như vậy, dù kết quả của công cụ cũng đã nói điều đó.
- Gọi
wait_for_messagengay sau khi gửi biểu mẫu, và gọi lại khi gặptimed_outthay vì coi đó là một lỗi. Hai hoặc ba lần là bình thường. - Truyền
since_idkhi hộp thư không còn mới, nếu không một thư cũ sẽ thỏa mãn lượt chờ và agent sẽ đọc phải một mã đã hết hạn từ một giờ trước. - Xóa thư ngay khi mã đã được dùng. Không bắt buộc — mọi thứ đằng nào cũng biến mất sau 5 ngày — nhưng việc này đóng khung thời gian rủi ro sớm hơn, và chỉ tốn một lượt gọi idempotent.
Viết thành một khối chỉ dẫn, nó dài chỉ khoảng chừng này:
Khi bạn cần một địa chỉ email, hãy gọi create_inbox và đưa ra ALIAS mà nó
trả về, không bao giờ đưa địa chỉ. Ngay sau khi gửi biểu mẫu, hãy gọi
wait_for_message với địa chỉ đó. Nếu nó trả lời timed_out, hãy gọi lại —
đó là chuyện bình thường và không có gì bị mất. Truyền since_id nếu hộp thư
đã có sẵn thư từ trước. Xóa thư ngay khi mã đã được dùng.Những giới hạn cần biết trước khi bạn xây dựng trên đó
Tất cả các giới hạn này đều được công bố rõ ràng chứ không phải để bạn tự mò ra, và không giới hạn nào có gói trả phí để nâng lên cả:
| Giới hạn | Giá trị | Ý nghĩa đối với một agent |
|---|---|---|
| Một lượt chờ | 25 giây | Sau đó là timed_out. Gọi lại; đừng coi đó là một lỗi. |
| Số lượt chờ cùng lúc | 8 | Vượt quá mức đó, công cụ trả về ngay lập tức và báo rõ điều đó. Hãy chuyển sang dùng list_messages. |
| Lượt đọc | Một lần mỗi giây, cho mỗi địa chỉ | Cao hơn nhiều so với những gì một vòng lặp gọi công cụ cần. Một lượt chờ chặn chỉ là một yêu cầu, không phải sáu mươi. |
| Kích thước thư | 5 MB | Bị từ chối ngay trong lúc trao đổi SMTP, nên bên gửi được báo ngay, thay vì để agent chờ một thứ sẽ không bao giờ đến. |
| Thời gian lưu trữ | 5 ngày | Một giới hạn cứng do một tác vụ tự động thực thi. Bất cứ điều gì agent cần giữ lại, nó phải tự ghi lại. |
Không có endpoint để gửi thư và cũng không có công cụ nào cho việc đó. Dịch vụ này chỉ nhận, và chính điều đó giữ cho một hộp thư không cần xác thực không biến thành một trạm chuyển tiếp spam — nên một agent cần trả lời một con người thì cần một hộp thư thật ở nơi khác.
Khi một biểu mẫu từ chối các tên miền công khai
Rất nhiều dịch vụ duy trì danh sách các tên miền email dùng một lần, và ba tên miền công khai ở đây đều nằm trong đó. Một agent gặp phải điều này dưới dạng một biểu mẫu từ chối địa chỉ vừa được đưa ra, hoặc tệ hơn, chấp nhận địa chỉ đó rồi không bao giờ gửi gì cả.
Cách khắc phục bền vững là một tên miền do bạn sở hữu. Một bản ghi MX biến mọi địa chỉ trên tên miền đó thành một hộp thư ở đây, nó không nằm trong danh sách của bất kỳ ai vì không được in ra ở bất kỳ đâu trên trang này, và cùng sáu công cụ đó vẫn hoạt động y nguyên — chỉ riêng create_inbox là không, vì nó chuyên nghĩ ra địa chỉ trên các tên miền công khai. Agent chỉ cần dùng you-pick-it@your-domain và gọi wait_for_message trên địa chỉ đó.
Bản ghi MX cho tên miền của bạn10 smtp.grabmail.io
Hướng dẫn đầy đủ, từng bước, ở đây — bản ghi cần thêm, nó chứng minh điều gì, và những giới hạn thực sự của một hộp thư không có mật khẩu bảo vệ.
Những điều không nên để agent làm với dịch vụ này
Đây là phần thành thật, và cũng là phần giúp bạn đỡ mất nguyên một buổi chiều:
- Không thứ gì bạn sẽ cần khôi phục lại. Bất cứ thứ gì liên quan đến tiền, danh tính hoặc công việc. Hộp thư sẽ trống trơn trở lại sau 5 ngày và ai biết địa chỉ cũng đọc được, nên một email đặt lại mật khẩu gửi đến đó vào năm sau sẽ chẳng đến được với ai — hoặc đến tay một người khác.
- Không dùng làm yếu tố xác thực thứ hai. Một hộp thư không có mật khẩu bảo vệ thì không phải là một yếu tố xác thực.
- Không dùng cho bất cứ điều gì riêng tư. Không phải vì chúng tôi đọc nó, mà vì địa chỉ là bí mật duy nhất đang được dùng, và rất có thể một agent đã ghi nó vào một log, một bản ghi hội thoại, hoặc một commit message.
- Không dùng cho số lượng lớn. Một agent mở hàng trăm tài khoản chính là hành vi mà mọi danh sách chặn sinh ra để đối phó, và đó là cách nhanh nhất khiến các tên miền công khai bị từ chối đối với tất cả mọi người khác.
Dùng đúng với bản chất của nó — bước xác nhận đứng giữa một agent và việc nó thực sự được giao — nó loại bỏ đúng bước vẫn luôn cản trở agent một cách chắc chắn nhất.
Câu hỏi
Tôi có cần API key hay tài khoản không?
Không. Các tên miền công khai, các công cụ, và một tên miền riêng của bạn đều miễn phí và không cần xác thực. Trường hợp duy nhất cần đến khóa là một tên miền đã bị đóng theo yêu cầu, khi đó cần có tiêu đề Authorization.
Dịch vụ này hoạt động được với những client nào?
Bất kỳ client nào nói được Model Context Protocol — Claude Desktop, Claude Code, Cursor, Continue, OpenAI Agents SDK và nhiều client khác. Giao vận là Streamable HTTP, mặc định của các client hiện nay, và ba phiên bản giao thức đều được chấp nhận nên một client cũ hơn vẫn kết nối được.
Vì sao wait_for_message trả về timed_out?
Vì mỗi lượt chờ bị giới hạn tối đa 25 giây, một cách có chủ đích. Đó không phải lỗi và không có gì bị mất cả: chỉ cần gọi lại. Thư thường mất nhiều thời gian hơn những gì trang web hứa hẹn, và chờ hai hoặc ba lần liên tiếp là chuyện bình thường trong một lượt đăng ký.
Agent có thể dùng tên miền riêng của tôi thay thế không?
Có, và không gì thay đổi ngoài địa chỉ. Trỏ một bản ghi MX về smtp.grabmail.io và mọi địa chỉ trên tên miền đó đều đọc được qua cùng những công cụ ấy. Chỉ riêng create_inbox là giới hạn ở tên miền công khai, vì đó là công cụ tự nghĩ ra một cái tên giúp bạn.
Agent có thể gửi email qua dịch vụ này không?
Không. Không có công cụ gửi thư và cũng không có endpoint để gửi, đây là chủ đích: một dịch vụ không cần xác thực mà lại gửi được thư sẽ trở thành một trạm chuyển tiếp spam chỉ trong một ngày. SPF của chúng tôi là v=spf1 -all và DMARC là p=reject, nên bất cứ thứ gì tự nhận là gửi từ một địa chỉ ở đây đều là giả mạo.
Hộp thư này có riêng tư không?
Không, và đây là điều duy nhất cần nói rõ ràng với một agent. Trên một tên miền công khai, bất kỳ ai biết hoặc đoán được địa chỉ đều đọc được nó. Hãy dùng một địa chỉ khó đoán, đưa ra bí danh thay vì địa chỉ, và đừng bao giờ để bất cứ thứ gì riêng tư đến gần nó.
Hai agent có thể cùng chờ trên một địa chỉ cùng lúc không?
Được, và cả hai sẽ cùng nhận được thư khi nó đến. Cái bị giới hạn là số lượt chờ diễn ra cùng lúc trên toàn dịch vụ, 8; vượt quá mức đó, công cụ sẽ trả lời ngay lập tức và báo rõ điều đó, còn list_messages thì vẫn hoạt động bình thường.
Thư lưu lại được bao lâu?
5 ngày kể từ khi đến, dù đã đọc hay chưa, và không có tùy chọn nào kéo dài thêm. Mỗi thư đều mang theo expires_at, nên một agent không bao giờ phải tự tính ra ngày đó.
Cách này khác gì so với gọi REST API từ một script?
Với một script thì không khác gì cả, và REST API mới là lựa chọn phù hợp hơn — hướng dẫn kiểm thử các luồng xác minh đã nói về hình dạng đó, cả thời hạn lẫn các hàm hỗ trợ. MCP dành cho trường hợp không ai viết vòng lặp đó cả: chính mô hình quyết định mở một hộp thư, và cần các công cụ có thể được khám phá thay vì chỉ được ghi trong tài liệu.


