diff --git a/attached_assets/Pasted-Below-is-a-surgical-diff-style-checklist-that-shows-exactly-what-you-have-to-change-and-what--1745137551734.txt b/attached_assets/Pasted-Below-is-a-surgical-diff-style-checklist-that-shows-exactly-what-you-have-to-change-and-what--1745137551734.txt new file mode 100644 index 00000000..21972ddb --- /dev/null +++ b/attached_assets/Pasted-Below-is-a-surgical-diff-style-checklist-that-shows-exactly-what-you-have-to-change-and-what--1745137551734.txt @@ -0,0 +1,189 @@ +Below is a **surgical “diff‑style” checklist** that shows exactly what you have to change (and what you can delete outright) to migrate your current `Agent`/`mcp_integration` pair from **JSON‑function‑calling → FastMCP**. + +I kept it to the two files you pasted, so you can copy‑paste or cherry‑pick with your IDE’s patch tool. + +--- + +## 0. New dependency + +```bash +pip install fastmcp # tiny async wrapper around mcp.ClientSession in “fast” mode +``` + +--- + +## 1. `swarms/tools/mcp_integration.py` + +### 1.1 Imports + +```diff +-from mcp import ( +- ClientSession, +- StdioServerParameters, +- Tool as MCPTool, +- stdio_client, +-) ++# fastmcp gives us a drop‑in “FastClientSession” that sets the right SSE headers ++from fastmcp import FastClientSession as ClientSession ++from fastmcp.servers import fast_sse_client as sse_client # replaces std one +``` + +*(Keep the rest of the imports as‑is; they still compile.)* + +### 1.2 Replace the SSE transport factory + +FastMCP re‑uses the same SSE wire format but forces `FAST_MCP_MODE=1` headers and keeps the connection hot. + +```diff +- def create_streams(self, ...) -> AbstractAsyncContextManager[...]: +- return sse_client( +- url=self.params["url"], +- headers=self.params.get("headers", None), +- timeout=self.params.get("timeout", 5), +- sse_read_timeout=self.params.get("sse_read_timeout", 60*5), +- ) ++ def create_streams(self, ...) -> AbstractAsyncContextManager[...]: ++ return sse_client( # NOTE: imported from fastmcp.servers above ++ url=self.params["url"], ++ headers=self.params.get("headers", None), ++ timeout=self.params.get("timeout", 5), ++ sse_read_timeout=self.params.get("sse_read_timeout", 60 * 5), ++ ) +``` + +### 1.3 Add **fast** helper for a single call (optional) + +```python +async def call_tool_fast(server: MCPServerSse, payload: dict[str, Any]): + """ + Convenience wrapper that opens → calls → closes in one shot. + """ + await server.connect() + result = await server.call_tool(payload) + await server.cleanup() + return result.model_dump() if hasattr(result, "model_dump") else result +``` + +--- + +## 2. `swarms/structs/agent.py` + +### 2.1 accept `mcp_servers` parameter (you commented it out) + +```diff +- tools_list_dictionary: Optional[List[Dict[str, Any]]] = None, +- # mcp_servers: List[MCPServerSseParams] = [], ++ tools_list_dictionary: Optional[List[Dict[str, Any]]] = None, ++ mcp_servers: Optional[List[Dict[str, Any]]] = None, # NEW +``` + +and save it: + +```diff + self.tools_list_dictionary = tools_list_dictionary ++# FastMCP ++self.mcp_servers = mcp_servers or [] +``` + +### 2.2 Drop `parse_and_execute_json` branch and replace with FastMCP + +Inside `_run()` where you currently have: + +```python +if self.tools is not None or hasattr(self, 'mcp_servers'): + ... +``` + +replace everything in that `if` block with: + +```diff +-if self.tools is not None or hasattr(self, 'mcp_servers'): +- if self.tools: +- out = self.parse_and_execute_tools(response) +- if hasattr(self, 'mcp_servers') and self.mcp_servers: +- out = self.mcp_execution_flow(response) +- +- self.short_memory.add(role="Tool Executor", content=out) +- ... ++if self.mcp_servers: # ـ فقط FastMCP path ++ # Response from the model **will be** JSONRPC already. Convert str → dict ++ try: ++ fn_call = json.loads(response) if isinstance(response, str) else response ++ except Exception: ++ # Not a tool‑call, skip. ++ fn_call = None ++ ++ if fn_call and isinstance(fn_call, dict): ++ # round‑robin – you can pick a smarter load‑balancer later ++ target = random.choice(self.mcp_servers) ++ out = mcp_flow(target, fn_call) # <- from mcp_integration.py ++ ++ self.short_memory.add(role="Tool", content=out) ++ agent_print(f"{self.agent_name} – tool result", out, loop_count, self.streaming_on) ++ ++ # Let the model reflect on the tool’s answer ++ follow_up = self.llm.run(out) ++ self.short_memory.add(role=self.agent_name, content=follow_up) +``` + +### 2.3 Delete **parse_and_execute_tools** helper altogether +If nothing else in your codebase uses it, just remove the whole method to avoid dead weight. + +### 2.4 Optional: preload tool schemas into the model (good prompt hygiene) + +At the end of `init_handling()` add: + +```python +# Advertise remote tools to the model (tool descriptions feed) +if self.mcp_servers: + try: + first = self.mcp_servers[0] + schema_txt = any_to_str(mcp_flow_get_tool_schema(first)) + self.short_memory.add(role="system", content=f"REMOTE_TOOLS:\n{schema_txt}") + except Exception as e: + logger.warning(f"Could not fetch tool schema: {e}") +``` + +--- + +## 3. Quick smoke test + +```python +from swarms.structs.agent import Agent + +FLOWISE = {"url": "https://mcp.flowise.ai"} # no auth for public demo +bot = Agent( + agent_name="fastmcp-demo", + model_name="gpt-4o-mini", + streaming_on=True, + mcp_servers=[FLOWISE], # <- the only change you really need +) + +print( + bot("Use `serp.search` to fetch today’s ETH price and summarise in one sentence") +) +``` + +You should see: + +1. LLM emits a `call_tool` JSON. +2. Agent relays it to Flowise server via FastMCP. +3. Response streams back; LLM reasons on it; final answer printed. + +--- + +### What we just *removed* + +* `parse_and_execute_json` +* `tool_choice`, `function_calling_format_type`, etc. (they’re harmless but unused) +* Manual “function‑calling” retries. + +### What we just *added* + +* `fastmcp` dependency + a **single** SSE connection that stays alive for the whole agent run. + +--- + +That’s it! +Apply the diff, run the smoke test, and you’re on FastMCP. +If you bump into a specific traceback, paste it and we’ll debug the next inch. Happy hacking 🚀 \ No newline at end of file