Fix multi MCP execution and add example servers

pull/891/head
Pavan Kumar 1 month ago
parent 5850b75777
commit f9e65bf326

@ -2,8 +2,16 @@
This example demonstrates using a list of MCP servers with an `Agent`. This example demonstrates using a list of MCP servers with an `Agent`.
Start the example servers in separate terminals:
```bash
python examples/tools/mcp_examples/servers/weather_server.py
python examples/tools/mcp_examples/servers/news_server.py
```
```python ```python
import os import os
import json
from swarms import Agent from swarms import Agent
# Configure multiple MCP URLs # Configure multiple MCP URLs
@ -15,11 +23,11 @@ agent = Agent(
max_loops=1, max_loops=1,
) )
# Example payloads produced by your model # Example JSON payloads produced by your model
payloads = [ response = json.dumps([
{"function_name": "get_weather", "server_url": "http://localhost:8000/sse", "payload": {"city": "London"}}, {"function_name": "get_weather", "server_url": "http://localhost:8000/sse", "payload": {"city": "London"}},
{"function_name": "get_news", "server_url": "http://localhost:9001/sse", "payload": {"topic": "ai"}}, {"function_name": "get_news", "server_url": "http://localhost:9001/sse", "payload": {"topic": "ai"}},
] ])
agent.handle_multiple_mcp_tools(agent.mcp_urls, payloads) agent.handle_multiple_mcp_tools(agent.mcp_urls, response)
``` ```

