Skip to content

Chapter 19 · MCP Integration ​

Chapter goals: Understand MCP protocol's position in multi-agent systems, master CrewAI 1.15's recommended mcps DSL syntax and the advanced MCPServerAdapter, and safely mount local/remote MCP server tools for your Agents.

19.1 MCP Protocol in One Minute ​

MCP (Model Context Protocol) is an open protocol initiated by Anthropic that standardizes "how models call external capabilities" into three things:

  • Tools (tools): functions the model can call (the core concern of this tutorial);
  • Resources: readable data context;
  • Prompts: preset prompt templates.

MCP adopts a client-server architecture: your CrewAI application is the MCP client, connecting to MCP servers through three transport mechanisms:

TransportUse CaseCommunication
StdioLocal processes (same machine)Standard input/output
Streamable HTTPRemote services (recommended default)HTTPS, bidirectional
SSERemote services (real-time streaming)HTTP unidirectional push

Version note

CrewAI 1.15 provides two MCP integration approaches: the official recommended mcps field DSL (automatic connection management), and MCPServerAdapter in crewai-tools (manual connection management, suited for complex scenarios). Older tutorials claiming "only MCPServerAdapter works" are outdated.

The simplest way is to add the mcps field directly on Agent; CrewAI auto-discovers tools, manages connection lifecycle, and prefixes tool names to prevent conflicts:

python
import os
from crewai import Agent, Task, Crew, LLM

# Third-party OpenAI-compatible model
llm = LLM(
    model="openai/deepseek-chat",
    base_url="https://api.deepseek.com/v1",
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    temperature=0.7,
)

researcher = Agent(
    role="Research Analyst",
    goal="Investigate the latest developments in AI Agent frameworks using external search tools",
    backstory="Senior researcher skilled at cross-validating information from multiple data sources",
    llm=llm,
    mcps=[
        # Remote MCP server (string reference; can carry auth params)
        "https://mcp.exa.ai/mcp?api_key=your_exa_key",
        # Use # syntax to select only one tool from this server
        "https://api.weather.com/mcp#get_forecast",
    ],
)

task = Task(
    description="Research key evolutionary directions in multi-agent frameworks in 2025",
    expected_output="Chinese research brief with cited sources, under 500 words",
    agent=researcher,
)

crew = Crew(agents=[researcher], tasks=[task])
result = crew.kickoff()
print(result.raw)

No need to manually start/stop connections, no need to pass tools= — tools are auto-mounted.

19.3 Structured Config: Three Transports + Tool Filtering ​

For full control over connection parameters, use structured config objects from the crewai.mcp module:

python
import os
from crewai import Agent
from crewai.mcp import MCPServerStdio, MCPServerHTTP, MCPServerSSE
from crewai.mcp.filters import create_static_tool_filter

# Static filter: only allow whitelist tools, block dangerous ones
file_filter = create_static_tool_filter(
    allowed_tool_names=["read_file", "list_directory"],
    blocked_tool_names=["delete_file"],
)

agent = Agent(
    role="Document Organizer",
    goal="Safely organize local and remote documents",
    backstory="Cautious and meticulous document administrator",
    llm=llm,
    mcps=[
        # 1) Stdio: local filesystem server (start official example server via npx)
        MCPServerStdio(
            command="npx",
            args=["-y", "@modelcontextprotocol/server-filesystem", "/tmp/docs"],
            tool_filter=file_filter,       # Only expose read-type tools
            cache_tools_list=True,         # Cache tool list; speeds subsequent connections
        ),
        # 2) Streamable HTTP: remote service (default streamable=True)
        MCPServerHTTP(
            url="https://api.example.com/mcp",
            headers={"Authorization": f"Bearer {os.getenv('MCP_TOKEN')}"},
            cache_tools_list=True,
        ),
        # 3) SSE: real-time streaming remote service
        MCPServerSSE(
            url="https://stream.example.com/mcp/sse",
            headers={"Authorization": f"Bearer {os.getenv('MCP_TOKEN')}"},
        ),
    ],
)

Common parameters across the three config objects:

  • tool_filter: None (all tools) / static filter (allow/block lists) / dynamic filter function (can decide per context.agent.role which tools to expose);
  • cache_tools_list: cache discovered tools after first fetch; avoids re-pulling on every connection;
  • Connection failure gracefully degrades: if one server goes down, only a warning log is emitted; the Agent continues with remaining tools; invalid config raises a validation error at Agent creation time.

19.4 Approach 2: MCPServerAdapter Manual Management ​

When you need fine-grained control over connection lifecycle (e.g., connection reuse, per-tool granularity), use MCPServerAdapter from crewai-tools. Recommended to pair with with context manager for auto start/stop:

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

# Local stdio server launch parameters
server_params = StdioServerParameters(
    command="python3",
    args=["servers/filesystem_server.py"],
    env={"UV_PYTHON": "3.12", **os.environ},
)

# Remote server can also use dict form:
# server_params = {"url": "http://localhost:8000/sse", "transport": "sse"}
# server_params = {"url": "http://localhost:8001/mcp", "transport": "streamable-http"}

