Overview

The Model Context Protocol (MCP) provides a standardized way for AI agents to provide context to LLMs by communicating with external services, known as MCP Servers. The crewai-tools library extends CrewAI’s capabilities by allowing you to seamlessly integrate tools from these MCP servers into your agents. This gives your crews access to a vast ecosystem of functionalities. For now, we support Standard Input/Output (Stdio) and Server-Sent Events (SSE) transport mechanisms.

We will also be integrating Streamable HTTP transport in the near future. Streamable HTTP is designed for efficient, bi-directional communication over a single HTTP connection.

Installation

Before you start using MCP with crewai-tools, you need to install the mcp extra crewai-tools dependency with the following command:

uv pip install 'crewai-tools[mcp]'

Integrating MCP Tools with MCPServerAdapter

The MCPServerAdapter class from crewai-tools is the primary way to connect to an MCP server and make its tools available to your CrewAI agents. It supports different transport mechanisms, primarily Stdio (for local servers) and SSE (Server-Sent Events).You have two main options for managing the connection lifecycle:

Using a Python context manager (with statement) is the recommended approach. It automatically handles starting and stopping the connection to the MCP server.

For a local Stdio-based MCP server:

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

server_params=StdioServerParameters(
    command="uxv", # Or your python3 executable i.e. "python3"
    args=["mock_server.py"],
    env={"UV_PYTHON": "3.12", **os.environ},
)

with MCPServerAdapter(server_params) as tools:
    print(f"Available tools from Stdio MCP server: {[tool.name for tool in tools]}")

    # Example: Using the tools from the Stdio MCP server in a CrewAI Agent
    agent = Agent(
        role="Web Information Retriever",
        goal="Scrape content from a specified URL.",
        backstory="An AI that can fetch and process web page data via an MCP tool.",
        tools=tools,
        verbose=True,
    )
    task = Task(
        description="Scrape content from a specified URL.",
        expected_output="Scraped content from the specified URL.",
        agent=agent,
    )
    crew = Crew(
        agents=[agent],
        tasks=[task],
        verbose=True,
    )
    result = crew.kickoff()
    print(result)

For a remote SSE-based MCP server:

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

server_params = {"url": "http://localhost:8000/sse"}

with MCPServerAdapter(server_params) as tools:
    print(f"Available tools from SSE MCP server: {[tool.name for tool in tools]}")

    # Example: Using the tools from the SSE MCP server in a CrewAI Agent
    agent = Agent(
        role="Web Information Retriever",
        goal="Scrape content from a specified URL.",
        backstory="An AI that can fetch and process web page data via an MCP tool.",
        tools=tools,
        verbose=True,
    )
    task = Task(
        description="Scrape content from a specified URL.",
        expected_output="Scraped content from the specified URL.",
        agent=agent,
    )
    crew = Crew(
        agents=[agent],
        tasks=[task],
        verbose=True,
    )
    result = crew.kickoff()
    print(result)

Option 2: More control over the MCP server connection lifecycle

If you need finer-grained control over the MCP server connection lifecycle, you can instantiate MCPServerAdapter directly and manage its start() and stop() methods.

You MUST call mcp_server_adapter.stop() to ensure the connection is closed and resources are released. Using a try...finally block is highly recommended.

Stdio Transport Example (Manual)

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

stdio_params = StdioServerParameters(
    command="uvx", # Or your python3 executable i.e. "python3"
    args=["--quiet", "your-mcp-server@0.1.3"],
    env={"UV_PYTHON": "3.12", **os.environ},
)

mcp_server_adapter = MCPServerAdapter(server_params=stdio_params)
try:
    mcp_server_adapter.start() # Manually start the connection
    tools = mcp_server_adapter.tools
    print(f"Available tools (manual Stdio): {[tool.name for tool in tools]}")

    # Use 'tools' with your Agent, Task, Crew setup as in Option 1
    agent = Agent(
        role="Medical Researcher", 
        goal="Find recent studies on a given topic using PubMed.", 
        backstory="An AI assistant specialized in biomedical literature research.", 
        tools=tools,
        verbose=True 
    )
    
    task = Task(
        description="Search for recent articles on 'crispr gene editing'.",
        expected_output="A summary of the top 3 recent articles.",
        agent=agent
    )
    
    crew = Crew(
        agents=[agent],
        tasks=[task],
        verbose=True,
        process=Process.sequential
    )
    
    result = crew.kickoff()
    print(result)
finally:
    print("Stopping Stdio MCP server connection (manual)...")
    mcp_server_adapter.stop() # **Crucial: Ensure stop is called**

SSE Transport Example (Manual)

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


server_params = {"url": "http://localhost:8000/sse"}

try:
    mcp_server_adapter = MCPServerAdapter(server_params)
    mcp_server_adapter.start()
    tools = mcp_server_adapter.tools
    print(f"Available tools (manual SSE): {[tool.name for tool in tools]}")

    agent = Agent(
        role="Medical Researcher", 
        goal="Find recent studies on a given topic using PubMed.", 
        backstory="An AI assistant specialized in biomedical literature research.", 
        tools=tools,
        verbose=True 
    )
    
    task = Task(
        description="Search for recent articles on 'crispr gene editing'.",
        expected_output="A summary of the top 3 recent articles.",
        agent=agent
    )
    
    crew = Crew(
        agents=[agent],
        tasks=[task],
        verbose=True,
        process=Process.sequential
    )
    
    result = crew.kickoff()
    print(result)
finally:
    print("Stopping SSE MCP server connection (manual)...")
    mcp_server_adapter.stop() # **Crucial: Ensure stop is called**

Staying Safe with MCP

Always ensure that you trust an MCP Server before using it.

Security Warning: DNS Rebinding Attacks

SSE transports can be vulnerable to DNS rebinding attacks if not properly secured. To prevent this:

  1. Always validate Origin headers on incoming SSE connections to ensure they come from expected sources
  2. Avoid binding servers to all network interfaces (0.0.0.0) when running locally - bind only to localhost (127.0.0.1) instead
  3. Implement proper authentication for all SSE connections

Without these protections, attackers could use DNS rebinding to interact with local MCP servers from remote websites.

For more details, see the MCP Transport Security documentation.

Limitations

  • Supported Primitives: Currently, MCPServerAdapter primarily supports adapting MCP tools. Other MCP primitives like prompts or resources are not directly integrated as CrewAI components through this adapter at this time.
  • Output Handling: The adapter typically processes the primary text output from an MCP tool (e.g., .content[0].text). Complex or multi-modal outputs might require custom handling if not fitting this pattern.