Email MCP Server: Give Any MCP Agent an Inbox

Updated 11 min read

How to build an email MCP server: the tool surface for sending, replying, threading and receiving mail, with working TypeScript and API examples.

John Joubert

John Joubert

Founder, Robotomail

Email MCP Server: Give Any MCP Agent an Inbox
Table of contents

An email MCP server exposes mailbox operations as tools that compatible agents can discover and call. Robotomail now provides a hosted MCP server at https://robotomail.com/mcp, so you can connect an existing agent to read, send, and reply to email without building your own adapter.

This guide starts with Robotomail's hosted connection, then explains how an email MCP server works and when a custom adapter is useful. It also covers inbound email arriving while the agent is not running.

Connect to Robotomail's hosted MCP server

Create a Robotomail account and verify your email, then open Connections and choose your agent. Configure Streamable HTTP at https://robotomail.com/mcp, sign in, and approve the requested permissions through OAuth. You do not need to paste a Robotomail API key into the MCP client. Follow the MCP setup guide for the instructions for your client.

The hosted server provides these tools:

Tool What it does
list_mailboxes Lists your mailbox IDs, addresses, and sender display names
search_messages Finds messages by text, direction, or date and returns summaries
read_message Reads a message's plain-text body
send_email Sends a plain-text email from a selected mailbox
reply_to_email Replies to the sender while preserving the conversation thread
set_mailbox_display_name Sets the friendly From name for future sends and replies

Your existing mailbox and plan limits apply. The hosted tools currently handle plain-text email without attachments or reply-all. Use the REST API or official SDKs for mailbox creation, HTML email, attachments, webhooks, and domain management. A connection does not schedule inbox checks or automatically wake your agent.

The ChatGPT directory listing is still coming soon. Custom MCP setup depends on the features and permissions available in your account or workspace; see the current connection instructions.

What an email MCP server actually is

MCP is a transport and discovery protocol. It lets a host application (Claude Desktop, an IDE agent, your own orchestrator) list tools from a server, call them with JSON arguments, and get structured results back. It does not know anything about email.

So an email MCP server is a thin adapter with three jobs:

  1. Declare tools with schemas the model can reason about.
  2. Translate tool calls into HTTP calls against a real mail backend.
  3. Return results that are small enough to not blow up the context window.

The mail backend is where the actual difficulty lives: MX records, DKIM signing, bounce handling, threading headers, spam filtering, attachment storage. An MCP server that speaks SMTP directly can send, badly, and cannot receive at all. You want a mailbox API behind it.

Designing a custom MCP tool surface

If you need a custom adapter, keep its tool surface narrow. Every extra tool consumes context and adds another choice for the model. The table below is an example design for a server you build yourself; these are not the tool names or capabilities of Robotomail's hosted server listed above.

Tool Arguments Returns
create_mailbox address mailbox id, full address
send_email mailbox_id, to[], subject, body_text message id, thread id
reply_to_email mailbox_id, in_reply_to, body_text message id, thread id
list_new_messages mailbox_id, limit message ids, from, subject, snippet
get_message message_id full body, headers, attachment list
get_thread thread_id ordered message summaries
download_attachment attachment_id file reference or base64

Two design notes that matter more than they look.

Separate reply_to_email from send_email. Models are bad at remembering to pass an in_reply_to value to a generic send tool. If replying is its own tool with a required inbound message id, the correct threading behavior becomes the default rather than something the model has to remember. The mail API sets In-Reply-To and References from that id, which is what keeps the conversation in one thread in Gmail and Outlook. More on why that matters in email threading.

Never return full message bodies from a list tool. Return ids and a 200 character snippet. Let the model call get_message on the one it cares about. A list tool that dumps ten HTML emails into context will eat your whole budget and produce worse answers.

Tool descriptions are prompt engineering

The description string is the only instruction the model gets. Write it like an operator, not a doc generator:

send_email: Send a new email from the agent's own mailbox. Use only to
start a NEW conversation. To answer an email the agent received, use
reply_to_email instead so the message stays in the same thread.
Do not use for bulk or unsolicited messages.

That last line does real work. Constraints in tool descriptions reduce the number of times an agent decides to email fifty people.

Optional: building a custom server on Robotomail

The hosted connection above requires no server code. If you need your own tool policies or additional API operations, you can build a custom adapter using the REST API or an official Robotomail SDK. The example below uses direct HTTP calls. Provision a mailbox first, then wire the tools.

1. Create the mailbox. From the CLI:

npx @robotomail/cli mailbox create shopping-agent

Or over HTTP:

curl -X POST https://api.robotomail.com/v1/mailboxes \
  -H "Authorization: Bearer rm_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"address": "shopping-agent"}'

You get a real, deliverable address on our platform domain. Point the mailbox at your own domain later if you want the agent to look like part of your product.

2. Define the tools. Using the MCP TypeScript SDK to implement the custom server:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "email", version: "1.0.0" });
const API_KEY = process.env.ROBOTOMAIL_API_KEY!;

server.tool(
  "send_email",
  "Send a new email from the agent's mailbox. Use only to start a new " +
    "conversation; use reply_to_email to answer an existing one.",
  {
    mailbox_id: z.string(),
    to: z.array(z.string().email()).max(5),
    subject: z.string(),
    body_text: z.string(),
  },
  async ({ mailbox_id, to, subject, body_text }) => {
    const res = await fetch(
      `https://api.robotomail.com/v1/mailboxes/${mailbox_id}/messages`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ to, subject, bodyText: body_text }),
      }
    );

    if (!res.ok) {
      return {
        isError: true,
        content: [{ type: "text", text: `Send failed: ${res.status} ${await res.text()}` }],
      };
    }

    const msg = await res.json();
    return { content: [{ type: "text", text: JSON.stringify(msg) }] };
  }
);