with MCPServerAdapter(server_params, connect_timeout=60) as mcp_tools:
    print(f"Available tools: {[t.name for t in mcp_tools]}")

    agent = Agent(
        role="File Manager",
        goal="Complete file organization using MCP tools",
        backstory="Automation assistant familiar with filesystems",
        llm=llm,
        tools=[mcp_tools["read_file"]],  # Dict-style index: grab only one tool
    )
    task = Task(
        description="Read /tmp/docs/notes.md and summarize key points",
        expected_output="3-5 key point list",
        agent=agent,
    )
    crew = Crew(agents=[agent], tasks=[task])
    result = crew.kickoff()
    print(result.raw)
# Connection auto-closes when with block exits

Two filtering approaches: dict-style index mcp_tools["tool_name"], or pass a tool name list at construction MCPServerAdapter(server_params, "tool_1", "tool_2").

19.5 Using MCP in @CrewBase Engineering Projects ​

In the Chapter 17 CLI engineering structure, the @CrewBase class provides a get_mcp_tools() method; connection lifecycle is managed by the framework (auto-closes after kickoff ends):

python
import os
from mcp import StdioServerParameters
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task


@CrewBase
class DocCrew:
    """Doc-processing Crew with MCP file tools"""

    # Supports single config or config list (can mix three transports)
    mcp_server_params = [
        StdioServerParameters(
            command="npx",
            args=["-y", "@modelcontextprotocol/server-filesystem", "/tmp/docs"],
        ),
        {"url": "http://localhost:8001/mcp", "transport": "streamable-http"},
    ]
    mcp_connect_timeout = 60  # Default 30s; increase as needed

    @agent
    def reader(self) -> Agent:
        return Agent(
            config=self.agents_config["reader"],
            tools=self.get_mcp_tools("read_file", "list_directory"),  # Select by name
        )

    @task
    def summarize(self) -> Task:
        return Task(config=self.tasks_config["summarize"])

    @crew
    def crew(self) -> Crew:
        return Crew(agents=self.agents, tasks=self.tasks, process=Process.sequential)

get_mcp_tools() with no args returns all tools; the first call lazily creates a shared adapter; the entire Crew reuses the same connection.

19.6 MCP Security Considerations ​

MCP plugs "external services that can execute code / read files" directly into your Agent — treat it as carefully as "giving a stranger an account to your server":

  1. Only connect to trusted servers: the official docs repeatedly emphasize — you must trust the MCP server before using it. A malicious server can inject prompts via tool descriptions, inducing the model to leak data;
  2. Prevent DNS rebinding (SSE scenario): local SSE servers should validate the Origin header, bind only to 127.0.0.1 not 0.0.0.0, and add authentication;
  3. Principle of least privilege: use tool_filter whitelist to expose only必需 tools;坚决 block delete_*, execute_* high-risk tools;
  4. Credentials never on disk: env= and headers= credentials must always be read from environment variables (see Chapter 25);
  5. Timeout as fallback: always configure connect_timeout for remote servers to prevent one slow server from grinding the entire Crew to a halt (DSL defaults to 30-second timeout with graceful skip).

Known limitations

MCPServerAdapter currently primarily supports the MCP tools primitive; prompts and resources are not yet directly mapped to CrewAI components; complex/multi-modal tool outputs typically only grab .content[0].text.

Chapter Summary ​

  • MCP standardizes three capability types: tools, resources, and prompts; CrewAI as client supports Stdio, Streamable HTTP, and SSE transports;
  • Recommended: use mcps DSL — string references (supporting #tool_name precise selection) or structured config (MCPServerStdio/HTTP/SSE);
  • Advanced: use crewai-tools' MCPServerAdapter + with context for manual management; use get_mcp_tools() in @CrewBase projects;
  • Tool filtering (static whitelist / dynamic function) + connection timeout + graceful degradation are production must-haves;
  • Security surface: only connect to trusted servers, prevent DNS rebinding, least privilege, credentials via environment variables.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. In CrewAI 1.15, what is the officially recommended MCP integration approach?

2. What does the # syntax in mcps string "https://api.weather.com/mcp#get_forecast" do?

3. Regarding MCPServerAdapter connection management, what is the correct approach?

4. Which is NOT an official MCP security recommendation?

🛠️ Hands-On Exercises ​

  1. Start a local filesystem server with npx -y @modelcontextprotocol/server-filesystem /tmp/mcp-lab; mount it with DSL MCPServerStdio; configure a whitelist allowing only read_file and list_directory; have the Agent list and summarize a text file.
  2. Refactor practice 1 into @CrewBase project structure (mcp_server_params + get_mcp_tools("read_file")); verify the connection is auto-closed after kickoff (observe connection logs on the server side).
  3. Add an unreachable remote mcps entry to practice 1's config (e.g., https://unreachable.example.com/mcp); run and observe the log: confirm the Crew does not crash but warns and continues.

Next chapter addresses "how to scientifically evaluate whether a Crew is good or bad": Chapter 20 · Testing and Evaluation.