mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-08 15:48:29 +00:00
Compare commits
1 Commits
devin/1762
...
lg-mcp-cre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a7c21f0e7 |
@@ -6,11 +6,11 @@ icon: plug
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) provides a standardized way for AI agents to provide context to LLMs by communicating with external services, known as MCP Servers.
|
The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (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.
|
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.
|
This gives your crews access to a vast ecosystem of functionalities.
|
||||||
|
|
||||||
We currently support the following transport mechanisms:
|
We currently support the following transport mechanisms:
|
||||||
|
|
||||||
- **Stdio**: for local servers (communication via standard input/output between processes on the same machine)
|
- **Stdio**: for local servers (communication via standard input/output between processes on the same machine)
|
||||||
- **Server-Sent Events (SSE)**: for remote servers (unidirectional, real-time data streaming from server to client over HTTP)
|
- **Server-Sent Events (SSE)**: for remote servers (unidirectional, real-time data streaming from server to client over HTTP)
|
||||||
@@ -52,27 +52,27 @@ from mcp import StdioServerParameters # For Stdio Server
|
|||||||
# Example server_params (choose one based on your server type):
|
# Example server_params (choose one based on your server type):
|
||||||
# 1. Stdio Server:
|
# 1. Stdio Server:
|
||||||
server_params=StdioServerParameters(
|
server_params=StdioServerParameters(
|
||||||
command="python3",
|
command="python3",
|
||||||
args=["servers/your_server.py"],
|
args=["servers/your_server.py"],
|
||||||
env={"UV_PYTHON": "3.12", **os.environ},
|
env={"UV_PYTHON": "3.12", **os.environ},
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. SSE Server:
|
# 2. SSE Server:
|
||||||
server_params = {
|
server_params = {
|
||||||
"url": "http://localhost:8000/sse",
|
"url": "http://localhost:8000/sse",
|
||||||
"transport": "sse"
|
"transport": "sse"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 3. Streamable HTTP Server:
|
# 3. Streamable HTTP Server:
|
||||||
server_params = {
|
server_params = {
|
||||||
"url": "http://localhost:8001/mcp",
|
"url": "http://localhost:8001/mcp",
|
||||||
"transport": "streamable-http"
|
"transport": "streamable-http"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Example usage (uncomment and adapt once server_params is set):
|
# Example usage (uncomment and adapt once server_params is set):
|
||||||
with MCPServerAdapter(server_params) as mcp_tools:
|
with MCPServerAdapter(server_params) as mcp_tools:
|
||||||
print(f"Available tools: {[tool.name for tool in mcp_tools]}")
|
print(f"Available tools: {[tool.name for tool in mcp_tools]}")
|
||||||
|
|
||||||
my_agent = Agent(
|
my_agent = Agent(
|
||||||
role="MCP Tool User",
|
role="MCP Tool User",
|
||||||
goal="Utilize tools from an MCP server.",
|
goal="Utilize tools from an MCP server.",
|
||||||
@@ -101,44 +101,79 @@ with MCPServerAdapter(server_params) as mcp_tools:
|
|||||||
)
|
)
|
||||||
# ... rest of your crew setup ...
|
# ... rest of your crew setup ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Using with CrewBase
|
||||||
|
|
||||||
|
To use MCPServer tools within a CrewBase class, use the `mcp_tools` method. Server configurations should be provided via the mcp_server_params attribute. You can pass either a single configuration or a list of multiple server configurations.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithMCP:
|
||||||
|
# ... define your agents and tasks config file ...
|
||||||
|
|
||||||
|
mcp_server_params = [
|
||||||
|
# Streamable HTTP Server
|
||||||
|
{
|
||||||
|
"url": "http://localhost:8001/mcp",
|
||||||
|
"transport": "streamable-http"
|
||||||
|
},
|
||||||
|
# SSE Server
|
||||||
|
{
|
||||||
|
"url": "http://localhost:8000/sse",
|
||||||
|
"transport": "sse"
|
||||||
|
},
|
||||||
|
# StdIO Server
|
||||||
|
StdioServerParameters(
|
||||||
|
command="python3",
|
||||||
|
args=["servers/your_stdio_server.py"],
|
||||||
|
env={"UV_PYTHON": "3.12", **os.environ},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def your_agent(self):
|
||||||
|
return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) # you can filter which tool are available also
|
||||||
|
|
||||||
|
# ... rest of your crew setup ...
|
||||||
|
```
|
||||||
## Explore MCP Integrations
|
## Explore MCP Integrations
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
<CardGroup cols={2}>
|
||||||
<Card
|
<Card
|
||||||
title="Stdio Transport"
|
title="Stdio Transport"
|
||||||
icon="server"
|
icon="server"
|
||||||
href="/mcp/stdio"
|
href="/mcp/stdio"
|
||||||
color="#3B82F6"
|
color="#3B82F6"
|
||||||
>
|
>
|
||||||
Connect to local MCP servers via standard input/output. Ideal for scripts and local executables.
|
Connect to local MCP servers via standard input/output. Ideal for scripts and local executables.
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title="SSE Transport"
|
title="SSE Transport"
|
||||||
icon="wifi"
|
icon="wifi"
|
||||||
href="/mcp/sse"
|
href="/mcp/sse"
|
||||||
color="#10B981"
|
color="#10B981"
|
||||||
>
|
>
|
||||||
Integrate with remote MCP servers using Server-Sent Events for real-time data streaming.
|
Integrate with remote MCP servers using Server-Sent Events for real-time data streaming.
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title="Streamable HTTP Transport"
|
title="Streamable HTTP Transport"
|
||||||
icon="globe"
|
icon="globe"
|
||||||
href="/mcp/streamable-http"
|
href="/mcp/streamable-http"
|
||||||
color="#F59E0B"
|
color="#F59E0B"
|
||||||
>
|
>
|
||||||
Utilize flexible Streamable HTTP for robust communication with remote MCP servers.
|
Utilize flexible Streamable HTTP for robust communication with remote MCP servers.
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title="Connecting to Multiple Servers"
|
title="Connecting to Multiple Servers"
|
||||||
icon="layer-group"
|
icon="layer-group"
|
||||||
href="/mcp/multiple-servers"
|
href="/mcp/multiple-servers"
|
||||||
color="#8B5CF6"
|
color="#8B5CF6"
|
||||||
>
|
>
|
||||||
Aggregate tools from several MCP servers simultaneously using a single adapter.
|
Aggregate tools from several MCP servers simultaneously using a single adapter.
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title="Security Considerations"
|
title="Security Considerations"
|
||||||
icon="lock"
|
icon="lock"
|
||||||
href="/mcp/security"
|
href="/mcp/security"
|
||||||
color="#EF4444"
|
color="#EF4444"
|
||||||
>
|
>
|
||||||
@@ -148,7 +183,7 @@ with MCPServerAdapter(server_params) as mcp_tools:
|
|||||||
|
|
||||||
Checkout this repository for full demos and examples of MCP integration with CrewAI! 👇
|
Checkout this repository for full demos and examples of MCP integration with CrewAI! 👇
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
title="GitHub Repository"
|
title="GitHub Repository"
|
||||||
icon="github"
|
icon="github"
|
||||||
href="https://github.com/tonykipkemboi/crewai-mcp-demo"
|
href="https://github.com/tonykipkemboi/crewai-mcp-demo"
|
||||||
@@ -163,7 +198,7 @@ Always ensure that you trust an MCP Server before using it.
|
|||||||
</Warning>
|
</Warning>
|
||||||
|
|
||||||
#### Security Warning: DNS Rebinding Attacks
|
#### Security Warning: DNS Rebinding Attacks
|
||||||
SSE transports can be vulnerable to DNS rebinding attacks if not properly secured.
|
SSE transports can be vulnerable to DNS rebinding attacks if not properly secured.
|
||||||
To prevent this:
|
To prevent this:
|
||||||
|
|
||||||
1. **Always validate Origin headers** on incoming SSE connections to ensure they come from expected sources
|
1. **Always validate Origin headers** on incoming SSE connections to ensure they come from expected sources
|
||||||
@@ -175,6 +210,6 @@ Without these protections, attackers could use DNS rebinding to interact with lo
|
|||||||
For more details, see the [Anthropic's MCP Transport Security docs](https://modelcontextprotocol.io/docs/concepts/transports#security-considerations).
|
For more details, see the [Anthropic's MCP Transport Security docs](https://modelcontextprotocol.io/docs/concepts/transports#security-considerations).
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
* **Supported Primitives**: Currently, `MCPServerAdapter` primarily supports adapting MCP `tools`.
|
* **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.
|
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.
|
* **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.
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, TypeVar, cast
|
from typing import Any, Callable, Dict, TypeVar, cast, List
|
||||||
|
from crewai.tools import BaseTool
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
@@ -27,6 +28,8 @@ def CrewBase(cls: T) -> T:
|
|||||||
)
|
)
|
||||||
original_tasks_config_path = getattr(cls, "tasks_config", "config/tasks.yaml")
|
original_tasks_config_path = getattr(cls, "tasks_config", "config/tasks.yaml")
|
||||||
|
|
||||||
|
mcp_server_params: Any = getattr(cls, "mcp_server_params", None)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.load_configurations()
|
self.load_configurations()
|
||||||
@@ -64,6 +67,39 @@ def CrewBase(cls: T) -> T:
|
|||||||
self._original_functions, "is_kickoff"
|
self._original_functions, "is_kickoff"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add close mcp server method to after kickoff
|
||||||
|
bound_method = self._create_close_mcp_server_method()
|
||||||
|
self._after_kickoff['_close_mcp_server'] = bound_method
|
||||||
|
|
||||||
|
def _create_close_mcp_server_method(self):
|
||||||
|
def _close_mcp_server(self, instance, outputs):
|
||||||
|
adapter = getattr(self, '_mcp_server_adapter', None)
|
||||||
|
if adapter is not None:
|
||||||
|
try:
|
||||||
|
adapter.stop()
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f"Error stopping MCP server: {e}")
|
||||||
|
return outputs
|
||||||
|
|
||||||
|
_close_mcp_server.is_after_kickoff = True
|
||||||
|
|
||||||
|
import types
|
||||||
|
return types.MethodType(_close_mcp_server, self)
|
||||||
|
|
||||||
|
def get_mcp_tools(self) -> List[BaseTool]:
|
||||||
|
if not self.mcp_server_params:
|
||||||
|
return []
|
||||||
|
|
||||||
|
from crewai_tools import MCPServerAdapter
|
||||||
|
|
||||||
|
adapter = getattr(self, '_mcp_server_adapter', None)
|
||||||
|
if adapter and isinstance(adapter, MCPServerAdapter):
|
||||||
|
return adapter.tools
|
||||||
|
|
||||||
|
self._mcp_server_adapter = MCPServerAdapter(self.mcp_server_params)
|
||||||
|
return self._mcp_server_adapter.tools
|
||||||
|
|
||||||
|
|
||||||
def load_configurations(self):
|
def load_configurations(self):
|
||||||
"""Load agent and task configurations from YAML files."""
|
"""Load agent and task configurations from YAML files."""
|
||||||
if isinstance(self.original_agents_config_path, str):
|
if isinstance(self.original_agents_config_path, str):
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
from unittest.mock import Mock, patch
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from crewai.agent import Agent
|
from crewai.agent import Agent
|
||||||
@@ -16,7 +16,7 @@ from crewai.project import (
|
|||||||
task,
|
task,
|
||||||
)
|
)
|
||||||
from crewai.task import Task
|
from crewai.task import Task
|
||||||
|
from crewai.tools import tool
|
||||||
|
|
||||||
class SimpleCrew:
|
class SimpleCrew:
|
||||||
@agent
|
@agent
|
||||||
@@ -85,6 +85,14 @@ class InternalCrew:
|
|||||||
def crew(self):
|
def crew(self):
|
||||||
return Crew(agents=self.agents, tasks=self.tasks, verbose=True)
|
return Crew(agents=self.agents, tasks=self.tasks, verbose=True)
|
||||||
|
|
||||||
|
@CrewBase
|
||||||
|
class InternalCrewWithMCP(InternalCrew):
|
||||||
|
mcp_server_params = {"host": "localhost", "port": 8000}
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def reporting_analyst(self):
|
||||||
|
return Agent(config=self.agents_config["reporting_analyst"], tools=self.get_mcp_tools()) # type: ignore[index]
|
||||||
|
|
||||||
|
|
||||||
def test_agent_memoization():
|
def test_agent_memoization():
|
||||||
crew = SimpleCrew()
|
crew = SimpleCrew()
|
||||||
@@ -237,3 +245,17 @@ def test_multiple_before_after_kickoff():
|
|||||||
def test_crew_name():
|
def test_crew_name():
|
||||||
crew = InternalCrew()
|
crew = InternalCrew()
|
||||||
assert crew._crew_name == "InternalCrew"
|
assert crew._crew_name == "InternalCrew"
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def simple_tool():
|
||||||
|
"""Return 'Hi!'"""
|
||||||
|
return "Hi!"
|
||||||
|
|
||||||
|
def test_internal_crew_with_mcp():
|
||||||
|
mock = Mock()
|
||||||
|
mock.tools = [simple_tool]
|
||||||
|
with patch("crewai_tools.MCPServerAdapter", return_value=mock) as adapter_mock:
|
||||||
|
crew = InternalCrewWithMCP()
|
||||||
|
assert crew.reporting_analyst().tools == [simple_tool]
|
||||||
|
|
||||||
|
adapter_mock.assert_called_once_with({"host": "localhost", "port": 8000})
|
||||||
Reference in New Issue
Block a user