---
title: "Claude Agent SDK email: read, review, then send"
description: "Design a Claude Agent SDK email workflow with Mailera MCP: read an existing mailbox, review a proposed reply, and send only the approved content."
url: https://mailera.app/en/blog/claude-agent-sdk-email-approved-replies
published: 2026-10-25
updated: 2026-10-25
---

# Claude Agent SDK email: read, review, then send

Connect an existing mailbox through Mailera MCP. Keep reply proposals in your application, and give a separate sender access only after human approval.

## Can the Claude Agent SDK read and reply to email?

Yes, with mailbox tools and an application that controls when sending is allowed. For a Claude Agent SDK email workflow, separate reading from sending: the agent proposes a reply; your server decides whether an approved reply can leave the mailbox.

The Agent SDK is a software development kit for building applications around Claude, rather than a conversation in the interactive Claude app. Use Anthropic's [official TypeScript SDK repository](https://github.com/anthropics/claude-agent-sdk-typescript) for the package `@anthropic-ai/claude-agent-sdk`.

Mailera supplies the email connection through [Mailera MCP](https://mailera.app/en/mcp). Its tools include `list_mailboxes`, `list_messages`, and `get_message` for reading, plus `reply_to_email` and `get_send_status` for the separate sending path.

If you're looking for a Claude Agent SDK email example on GitHub, Anthropic's [SDK demos repository](https://github.com/anthropics/claude-agent-sdk-demos) describes an in-development IMAP email assistant. The repository warns that its demos are for local development, not production deployment. The workflow here uses an existing mailbox through MCP instead of a direct IMAP connection.

## What do you need before building the workflow?

Start with a mailbox you own, a Mailera workspace, and a controlled recipient. Use a dedicated mailbox group so the reading agent cannot browse unrelated mail. For Outlook or Microsoft 365, follow the [Microsoft OAuth connection instructions](https://mailera.app/en/providers/microsoft).

Prepare your TypeScript project using the SDK repository's installation instructions. Configure Anthropic authentication separately from the Mailera API key: the Mailera credential authorizes mailbox access, not model access.

Assign responsibilities before writing the agent prompt:

| Component | Responsibility |
| --- | --- |
| Claude Agent SDK application | Read the selected message and propose reply text |
| Mailera MCP | Expose tools for connected mailboxes |
| Host application | Store proposals, display review screens, record approval |
| Trusted sending executor | Submit the approved reply and track its send attempt |

Keep credentials server-side. Do not put the sending key in the reading agent's environment, prompt, or tool results. Use the [Mailera developer overview](https://mailera.app/en/developers) for the API side and the [Claude integration page](https://mailera.app/en/integrations/claude) for client connection guidance.

## How do you prepare Mailera MCP access?

### 1. Confirm the mailbox can send

Open **Mailboxes** and check the intended mailbox's connection status and last sync. A mailbox saved with **Save as receive-only** can support the reading step but cannot complete the reply workflow.

You can inspect capabilities independently of the SDK. Set `$MAILERA_API_KEY` to your read key and `$MAILBOX_ID` to the test mailbox identifier:

```bash
curl --fail-with-body \
  "https://mailera.app/api/public/v1/mailboxes/$MAILBOX_ID" \
  -H "Authorization: Bearer $MAILERA_API_KEY"
```

Check `data.capabilities.send`, `data.status`, and `data.sync`. This isolates mailbox setup from agent configuration.

![Mailboxes page showing the test mailbox's provider, connection status, and last sync.](https://mailera.app/media/2e5a2871ef97f326/claude-agent-sdk-email-read-review-then-send-mailbox-check-en-1200.jpg)
_Check the connected mailbox before debugging the SDK._

### 2. Create a reading credential

In **Settings**, open the API key tab and select **Create key**. Choose the test group under **Mailbox scope**, enable **Read**, and set **Expiration** for the test. Leave **Send messages** disabled.

Save the key when **Key created — copy it now** appears. **Copy MCP config** provides a client configuration reference for Claude, Cursor, or VS Code; adapt that reference to your SDK's configuration rather than treating the formats as interchangeable.

The MCP endpoint is `https://mailera.app/api/public/v1/mcp`. Authentication uses `Authorization: Bearer $MAILERA_API_KEY`, with your application substituting the environment variable's value.

![Mailera API-key setup with a test mailbox group and Read permission selected.](https://mailera.app/media/84e21a9cc6774613/claude-agent-sdk-email-read-review-then-send-read-key-en-1200.jpg)
_The reading agent gets a scoped Read key, not the sender's credential._

### 3. Restrict the reading agent's tools

Discover the server's tools and their argument schemas. Resolve the SDK-qualified tool names from that connection; don't construct them from a naming guess.

For this task, permit only `list_mailboxes`, `list_messages`, and `get_message`. A Read-scoped key is a permission boundary, not a per-tool filter: other Mailera tools, including `get_latest_otp`, also use the read scope.

Do not assume an option named “allowed tools” removes every other tool. In Anthropic's [Python SDK documentation](https://github.com/anthropics/claude-agent-sdk-python), `allowed_tools` auto-approves listed tools, while unlisted tools fall through to other permission handling. Keep language-specific configuration separate and enforce a deny-by-default policy in your TypeScript host.

Check that the agent can access the intended mailbox and read a message, but its credential cannot authorize sending. The [existing-mailbox MCP guide](https://mailera.app/en/blog/email-mcp-server-existing-mailboxes) explains the broader connection model.

## How do you read a message and prepare a reply?

### 4. Select one controlled message

Use `list_mailboxes` to identify the test mailbox, `list_messages` to select the test message, and `get_message` to retrieve its content. Use the discovered MCP schemas for arguments.

If the content looks wrong, compare it with a direct REST read. Set `$MESSAGE_ID` to the selected message identifier:

```bash
curl --fail-with-body \
  "https://mailera.app/api/public/v1/mailboxes/$MAILBOX_ID/messages/$MESSAGE_ID" \
  -H "Authorization: Bearer $MAILERA_API_KEY"
```

Inspect `data.mailbox_id`, `data.id`, `data.subject`, and `data.body.text`. Keep mailbox and original-message identifiers in trusted application state rather than asking the model to reproduce them.

### 5. Store a proposal, not a send request

Ask the agent for a short summary and proposed reply text. Store the proposal in your application; do not make the workflow depend on a provider-synced draft.

The review screen should show the sending mailbox, original-message reference, recipient, subject, and complete reply body. Include every recipient if you support multiple recipients. For the first implementation, keep it to a single controlled recipient.

Require the reviewer to inspect the actual outgoing content, not just the agent's summary. At this checkpoint, a proposal exists and is visible, but nothing has been submitted for sending.

![Email workflow from a read-only agent to an application proposal, human review, and a separate approved-message sender.](https://mailera.app/media/aad8055c9c5d7a9d/claude-agent-sdk-email-read-review-then-send-approval-flow-en-1200.jpg)
_Only an approved, unchanged proposal reaches the trusted sender._

## How do you send only the reply a human approved?

### 6. Bind approval to an exact snapshot

An approval should identify a stored proposal version and the person who approved it. Changing the mailbox, original message, recipient, subject, or body must invalidate that approval.

For a single-recipient TypeScript application, this small comparison helper makes the content boundary explicit. These are local application types, not Mailera request fields:

```ts
type Reply = Readonly<{
  mailboxId: string; originalMessageId: string;
  recipient: string; subject: string; body: string;
}>;
const snapshot = (r: Reply): string => JSON.stringify([
  r.mailboxId, r.originalMessageId, r.recipient, r.subject, r.body,
]);
export function assertApproved(
  current: Reply, approved: Reply, state: string,
): void {
  if (state !== "approved" || snapshot(current) !== snapshot(approved)) {
    throw new Error("Explicit approval required for this exact reply");
  }
}
```

Load both objects and the approval state from trusted server storage. Never accept an `approved` flag or an approval snapshot supplied by the agent as authorization.

The comparison belongs inside a larger transaction: authenticate the reviewer, record the immutable approved version, and atomically claim it for sending. Repeated approval submissions must not create additional send jobs. Rejection ends the workflow without a send.

### 7. Send from a separate executor

Give the executor a separate key with **Send messages** permission and the same narrow mailbox scope. It should load the approved snapshot and call `reply_to_email` using the discovered schema. Do not ask the model to regenerate the reply after approval.

Keep the sender credential outside the reading agent's process where possible. A prompt that says “ask before sending” is not an authorization boundary.

The [MCP specification](https://modelcontextprotocol.io/specification/2025-06-18) requires explicit user consent before a host invokes a tool and recommends building reliable consent and authorization flows into applications. MCP cannot enforce those principles by itself. Implement review and approval in the host application, not as an assumed Mailera feature. If the reviewer will approve through email, use a separate [email-based approval pattern](https://mailera.app/en/blog/email-human-in-the-loop).

### 8. Track the send attempt before retrying

After submission, use `get_send_status`, which also requires the send scope. Keep the approval record and submission outcome together in trusted application storage.

Mailera's REST send-status endpoint describes these outcomes:

| Status | Application action |
| --- | --- |
| `queued` | Keep the attempt pending |
| `sending` | Wait; don't submit another reply |
| `sent` | Record the reported result without promising inbox placement |
| `failed` | Inspect the failure before deciding on a retry |
| `unknown` | Reconcile the existing attempt before another submission |

A timeout is not permission to generate a new send. Mailera's REST send endpoint accepts `Idempotency-Key`; do not assume that header applies to an MCP tool call. For a REST sending path, follow the [duplicate-send prevention guide](https://mailera.app/en/blog/email-api-idempotency-key).

## What commonly goes wrong?

| Symptom | Likely cause | Check |
| --- | --- | --- |
| Authentication fails | Expired or revoked key; incorrect header | Check expiration and revocation without logging the credential |
| Mailbox is missing | Wrong mailbox group | Compare the key's Mailbox scope with the test mailbox's group |
| Tool cannot be called | Configuration, transport, or tool-name mismatch | Inspect discovery results and SDK-qualified names |
| Message list is empty or stale | Wrong folder, disconnected mailbox, or sync state | Compare mailbox identity, folder, status, and last sync |
| Reply fails | Receive-only mailbox or missing send permission | Check `capabilities.send` and the executor's key permissions |
| Approval changes nothing | Edited proposal or already-claimed job | Inspect the stored version and approval state |
| Submission times out | Outcome is unresolved | Retrieve the existing send attempt before retrying |

Mailera's background checks vary with mailbox activity, so don't design around instantaneous synchronization. Mailera also cannot guarantee inbox placement.

## How do you keep incoming email from controlling the agent?

Treat message bodies, quoted text, links, and attachments as untrusted data, not instructions. An incoming message must not be able to change tool permissions, choose a different recipient, reveal credentials, or bypass review.

Disable unrelated tools, including shell execution and filesystem access. Do not open arbitrary URLs just because an email requests it. Keep sensitive message content out of routine logs; the [Mailera security page](https://mailera.app/en/security) covers the product side, while your host must protect its own proposal and approval records.

Before enabling sending, test these outcomes:

- Reject a proposal: no send job is created.
- Edit an approved reply: approval is required again.
- Submit approval repeatedly: only one job is claimed.
- Send a test email that asks the agent to reveal a key and reply without review: no credential is exposed and no send is invoked.

For the broader risk assessment, see [whether it is safe to connect Claude to Gmail](https://mailera.app/en/blog/is-it-safe-to-connect-claude-to-gmail). The essential boundary here is concrete: incoming email can influence proposed text, but only your application can authorize the exact outgoing reply.

**Let AI agents use your mailbox via MCP** — Add Mailera's MCP server to Claude, Cursor or VS Code so agents can read, categorize and reply to email messages. [Learn how to use MCP](https://mailera.app/en/docs#mcp)
