Files
Josh Zhang 7d0a76a8b8 Update e2a integration page with remote MCP server and 7 tools (#1793)
* docs(integrations): refresh e2a — hosted endpoint + 7 missing tools

The e2a integration page has drifted from what the e2a MCP server
actually ships. Two gaps this PR closes:

1. **Hosted endpoint not documented.** e2a runs a hosted Streamable
   HTTP MCP at https://mcp.e2a.dev/mcp that accepts an API key in
   the Authorization header. The page currently only shows the
   stdio path (npx -y @e2a/mcp-server), which doesn't work for ADK
   agents deployed to Cloud Run since Cloud Run can't host stdio
   MCP child processes. Adds a "Hosted MCP Server" tab to both the
   Python and TypeScript code examples using
   StreamableHTTPConnectionParams.

2. **7 tools missing from the table.** The MCP server now registers
   18 tools but the page lists 11. Adds:
     Identity:  update_agent, delete_agent
     Messages:  get_attachment_data
     Domains:   list_domains, register_domain, verify_domain,
                delete_domain (new section)

Also clarifies a few descriptions:

- `whoami` notes E2A_AGENT_EMAIL is only required for multi-agent
  accounts (the hosted endpoint auto-resolves the sole agent at
  session init).
- `delete_agent` and `delete_domain` flag the `confirm: true`
  required arg.
- `get_attachment_data` uses 0-based `attachment_index`, not an
  attachment ID.
- `E2A_BASE_URL` clarifies the var has no effect when connecting
  to the public hosted endpoint.

Cross-linked the cloud-mode webhook example (already shipped in
the e2a repo, not previously referenced from this page) and the
hosted endpoint URL under Additional resources.

No frontmatter / catalog metadata changes — title, description,
icon, and tags (`["mcp"]`) all unchanged.

* fix: import McpToolset from deep path (google-adk 2.0 compat)

google-adk 2.0 stopped re-exporting McpToolset from
`google.adk.tools.mcp_tool`'s package __init__ — only the deep path
`google.adk.tools.mcp_tool.mcp_toolset.McpToolset` still works.

Caught when I actually ran the example against a fresh
google-adk 2.0 install:

  ImportError: cannot import name 'McpToolset' from
  'google.adk.tools.mcp_tool'
  ($VENV/site-packages/google/adk/tools/mcp_tool/__init__.py)

Applies to both the stdio code block and the new hosted code block.
StdioConnectionParams and StreamableHTTPConnectionParams already use
deep paths and continue to work — only McpToolset was broken.

* Revert McpToolset import to standard path

The claim that google-adk 2.0 stopped re-exporting McpToolset from
google.adk.tools.mcp_tool is incorrect. The __init__.py at
src/google/adk/tools/mcp_tool/__init__.py explicitly re-exports
McpToolset (and MCPToolset) via `from .mcp_toolset import McpToolset`.
It has done so in every release, including v2.0.0.

* Rename tab to Remote MCP server for consistency with all other integration pages

* Remove link to raw MCP endpoint

* Match API key variable / format with all other integration pages

* Formatting and consistency edits

---------

Co-authored-by: Kristopher Overholt <koverholt@google.com>
2026-05-22 16:07:19 -05:00

9.8 KiB

catalog_title, catalog_description, catalog_icon, catalog_tags
catalog_title catalog_description catalog_icon catalog_tags
e2a Authenticated email gateway for AI agents with human-in-the-loop approval /integrations/assets/e2a.png
mcp

e2a MCP tool for ADK

Supported in ADKPythonTypeScript

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

=== "Python"

=== "Local MCP Server"

    ```python
    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,
                ),
            )
        ],
    )
    ```

=== "Remote MCP Server"

    ```python
    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,
                ),
            )
        ],
    )
    ```

=== "TypeScript"

=== "Local MCP Server"

    ```typescript
    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 };
    ```

=== "Remote MCP Server"

    ```typescript
    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

!!! warning "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](https://github.com/Mnexa-AI/e2a/tree/main/examples/adk-cloud-webhook)
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)

Additional resources