feat: add official way to use MCP Tools within a CrewBase (#3058)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled

This commit is contained in:
Lucas Gomide
2025-06-24 16:14:59 -03:00
committed by GitHub
parent 060c486948
commit f6dfec61d6
5 changed files with 344 additions and 29 deletions

View File

@@ -101,6 +101,41 @@ with MCPServerAdapter(server_params) as mcp_tools:
)
# ... 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
<CardGroup cols={2}>

View File

@@ -1,7 +1,8 @@
import inspect
import logging
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
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")
mcp_server_params: Any = getattr(cls, "mcp_server_params", None)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_configurations()
@@ -64,6 +67,39 @@ def CrewBase(cls: T) -> T:
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):
"""Load agent and task configurations from YAML files."""
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

View File

@@ -1,5 +1,5 @@
from typing import List
from unittest.mock import Mock, patch
import pytest
from crewai.agent import Agent
@@ -16,7 +16,7 @@ from crewai.project import (
task,
)
from crewai.task import Task
from crewai.tools import tool
class SimpleCrew:
@agent
@@ -85,6 +85,14 @@ class InternalCrew:
def crew(self):
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():
crew = SimpleCrew()
@@ -237,3 +245,17 @@ def test_multiple_before_after_kickoff():
def test_crew_name():
crew = 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})