From 5b85adebcf2d047bbf04ef7179030a2021ca73e2 Mon Sep 17 00:00:00 2001 From: jiashuoz Date: Wed, 27 May 2026 18:52:38 -0700 Subject: [PATCH 1/2] docs(examples): add CrewAI MCP example Mirrors the LangChain / ADK / OpenAI Agents examples: a single-agent CrewAI crew wired to the e2a MCP surface via `crewai-tools`' MCPServerAdapter, in both stdio and hosted Streamable HTTP variants. Co-Authored-By: Claude Opus 4.7 (1M context) --- mcp/examples/README.md | 1 + mcp/examples/crewai/README.md | 45 ++++++++++++++++ mcp/examples/crewai/agent.py | 79 ++++++++++++++++++++++++++++ mcp/examples/crewai/agent_hosted.py | 76 ++++++++++++++++++++++++++ mcp/examples/crewai/requirements.txt | 3 ++ 5 files changed, 204 insertions(+) create mode 100644 mcp/examples/crewai/README.md create mode 100644 mcp/examples/crewai/agent.py create mode 100644 mcp/examples/crewai/agent_hosted.py create mode 100644 mcp/examples/crewai/requirements.txt diff --git a/mcp/examples/README.md b/mcp/examples/README.md index 7fff545..9ea0be7 100644 --- a/mcp/examples/README.md +++ b/mcp/examples/README.md @@ -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` | diff --git a/mcp/examples/crewai/README.md b/mcp/examples/crewai/README.md new file mode 100644 index 0000000..0423b83 --- /dev/null +++ b/mcp/examples/crewai/README.md @@ -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). diff --git a/mcp/examples/crewai/agent.py b/mcp/examples/crewai/agent.py new file mode 100644 index 0000000..faa78db --- /dev/null +++ b/mcp/examples/crewai/agent.py @@ -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) diff --git a/mcp/examples/crewai/agent_hosted.py b/mcp/examples/crewai/agent_hosted.py new file mode 100644 index 0000000..0f59ae1 --- /dev/null +++ b/mcp/examples/crewai/agent_hosted.py @@ -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) diff --git a/mcp/examples/crewai/requirements.txt b/mcp/examples/crewai/requirements.txt new file mode 100644 index 0000000..e6b3d0e --- /dev/null +++ b/mcp/examples/crewai/requirements.txt @@ -0,0 +1,3 @@ +crewai>=0.80.0 +crewai-tools>=0.25.0 +mcp>=1.0.0 From 264b6852e4a47b7379b8c3ccc5290eccf9ba14fb Mon Sep 17 00:00:00 2001 From: jiashuoz Date: Wed, 27 May 2026 19:11:04 -0700 Subject: [PATCH 2/2] docs(examples): fix CrewAI requirements Verified via dry run that the bare deps don't pull the right extras: - `crewai-tools[mcp]` is required for the `mcpadapt` bridge (otherwise `MCPServerAdapter` reports `MCP_AVAILABLE = False`). - `crewai[anthropic]` is required for the native Anthropic provider used by `"anthropic/claude-sonnet-4-6"`. - `email-validator` is needed at runtime because the e2a tool schemas carry email-typed fields that crewai-tools wires through pydantic. With these, both `agent.py` and `agent_hosted.py` cleanly load all 18 e2a tools and reach the LLM call (verified end-to-end against a dummy Anthropic key, which 401s as expected). Co-Authored-By: Claude Opus 4.7 (1M context) --- mcp/examples/crewai/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mcp/examples/crewai/requirements.txt b/mcp/examples/crewai/requirements.txt index e6b3d0e..efdd845 100644 --- a/mcp/examples/crewai/requirements.txt +++ b/mcp/examples/crewai/requirements.txt @@ -1,3 +1,3 @@ -crewai>=0.80.0 -crewai-tools>=0.25.0 -mcp>=1.0.0 +crewai[anthropic]>=0.80.0 +crewai-tools[mcp]>=0.25.0 +email-validator>=2.0