e2a MCP tool for ADK¶
The e2a MCP Server connects your ADK agent to e2a, an authenticated email gateway built for AI agents. This integration gives your agent its own email inbox to send, receive, and reply to messages using natural language, with SPF/DKIM-verified inbound mail and optional human-in-the-loop approval on outbound messages.
Use cases¶
-
Give agents their own inboxes: Provision dedicated email addresses (e.g.
support-bot@your-domain.com) and let agents send and receive mail just like a teammate. -
Authenticated inbound: Every incoming message arrives with SPF and DKIM verification results so your agent knows whether the sender is who they claim to be.
-
Human-in-the-loop approval: Configure HITL on any agent and outbound messages are held in a pending queue until a reviewer approves them, optionally with edits to subject, body, or recipients before sending.
-
Automate threaded conversations: Reply to received emails with proper In-Reply-To and References headers preserved, so threads stay intact across multiple turns.
Prerequisites¶
- A free e2a account and an API key from the dashboard
- Node.js 18+ (only required for the local MCP server)
Use with agent¶
from google.adk.agents import Agent
from google.adk.tools.mcp_tool import McpToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
from mcp import StdioServerParameters
E2A_API_KEY = "YOUR_E2A_API_KEY"
E2A_AGENT_EMAIL = "your-bot@your-domain.com" # optional default inbox
root_agent = Agent(
model="gemini-flash-latest",
name="e2a_agent",
instruction=(
"You manage email through the e2a tools. Call whoami once "
"to find your inbox address. Use list_messages and "
"get_message to read; use reply_to_message (not "
"send_email) when replying to an existing thread so "
"threading headers are preserved."
),
tools=[
McpToolset(
connection_params=StdioConnectionParams(
server_params=StdioServerParameters(
command="npx",
args=["-y", "@e2a/mcp-server"],
env={
"E2A_API_KEY": E2A_API_KEY,
"E2A_AGENT_EMAIL": E2A_AGENT_EMAIL,
},
),
timeout=30,
),
)
],
)
from google.adk.agents import Agent
from google.adk.tools.mcp_tool import McpToolset
from google.adk.tools.mcp_tool.mcp_session_manager import (
StreamableHTTPConnectionParams,
)
E2A_API_KEY = "YOUR_E2A_API_KEY"
root_agent = Agent(
model="gemini-flash-latest",
name="e2a_agent",
instruction=(
"You manage email through the e2a tools. Call whoami once "
"to find your inbox address. Use list_messages and "
"get_message to read; use reply_to_message (not "
"send_email) when replying to an existing thread so "
"threading headers are preserved."
),
tools=[
McpToolset(
connection_params=StreamableHTTPConnectionParams(
url="https://mcp.e2a.dev/mcp",
headers={"Authorization": f"Bearer {E2A_API_KEY}"},
timeout=30,
),
)
],
)
import { LlmAgent, MCPToolset } from "@google/adk";
const E2A_API_KEY = "YOUR_E2A_API_KEY";
const E2A_AGENT_EMAIL = "your-bot@your-domain.com"; // optional default inbox
const rootAgent = new LlmAgent({
model: "gemini-flash-latest",
name: "e2a_agent",
instruction:
"You manage email through the e2a tools. Call whoami once " +
"to find your inbox address. Use list_messages and " +
"get_message to read; use reply_to_message (not " +
"send_email) when replying to an existing thread so " +
"threading headers are preserved.",
tools: [
new MCPToolset({
type: "StdioConnectionParams",
serverParams: {
command: "npx",
args: ["-y", "@e2a/mcp-server"],
env: {
E2A_API_KEY: E2A_API_KEY,
E2A_AGENT_EMAIL: E2A_AGENT_EMAIL,
},
},
}),
],
});
export { rootAgent };
import { LlmAgent, MCPToolset } from "@google/adk";
const E2A_API_KEY = "YOUR_E2A_API_KEY";
const rootAgent = new LlmAgent({
model: "gemini-flash-latest",
name: "e2a_agent",
instruction:
"You manage email through the e2a tools. Call whoami once " +
"to find your inbox address. Use list_messages and " +
"get_message to read; use reply_to_message (not " +
"send_email) when replying to an existing thread so " +
"threading headers are preserved.",
tools: [
new MCPToolset({
type: "StreamableHTTPConnectionParams",
url: "https://mcp.e2a.dev/mcp",
transportOptions: {
requestInit: {
headers: {
Authorization: `Bearer ${E2A_API_KEY}`,
},
},
},
}),
],
});
export { rootAgent };
Available tools¶
Identity¶
| Tool | Description |
|---|---|
whoami |
Return the default agent's full record (requires E2A_AGENT_EMAIL when the account has more than one agent) |
list_agents |
List every agent inbox owned by the authenticated user |
create_agent |
Register a new inbox using a slug on the shared domain; defaults to local mode so the agent receives mail by polling and no webhook is required |
update_agent |
Update an existing agent's webhook URL, mode, or HITL setting |
delete_agent |
Permanently delete an agent (requires confirm: true) and stop accepting mail for that address |
Cloud-mode agents must verify webhook signatures
Agents created with agent_mode: "cloud" receive mail via webhooks instead
of polling. Your webhook handler must verify the HMAC signature on every
delivery. See the cloud-mode webhook
example
for a complete setup with signature verification.
Messages¶
| Tool | Description |
|---|---|
send_email |
Send a new email; returns status: pending_approval instead of sent when HITL is enabled |
reply_to_message |
Reply to an inbound message; preserves In-Reply-To and References headers |
list_messages |
List inbound mail with status filter (unread / read / all) and pagination |
get_message |
Fetch full body, headers, and attachment metadata for one message |
get_attachment_data |
Download an attachment's bytes by message id and 0-based attachment index (returned as base64) |
Human-in-the-loop approval¶
| Tool | Description |
|---|---|
list_pending_messages |
List outbound mail awaiting human approval, soonest-expiring first |
get_pending_message |
Get the full draft (subject, recipients, body) of a pending message |
approve_pending_message |
Send a held message, optionally with reviewer edits (subject / body / recipients) |
reject_pending_message |
Discard a held message; optional reason stored for audit |
Domains¶
| Tool | Description |
|---|---|
list_domains |
List every custom domain registered to the authenticated user, with verification state |
register_domain |
Add a custom domain and receive the DNS records needed to prove ownership |
verify_domain |
Re-run DNS verification on a registered domain after the records are in place |
delete_domain |
Remove a custom domain (requires confirm: true; agents on the shared domain are unaffected) |
Configuration¶
| Variable | Required | Default | Description |
|---|---|---|---|
E2A_API_KEY |
Yes | — | Your e2a API key |
E2A_AGENT_EMAIL |
No | — | Default agent inbox; scopes tools so the LLM doesn't need to specify it on every call |
E2A_BASE_URL |
No | https://e2a.dev |
Self-hosted deployment URL (local MCP server only) |