3. Add the reply tool. Same call, plus the inbound message id:

server.tool(
  "reply_to_email",
  "Reply to an email the agent received. Keeps the message in the same thread.",
  {
    mailbox_id: z.string(),
    in_reply_to: z.string().describe("message_id from the received email"),
    body_text: z.string(),
  },
  async ({ mailbox_id, in_reply_to, body_text }) => {
    const res = await fetch(
      `https://api.robotomail.com/v1/mailboxes/${mailbox_id}/messages`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ inReplyTo: in_reply_to, bodyText: body_text }),
      }
    );
    return { content: [{ type: "text", text: await res.text() }] };
  }
);

await server.connect(new StdioServerTransport());

4. Scope the credentials. One API key per agent, not one shared key across your whole fleet. When an agent misbehaves you revoke its key and nothing else breaks. Key handling is covered in authentication.

Receiving email: MCP pulls, mail pushes

This is the part most email MCP servers get wrong. MCP is request/response. The host calls a tool, your server answers. There is no reliable way for an MCP server to wake up an idle agent because a vendor replied twenty minutes later.

Email is push. Inbound mail arrives at a webhook whenever it arrives:

{
  "event": "message.received",
  "data": {
    "message_id": "...", "mailbox_id": "...", "mailbox_address": "shopping-agent@robotomail.co",
    "from": "vendor@example.com", "subject": "Re: Order #A-4521",
    "body_text": "...", "thread_id": "...", "received_at": "2026-04-17T10:00:00.000Z"
  }
}

Two architectures work, and they are not exclusive:

Store and expose. Your webhook handler writes inbound messages to a table with a handled flag. The list_new_messages tool reads unhandled rows for that mailbox, and the agent marks them handled after replying. This keeps everything inside MCP and works with any host, including ones you do not control. The cost is that mail is only processed when someone runs the agent.

Wake the agent. Your webhook handler starts an agent run and injects the inbound message as the initial input. The agent can use the hosted MCP tools to read plain-text mail and reply. A custom adapter or the REST API/SDKs can add thread retrieval and attachment processing. This pattern suits time-sensitive work such as support intake or order updates.

We usually do both. The webhook triggers a run, and the pull tool exists as a recovery path for messages that arrived while the worker was down.

A note on idempotency: webhook deliveries can repeat. Deduplicate on message_id before you start an agent run, or you will send the same vendor two nearly identical replies.

MCP server vs direct API vs a skill

Approach Best when
MCP server You want one email integration shared across multiple hosts and agent frameworks, including third-party clients
Direct HTTP calls You control the agent loop and want tight control over prompting, retries, and message trimming
Packaged skill You are on Claude Code or a similar host and want mail working in minutes

Choose the hosted MCP connection when you want an existing agent client to use Robotomail through discovered tools and OAuth approval. For an agent loop you control, the REST API or official SDKs give you direct access with an API key; Python has synchronous and asynchronous clients. The agent skill is another option for giving a compatible agent instructions for the API and CLI.

Operational details that bite

  • Rate limits. Give the MCP server its own limiter. A model in a retry loop will happily call send_email a dozen times in five seconds. Return a clear error and let the model back off rather than passing every attempt through. See limits.
  • Loop protection. If your agent replies to an autoresponder that replies to your agent, you have built a mail loop. Drop messages with Auto-Submitted headers, and cap replies per thread per hour.
  • Attachments. Do not return base64 blobs through MCP. Return metadata and a reference, download server side, and only surface extracted text to the model.
  • Error shape. Return isError: true with the real status code and body. Models recover well from "422: recipient address is invalid" and badly from "something went wrong".
  • Recipient caps. Cap to[] at a small number in the schema. Schema-level limits are cheaper than trusting the description.

FAQ

Does an email MCP server let agents receive email?

Not by itself. MCP tools are pull based, so the agent only sees mail when it calls a tool. Combine the MCP server with an inbound webhook so mail is captured the moment it arrives, then either wake an agent run or let a list_new_messages tool serve it up on the next turn.

Can I use SMTP behind the MCP server instead of an API?

You can send that way, though you will handle DKIM, bounces, and reputation yourself, and you still cannot receive. Receiving over SMTP means running an MX endpoint and parsing MIME. An email API with inbound support removes both problems.

How many tools should an email MCP server expose?

Five to seven. Send, reply, list, read a message, get a thread, plus optional create-mailbox and download-attachment tools. Every additional tool consumes context in every request and increases the chance the model picks the wrong one. Robotomail's hosted server exposes six: list mailboxes, search messages, read a message, send email, reply, and set the sender display name. A custom server can add further REST API operations, but those are separate from the hosted tool set.

Do replies stay in the same conversation thread?

Yes, if your reply tool passes the inbound message_id. We set the In-Reply-To and References headers from it so Gmail, Outlook, and Apple Mail group the messages correctly. Omit it and every reply looks like a brand new email.

Ready to connect an agent? Start with the hosted MCP setup guide. If you are building an application, choose an official SDK or follow the REST quickstart. For a full walkthrough, see how to give your AI agent an email address.

Give your AI agent a real email address

Create a mailbox, connect your agent and test a conversation. Send, receive and retrieve the thread through one API.

Related posts