Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyagentspec/constraints/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ httpx==0.28.1

# CrewAI adapter
crewai==1.6.1
mcp==1.22.0

# AutoGen adapter
autogen-core==0.7.4
Expand Down
1 change: 1 addition & 0 deletions pyagentspec/requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ sphinx_design==0.6.1

# For remote tool tests
fastapi[standard-no-fastapi-cloud-cli]
cryptography
79 changes: 71 additions & 8 deletions pyagentspec/src/pyagentspec/adapters/crewai/_agentspecconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@
from pyagentspec.adapters.crewai._types import (
CrewAIAgent,
CrewAIBaseTool,
CrewAIHTTPTransport,
CrewAILlm,
CrewAIMCPClient,
CrewAIMCPNativeTool,
CrewAISSETransport,
CrewAIStdioTransport,
CrewAIStructuredTool,
CrewAITool,
)
Expand All @@ -39,6 +44,13 @@
)
from pyagentspec.llms.openaiconfig import OpenAiConfig as AgentSpecOpenAiConfig
from pyagentspec.llms.vllmconfig import VllmConfig as AgentSpecVllmModel
from pyagentspec.mcp.clienttransport import ClientTransport as AgentSpecClientTransport
from pyagentspec.mcp.clienttransport import SSETransport as AgentSpecSSETransport
from pyagentspec.mcp.clienttransport import StdioTransport as AgentSpecStdioTransport
from pyagentspec.mcp.clienttransport import (
StreamableHTTPTransport as AgentSpecStreamableHTTPTransport,
)
from pyagentspec.mcp.tools import MCPTool as AgentSpecMCPTool
from pyagentspec.property import Property as AgentSpecProperty
from pyagentspec.tools import ServerTool as AgentSpecServerTool
from pyagentspec.tools import Tool as AgentSpecTool
Expand Down Expand Up @@ -119,6 +131,10 @@ def convert(
agentspec_component = self._agent_convert_to_agentspec(
crewai_component, referenced_objects
)
elif isinstance(crewai_component, CrewAIMCPClient):
agentspec_component = self._mcp_client_convert_to_agentspec(
crewai_component, referenced_objects
)
elif isinstance(crewai_component, CrewAIBaseTool):
agentspec_component = self._tool_convert_to_agentspec(
crewai_component, referenced_objects
Expand Down Expand Up @@ -175,6 +191,35 @@ def _llm_convert_to_agentspec(

raise ValueError(f"Unsupported type of LLM in Agent Spec: {model_provider}")

def _mcp_client_convert_to_agentspec(
self, crewai_mcp_client: CrewAIMCPClient, referenced_objects: Dict[str, Any]
) -> AgentSpecClientTransport:
crewai_transport = crewai_mcp_client.transport
server_name, server_url, _ = crewai_mcp_client._get_server_info()
if isinstance(crewai_transport, CrewAIStdioTransport):
return AgentSpecStdioTransport(
name=server_name,
command=crewai_transport.command,
args=crewai_transport.args,
env=crewai_transport.env,
)
elif isinstance(crewai_transport, CrewAIHTTPTransport):
return AgentSpecStreamableHTTPTransport(
name=server_name,
url=server_url or "",
headers=crewai_transport.headers,
)
elif isinstance(crewai_transport, CrewAISSETransport):
return AgentSpecSSETransport(
name=server_name,
url=server_url or "",
headers=crewai_transport.headers,
)

raise ValueError(
f"Transports of type {type(crewai_transport)} are not yet supported for translation to AgentSpec"
)

def _tool_convert_to_agentspec(
self, crewai_tool: CrewAIBaseTool, referenced_objects: Dict[str, Any]
) -> AgentSpecTool:
Expand All @@ -189,17 +234,35 @@ def _tool_convert_to_agentspec(
output_json_schema = _get_return_type_json_schema_from_function_reference(
crewai_tool._run
)
# There seem to be no counterparts for client tools and remote tools in CrewAI at the moment
return AgentSpecServerTool(
name=crewai_tool.name,
description=crewai_tool.description,
inputs=_pydantic_model_to_properties_list(crewai_tool.args_schema),
outputs=[AgentSpecProperty(title="result", json_schema=output_json_schema)],
)
if isinstance(crewai_tool, CrewAIMCPNativeTool):
return AgentSpecMCPTool(
name=crewai_tool.original_tool_name,
description=crewai_tool.description.split("Tool Description: ")[1],
inputs=_pydantic_model_to_properties_list(crewai_tool.args_schema),
outputs=[AgentSpecProperty(title="result", json_schema=output_json_schema)],
client_transport=cast(
AgentSpecClientTransport,
self.convert(
crewai_tool.mcp_client,
referenced_objects=referenced_objects,
),
),
)
else:
# There seem to be no counterparts for client tools and remote tools in CrewAI at the moment
return AgentSpecServerTool(
name=crewai_tool.name,
description=crewai_tool.description,
inputs=_pydantic_model_to_properties_list(crewai_tool.args_schema),
outputs=[AgentSpecProperty(title="result", json_schema=output_json_schema)],
)

def _agent_convert_to_agentspec(
self, crewai_agent: CrewAIAgent, referenced_objects: Dict[str, Any]
) -> AgentSpecAgent:
tools = crewai_agent.tools or []
if crewai_agent.mcps:
tools += crewai_agent.get_mcp_tools(crewai_agent.mcps)
return AgentSpecAgent(
id=str(crewai_agent.id),
name=crewai_agent.role,
Expand All @@ -214,6 +277,6 @@ def _agent_convert_to_agentspec(
),
tools=[
cast(AgentSpecTool, self.convert(tool, referenced_objects=referenced_objects))
for tool in (crewai_agent.tools or [])
for tool in tools
],
)
77 changes: 77 additions & 0 deletions pyagentspec/src/pyagentspec/adapters/crewai/_crewaiconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@
from pyagentspec.adapters.crewai._types import (
CrewAIAgent,
CrewAIBaseTool,
CrewAIHTTPTransport,
CrewAILlm,
CrewAIMCPClient,
CrewAIMCPNativeTool,
CrewAIServerToolType,
CrewAISSETransport,
CrewAIStdioTransport,
CrewAITool,
CrewAITransport,
)
from pyagentspec.adapters.crewai.tracing import CrewAIAgentWithTracing
from pyagentspec.agent import Agent as AgentSpecAgent
Expand All @@ -27,6 +33,14 @@
)
from pyagentspec.llms.openaiconfig import OpenAiConfig as AgentSpecOpenAiConfig
from pyagentspec.llms.vllmconfig import VllmConfig as AgentSpecVllmModel
from pyagentspec.mcp.clienttransport import ClientTransport as AgentSpecClientTransport
from pyagentspec.mcp.clienttransport import SSETransport as AgentSpecSSETransport
from pyagentspec.mcp.clienttransport import StdioTransport as AgentSpecStdioTransport
from pyagentspec.mcp.clienttransport import (
StreamableHTTPTransport as AgentSpecStreamableHTTPTransport,
)
from pyagentspec.mcp.tools import MCPTool as AgentSpecMCPTool
from pyagentspec.mcp.tools import MCPToolBox as AgentSpecMCPToolBox
from pyagentspec.property import Property as AgentSpecProperty
from pyagentspec.property import _empty_default as _agentspec_empty_default
from pyagentspec.tools import Tool as AgentSpecTool
Expand Down Expand Up @@ -119,6 +133,10 @@ def convert(
crewai_component = self._tool_convert_to_crewai(
agentspec_component, tool_registry, converted_components
)
elif isinstance(agentspec_component, AgentSpecClientTransport):
crewai_component = self._client_transport_convert_to_crewai(
agentspec_component, tool_registry, converted_components
)
elif isinstance(agentspec_component, AgentSpecComponent):
raise NotImplementedError(
f"The AgentSpec Component type '{agentspec_component.__class__.__name__}' is not yet supported "
Expand Down Expand Up @@ -243,6 +261,14 @@ def client_tool(**kwargs: Any) -> Any:
)
elif isinstance(agentspec_tool, AgentSpecRemoteTool):
return self._remote_tool_convert_to_crewai(agentspec_tool)
elif isinstance(agentspec_tool, AgentSpecMCPTool):
return self._mcp_tool_convert_to_crewai(
agentspec_tool, tool_registry, converted_components
)
elif isinstance(agentspec_tool, AgentSpecMCPToolBox):
raise NotImplementedError(
"Conversion of AgentSpec MCPToolBox objects is not yet implemented"
)
raise ValueError(
f"Tools of type {type(agentspec_tool)} are not yet supported for translation to CrewAI"
)
Expand Down Expand Up @@ -277,6 +303,57 @@ def _remote_tool(**kwargs: Any) -> Any:
func=_remote_tool,
)

def _mcp_tool_convert_to_crewai(
self,
mcp_tool: AgentSpecMCPTool,
tool_registry: Dict[str, CrewAIServerToolType],
converted_components: Optional[Dict[str, Any]] = None,
) -> CrewAIMCPNativeTool:
return CrewAIMCPNativeTool(
mcp_client=self.convert(mcp_tool.client_transport, tool_registry, converted_components),
tool_name=mcp_tool.name,
tool_schema={
"description": mcp_tool.description or "",
"args_schema": _create_pydantic_model_from_properties(
mcp_tool.name.title() + "InputSchema", mcp_tool.inputs or []
),
},
server_name=mcp_tool.client_transport.name,
)

def _client_transport_convert_to_crewai(
self,
agentspec_transport: AgentSpecClientTransport,
tool_registry: Dict[str, CrewAIServerToolType],
converted_components: Optional[Dict[str, Any]] = None,
) -> CrewAIMCPClient:
transport: Optional[CrewAITransport] = None
if isinstance(agentspec_transport, AgentSpecStdioTransport):
transport = CrewAIStdioTransport(
command=agentspec_transport.command,
args=agentspec_transport.args,
env=agentspec_transport.env,
)
elif isinstance(agentspec_transport, AgentSpecSSETransport):
transport = CrewAISSETransport(
url=agentspec_transport.url,
headers=agentspec_transport.headers,
)
elif isinstance(agentspec_transport, AgentSpecStreamableHTTPTransport):
transport = CrewAIHTTPTransport(
url=agentspec_transport.url,
headers=agentspec_transport.headers,
streamable=True,
)
else:
raise ValueError(
f"Transports of type {type(agentspec_transport)} are not yet supported for translation to CrewAI"
)
return CrewAIMCPClient(
transport=transport,
cache_tools_list=True,
)

def _agent_convert_to_crewai(
self,
agentspec_agent: AgentSpecAgent,
Expand Down
18 changes: 18 additions & 0 deletions pyagentspec/src/pyagentspec/adapters/crewai/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,29 @@
from crewai.events.types.tool_usage_events import (
ToolUsageStartedEvent as CrewAIToolUsageStartedEvent,
)
from crewai.mcp.client import MCPClient as CrewAIMCPClient
from crewai.mcp.transports import BaseTransport as CrewAITransport
from crewai.mcp.transports import HTTPTransport as CrewAIHTTPTransport
from crewai.mcp.transports import SSETransport as CrewAISSETransport
from crewai.mcp.transports import StdioTransport as CrewAIStdioTransport
from crewai.tools import BaseTool as CrewAIBaseTool
from crewai.tools.base_tool import Tool as CrewAITool
from crewai.tools.mcp_native_tool import MCPNativeTool as CrewAIMCPNativeTool
from crewai.tools.structured_tool import CrewStructuredTool as CrewAIStructuredTool
else:
crewai = LazyLoader("crewai")
# We need to import the classes this way because it's the only one accepted by the lazy loader
CrewAILlm = crewai.LLM
CrewAIAgent = crewai.Agent
CrewAIFlow = crewai.Flow
CrewAIMCPClient = crewai.mcp.client.MCPClient
CrewAITransport = crewai.mcp.transports.BaseTransport
CrewAIHTTPTransport = crewai.mcp.transports.HTTPTransport
CrewAISSETransport = crewai.mcp.transports.SSETransport
CrewAIStdioTransport = crewai.mcp.transports.StdioTransport
CrewAIBaseTool = LazyLoader("crewai.tools").BaseTool
CrewAITool = LazyLoader("crewai.tools.base_tool").Tool
CrewAIMCPNativeTool = LazyLoader("crewai.tools.mcp_native_tool").MCPNativeTool
CrewAIStructuredTool = LazyLoader("crewai.tools.structured_tool").CrewStructuredTool
CrewAIBaseEventListener = LazyLoader("crewai.events.base_event_listener").BaseEventListener
CrewAIEventsBus = LazyLoader("crewai.events.event_bus").CrewAIEventsBus
Expand Down Expand Up @@ -88,10 +100,16 @@
"CrewAILlm",
"CrewAIAgent",
"CrewAIFlow",
"CrewAIMCPClient",
"CrewAITransport",
"CrewAIHTTPTransport",
"CrewAISSETransport",
"CrewAIStdioTransport",
"CrewAIBaseTool",
"CrewAITool",
"CrewAIStructuredTool",
"CrewAIComponent",
"CrewAIMCPNativeTool",
"CrewAIServerToolType",
"CrewAIBaseEvent",
"CrewAIBaseEventListener",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ def to_component(self, crewai_component: CrewAIComponent) -> Component:
"""
if not isinstance(crewai_component, (CrewAIAgent, CrewAIFlow)):
raise TypeError(
f"Expected an Agent of Flow, but got '{type(crewai_component)}' instead"
f"Expected an Agent or Flow, but got '{type(crewai_component)}' instead"
)
return CrewAIToAgentSpecConverter().convert(crewai_component)
Loading