Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mcp/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ End-to-end demos showing how to wire the e2a MCP surface into popular agent fram
| Framework | Path | LLM | Stdio variant | Hosted variant |
| --- | --- | --- | --- | --- |
| LangChain (LangGraph ReAct) | [langchain/](./langchain/) | Anthropic Claude | `agent.py` | `agent_hosted.py` |
| CrewAI | [crewai/](./crewai/) | Anthropic Claude | `agent.py` | `agent_hosted.py` |
| Google ADK | [adk/](./adk/) | Google Gemini | `agent.py` | `agent_hosted.py` |
| OpenAI Agents SDK | [openai-agents/](./openai-agents/) | OpenAI GPT | `agent.py` | `agent_hosted.py` |
| OpenAI Codex CLI | [codex/](./codex/) | Codex (OpenAI) | `[mcp_servers.e2a]` in `config.toml` | `[mcp_servers.e2a-hosted]` in `config.toml` |
Expand Down
45 changes: 45 additions & 0 deletions mcp/examples/crewai/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# CrewAI × e2a

Single-agent CrewAI crew that drives the e2a [MCP server](https://www.npmjs.com/package/@e2a/mcp-server) via [`crewai-tools`](https://github.com/crewAIInc/crewAI-tools)' `MCPServerAdapter`. Picks up the e2a tool surface and uses it to answer natural-language email tasks.

Two transport options:

- **`agent.py`** — runs the MCP server locally via `npx -y @e2a/mcp-server` (stdio). Simplest for laptop dev; needs a Node toolchain.
- **`agent_hosted.py`** — talks to the hosted endpoint at `https://mcp.e2a.dev/mcp` (Streamable HTTP). Pick this when deploying to serverless runtimes (Cloud Run, Lambda) where spawning a stdio child process is awkward or impossible, or when you don't want a Node toolchain on the agent host.

## Prerequisites

- Python 3.10+
- An [e2a API key](https://e2a.dev)
- An [Anthropic API key](https://console.anthropic.com/)
- For `agent.py` only: Node 18+ (the script shells out to `npx -y @e2a/mcp-server`)

## Run (local stdio)

```bash
pip install -r requirements.txt
export E2A_API_KEY=e2a_…
export E2A_AGENT_EMAIL=your-bot@your-domain.com # optional default inbox
export ANTHROPIC_API_KEY=sk-ant-…

python agent.py "what's in my inbox?"
python agent.py "reply to the most recent message politely"
```

## Run (hosted)

```bash
pip install -r requirements.txt
export E2A_API_KEY=e2a_…
export ANTHROPIC_API_KEY=sk-ant-…

python agent_hosted.py "what's in my inbox?"
```

If your account has exactly one agent, the hosted endpoint auto-resolves it at session init — no `E2A_AGENT_EMAIL` needed. With multiple agents, pass `agent_email` per tool call.

## How it works

`MCPServerAdapter` connects to either a stdio child process (via `StdioServerParameters`) or a Streamable HTTP endpoint (via a dict with `transport: "streamable-http"` and a Bearer header). Inside the `with` block, the adapter yields one CrewAI tool per MCP tool — wired straight into the `Agent` so the crew can call them.

To swap models, change `"anthropic/claude-sonnet-4-6"` to any [LiteLLM-compatible model string](https://docs.litellm.ai/docs/providers) CrewAI accepts (CrewAI uses LiteLLM under the hood).
79 changes: 79 additions & 0 deletions mcp/examples/crewai/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""End-to-end demo: CrewAI agent driving @e2a/mcp-server over stdio.

Wires the e2a MCP server into a single-agent CrewAI crew so the LLM can
send, read, and reply to email through natural-language prompts.

Requires:
E2A_API_KEY e2a API key (https://e2a.dev)
E2A_AGENT_EMAIL (optional) default agent inbox
E2A_BASE_URL (optional) self-hosted e2a base URL
ANTHROPIC_API_KEY Anthropic API key

Run:
pip install -r requirements.txt
python agent.py "what's in my inbox?"
"""

import os
import sys

from crewai import Agent, Crew, Process, Task
from crewai_tools import MCPServerAdapter
from mcp import StdioServerParameters

BACKSTORY = (
"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 In-Reply-To and References headers are preserved."
)


def _e2a_env() -> dict[str, str]:
env = {"E2A_API_KEY": os.environ["E2A_API_KEY"]}
for k in ("E2A_AGENT_EMAIL", "E2A_BASE_URL"):
if k in os.environ:
env[k] = os.environ[k]
return env


def main(prompt: str) -> None:
server_params = StdioServerParameters(
command="npx",
args=["-y", "@e2a/mcp-server"],
env=_e2a_env(),
)

with MCPServerAdapter(server_params) as e2a_tools:
print(
f"Loaded {len(e2a_tools)} e2a tools: "
f"{', '.join(t.name for t in e2a_tools)}\n"
)

agent = Agent(
role="Email Manager",
goal="Handle the operator's email request precisely and concisely.",
backstory=BACKSTORY,
tools=e2a_tools,
llm="anthropic/claude-sonnet-4-6",
allow_delegation=False,
verbose=True,
)
task = Task(
description=prompt,
expected_output="A clear, concise answer to the user's email-related request.",
agent=agent,
)
crew = Crew(
agents=[agent],
tasks=[task],
process=Process.sequential,
verbose=False,
)
result = crew.kickoff()
print(result)


if __name__ == "__main__":
prompt = " ".join(sys.argv[1:]) or "what's in my inbox?"
main(prompt)
76 changes: 76 additions & 0 deletions mcp/examples/crewai/agent_hosted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""End-to-end demo: CrewAI agent against the hosted e2a MCP server.

Same shape as `agent.py` but uses the hosted MCP endpoint
(https://mcp.e2a.dev/mcp) over Streamable HTTP instead of spawning
@e2a/mcp-server locally over stdio. Pick this variant when:
- Deploying the CrewAI agent to a serverless runtime (Cloud Run,
Lambda, etc.) where launching a stdio child process per request is
awkward or impossible.
- You don't want a Node toolchain on the agent host.
- You want updates to land without rebuilding the agent's image.

Requires:
E2A_API_KEY e2a API key (https://e2a.dev)
ANTHROPIC_API_KEY Anthropic API key

Run:
pip install -r requirements.txt
python agent_hosted.py "what's in my inbox?"
"""

import os
import sys

from crewai import Agent, Crew, Process, Task
from crewai_tools import MCPServerAdapter

BACKSTORY = (
"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 In-Reply-To and References headers are preserved."
)


def main(prompt: str) -> None:
server_params = {
"url": "https://mcp.e2a.dev/mcp",
"transport": "streamable-http",
"headers": {
"Authorization": f"Bearer {os.environ['E2A_API_KEY']}",
},
}

with MCPServerAdapter(server_params) as e2a_tools:
print(
f"Loaded {len(e2a_tools)} e2a tools: "
f"{', '.join(t.name for t in e2a_tools)}\n"
)

agent = Agent(
role="Email Manager",
goal="Handle the operator's email request precisely and concisely.",
backstory=BACKSTORY,
tools=e2a_tools,
llm="anthropic/claude-sonnet-4-6",
allow_delegation=False,
verbose=True,
)
task = Task(
description=prompt,
expected_output="A clear, concise answer to the user's email-related request.",
agent=agent,
)
crew = Crew(
agents=[agent],
tasks=[task],
process=Process.sequential,
verbose=False,
)
result = crew.kickoff()
print(result)


if __name__ == "__main__":
prompt = " ".join(sys.argv[1:]) or "what's in my inbox?"
main(prompt)
3 changes: 3 additions & 0 deletions mcp/examples/crewai/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
crewai[anthropic]>=0.80.0
crewai-tools[mcp]>=0.25.0
email-validator>=2.0
Loading