Skip to content

Commit cf3d0aa

Browse files
committed
Add support for MCP tools to CrewAI adapter
1 parent 38c62ae commit cf3d0aa

File tree

10 files changed

+1071
-34
lines changed

10 files changed

+1071
-34
lines changed

pyagentspec/requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ sphinx_toolbox<=3.8.0
3838

3939
# For remote tool tests
4040
fastapi[standard-no-fastapi-cloud-cli]
41+
cryptography

pyagentspec/src/pyagentspec/adapters/crewai/_agentspecconverter.py

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525
from pyagentspec.adapters.crewai._types import (
2626
CrewAIAgent,
2727
CrewAIBaseTool,
28+
CrewAIHTTPTransport,
2829
CrewAILlm,
30+
CrewAIMCPClient,
31+
CrewAIMCPNativeTool,
32+
CrewAISSETransport,
33+
CrewAIStdioTransport,
2934
CrewAIStructuredTool,
3035
CrewAITool,
3136
)
@@ -39,6 +44,13 @@
3944
)
4045
from pyagentspec.llms.openaiconfig import OpenAiConfig as AgentSpecOpenAiConfig
4146
from pyagentspec.llms.vllmconfig import VllmConfig as AgentSpecVllmModel
47+
from pyagentspec.mcp.clienttransport import ClientTransport as AgentSpecClientTransport
48+
from pyagentspec.mcp.clienttransport import SSETransport as AgentSpecSSETransport
49+
from pyagentspec.mcp.clienttransport import StdioTransport as AgentSpecStdioTransport
50+
from pyagentspec.mcp.clienttransport import (
51+
StreamableHTTPTransport as AgentSpecStreamableHTTPTransport,
52+
)
53+
from pyagentspec.mcp.tools import MCPTool as AgentSpecMCPTool
4254
from pyagentspec.property import Property as AgentSpecProperty
4355
from pyagentspec.tools import ServerTool as AgentSpecServerTool
4456
from pyagentspec.tools import Tool as AgentSpecTool
@@ -119,6 +131,10 @@ def convert(
119131
agentspec_component = self._agent_convert_to_agentspec(
120132
crewai_component, referenced_objects
121133
)
134+
elif isinstance(crewai_component, CrewAIMCPClient):
135+
agentspec_component = self._mcp_client_convert_to_agentspec(
136+
crewai_component, referenced_objects
137+
)
122138
elif isinstance(crewai_component, CrewAIBaseTool):
123139
agentspec_component = self._tool_convert_to_agentspec(
124140
crewai_component, referenced_objects
@@ -175,6 +191,35 @@ def _llm_convert_to_agentspec(
175191

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

194+
def _mcp_client_convert_to_agentspec(
195+
self, crewai_mcp_client: CrewAIMCPClient, referenced_objects: Dict[str, Any]
196+
) -> AgentSpecClientTransport:
197+
crewai_transport = crewai_mcp_client.transport
198+
server_name, server_url, _ = crewai_mcp_client._get_server_info()
199+
if isinstance(crewai_transport, CrewAIStdioTransport):
200+
return AgentSpecStdioTransport(
201+
name=server_name,
202+
command=crewai_transport.command,
203+
args=crewai_transport.args,
204+
env=crewai_transport.env,
205+
)
206+
elif isinstance(crewai_transport, CrewAIHTTPTransport):
207+
return AgentSpecStreamableHTTPTransport(
208+
name=server_name,
209+
url=server_url or "",
210+
headers=crewai_transport.headers,
211+
)
212+
elif isinstance(crewai_transport, CrewAISSETransport):
213+
return AgentSpecSSETransport(
214+
name=server_name,
215+
url=server_url or "",
216+
headers=crewai_transport.headers,
217+
)
218+
219+
raise ValueError(
220+
f"Transports of type {type(crewai_transport)} are not yet supported for translation to AgentSpec"
221+
)
222+
178223
def _tool_convert_to_agentspec(
179224
self, crewai_tool: CrewAIBaseTool, referenced_objects: Dict[str, Any]
180225
) -> AgentSpecTool:
@@ -189,17 +234,35 @@ def _tool_convert_to_agentspec(
189234
output_json_schema = _get_return_type_json_schema_from_function_reference(
190235
crewai_tool._run
191236
)
192-
# There seem to be no counterparts for client tools and remote tools in CrewAI at the moment
193-
return AgentSpecServerTool(
194-
name=crewai_tool.name,
195-
description=crewai_tool.description,
196-
inputs=_pydantic_model_to_properties_list(crewai_tool.args_schema),
197-
outputs=[AgentSpecProperty(title="result", json_schema=output_json_schema)],
198-
)
237+
if isinstance(crewai_tool, CrewAIMCPNativeTool):
238+
return AgentSpecMCPTool(
239+
name=crewai_tool.original_tool_name,
240+
description=crewai_tool.description.split("Tool Description: ")[1],
241+
inputs=_pydantic_model_to_properties_list(crewai_tool.args_schema),
242+
outputs=[AgentSpecProperty(title="result", json_schema=output_json_schema)],
243+
client_transport=cast(
244+
AgentSpecClientTransport,
245+
self.convert(
246+
crewai_tool.mcp_client,
247+
referenced_objects=referenced_objects,
248+
),
249+
),
250+
)
251+
else:
252+
# There seem to be no counterparts for client tools and remote tools in CrewAI at the moment
253+
return AgentSpecServerTool(
254+
name=crewai_tool.name,
255+
description=crewai_tool.description,
256+
inputs=_pydantic_model_to_properties_list(crewai_tool.args_schema),
257+
outputs=[AgentSpecProperty(title="result", json_schema=output_json_schema)],
258+
)
199259

200260
def _agent_convert_to_agentspec(
201261
self, crewai_agent: CrewAIAgent, referenced_objects: Dict[str, Any]
202262
) -> AgentSpecAgent:
263+
tools = crewai_agent.tools or []
264+
if crewai_agent.mcps:
265+
tools += crewai_agent.get_mcp_tools(crewai_agent.mcps)
203266
return AgentSpecAgent(
204267
id=str(crewai_agent.id),
205268
name=crewai_agent.role,
@@ -214,6 +277,6 @@ def _agent_convert_to_agentspec(
214277
),
215278
tools=[
216279
cast(AgentSpecTool, self.convert(tool, referenced_objects=referenced_objects))
217-
for tool in (crewai_agent.tools or [])
280+
for tool in tools
218281
],
219282
)

pyagentspec/src/pyagentspec/adapters/crewai/_crewaiconverter.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,15 @@
1313
from pyagentspec.adapters.crewai._types import (
1414
CrewAIAgent,
1515
CrewAIBaseTool,
16+
CrewAIHTTPTransport,
1617
CrewAILlm,
18+
CrewAIMCPClient,
19+
CrewAIMCPNativeTool,
1720
CrewAIServerToolType,
21+
CrewAISSETransport,
22+
CrewAIStdioTransport,
1823
CrewAITool,
24+
CrewAITransport,
1925
)
2026
from pyagentspec.agent import Agent as AgentSpecAgent
2127
from pyagentspec.component import Component as AgentSpecComponent
@@ -26,6 +32,14 @@
2632
)
2733
from pyagentspec.llms.openaiconfig import OpenAiConfig as AgentSpecOpenAiConfig
2834
from pyagentspec.llms.vllmconfig import VllmConfig as AgentSpecVllmModel
35+
from pyagentspec.mcp.clienttransport import ClientTransport as AgentSpecClientTransport
36+
from pyagentspec.mcp.clienttransport import SSETransport as AgentSpecSSETransport
37+
from pyagentspec.mcp.clienttransport import StdioTransport as AgentSpecStdioTransport
38+
from pyagentspec.mcp.clienttransport import (
39+
StreamableHTTPTransport as AgentSpecStreamableHTTPTransport,
40+
)
41+
from pyagentspec.mcp.tools import MCPTool as AgentSpecMCPTool
42+
from pyagentspec.mcp.tools import MCPToolBox as AgentSpecMCPToolBox
2943
from pyagentspec.property import Property as AgentSpecProperty
3044
from pyagentspec.property import _empty_default as _agentspec_empty_default
3145
from pyagentspec.tools import Tool as AgentSpecTool
@@ -108,6 +122,10 @@ def convert(
108122
crewai_component = self._tool_convert_to_crewai(
109123
agentspec_component, tool_registry, converted_components
110124
)
125+
elif isinstance(agentspec_component, AgentSpecClientTransport):
126+
crewai_component = self._client_transport_convert_to_crewai(
127+
agentspec_component, tool_registry, converted_components
128+
)
111129
elif isinstance(agentspec_component, AgentSpecComponent):
112130
raise NotImplementedError(
113131
f"The AgentSpec Component type '{agentspec_component.__class__.__name__}' is not yet supported "
@@ -212,6 +230,14 @@ def client_tool(**kwargs: Any) -> Any:
212230
)
213231
elif isinstance(agentspec_tool, AgentSpecRemoteTool):
214232
return self._remote_tool_convert_to_crewai(agentspec_tool)
233+
elif isinstance(agentspec_tool, AgentSpecMCPTool):
234+
return self._mcp_tool_convert_to_crewai(
235+
agentspec_tool, tool_registry, converted_components
236+
)
237+
elif isinstance(agentspec_tool, AgentSpecMCPToolBox):
238+
raise NotImplementedError(
239+
"Conversion of AgentSpec MCPToolBox objects is not yet implemented"
240+
)
215241
raise ValueError(
216242
f"Tools of type {type(agentspec_tool)} are not yet supported for translation to CrewAI"
217243
)
@@ -246,6 +272,57 @@ def _remote_tool(**kwargs: Any) -> Any:
246272
func=_remote_tool,
247273
)
248274

275+
def _mcp_tool_convert_to_crewai(
276+
self,
277+
mcp_tool: AgentSpecMCPTool,
278+
tool_registry: Dict[str, CrewAIServerToolType],
279+
converted_components: Optional[Dict[str, Any]] = None,
280+
) -> CrewAIMCPNativeTool:
281+
return CrewAIMCPNativeTool(
282+
mcp_client=self.convert(mcp_tool.client_transport, tool_registry, converted_components),
283+
tool_name=mcp_tool.name,
284+
tool_schema={
285+
"description": mcp_tool.description or "",
286+
"args_schema": _create_pydantic_model_from_properties(
287+
mcp_tool.name.title() + "InputSchema", mcp_tool.inputs or []
288+
),
289+
},
290+
server_name=mcp_tool.client_transport.name,
291+
)
292+
293+
def _client_transport_convert_to_crewai(
294+
self,
295+
agentspec_transport: AgentSpecClientTransport,
296+
tool_registry: Dict[str, CrewAIServerToolType],
297+
converted_components: Optional[Dict[str, Any]] = None,
298+
) -> CrewAIMCPClient:
299+
transport: Optional[CrewAITransport] = None
300+
if isinstance(agentspec_transport, AgentSpecStdioTransport):
301+
transport = CrewAIStdioTransport(
302+
command=agentspec_transport.command,
303+
args=agentspec_transport.args,
304+
env=agentspec_transport.env,
305+
)
306+
elif isinstance(agentspec_transport, AgentSpecSSETransport):
307+
transport = CrewAISSETransport(
308+
url=agentspec_transport.url,
309+
headers=agentspec_transport.headers,
310+
)
311+
elif isinstance(agentspec_transport, AgentSpecStreamableHTTPTransport):
312+
transport = CrewAIHTTPTransport(
313+
url=agentspec_transport.url,
314+
headers=agentspec_transport.headers,
315+
streamable=True,
316+
)
317+
else:
318+
raise ValueError(
319+
f"Transports of type {type(agentspec_transport)} are not yet supported for translation to CrewAI"
320+
)
321+
return CrewAIMCPClient(
322+
transport=transport,
323+
cache_tools_list=True,
324+
)
325+
249326
def _agent_convert_to_crewai(
250327
self,
251328
agentspec_agent: AgentSpecAgent,

pyagentspec/src/pyagentspec/adapters/crewai/_types.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,29 @@
1616
from crewai import LLM as CrewAILlm
1717
from crewai import Agent as CrewAIAgent
1818
from crewai import Flow as CrewAIFlow
19+
from crewai.mcp.client import MCPClient as CrewAIMCPClient
20+
from crewai.mcp.transports import BaseTransport as CrewAITransport
21+
from crewai.mcp.transports import HTTPTransport as CrewAIHTTPTransport
22+
from crewai.mcp.transports import SSETransport as CrewAISSETransport
23+
from crewai.mcp.transports import StdioTransport as CrewAIStdioTransport
1924
from crewai.tools import BaseTool as CrewAIBaseTool
2025
from crewai.tools.base_tool import Tool as CrewAITool
26+
from crewai.tools.mcp_native_tool import MCPNativeTool as CrewAIMCPNativeTool
2127
from crewai.tools.structured_tool import CrewStructuredTool as CrewAIStructuredTool
2228
else:
2329
crewai = LazyLoader("crewai")
2430
# We need to import the classes this way because it's the only one accepted by the lazy loader
2531
CrewAILlm = crewai.LLM
2632
CrewAIAgent = crewai.Agent
2733
CrewAIFlow = crewai.Flow
34+
CrewAIMCPClient = crewai.mcp.client.MCPClient
35+
CrewAITransport = crewai.mcp.transports.BaseTransport
36+
CrewAIHTTPTransport = crewai.mcp.transports.HTTPTransport
37+
CrewAISSETransport = crewai.mcp.transports.SSETransport
38+
CrewAIStdioTransport = crewai.mcp.transports.StdioTransport
2839
CrewAIBaseTool = LazyLoader("crewai.tools").BaseTool
2940
CrewAITool = LazyLoader("crewai.tools.base_tool").Tool
41+
CrewAIMCPNativeTool = LazyLoader("crewai.tools.mcp_native_tool").MCPNativeTool
3042
CrewAIStructuredTool = LazyLoader("crewai.tools.structured_tool").CrewStructuredTool
3143

3244
CrewAIComponent = Union[CrewAIAgent, CrewAIFlow[Any]]
@@ -36,9 +48,15 @@
3648
"CrewAILlm",
3749
"CrewAIAgent",
3850
"CrewAIFlow",
51+
"CrewAIMCPClient",
52+
"CrewAITransport",
53+
"CrewAIHTTPTransport",
54+
"CrewAISSETransport",
55+
"CrewAIStdioTransport",
3956
"CrewAIBaseTool",
4057
"CrewAITool",
4158
"CrewAIStructuredTool",
4259
"CrewAIComponent",
60+
"CrewAIMCPNativeTool",
4361
"CrewAIServerToolType",
4462
]

0 commit comments

Comments
 (0)