@ -483,8 +483,16 @@ print(agent.to_toml())
Execute tools from multiple MCP servers by providing a list of URLs via the Execute tools from multiple MCP servers by providing a list of URLs via the
`mcp_urls` parameter or the `MCP_URLS` environment variable. `mcp_urls` parameter or the `MCP_URLS` environment variable.
Start the example servers:
```bash
python examples/tools/mcp_examples/servers/weather_server.py
python examples/tools/mcp_examples/servers/news_server.py
```
```python ```python
import os import os
import json
from swarms import Agent from swarms import Agent
# Using an environment variable for server configuration # Using an environment variable for server configuration
@ -497,7 +505,7 @@ agent = Agent(
) )
# Example MCP payloads returned by your model # Example MCP payloads returned by your model
mcp_payloads = [ mcp_response = json.dumps([
{ {
"function_name": "get_price", "function_name": "get_price",
"server_url": "http://localhost:8000/sse", "server_url": "http://localhost:8000/sse",
@ -508,9 +516,9 @@ mcp_payloads = [
"server_url": "http://localhost:9001/sse", "server_url": "http://localhost:9001/sse",
"payload": {"symbol": "BTC"}, "payload": {"symbol": "BTC"},
}, },
] ])
agent.handle_multiple_mcp_tools(agent.mcp_urls, mcp_payloads) agent.handle_multiple_mcp_tools(agent.mcp_urls, mcp_response)
``` ```
## Auto Generate Prompt + CPU Execution ## Auto Generate Prompt + CPU Execution

@ -145,6 +145,13 @@ payload in the following format:
Use `handle_multiple_mcp_tools` to execute each payload across the configured Use `handle_multiple_mcp_tools` to execute each payload across the configured
servers. servers.
Example servers can be started with:
```bash
python examples/tools/mcp_examples/servers/weather_server.py
python examples/tools/mcp_examples/servers/news_server.py
```
--- ---
## Integration Flow ## Integration Flow

@ -0,0 +1,12 @@
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("NewsServer")
mcp.settings.port = 9001
@mcp.tool(name="get_news", description="Return simple news headline")
def get_news(topic: str) -> str:
return f"Latest {topic} news headline"
if __name__ == "__main__":
mcp.run(transport="sse")

@ -0,0 +1,12 @@
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("WeatherServer")
mcp.settings.port = 8000
@mcp.tool(name="get_weather", description="Return simple weather info")
def get_weather(city: str) -> str:
return f"Weather in {city}: Sunny 22°C"
if __name__ == "__main__":
mcp.run(transport="sse")

@ -1,5 +1,6 @@
import asyncio import asyncio
import json import json
import re
import logging import logging
import os import os
import random import random
@ -74,6 +75,7 @@ from swarms.structs.ma_utils import set_random_models_for_agents
from swarms.tools.mcp_client_call import ( from swarms.tools.mcp_client_call import (
execute_tool_call_simple, execute_tool_call_simple,
get_mcp_tools_sync, get_mcp_tools_sync,
execute_mcp_call,
) )
from swarms.schemas.mcp_schemas import ( from swarms.schemas.mcp_schemas import (
MCPConnection, MCPConnection,
@ -98,6 +100,25 @@ def parse_done_token(response: str) -> bool:
return "<DONE>" in response return "<DONE>" in response
def extract_json_from_response(response: str) -> List[Dict[str, Any]]:
"""Extract a JSON list from a model response string."""
if not response:
return []
if not isinstance(response, str):
return response if isinstance(response, list) else []
try:
return json.loads(response)
except json.JSONDecodeError:
match = re.search(r"\[[\s\S]*?\]", response)
if match:
try:
return json.loads(match.group(0))
except json.JSONDecodeError:
pass
logger.error("Failed to parse MCP payloads from response")
return []
# Agent ID generator # Agent ID generator
def agent_id(): def agent_id():
"""Generate an agent id""" """Generate an agent id"""
@ -442,6 +463,8 @@ class Agent:
self.sop = sop self.sop = sop
self.sop_list = sop_list self.sop_list = sop_list
self.tools = tools self.tools = tools
# Ensure tool_struct exists even when no tools are provided
self.tool_struct = {}
self.system_prompt = system_prompt self.system_prompt = system_prompt
self.agent_name = agent_name self.agent_name = agent_name
self.agent_description = agent_description self.agent_description = agent_description
@ -1098,18 +1121,11 @@ class Agent:
if exists(self.mcp_urls): if exists(self.mcp_urls):
try: try:
payload = ( self.handle_multiple_mcp_tools(
json.loads(response) self.mcp_urls,
if isinstance(response, str) response,
else response current_loop=loop_count,
) )
if isinstance(payload, list):
self.handle_multiple_mcp_tools(
self.mcp_urls,
payload,
current_loop=loop_count,
)
except Exception as e: except Exception as e:
logger.error( logger.error(
f"Error handling multiple MCP tools: {e}" f"Error handling multiple MCP tools: {e}"
@ -2820,12 +2836,13 @@ class Agent:
def handle_multiple_mcp_tools( def handle_multiple_mcp_tools(
self, self,
mcp_url_list: List[str], mcp_url_list: List[str],
mcp_payloads: List[Dict[str, Any]], response: Union[str, List[Dict[str, Any]]],
current_loop: int = 0, current_loop: int = 0,
) -> None: ) -> None:
"""Execute a list of MCP tool calls across multiple servers.""" """Execute multiple MCP tool calls across configured servers."""
for payload in mcp_payloads: payloads = extract_json_from_response(response)
for payload in payloads:
function_name = payload.get("function_name") function_name = payload.get("function_name")
server_url = payload.get("server_url") server_url = payload.get("server_url")
arguments = payload.get("payload", {}) arguments = payload.get("payload", {})
@ -2836,25 +2853,31 @@ class Agent:
) )
continue continue
try: attempt = 0
tool_response = asyncio.run( while attempt < 3:
execute_mcp_call( try:
function_name=function_name, tool_response = asyncio.run(
server_url=server_url, execute_mcp_call(
payload=arguments, function_name=function_name,
server_url=server_url,
payload=arguments,
)
) )
) self.short_memory.add(
self.short_memory.add( role="Tool Executor",
role="Tool Executor", content=str(tool_response),
content=str(tool_response), )
) self.pretty_print(
self.pretty_print( str(tool_response),
str(tool_response), loop_count=current_loop loop_count=current_loop,
) )
except Exception as e: # noqa: PERF203 break
logger.error( except Exception as e: # noqa: PERF203
f"Error executing {function_name} on {server_url}: {e}" attempt += 1
) logger.error(
f"Error executing {function_name} on {server_url}: {e}"
)
time.sleep(1)
def temp_llm_instance_for_tool_summary(self): def temp_llm_instance_for_tool_summary(self):
return LiteLLM( return LiteLLM(

@ -1,5 +1,6 @@
import asyncio import asyncio
from swarms.structs.agent import Agent import json
from swarms.structs.agent import Agent, extract_json_from_response
from swarms.structs.agent import execute_mcp_call from swarms.structs.agent import execute_mcp_call
from unittest.mock import patch from unittest.mock import patch
@ -30,9 +31,18 @@ def test_handle_multiple_mcp_tools():
with patch( with patch(
"swarms.structs.agent.execute_mcp_call", side_effect=fake_exec "swarms.structs.agent.execute_mcp_call", side_effect=fake_exec
): ):
agent.handle_multiple_mcp_tools(urls, payloads) agent.handle_multiple_mcp_tools(urls, json.dumps(payloads))
assert called == [ assert called == [
("tool1", "http://server1", {"a": 1}), ("tool1", "http://server1", {"a": 1}),
("tool2", "http://server2", {}), ("tool2", "http://server2", {}),
] ]
def test_extract_json_from_response():
payloads = [
{"function_name": "foo", "server_url": "http://x", "payload": {"x": 1}}
]
text = "Random text" + json.dumps(payloads) + " end"
result = extract_json_from_response(text)
assert result == payloads

Loading…
Cancel
Save