MCP setup
Postfleet ships a Model Context Protocol (MCP) server so an agent can send and read mail as
tools. It is thin-over-REST: every tool calls the same /api/v1/ endpoints as your key, so
auth, scoping, and quotas are enforced exactly once.
Configure the server
The server speaks streamable HTTP at /api/mcp and authenticates with a bearer pf_ key. Pass
the key in the Authorization header:
{
"mcpServers": {
"postfleet": {
"url": "https://your-app.example.com/api/mcp",
"headers": { "Authorization": "Bearer pf_..." }
}
}
}
A request without a Bearer token is rejected 401:
{ "error": "missing bearer token",
"hint": "Configure this MCP server with an Authorization: Bearer pf_... header." }
The key's scope and can_read/can_send/mailbox binding apply to tool calls exactly as they
do to the REST API — see Authentication. Note that wait_for_email can hold a request
open up to ~2 minutes, so a deployment must allow a long request duration.
Tools
| Tool | Arguments | Purpose |
|---|---|---|
create_mailbox |
slug?, display_name?, extraction_schema_id? |
Create a mailbox this agent owns. |
send_email |
mailbox_id, to, subject, text |
Send a new email. |
reply_email |
mailbox_id, reply_to_message_id, text |
Reply to a received message (threads automatically). |
list_inbox |
mailbox_id, limit? |
List recent messages, newest first. |
read_email |
message_id |
Read one message in full (cleaned body, sanitization report, classification, extraction). |
wait_for_email |
mailbox_id, from_contains?, subject_contains?, timeout_seconds? |
Block until a matching email arrives, or time out ({timed_out:true}). |
create_draft |
mailbox_id, to?, subject?, text, reply_to_message_id? |
Stage a draft without sending. Returns {id, status:"draft"}. |
list_drafts |
mailbox_id |
List open drafts (status draft, pending_approval, sending). |
send_draft |
id |
Send a staged draft by id. |
read_email never returns the pre-sanitization raw body — only the cleaned, screened content
reaches the agent.
The 202 pending-approval outcome
If a mailbox requires human approval, send_email, reply_email, and send_draft do not
send immediately. They return the queued-draft shape instead of a message id:
{ "draft_id": "d_123...", "status": "pending_approval" }
This is a success, not an error, and it must not be retried. The email is delivered once a
human approves the draft in the dashboard. To follow up, call list_drafts — a pending_approval
entry is still waiting on a human; once approved it is sent and tombstoned (drops off the list).
Distinguish the two success shapes:
{ "id": "...", "thread_id": "..." }→ sent.{ "draft_id": "...", "status": "pending_approval" }→ queued for human approval.
See Drafts & human-in-the-loop for the full approval model.
Errors
Tool errors come back as a short message plus a recovery hint (never a stack trace), for
example an invalid key (401), a scope/capability rejection (403), quota exhaustion (402),
a not-found id (404), a suppressed recipient (422), or a transient provider issue (502,
retry in a few seconds). The underlying status and shapes match the REST API documented
throughout these pages.