diff --git a/docs/en/mcp/overview.mdx b/docs/en/mcp/overview.mdx index 7e589d3b4..bd61a16f7 100644 --- a/docs/en/mcp/overview.mdx +++ b/docs/en/mcp/overview.mdx @@ -142,7 +142,7 @@ with MCPServerAdapter(server_params, "tool_name", connect_timeout=60) as mcp_too ## 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. +To use MCPServer tools within a CrewBase class, use the `get_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 @@ -175,6 +175,34 @@ class CrewWithMCP: # ... rest of your crew setup ... ``` +### Connection Timeout Configuration + +You can configure the connection timeout for MCP servers by setting the `mcp_connect_timeout` class attribute. If no timeout is specified, it defaults to 30 seconds. + +```python +@CrewBase +class CrewWithMCP: + mcp_server_params = [...] + mcp_connect_timeout = 60 # 60 seconds timeout for all MCP connections + + @agent + def your_agent(self): + return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) +``` + +```python +@CrewBase +class CrewWithDefaultTimeout: + mcp_server_params = [...] + # No mcp_connect_timeout specified - uses default 30 seconds + + @agent + def your_agent(self): + return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) +``` + +### Filtering Tools + You can filter which tools are available to your agent by passing a list of tool names to the `get_mcp_tools` method. ```python @@ -186,6 +214,22 @@ def another_agent(self): ) ``` +The timeout configuration applies to all MCP tool calls within the crew: + +```python +@CrewBase +class CrewWithCustomTimeout: + mcp_server_params = [...] + mcp_connect_timeout = 90 # 90 seconds timeout for all MCP connections + + @agent + def filtered_agent(self): + return Agent( + config=self.agents_config["your_agent"], + tools=self.get_mcp_tools("tool_1", "tool_2") # specific tools with custom timeout + ) +``` + ## Explore MCP Integrations diff --git a/docs/ko/mcp/overview.mdx b/docs/ko/mcp/overview.mdx index 7c4043552..256ff1161 100644 --- a/docs/ko/mcp/overview.mdx +++ b/docs/ko/mcp/overview.mdx @@ -7,8 +7,8 @@ mode: "wide" ## 개요 -[Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)는 AI 에이전트가 MCP 서버로 알려진 외부 서비스와 통신함으로써 LLM에 컨텍스트를 제공할 수 있도록 표준화된 방식을 제공합니다. -`crewai-tools` 라이브러리는 CrewAI의 기능을 확장하여, 이러한 MCP 서버에서 제공하는 툴을 에이전트에 원활하게 통합할 수 있도록 해줍니다. +[Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)는 AI 에이전트가 MCP 서버로 알려진 외부 서비스와 통신함으로써 LLM에 컨텍스트를 제공할 수 있도록 표준화된 방식을 제공합니다. +`crewai-tools` 라이브러리는 CrewAI의 기능을 확장하여, 이러한 MCP 서버에서 제공하는 툴을 에이전트에 원활하게 통합할 수 있도록 해줍니다. 이를 통해 여러분의 crew는 방대한 기능 에코시스템에 접근할 수 있습니다. 현재 다음과 같은 전송 메커니즘을 지원합니다: @@ -142,7 +142,7 @@ with MCPServerAdapter(server_params, "tool_name", connect_timeout=60) as mcp_too ## CrewBase와 함께 사용하기 -CrewBase 클래스 내에서 MCPServer 도구를 사용하려면 `mcp_tools` 메서드를 사용하세요. 서버 구성은 mcp_server_params 속성을 통해 제공되어야 합니다. 단일 구성 또는 여러 서버 구성을 리스트 형태로 전달할 수 있습니다. +CrewBase 클래스 내에서 MCPServer 도구를 사용하려면 `get_mcp_tools` 메서드를 사용하세요. 서버 구성은 `mcp_server_params` 속성을 통해 제공되어야 합니다. 단일 구성 또는 여러 서버 구성을 리스트 형태로 전달할 수 있습니다. ```python @CrewBase @@ -175,6 +175,34 @@ class CrewWithMCP: # ... 나머지 crew 설정 ... ``` +### 연결 타임아웃 구성 + +`mcp_connect_timeout` 클래스 속성을 설정하여 MCP 서버의 연결 타임아웃을 구성할 수 있습니다. 타임아웃을 지정하지 않으면 기본값으로 30초가 사용됩니다. + +```python +@CrewBase +class CrewWithMCP: + mcp_server_params = [...] + mcp_connect_timeout = 60 # 모든 MCP 연결에 60초 타임아웃 + + @agent + def your_agent(self): + return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) +``` + +```python +@CrewBase +class CrewWithDefaultTimeout: + mcp_server_params = [...] + # mcp_connect_timeout 지정하지 않음 - 기본 30초 사용 + + @agent + def your_agent(self): + return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) +``` + +### 도구 필터링 + `get_mcp_tools` 메서드에 도구 이름의 리스트를 전달하여, 에이전트에 제공되는 도구를 필터링할 수 있습니다. ```python @@ -186,6 +214,22 @@ def another_agent(self): ) ``` +타임아웃 구성은 crew 내의 모든 MCP 도구 호출에 적용됩니다: + +```python +@CrewBase +class CrewWithCustomTimeout: + mcp_server_params = [...] + mcp_connect_timeout = 90 # 모든 MCP 연결에 90초 타임아웃 + + @agent + def filtered_agent(self): + return Agent( + config=self.agents_config["your_agent"], + tools=self.get_mcp_tools("tool_1", "tool_2") # 사용자 지정 타임아웃으로 특정 도구 + ) +``` + ## MCP 통합 탐색 @@ -261,4 +305,4 @@ SSE 전송은 적절하게 보안되지 않은 경우 DNS 리바인딩 공격에 ### 제한 사항 * **지원되는 프리미티브**: 현재 `MCPServerAdapter`는 주로 MCP `tools`를 어댑팅하는 기능을 지원합니다. 다른 MCP 프리미티브(예: `prompts` 또는 `resources`)는 현재 이 어댑터를 통해 CrewAI 컴포넌트로 직접 통합되어 있지 않습니다. -* **출력 처리**: 어댑터는 일반적으로 MCP tool의 주요 텍스트 출력(예: `.content[0].text`)을 처리합니다. 복잡하거나 멀티모달 출력의 경우 이 패턴에 맞지 않으면 별도의 커스텀 처리가 필요할 수 있습니다. \ No newline at end of file +* **출력 처리**: 어댑터는 일반적으로 MCP tool의 주요 텍스트 출력(예: `.content[0].text`)을 처리합니다. 복잡하거나 멀티모달 출력의 경우 이 패턴에 맞지 않으면 별도의 커스텀 처리가 필요할 수 있습니다. diff --git a/docs/pt-BR/mcp/overview.mdx b/docs/pt-BR/mcp/overview.mdx index cf449d3eb..5c0bb4878 100644 --- a/docs/pt-BR/mcp/overview.mdx +++ b/docs/pt-BR/mcp/overview.mdx @@ -118,7 +118,7 @@ with MCPServerAdapter(server_params, connect_timeout=60) as mcp_tools: ## Usando com CrewBase -Para usar ferramentas de servidores MCP dentro de uma classe CrewBase, utilize o método `mcp_tools`. As configurações dos servidores devem ser fornecidas via o atributo mcp_server_params. Você pode passar uma configuração única ou uma lista com múltiplas configurações. +Para usar ferramentas de servidores MCP dentro de uma classe CrewBase, utilize o método `get_mcp_tools`. As configurações dos servidores devem ser fornecidas via o atributo `mcp_server_params`. Você pode passar uma configuração única ou uma lista com múltiplas configurações. ```python @CrewBase @@ -146,10 +146,65 @@ class CrewWithMCP: @agent def your_agent(self): - return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) # você também pode filtrar quais ferramentas estarão disponíveis + return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) # obter todas as ferramentas disponíveis # ... restante da configuração do seu crew ... ``` + +### Configuração de Timeout de Conexão + +Você pode configurar o timeout de conexão para servidores MCP definindo o atributo de classe `mcp_connect_timeout`. Se nenhum timeout for especificado, o padrão é 30 segundos. + +```python +@CrewBase +class CrewWithMCP: + mcp_server_params = [...] + mcp_connect_timeout = 60 # timeout de 60 segundos para todas as conexões MCP + + @agent + def your_agent(self): + return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) +``` + +```python +@CrewBase +class CrewWithDefaultTimeout: + mcp_server_params = [...] + # Nenhum mcp_connect_timeout especificado - usa padrão de 30 segundos + + @agent + def your_agent(self): + return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) +``` + +### Filtragem de Ferramentas + +Você pode filtrar quais ferramentas estão disponíveis para seu agente passando uma lista de nomes de ferramentas para o método `get_mcp_tools`. + +```python +@agent +def another_agent(self): + return Agent( + config=self.agents_config["your_agent"], + tools=self.get_mcp_tools("tool_1", "tool_2") # obter ferramentas específicas + ) +``` + +A configuração de timeout se aplica a todas as chamadas de ferramentas MCP dentro do crew: + +```python +@CrewBase +class CrewWithCustomTimeout: + mcp_server_params = [...] + mcp_connect_timeout = 90 # timeout de 90 segundos para todas as conexões MCP + + @agent + def filtered_agent(self): + return Agent( + config=self.agents_config["your_agent"], + tools=self.get_mcp_tools("tool_1", "tool_2") # ferramentas específicas com timeout personalizado + ) +``` ## Explore Integrações MCP diff --git a/src/crewai/project/crew_base.py b/src/crewai/project/crew_base.py index e1602acf0..44871f6a0 100644 --- a/src/crewai/project/crew_base.py +++ b/src/crewai/project/crew_base.py @@ -1,12 +1,14 @@ import inspect import logging +from collections.abc import Callable from pathlib import Path -from typing import Any, Callable, Dict, TypeVar, cast, List -from crewai.tools import BaseTool +from typing import Any, TypeVar, cast import yaml from dotenv import load_dotenv +from crewai.tools import BaseTool + load_dotenv() T = TypeVar("T", bound=type) @@ -14,7 +16,7 @@ T = TypeVar("T", bound=type) """Base decorator for creating crew classes with configuration and function management.""" -def CrewBase(cls: T) -> T: +def CrewBase(cls: T) -> T: # noqa: N802 """Wraps a class with crew functionality and configuration management.""" class WrappedClass(cls): # type: ignore @@ -29,6 +31,7 @@ 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) + mcp_connect_timeout: int = getattr(cls, "mcp_connect_timeout", 30) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -86,15 +89,18 @@ def CrewBase(cls: T) -> T: import types return types.MethodType(_close_mcp_server, self) - def get_mcp_tools(self, *tool_names: list[str]) -> List[BaseTool]: + def get_mcp_tools(self, *tool_names: list[str]) -> list[BaseTool]: if not self.mcp_server_params: return [] - from crewai_tools import MCPServerAdapter + from crewai_tools import MCPServerAdapter # type: ignore[import-untyped] adapter = getattr(self, '_mcp_server_adapter', None) if not adapter: - self._mcp_server_adapter = MCPServerAdapter(self.mcp_server_params) + self._mcp_server_adapter = MCPServerAdapter( + self.mcp_server_params, + connect_timeout=self.mcp_connect_timeout + ) return self._mcp_server_adapter.tools.filter_by_names(tool_names or None) @@ -154,8 +160,8 @@ def CrewBase(cls: T) -> T: } def _filter_functions( - self, functions: Dict[str, Callable], attribute: str - ) -> Dict[str, Callable]: + self, functions: dict[str, Callable], attribute: str + ) -> dict[str, Callable]: return { name: func for name, func in functions.items() @@ -184,11 +190,11 @@ def CrewBase(cls: T) -> T: def _map_agent_variables( self, agent_name: str, - agent_info: Dict[str, Any], - llms: Dict[str, Callable], - tool_functions: Dict[str, Callable], - cache_handler_functions: Dict[str, Callable], - callbacks: Dict[str, Callable], + agent_info: dict[str, Any], + llms: dict[str, Callable], + tool_functions: dict[str, Callable], + cache_handler_functions: dict[str, Callable], + callbacks: dict[str, Callable], ) -> None: if llm := agent_info.get("llm"): try: @@ -245,13 +251,13 @@ def CrewBase(cls: T) -> T: def _map_task_variables( self, task_name: str, - task_info: Dict[str, Any], - agents: Dict[str, Callable], - tasks: Dict[str, Callable], - output_json_functions: Dict[str, Callable], - tool_functions: Dict[str, Callable], - callback_functions: Dict[str, Callable], - output_pydantic_functions: Dict[str, Callable], + task_info: dict[str, Any], + agents: dict[str, Callable], + tasks: dict[str, Callable], + output_json_functions: dict[str, Callable], + tool_functions: dict[str, Callable], + callback_functions: dict[str, Callable], + output_pydantic_functions: dict[str, Callable], ) -> None: if context_list := task_info.get("context"): self.tasks_config[task_name]["context"] = [ diff --git a/tests/test_project.py b/tests/test_project.py index 70b69bb8a..dc615da3b 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -1,5 +1,6 @@ -from typing import List +from typing import Any, ClassVar from unittest.mock import Mock, patch + import pytest from crewai.agent import Agent @@ -44,8 +45,8 @@ class InternalCrew: agents_config = "config/agents.yaml" tasks_config = "config/tasks.yaml" - agents: List[BaseAgent] - tasks: List[Task] + agents: list[BaseAgent] + tasks: list[Task] @llm def local_llm(self): @@ -89,7 +90,8 @@ class InternalCrew: @CrewBase class InternalCrewWithMCP(InternalCrew): - mcp_server_params = {"host": "localhost", "port": 8000} + mcp_server_params: ClassVar[dict[str, Any]] = {"host": "localhost", "port": 8000} + mcp_connect_timeout = 120 @agent def reporting_analyst(self): @@ -200,8 +202,8 @@ def test_before_kickoff_with_none_input(): def test_multiple_before_after_kickoff(): @CrewBase class MultipleHooksCrew: - agents: List[BaseAgent] - tasks: List[Task] + agents: list[BaseAgent] + tasks: list[Task] agents_config = "config/agents.yaml" tasks_config = "config/tasks.yaml" @@ -284,4 +286,7 @@ def test_internal_crew_with_mcp(): assert crew.reporting_analyst().tools == [simple_tool, another_simple_tool] assert crew.researcher().tools == [simple_tool] - adapter_mock.assert_called_once_with({"host": "localhost", "port": 8000}) + adapter_mock.assert_called_once_with( + {"host": "localhost", "port": 8000}, + connect_timeout=120 + )