# Send & Receive Emails with Mastra (/integrations/frameworks/mastra)

<!-- agent-signals: reading_time_min: 5 · est_tokens: 1827 · updated: 2026-09-06 -->
Related: [Send & Receive Emails with Claude Cowork](/integrations/frameworks/claude-cowork.md), [Send & Receive Emails with Claude Code](/integrations/frameworks/claude-code.md), [Send & Receive Emails with OpenAI Codex](/integrations/frameworks/codex.md), [Send & Receive Emails with OpenClaw](/integrations/frameworks/openclaw.md), [Send & Receive Emails with Grok](/integrations/frameworks/grok.md), [Send & Receive Emails with Manus](/integrations/frameworks/manus.md)



# Wire a Mastra agent to AgentMail over MCP

Connect Mastra's `MCPClient` to AgentMail's hosted MCP server so a Mastra agent can create inboxes, send email, and read and reply to threads. Use this in a Mastra app or a plain Node.js project when the agent needs its own `@agentmail.to` address.

## Do this

Install the packages and set both keys. `AGENTMAIL_API_KEY` authenticates the email tools, `OPENAI_API_KEY` authenticates the model calls through Mastra's model router.

```bash
npm install @mastra/core @mastra/mcp
npm install -D tsx
npm pkg set type=module
export AGENTMAIL_API_KEY="<API_KEY>"
export OPENAI_API_KEY="<OPENAI_API_KEY>"
```

```typescript
// mcp.ts
import { MCPClient } from "@mastra/mcp";

export const mcp = new MCPClient({
  servers: {
    agentmail: {
      url: new URL("https://mcp.agentmail.to/mcp"),
      requestInit: {
        headers: { "x-api-key": process.env.AGENTMAIL_API_KEY! },
      },
    },
  },
});
```

```typescript
// agent.ts
import { Agent } from "@mastra/core/agent";
import { mcp } from "./mcp";

export const emailAgent = new Agent({
  id: "email-agent",
  name: "Email agent",
  instructions:
    "You are an email agent with your own AgentMail inbox. You can create inboxes, send email, and read and reply to threads.",
  model: "openai/gpt-5.6-sol",
  tools: await mcp.listTools(),
});
```

```typescript
// send.ts
import { emailAgent } from "./agent";

const output = await emailAgent.generate(
  "Create an inbox, then send an email from it to you@example.com with the subject 'Hello from my agent'. Include the inbox address in your answer.",
);
console.log(output.text);
```

Run it with `npx tsx send.ts`. The agent calls `agentmail_create_inbox`, then `agentmail_send_message`, and reports the new address.

## SDK

MCP path, package `@mastra/mcp`:

* Connect: `new MCPClient({ servers: { agentmail: { url, requestInit } } })` with URL `https://mcp.agentmail.to/mcp` and the API key in an `x-api-key` header
* Tools for an agent's `tools` map: `await mcp.listTools()`
* Per-request tools (multi-tenant): `await mcp.listToolsets()` passed as `toolsets` to `generate` or `stream`
* Release a client: `await mcp.disconnect()`

Direct SDK path, package `agentmail` (`npm install agentmail`):

* Client: `new AgentMailClient()` picks up `AGENTMAIL_API_KEY` from the environment
* Send: `agentmail.inboxes.messages.send(inboxId, { to, subject, text })`
* Reply: `agentmail.inboxes.messages.reply(inboxId, messageId, { text })`
* Wrap either call as a Mastra tool with `createTool` from `@mastra/core/tools`. The zod `inputSchema` validates the input, and `execute` receives it as its first parameter.

Full SDK surface and installs: `/integrations/sdks-and-cli`.

## Facts

* Hosted MCP server URL: `https://mcp.agentmail.to/mcp`. Auth is the API key in the `x-api-key` header.
* Packages: `@mastra/core`, `@mastra/mcp`, dev dependency `tsx`, TypeScript SDK `agentmail`.
* `mcp.listTools()` prefixes each tool with the server key from the config, so the `agentmail` key yields `agentmail_list_inboxes`, `agentmail_get_inbox`, `agentmail_create_inbox`, `agentmail_list_threads`, `agentmail_get_thread`, `agentmail_send_message`, `agentmail_reply_to_message`, and more.
* Every MCP tool call runs as the organization that owns the API key. An inbox-scoped key limits the agent to a single inbox.
* Tool discovery is open: `mcp.listTools()` returns the catalog even when `AGENTMAIL_API_KEY` is unset. Auth failures surface at call time as `Forbidden (HTTP 403)`.
* A failed tool call does not end a Mastra run. The error text, `suggestions` included, becomes that call's result so the agent can retry.
* A run ends after 5 steps by default. Pass `maxSteps` in the `generate` options to raise it.
* Each `generate` call starts from a blank conversation. A follow-up script must name the inbox in its prompt.
* Multi-tenant: build an `MCPClient` per request with the tenant's API key, pass `toolsets: await mcp.listToolsets()` to `generate`, then `await mcp.disconnect()` when the request is done.
* The SDK-defined tools on this page return the new message's `messageId` and the `threadId` it belongs to.
* The scripts use top-level `await`, which requires `"type": "module"` in `package.json`.
* Inbound email takes roughly two seconds of transit in each direction.

## Not supported

* Do not skip `mcp.disconnect()` in per-request code. Mastra treats a duplicate live `MCPClient` with the same configuration as a memory leak, so the next construction throws.
* A successful `listTools()` does not prove the API key is valid, because discovery works without credentials.
* `AGENTMAIL_API_KEY` does not authenticate model calls, and `OPENAI_API_KEY` does not authenticate email tools. Both keys are required.
* The scripts do not run as CommonJS.
* A job longer than 5 steps does not finish without raising `maxSteps`.

## Errors

| Error                                                                               | HTTP status                | Cause                                               | Fix                                                                       |
| ----------------------------------------------------------------------------------- | -------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------- |
| `Could not find API key process.env.OPENAI_API_KEY for model id openai/gpt-5.6-sol` | none, thrown by `generate` | `OPENAI_API_KEY` unset                              | Set `OPENAI_API_KEY` in the script's environment                          |
| `Forbidden (HTTP 403)`                                                              | 403                        | `AGENTMAIL_API_KEY` unset or wrong when a tool runs | Set `AGENTMAIL_API_KEY` in the environment the script runs in             |
| 403 on `agentmail_create_inbox` with `suggestions`                                  | 403                        | Requested username is taken                         | Retry with one of the up to three available alternatives in `suggestions` |
| `Inbox limit exceeded`                                                              | 403                        | Plan's inbox limit reached, every username fails    | Follow the upgrade link the error carries                                 |
| `Top-level await is currently not supported with the "cjs" output format`           | none                       | Project runs as CommonJS                            | Run `npm pkg set type=module`                                             |

## Verify

Create `check.ts` with `import { mcp } from "./mcp"; console.log(Object.keys(await mcp.listTools()));` and run `npx tsx check.ts`. Success prints tool names prefixed `agentmail_`. This confirms the connection only, not the key. To confirm the key, run `send.ts` and check that the email arrives from a fresh `@agentmail.to` address.

## Related

* `/integrations/mcp-and-skills` for the hosted server's full catalog and credential options
* `/core/send` for the full send and reply contract behind these tools
* `/advanced/webhooks` to run the agent when mail arrives instead of prompting by hand
* `/advanced/multi-tenant` for provisioning a per-user inbox and a key scoped to it
